summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/baseruby.yml2
-rw-r--r--.github/workflows/check_dependencies.yml4
-rw-r--r--.github/workflows/check_misc.yml2
-rw-r--r--.github/workflows/codeql-analysis.yml10
-rw-r--r--.github/workflows/compilers.yml151
-rw-r--r--.github/workflows/macos.yml8
-rw-r--r--.github/workflows/mingw.yml22
-rw-r--r--.github/workflows/mjit.yml32
-rw-r--r--.github/workflows/ubuntu.yml10
-rw-r--r--README.ja.md23
-rw-r--r--README.md12
-rw-r--r--aclocal.m448
-rw-r--r--addr2line.c5
-rw-r--r--appveyor.yml20
-rw-r--r--array.c6
-rwxr-xr-xautogen.sh9
-rw-r--r--bootstraptest/test_ractor.rb103
-rw-r--r--class.c51
-rw-r--r--common.mk12
-rw-r--r--compile.c73
-rw-r--r--complex.c12
-rw-r--r--configure.ac321
-rw-r--r--cont.c8
-rw-r--r--debug_counter.h3
-rw-r--r--dir.c2
-rw-r--r--enc/Makefile.in2
-rw-r--r--encoding.c28
-rw-r--r--enum.c4
-rw-r--r--enumerator.c7
-rw-r--r--error.c7
-rw-r--r--eval.c12
-rw-r--r--eval_intern.h11
-rw-r--r--ext/-test-/array/concat/depend322
-rw-r--r--ext/-test-/array/concat/extconf.rb2
-rw-r--r--ext/-test-/array/concat/to_ary_conact.c34
-rw-r--r--ext/-test-/postponed_job/postponed_job.c31
-rw-r--r--ext/-test-/string/depend3
-rw-r--r--ext/-test-/string/enc_str_buf_cat.c14
-rw-r--r--ext/-test-/string/fstring.c15
-rw-r--r--ext/cgi/escape/escape.c5
-rw-r--r--ext/date/date.gemspec7
-rw-r--r--ext/date/date_core.c387
-rw-r--r--ext/date/lib/date.rb1
-rw-r--r--ext/etc/etc.c32
-rw-r--r--ext/etc/extconf.rb5
-rwxr-xr-xext/extmk.rb5
-rw-r--r--ext/fcntl/fcntl.gemspec2
-rw-r--r--ext/fiddle/closure.c26
-rw-r--r--ext/fiddle/conversions.h1
-rw-r--r--ext/fiddle/extconf.rb43
-rw-r--r--ext/fiddle/function.c11
-rw-r--r--ext/fiddle/lib/fiddle.rb12
-rw-r--r--ext/fiddle/lib/fiddle/cparser.rb6
-rw-r--r--ext/fiddle/lib/fiddle/types.rb35
-rw-r--r--ext/fiddle/lib/fiddle/version.rb2
-rw-r--r--ext/io/console/console.c13
-rw-r--r--ext/io/console/io-console.gemspec2
-rw-r--r--ext/io/wait/io-wait.gemspec8
-rw-r--r--ext/io/wait/wait.c40
-rw-r--r--ext/monitor/monitor.c12
-rw-r--r--ext/objspace/object_tracing.c3
-rw-r--r--ext/objspace/objspace.c1
-rw-r--r--ext/openssl/History.md69
-rw-r--r--ext/openssl/extconf.rb45
-rw-r--r--ext/openssl/lib/openssl/version.rb2
-rw-r--r--ext/openssl/openssl.gemspec5
-rw-r--r--ext/openssl/ossl_bn.c34
-rw-r--r--ext/openssl/ossl_cipher.c26
-rw-r--r--ext/openssl/ossl_digest.c8
-rw-r--r--ext/openssl/ossl_pkey_ec.c16
-rw-r--r--ext/openssl/ossl_ssl.c80
-rw-r--r--ext/openssl/ossl_ts.c38
-rw-r--r--ext/openssl/ossl_x509store.c59
-rw-r--r--ext/psych/lib/psych.rb8
-rw-r--r--ext/psych/lib/psych/handler.rb2
-rw-r--r--ext/psych/lib/psych/nodes/scalar.rb2
-rw-r--r--ext/psych/lib/psych/versions.rb6
-rw-r--r--ext/psych/lib/psych/visitors/to_ruby.rb16
-rw-r--r--ext/psych/lib/psych/visitors/visitor.rb2
-rw-r--r--ext/psych/lib/psych/visitors/yaml_tree.rb4
-rw-r--r--ext/psych/yaml/loader.c2
-rw-r--r--ext/psych/yaml/scanner.c4
-rw-r--r--ext/psych/yaml/yaml.h8
-rw-r--r--ext/psych/yaml/yaml_private.h2
-rw-r--r--ext/racc/cparse/cparse.c2
-rw-r--r--ext/ripper/lib/ripper/lexer.rb2
-rw-r--r--ext/socket/ifaddr.c1
-rw-r--r--ext/stringio/stringio.c28
-rw-r--r--ext/stringio/stringio.gemspec3
-rw-r--r--ext/strscan/strscan.c19
-rw-r--r--ext/strscan/strscan.gemspec4
-rw-r--r--ext/zlib/extlibs8
-rw-r--r--ext/zlib/win32/zlib-1.2.11-mswin.patch95
-rw-r--r--ext/zlib/zlib.c144
-rw-r--r--ext/zlib/zlib.gemspec2
-rw-r--r--file.c48
-rw-r--r--gc.c409
-rw-r--r--gems/bundled_gems8
-rw-r--r--hash.c16
-rw-r--r--include/ruby/internal/intern/select/posix.h2
-rw-r--r--include/ruby/internal/memory.h8
-rw-r--r--internal/bits.h6
-rw-r--r--internal/complex.h1
-rw-r--r--internal/cont.h1
-rw-r--r--internal/encoding.h3
-rw-r--r--internal/gc.h6
-rw-r--r--internal/rational.h1
-rw-r--r--internal/string.h1
-rw-r--r--internal/vm.h7
-rw-r--r--io.c87
-rw-r--r--iseq.c8
-rw-r--r--lib/bundler.rb47
-rw-r--r--lib/bundler/bundler.gemspec5
-rw-r--r--lib/bundler/cli.rb78
-rw-r--r--lib/bundler/cli/cache.rb2
-rw-r--r--lib/bundler/cli/check.rb6
-rw-r--r--lib/bundler/cli/common.rb17
-rw-r--r--lib/bundler/cli/doctor.rb19
-rw-r--r--lib/bundler/cli/exec.rb7
-rw-r--r--lib/bundler/cli/gem.rb144
-rw-r--r--lib/bundler/cli/info.rb20
-rw-r--r--lib/bundler/cli/install.rb34
-rw-r--r--lib/bundler/cli/issue.rb7
-rw-r--r--lib/bundler/cli/list.rb8
-rw-r--r--lib/bundler/cli/lock.rb6
-rw-r--r--lib/bundler/cli/open.rb3
-rw-r--r--lib/bundler/cli/outdated.rb21
-rw-r--r--lib/bundler/cli/remove.rb3
-rw-r--r--lib/bundler/cli/update.rb17
-rw-r--r--lib/bundler/compact_index_client.rb4
-rw-r--r--lib/bundler/compact_index_client/updater.rb21
-rw-r--r--lib/bundler/current_ruby.rb9
-rw-r--r--lib/bundler/definition.rb413
-rw-r--r--lib/bundler/digest.rb71
-rw-r--r--lib/bundler/dsl.rb106
-rw-r--r--lib/bundler/environment_preserver.rb5
-rw-r--r--lib/bundler/errors.rb22
-rw-r--r--lib/bundler/feature_flag.rb4
-rw-r--r--lib/bundler/fetcher.rb6
-rw-r--r--lib/bundler/fetcher/compact_index.rb2
-rw-r--r--lib/bundler/fetcher/downloader.rb15
-rw-r--r--lib/bundler/fetcher/index.rb1
-rw-r--r--lib/bundler/friendly_errors.rb37
-rw-r--r--lib/bundler/gem_helper.rb37
-rw-r--r--lib/bundler/index.rb9
-rw-r--r--lib/bundler/injector.rb4
-rw-r--r--lib/bundler/inline.rb3
-rw-r--r--lib/bundler/installer.rb20
-rw-r--r--lib/bundler/installer/gem_installer.rb19
-rw-r--r--lib/bundler/installer/parallel_installer.rb51
-rw-r--r--lib/bundler/installer/standalone.rb23
-rw-r--r--lib/bundler/lazy_specification.rb25
-rw-r--r--lib/bundler/lockfile_parser.rb40
-rw-r--r--lib/bundler/man/bundle-add.12
-rw-r--r--lib/bundler/man/bundle-binstubs.12
-rw-r--r--lib/bundler/man/bundle-cache.12
-rw-r--r--lib/bundler/man/bundle-check.12
-rw-r--r--lib/bundler/man/bundle-clean.12
-rw-r--r--lib/bundler/man/bundle-config.142
-rw-r--r--lib/bundler/man/bundle-config.1.ronn50
-rw-r--r--lib/bundler/man/bundle-doctor.12
-rw-r--r--lib/bundler/man/bundle-exec.12
-rw-r--r--lib/bundler/man/bundle-gem.115
-rw-r--r--lib/bundler/man/bundle-gem.1.ronn16
-rw-r--r--lib/bundler/man/bundle-info.12
-rw-r--r--lib/bundler/man/bundle-init.12
-rw-r--r--lib/bundler/man/bundle-inject.12
-rw-r--r--lib/bundler/man/bundle-install.12
-rw-r--r--lib/bundler/man/bundle-list.12
-rw-r--r--lib/bundler/man/bundle-lock.12
-rw-r--r--lib/bundler/man/bundle-open.12
-rw-r--r--lib/bundler/man/bundle-outdated.12
-rw-r--r--lib/bundler/man/bundle-platform.12
-rw-r--r--lib/bundler/man/bundle-pristine.12
-rw-r--r--lib/bundler/man/bundle-remove.12
-rw-r--r--lib/bundler/man/bundle-show.12
-rw-r--r--lib/bundler/man/bundle-update.18
-rw-r--r--lib/bundler/man/bundle-update.1.ronn6
-rw-r--r--lib/bundler/man/bundle-viz.12
-rw-r--r--lib/bundler/man/bundle.12
-rw-r--r--lib/bundler/man/gemfile.528
-rw-r--r--lib/bundler/man/gemfile.5.ronn8
-rw-r--r--lib/bundler/plugin.rb36
-rw-r--r--lib/bundler/plugin/api/source.rb22
-rw-r--r--lib/bundler/plugin/index.rb5
-rw-r--r--lib/bundler/plugin/installer.rb20
-rw-r--r--lib/bundler/plugin/source_list.rb4
-rw-r--r--lib/bundler/resolver.rb152
-rw-r--r--lib/bundler/resolver/spec_group.rb24
-rw-r--r--lib/bundler/retry.rb2
-rw-r--r--lib/bundler/rubygems_ext.rb36
-rw-r--r--lib/bundler/rubygems_gem_installer.rb69
-rw-r--r--lib/bundler/rubygems_integration.rb44
-rw-r--r--lib/bundler/runtime.rb29
-rw-r--r--lib/bundler/settings.rb115
-rw-r--r--lib/bundler/setup.rb4
-rw-r--r--lib/bundler/shared_helpers.rb12
-rw-r--r--lib/bundler/source.rb21
-rw-r--r--lib/bundler/source/git.rb26
-rw-r--r--lib/bundler/source/git/git_proxy.rb14
-rw-r--r--lib/bundler/source/metadata.rb4
-rw-r--r--lib/bundler/source/path.rb4
-rw-r--r--lib/bundler/source/path/installer.rb2
-rw-r--r--lib/bundler/source/rubygems.rb217
-rw-r--r--lib/bundler/source/rubygems_aggregate.rb68
-rw-r--r--lib/bundler/source_list.rb160
-rw-r--r--lib/bundler/source_map.rb58
-rw-r--r--lib/bundler/spec_set.rb48
-rw-r--r--lib/bundler/stub_specification.rb8
-rw-r--r--lib/bundler/templates/Executable.bundler12
-rw-r--r--lib/bundler/templates/Gemfile2
-rw-r--r--lib/bundler/templates/gems.rb2
-rw-r--r--lib/bundler/templates/newgem/Gemfile.tt7
-rw-r--r--lib/bundler/templates/newgem/README.md.tt8
-rw-r--r--lib/bundler/templates/newgem/Rakefile.tt6
-rw-r--r--lib/bundler/templates/newgem/github/workflows/main.yml.tt21
-rw-r--r--lib/bundler/templates/newgem/newgem.gemspec.tt32
-rw-r--r--lib/bundler/templates/newgem/sig/newgem.rbs.tt8
-rw-r--r--lib/bundler/templates/newgem/standard.yml.tt2
-rw-r--r--lib/bundler/vendor/.document1
-rw-r--r--lib/bundler/vendor/connection_pool/LICENSE20
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool.rb113
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb66
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb40
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb2
-rw-r--r--lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb57
-rw-r--r--lib/bundler/vendor/fileutils/LICENSE.txt22
-rw-r--r--lib/bundler/vendor/molinillo/LICENSE9
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb4
-rw-r--r--lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb2
-rw-r--r--lib/bundler/vendor/net-http-persistent/README.rdoc82
-rw-r--r--lib/bundler/vendor/thor/LICENSE.md20
-rw-r--r--lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb2
-rw-r--r--lib/bundler/vendor/tmpdir/lib/tmpdir.rb2
-rw-r--r--lib/bundler/vendor/tsort/LICENSE.txt22
-rw-r--r--lib/bundler/vendor/tsort/lib/tsort.rb453
-rw-r--r--lib/bundler/vendor/uri/LICENSE.txt22
-rw-r--r--lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb4
-rw-r--r--lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb6
-rw-r--r--lib/bundler/vendor/uri/lib/uri/version.rb2
-rw-r--r--lib/bundler/vendored_tsort.rb4
-rw-r--r--lib/bundler/version.rb2
-rw-r--r--lib/bundler/worker.rb23
-rw-r--r--lib/cgi.rb2
-rw-r--r--lib/cgi/cookie.rb45
-rw-r--r--lib/cgi/core.rb45
-rw-r--r--lib/debug.gemspec4
-rw-r--r--lib/debug.rb28
-rw-r--r--lib/drb/version.rb2
-rw-r--r--lib/irb.rb22
-rw-r--r--lib/irb/cmd/ls.rb83
-rw-r--r--lib/irb/cmd/nop.rb14
-rw-r--r--lib/irb/cmd/show_source.rb86
-rw-r--r--lib/irb/cmd/whereami.rb20
-rw-r--r--lib/irb/color.rb5
-rw-r--r--lib/irb/color_printer.rb9
-rw-r--r--lib/irb/completion.rb76
-rw-r--r--lib/irb/ext/save-history.rb20
-rw-r--r--lib/irb/extend-command.rb25
-rw-r--r--lib/irb/input-method.rb1
-rw-r--r--lib/irb/irb.gemspec47
-rw-r--r--lib/irb/lc/help-message12
-rw-r--r--lib/irb/ruby-lex.rb86
-rw-r--r--lib/irb/version.rb4
-rw-r--r--lib/mkmf.rb16
-rw-r--r--lib/net/ftp.rb65
-rw-r--r--lib/net/http/header.rb8
-rw-r--r--lib/net/imap.rb8
-rw-r--r--lib/net/protocol.rb2
-rw-r--r--lib/optparse.rb61
-rw-r--r--lib/optparse/kwargs.rb3
-rw-r--r--lib/optparse/optparse.gemspec4
-rw-r--r--lib/pp.gemspec13
-rw-r--r--lib/pp.rb2
-rw-r--r--lib/prettyprint.gemspec2
-rw-r--r--lib/racc/info.rb2
-rw-r--r--lib/racc/racc.gemspec8
-rw-r--r--lib/rdoc/generator/template/darkfish/css/rdoc.css20
-rw-r--r--lib/rdoc/markdown.rb517
-rw-r--r--lib/rdoc/markdown/literals.rb15
-rw-r--r--lib/rdoc/markup.rb1
-rw-r--r--lib/rdoc/markup/attr_span.rb10
-rw-r--r--lib/rdoc/markup/attribute_manager.rb121
-rw-r--r--lib/rdoc/markup/table.rb47
-rw-r--r--lib/rdoc/markup/to_html.rb27
-rw-r--r--lib/rdoc/markup/to_joined_paragraph.rb1
-rw-r--r--lib/rdoc/markup/to_rdoc.rb28
-rw-r--r--lib/rdoc/markup/to_table_of_contents.rb1
-rw-r--r--lib/rdoc/options.rb34
-rw-r--r--lib/rdoc/parser/changelog.rb159
-rw-r--r--lib/rdoc/rd/block_parser.rb2
-rw-r--r--lib/rdoc/rd/inline_parser.rb2
-rw-r--r--lib/rdoc/rdoc.gemspec5
-rw-r--r--lib/rdoc/rdoc.rb14
-rw-r--r--lib/rdoc/store.rb45
-rw-r--r--lib/rdoc/version.rb2
-rw-r--r--lib/reline.rb4
-rw-r--r--lib/reline/line_editor.rb81
-rw-r--r--lib/reline/version.rb2
-rw-r--r--lib/resolv.gemspec4
-rw-r--r--lib/resolv.rb6
-rw-r--r--lib/rinda/rinda.gemspec2
-rw-r--r--lib/rubygems.rb114
-rw-r--r--lib/rubygems/command.rb11
-rw-r--r--lib/rubygems/command_manager.rb10
-rw-r--r--lib/rubygems/commands/build_command.rb8
-rw-r--r--lib/rubygems/commands/cert_command.rb99
-rw-r--r--lib/rubygems/commands/check_command.rb8
-rw-r--r--lib/rubygems/commands/cleanup_command.rb6
-rw-r--r--lib/rubygems/commands/contents_command.rb4
-rw-r--r--lib/rubygems/commands/dependency_command.rb6
-rw-r--r--lib/rubygems/commands/environment_command.rb2
-rw-r--r--lib/rubygems/commands/fetch_command.rb8
-rw-r--r--lib/rubygems/commands/generate_index_command.rb4
-rw-r--r--lib/rubygems/commands/help_command.rb2
-rw-r--r--lib/rubygems/commands/info_command.rb4
-rw-r--r--lib/rubygems/commands/install_command.rb25
-rw-r--r--lib/rubygems/commands/list_command.rb4
-rw-r--r--lib/rubygems/commands/lock_command.rb2
-rw-r--r--lib/rubygems/commands/mirror_command.rb2
-rw-r--r--lib/rubygems/commands/open_command.rb6
-rw-r--r--lib/rubygems/commands/outdated_command.rb8
-rw-r--r--lib/rubygems/commands/owner_command.rb8
-rw-r--r--lib/rubygems/commands/pristine_command.rb20
-rw-r--r--lib/rubygems/commands/push_command.rb8
-rw-r--r--lib/rubygems/commands/query_command.rb6
-rw-r--r--lib/rubygems/commands/rdoc_command.rb6
-rw-r--r--lib/rubygems/commands/search_command.rb4
-rw-r--r--lib/rubygems/commands/server_command.rb12
-rw-r--r--lib/rubygems/commands/setup_command.rb154
-rw-r--r--lib/rubygems/commands/signin_command.rb4
-rw-r--r--lib/rubygems/commands/signout_command.rb2
-rw-r--r--lib/rubygems/commands/sources_command.rb8
-rw-r--r--lib/rubygems/commands/specification_command.rb8
-rw-r--r--lib/rubygems/commands/stale_command.rb2
-rw-r--r--lib/rubygems/commands/uninstall_command.rb8
-rw-r--r--lib/rubygems/commands/unpack_command.rb10
-rw-r--r--lib/rubygems/commands/update_command.rb44
-rw-r--r--lib/rubygems/commands/which_command.rb2
-rw-r--r--lib/rubygems/commands/yank_command.rb10
-rw-r--r--lib/rubygems/config_file.rb19
-rw-r--r--lib/rubygems/core_ext/tcpsocket_init.rb52
-rw-r--r--lib/rubygems/defaults.rb4
-rw-r--r--lib/rubygems/dependency_installer.rb16
-rw-r--r--lib/rubygems/dependency_list.rb6
-rw-r--r--lib/rubygems/deprecate.rb4
-rw-r--r--lib/rubygems/doctor.rb4
-rw-r--r--lib/rubygems/errors.rb3
-rw-r--r--lib/rubygems/exceptions.rb3
-rw-r--r--lib/rubygems/ext/builder.rb12
-rw-r--r--lib/rubygems/ext/cmake_builder.rb2
-rw-r--r--lib/rubygems/ext/ext_conf_builder.rb11
-rw-r--r--lib/rubygems/ext/rake_builder.rb3
-rw-r--r--lib/rubygems/gem_runner.rb6
-rw-r--r--lib/rubygems/gemcutter_utilities.rb20
-rw-r--r--lib/rubygems/indexer.rb6
-rw-r--r--lib/rubygems/install_default_message.rb4
-rw-r--r--lib/rubygems/install_message.rb4
-rw-r--r--lib/rubygems/install_update_options.rb8
-rw-r--r--lib/rubygems/installer.rb95
-rw-r--r--lib/rubygems/local_remote_options.rb8
-rw-r--r--lib/rubygems/mock_gem_ui.rb2
-rw-r--r--lib/rubygems/optparse.rb3
-rw-r--r--lib/rubygems/optparse/.document1
-rw-r--r--lib/rubygems/optparse/COPYING56
-rw-r--r--lib/rubygems/optparse/lib/optionparser.rb2
-rw-r--r--lib/rubygems/optparse/lib/optparse.rb2230
-rw-r--r--lib/rubygems/optparse/lib/optparse/ac.rb54
-rw-r--r--lib/rubygems/optparse/lib/optparse/date.rb18
-rw-r--r--lib/rubygems/optparse/lib/optparse/kwargs.rb22
-rw-r--r--lib/rubygems/optparse/lib/optparse/shellwords.rb7
-rw-r--r--lib/rubygems/optparse/lib/optparse/time.rb11
-rw-r--r--lib/rubygems/optparse/lib/optparse/uri.rb7
-rw-r--r--lib/rubygems/optparse/lib/optparse/version.rb71
-rw-r--r--lib/rubygems/package.rb91
-rw-r--r--lib/rubygems/package/io_source.rb4
-rw-r--r--lib/rubygems/package/tar_reader.rb2
-rw-r--r--lib/rubygems/package_task.rb4
-rw-r--r--lib/rubygems/platform.rb3
-rw-r--r--lib/rubygems/query_utils.rb8
-rw-r--r--lib/rubygems/rdoc.rb2
-rw-r--r--lib/rubygems/remote_fetcher.rb49
-rw-r--r--lib/rubygems/request.rb14
-rw-r--r--lib/rubygems/request/connection_pools.rb2
-rw-r--r--lib/rubygems/request/http_pool.rb2
-rw-r--r--lib/rubygems/request_set.rb14
-rw-r--r--lib/rubygems/request_set/lockfile.rb2
-rw-r--r--lib/rubygems/request_set/lockfile/tokenizer.rb2
-rw-r--r--lib/rubygems/requirement.rb25
-rw-r--r--lib/rubygems/resolver.rb62
-rw-r--r--lib/rubygems/resolver/git_specification.rb2
-rw-r--r--lib/rubygems/resolver/installer_set.rb6
-rw-r--r--lib/rubygems/resolver/molinillo.rb2
-rw-r--r--lib/rubygems/resolver/molinillo/LICENSE9
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb4
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb2
-rw-r--r--lib/rubygems/resolver/set.rb1
-rw-r--r--lib/rubygems/resolver/specification.rb2
-rw-r--r--lib/rubygems/s3_uri_signer.rb10
-rw-r--r--lib/rubygems/safe_yaml.rb2
-rw-r--r--lib/rubygems/security.rb74
-rw-r--r--lib/rubygems/security/policy.rb10
-rw-r--r--lib/rubygems/security/signer.rb9
-rw-r--r--lib/rubygems/security/trust_dir.rb1
-rw-r--r--lib/rubygems/security_option.rb10
-rw-r--r--lib/rubygems/server.rb4
-rw-r--r--lib/rubygems/source.rb14
-rw-r--r--lib/rubygems/source/git.rb4
-rw-r--r--lib/rubygems/spec_fetcher.rb10
-rw-r--r--lib/rubygems/specification.rb129
-rw-r--r--lib/rubygems/specification_policy.rb13
-rw-r--r--lib/rubygems/syck_hack.rb77
-rw-r--r--lib/rubygems/tsort.rb3
-rw-r--r--lib/rubygems/tsort/.document1
-rw-r--r--lib/rubygems/tsort/LICENSE.txt22
-rw-r--r--lib/rubygems/tsort/lib/tsort.rb454
-rw-r--r--lib/rubygems/uninstaller.rb53
-rw-r--r--lib/rubygems/uri.rb111
-rw-r--r--lib/rubygems/uri_parser.rb34
-rw-r--r--lib/rubygems/uri_parsing.rb23
-rw-r--r--lib/rubygems/user_interaction.rb6
-rw-r--r--lib/rubygems/util.rb2
-rw-r--r--lib/rubygems/util/licenses.rb113
-rw-r--r--lib/rubygems/validator.rb4
-rw-r--r--lib/rubygems/version_option.rb6
-rw-r--r--lib/time.gemspec4
-rw-r--r--lib/time.rb4
-rw-r--r--lib/tmpdir.gemspec2
-rw-r--r--lib/tmpdir.rb2
-rw-r--r--lib/uri/rfc2396_parser.rb4
-rw-r--r--lib/uri/rfc3986_parser.rb6
-rw-r--r--lib/uri/version.rb2
-rw-r--r--load.c62
-rw-r--r--marshal.c81
-rw-r--r--method.h1
-rw-r--r--missing/dtoa.c111
-rw-r--r--mjit.c60
-rw-r--r--mjit_compile.c24
-rw-r--r--mjit_worker.c81
-rw-r--r--numeric.c2
-rw-r--r--parse.y43
-rw-r--r--proc.c2
-rw-r--r--ractor.c110
-rw-r--r--ractor_core.h6
-rw-r--r--random.c10
-rw-r--r--rational.c13
-rw-r--r--re.c66
-rw-r--r--regcomp.c35
-rw-r--r--regexec.c12
-rw-r--r--ruby.c49
-rw-r--r--scheduler.c24
-rw-r--r--signal.c5
-rw-r--r--spec/bundler/bundler/bundler_spec.rb27
-rw-r--r--spec/bundler/bundler/cli_spec.rb56
-rw-r--r--spec/bundler/bundler/compact_index_client/updater_spec.rb22
-rw-r--r--spec/bundler/bundler/definition_spec.rb42
-rw-r--r--spec/bundler/bundler/dep_proxy_spec.rb2
-rw-r--r--spec/bundler/bundler/digest_spec.rb17
-rw-r--r--spec/bundler/bundler/dsl_spec.rb71
-rw-r--r--spec/bundler/bundler/env_spec.rb32
-rw-r--r--spec/bundler/bundler/fetcher/downloader_spec.rb18
-rw-r--r--spec/bundler/bundler/fetcher/index_spec.rb2
-rw-r--r--spec/bundler/bundler/gem_helper_spec.rb41
-rw-r--r--spec/bundler/bundler/installer/parallel_installer_spec.rb33
-rw-r--r--spec/bundler/bundler/installer/spec_installation_spec.rb4
-rw-r--r--spec/bundler/bundler/plugin/dsl_spec.rb2
-rw-r--r--spec/bundler/bundler/plugin/index_spec.rb12
-rw-r--r--spec/bundler/bundler/plugin_spec.rb10
-rw-r--r--spec/bundler/bundler/rubygems_integration_spec.rb16
-rw-r--r--spec/bundler/bundler/settings_spec.rb23
-rw-r--r--spec/bundler/bundler/source/git/git_proxy_spec.rb28
-rw-r--r--spec/bundler/bundler/source/git_spec.rb45
-rw-r--r--spec/bundler/bundler/source/rubygems_spec.rb14
-rw-r--r--spec/bundler/bundler/source_list_spec.rb78
-rw-r--r--spec/bundler/bundler/stub_specification_spec.rb27
-rw-r--r--spec/bundler/bundler/worker_spec.rb47
-rw-r--r--spec/bundler/cache/gems_spec.rb30
-rw-r--r--spec/bundler/cache/git_spec.rb12
-rw-r--r--spec/bundler/cache/path_spec.rb12
-rw-r--r--spec/bundler/commands/binstubs_spec.rb23
-rw-r--r--spec/bundler/commands/cache_spec.rb69
-rw-r--r--spec/bundler/commands/check_spec.rb157
-rw-r--r--spec/bundler/commands/clean_spec.rb17
-rw-r--r--spec/bundler/commands/config_spec.rb46
-rw-r--r--spec/bundler/commands/doctor_spec.rb34
-rw-r--r--spec/bundler/commands/exec_spec.rb233
-rw-r--r--spec/bundler/commands/info_spec.rb41
-rw-r--r--spec/bundler/commands/install_spec.rb362
-rw-r--r--spec/bundler/commands/lock_spec.rb53
-rw-r--r--spec/bundler/commands/newgem_spec.rb569
-rw-r--r--spec/bundler/commands/open_spec.rb1
-rw-r--r--spec/bundler/commands/outdated_spec.rb452
-rw-r--r--spec/bundler/commands/post_bundle_message_spec.rb66
-rw-r--r--spec/bundler/commands/pristine_spec.rb10
-rw-r--r--spec/bundler/commands/remove_spec.rb61
-rw-r--r--spec/bundler/commands/update_spec.rb649
-rw-r--r--spec/bundler/install/allow_offline_install_spec.rb2
-rw-r--r--spec/bundler/install/bundler_spec.rb4
-rw-r--r--spec/bundler/install/deploy_spec.rb91
-rw-r--r--spec/bundler/install/failure_spec.rb97
-rw-r--r--spec/bundler/install/gemfile/eval_gemfile_spec.rb41
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb78
-rw-r--r--spec/bundler/install/gemfile/git_spec.rb102
-rw-r--r--spec/bundler/install/gemfile/groups_spec.rb42
-rw-r--r--spec/bundler/install/gemfile/install_if_spec.rb2
-rw-r--r--spec/bundler/install/gemfile/path_spec.rb121
-rw-r--r--spec/bundler/install/gemfile/platform_spec.rb93
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb1257
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb83
-rw-r--r--spec/bundler/install/gemfile_spec.rb8
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb79
-rw-r--r--spec/bundler/install/gems/dependency_api_spec.rb73
-rw-r--r--spec/bundler/install/gems/flex_spec.rb89
-rw-r--r--spec/bundler/install/gems/native_extensions_spec.rb4
-rw-r--r--spec/bundler/install/gems/post_install_spec.rb2
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb49
-rw-r--r--spec/bundler/install/gems/standalone_spec.rb98
-rw-r--r--spec/bundler/install/gems/sudo_spec.rb17
-rw-r--r--spec/bundler/install/gemspecs_spec.rb13
-rw-r--r--spec/bundler/install/git_spec.rb10
-rw-r--r--spec/bundler/install/global_cache_spec.rb23
-rw-r--r--spec/bundler/install/path_spec.rb22
-rw-r--r--spec/bundler/install/prereleases_spec.rb6
-rw-r--r--spec/bundler/install/redownload_spec.rb1
-rw-r--r--spec/bundler/install/yanked_spec.rb31
-rw-r--r--spec/bundler/lock/git_spec.rb1
-rw-r--r--spec/bundler/lock/lockfile_spec.rb343
-rw-r--r--spec/bundler/other/major_deprecation_spec.rb275
-rw-r--r--spec/bundler/other/platform_spec.rb16
-rw-r--r--spec/bundler/plugins/command_spec.rb6
-rw-r--r--spec/bundler/plugins/install_spec.rb38
-rw-r--r--spec/bundler/plugins/source/example_spec.rb8
-rw-r--r--spec/bundler/quality_spec.rb40
-rw-r--r--spec/bundler/realworld/double_check_spec.rb4
-rw-r--r--spec/bundler/realworld/edgecases_spec.rb159
-rw-r--r--spec/bundler/realworld/ffi_spec.rb57
-rw-r--r--spec/bundler/realworld/fixtures/warbler/Gemfile.lock5
-rw-r--r--spec/bundler/realworld/mirror_probe_spec.rb23
-rw-r--r--spec/bundler/realworld/slow_perf_spec.rb10
-rw-r--r--spec/bundler/resolver/platform_spec.rb17
-rw-r--r--spec/bundler/runtime/executable_spec.rb1
-rw-r--r--spec/bundler/runtime/inline_spec.rb38
-rw-r--r--spec/bundler/runtime/load_spec.rb2
-rw-r--r--spec/bundler/runtime/platform_spec.rb122
-rw-r--r--spec/bundler/runtime/require_spec.rb17
-rw-r--r--spec/bundler/runtime/setup_spec.rb260
-rw-r--r--spec/bundler/runtime/with_unbundled_env_spec.rb2
-rw-r--r--spec/bundler/spec_helper.rb16
-rw-r--r--spec/bundler/support/api_request_limit_hax.rb16
-rw-r--r--spec/bundler/support/artifice/compact_index.rb4
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb40
-rw-r--r--spec/bundler/support/artifice/compact_index_rate_limited.rb2
-rw-r--r--spec/bundler/support/artifice/endpoint.rb32
-rw-r--r--spec/bundler/support/artifice/vcr.rb13
-rw-r--r--spec/bundler/support/artifice/windows.rb4
-rw-r--r--spec/bundler/support/builders.rb70
-rw-r--r--spec/bundler/support/bundle.rb8
-rw-r--r--spec/bundler/support/filters.rb1
-rw-r--r--spec/bundler/support/hax.rb36
-rw-r--r--spec/bundler/support/helpers.rb24
-rw-r--r--spec/bundler/support/indexes.rb7
-rw-r--r--spec/bundler/support/matchers.rb92
-rw-r--r--spec/bundler/support/path.rb61
-rw-r--r--spec/bundler/support/platforms.rb6
-rw-r--r--spec/bundler/support/rubygems_ext.rb70
-rw-r--r--spec/bundler/support/rubygems_version_manager.rb12
-rw-r--r--spec/bundler/update/git_spec.rb57
-rw-r--r--spec/bundler/update/path_spec.rb1
-rw-r--r--spec/ruby/core/hash/transform_keys_spec.rb12
-rw-r--r--spec/ruby/core/marshal/shared/load.rb127
-rw-r--r--spec/ruby/core/string/lstrip_spec.rb18
-rw-r--r--spec/ruby/core/string/strip_spec.rb22
-rw-r--r--spec/ruby/core/time/shared/local.rb2
-rw-r--r--spec/ruby/library/cgi/cookie/name_spec.rb12
-rw-r--r--spec/ruby/library/cgi/cookie/parse_spec.rb10
-rw-r--r--st.c1
-rw-r--r--string.c112
-rw-r--r--template/Makefile.in2
-rw-r--r--test/-ext-/array/test_to_ary_concat.rb20
-rw-r--r--test/-ext-/postponed_job/test_postponed_job.rb7
-rw-r--r--test/-ext-/string/test_enc_str_buf_cat.rb9
-rw-r--r--test/-ext-/string/test_fstring.rb16
-rw-r--r--test/cgi/test_cgi_cookie.rb87
-rw-r--r--test/cgi/test_cgi_header.rb8
-rw-r--r--test/date/test_date_parse.rb71
-rw-r--r--test/drb/test_drb.rb4
-rw-r--r--test/drb/test_drbssl.rb2
-rw-r--r--test/etc/test_etc.rb1
-rw-r--r--test/fiber/scheduler.rb24
-rw-r--r--test/fiber/test_io.rb35
-rw-r--r--test/fiber/test_process.rb15
-rw-r--r--test/fiber/test_scheduler.rb18
-rw-r--r--test/fiber/test_sleep.rb25
-rw-r--r--test/fiber/test_thread.rb108
-rw-r--r--test/fiddle/helper.rb4
-rw-r--r--test/fiddle/test_closure.rb25
-rw-r--r--test/fiddle/test_cparser.rb56
-rw-r--r--test/io/console/test_io_console.rb9
-rw-r--r--test/io/wait/test_io_wait_uncommon.rb33
-rw-r--r--test/io/wait/test_ractor.rb22
-rw-r--r--test/irb/test_cmd.rb121
-rw-r--r--test/irb/test_color.rb1
-rw-r--r--test/irb/test_color_printer.rb1
-rw-r--r--test/irb/test_completion.rb28
-rw-r--r--test/irb/test_history.rb38
-rw-r--r--test/irb/test_init.rb6
-rw-r--r--test/irb/test_ruby_lex.rb20
-rw-r--r--test/irb/yamatanooroti/test_rendering.rb165
-rw-r--r--test/mkmf/test_install.rb30
-rw-r--r--test/monitor/test_monitor.rb7
-rw-r--r--test/net/ftp/test_ftp.rb216
-rw-r--r--test/net/http/test_https.rb5
-rw-r--r--test/net/imap/test_imap.rb31
-rw-r--r--test/objspace/test_objspace.rb13
-rw-r--r--test/openssl/test_cipher.rb48
-rw-r--r--test/openssl/test_config.rb16
-rw-r--r--test/openssl/test_ssl.rb48
-rw-r--r--test/openssl/test_ssl_session.rb1
-rw-r--r--test/openssl/test_ts.rb23
-rw-r--r--test/openssl/test_x509store.rb4
-rw-r--r--test/openssl/utils.rb8
-rw-r--r--test/optparse/test_acceptable.rb1
-rw-r--r--test/optparse/test_optparse.rb30
-rw-r--r--test/psych/helper.rb40
-rw-r--r--test/psych/test_alias_and_anchor.rb12
-rw-r--r--test/psych/test_array.rb6
-rw-r--r--test/psych/test_class.rb4
-rw-r--r--test/psych/test_coder.rb135
-rw-r--r--test/psych/test_date_time.rb4
-rw-r--r--test/psych/test_deprecated.rb4
-rw-r--r--test/psych/test_document.rb2
-rw-r--r--test/psych/test_emitter.rb10
-rw-r--r--test/psych/test_encoding.rb4
-rw-r--r--test/psych/test_exception.rb38
-rw-r--r--test/psych/test_hash.rb18
-rw-r--r--test/psych/test_marshalable.rb12
-rw-r--r--test/psych/test_merge_keys.rb24
-rw-r--r--test/psych/test_object.rb4
-rw-r--r--test/psych/test_object_references.rb12
-rw-r--r--test/psych/test_omap.rb4
-rw-r--r--test/psych/test_parser.rb16
-rw-r--r--test/psych/test_psych.rb45
-rw-r--r--test/psych/test_ractor.rb4
-rw-r--r--test/psych/test_safe_load.rb28
-rw-r--r--test/psych/test_serialize_subclasses.rb4
-rw-r--r--test/psych/test_set.rb6
-rw-r--r--test/psych/test_string.rb14
-rw-r--r--test/psych/test_struct.rb6
-rw-r--r--test/psych/test_yaml.rb14
-rw-r--r--test/psych/test_yaml_special_cases.rb6
-rw-r--r--test/psych/test_yamlstore.rb47
-rw-r--r--test/psych/visitors/test_to_ruby.rb4
-rw-r--r--test/psych/visitors/test_yaml_tree.rb12
-rw-r--r--test/rdoc/test_rdoc_markdown.rb59
-rw-r--r--test/rdoc/test_rdoc_markup_attribute_manager.rb25
-rw-r--r--test/rdoc/test_rdoc_markup_to_html.rb32
-rw-r--r--test/rdoc/test_rdoc_options.rb6
-rw-r--r--test/rdoc/test_rdoc_parser_changelog.rb167
-rw-r--r--test/rdoc/test_rdoc_rdoc.rb35
-rw-r--r--test/rdoc/test_rdoc_rubygems_hook.rb89
-rw-r--r--test/rdoc/test_rdoc_top_level.rb3
-rw-r--r--test/reline/test_reline.rb4
-rw-r--r--test/reline/test_string_processing.rb54
-rw-r--r--test/reline/test_within_pipe.rb15
-rw-r--r--test/reline/yamatanooroti/test_rendering.rb11
-rw-r--r--test/rinda/test_rinda.rb4
-rw-r--r--test/ripper/test_lexer.rb12
-rw-r--r--test/ruby/test_alias.rb29
-rw-r--r--test/ruby/test_array.rb62
-rw-r--r--test/ruby/test_backtrace.rb56
-rw-r--r--test/ruby/test_enum.rb18
-rw-r--r--test/ruby/test_exception.rb29
-rw-r--r--test/ruby/test_fiber.rb11
-rw-r--r--test/ruby/test_file_exhaustive.rb33
-rw-r--r--test/ruby/test_float.rb18
-rw-r--r--test/ruby/test_gc.rb2
-rw-r--r--test/ruby/test_hash.rb72
-rw-r--r--test/ruby/test_io.rb69
-rw-r--r--test/ruby/test_iseq.rb10
-rw-r--r--test/ruby/test_jit.rb12
-rw-r--r--test/ruby/test_lazy_enumerator.rb4
-rw-r--r--test/ruby/test_marshal.rb45
-rw-r--r--test/ruby/test_method.rb28
-rw-r--r--test/ruby/test_module.rb131
-rw-r--r--test/ruby/test_nomethod_error.rb16
-rw-r--r--test/ruby/test_parse.rb15
-rw-r--r--test/ruby/test_refinement.rb71
-rw-r--r--test/ruby/test_regexp.rb43
-rw-r--r--test/ruby/test_require.rb51
-rw-r--r--test/ruby/test_rubyoptions.rb5
-rw-r--r--test/ruby/test_settracefunc.rb25
-rw-r--r--test/ruby/test_string.rb26
-rw-r--r--test/ruby/test_super.rb31
-rw-r--r--test/ruby/test_syntax.rb15
-rw-r--r--test/ruby/test_thread.rb4
-rw-r--r--test/ruby/test_time_tz.rb23
-rw-r--r--test/ruby/test_transcode.rb22
-rw-r--r--test/ruby/test_weakmap.rb9
-rw-r--r--test/rubygems/data/null-type.gemspec.rzbin554 -> 504 bytes
-rw-r--r--test/rubygems/encrypted_private_key.pem52
-rw-r--r--test/rubygems/helper.rb (renamed from lib/rubygems/test_case.rb)235
-rw-r--r--test/rubygems/installer_test_case.rb (renamed from lib/rubygems/installer_test_case.rb)2
-rw-r--r--test/rubygems/package/tar_test_case.rb (renamed from lib/rubygems/package/tar_test_case.rb)2
-rw-r--r--test/rubygems/packages/ascii_binder-0.1.10.1.gembin0 -> 244736 bytes
-rw-r--r--test/rubygems/packages/ill-formatted-platform-1.0.0.10.gembin0 -> 10240 bytes
-rw-r--r--test/rubygems/private_ec_key.pem9
-rw-r--r--test/rubygems/test_bundled_ca.rb6
-rw-r--r--test/rubygems/test_config.rb2
-rw-r--r--test/rubygems/test_deprecate.rb56
-rw-r--r--test/rubygems/test_gem.rb516
-rw-r--r--test/rubygems/test_gem_available_set.rb2
-rw-r--r--test/rubygems/test_gem_bundler_version_finder.rb10
-rw-r--r--test/rubygems/test_gem_command.rb16
-rw-r--r--test/rubygems/test_gem_command_manager.rb24
-rw-r--r--test/rubygems/test_gem_commands_build_command.rb22
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb141
-rw-r--r--test/rubygems/test_gem_commands_check_command.rb12
-rw-r--r--test/rubygems/test_gem_commands_cleanup_command.rb50
-rw-r--r--test/rubygems/test_gem_commands_contents_command.rb8
-rw-r--r--test/rubygems/test_gem_commands_dependency_command.rb6
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_fetch_command.rb56
-rw-r--r--test/rubygems/test_gem_commands_generate_index_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_help_command.rb11
-rw-r--r--test/rubygems/test_gem_commands_info_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb147
-rw-r--r--test/rubygems/test_gem_commands_list_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_lock_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_mirror.rb2
-rw-r--r--test/rubygems/test_gem_commands_open_command.rb19
-rw-r--r--test/rubygems/test_gem_commands_outdated_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_owner_command.rb8
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb30
-rw-r--r--test/rubygems/test_gem_commands_push_command.rb19
-rw-r--r--test/rubygems/test_gem_commands_query_command.rb16
-rw-r--r--test/rubygems/test_gem_commands_search_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_server_command.rb8
-rw-r--r--test/rubygems/test_gem_commands_setup_command.rb132
-rw-r--r--test/rubygems/test_gem_commands_signin_command.rb23
-rw-r--r--test/rubygems/test_gem_commands_signout_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_sources_command.rb12
-rw-r--r--test/rubygems/test_gem_commands_specification_command.rb14
-rw-r--r--test/rubygems/test_gem_commands_stale_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_uninstall_command.rb10
-rw-r--r--test/rubygems/test_gem_commands_unpack_command.rb6
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb51
-rw-r--r--test/rubygems/test_gem_commands_which_command.rb8
-rw-r--r--test/rubygems/test_gem_commands_yank_command.rb4
-rw-r--r--test/rubygems/test_gem_config_file.rb30
-rw-r--r--test/rubygems/test_gem_dependency.rb20
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb10
-rw-r--r--test/rubygems/test_gem_dependency_list.rb2
-rw-r--r--test/rubygems/test_gem_dependency_resolution_error.rb2
-rw-r--r--test/rubygems/test_gem_doctor.rb62
-rw-r--r--test/rubygems/test_gem_ext_builder.rb45
-rw-r--r--test/rubygems/test_gem_ext_cmake_builder.rb10
-rw-r--r--test/rubygems/test_gem_ext_configure_builder.rb10
-rw-r--r--test/rubygems/test_gem_ext_ext_conf_builder.rb39
-rw-r--r--test/rubygems/test_gem_ext_rake_builder.rb6
-rw-r--r--test/rubygems/test_gem_gem_runner.rb2
-rw-r--r--test/rubygems/test_gem_gemcutter_utilities.rb36
-rw-r--r--test/rubygems/test_gem_impossible_dependencies_error.rb2
-rw-r--r--test/rubygems/test_gem_indexer.rb2
-rw-r--r--test/rubygems/test_gem_install_update_options.rb20
-rw-r--r--test/rubygems/test_gem_installer.rb364
-rw-r--r--test/rubygems/test_gem_local_remote_options.rb4
-rw-r--r--test/rubygems/test_gem_name_tuple.rb2
-rw-r--r--test/rubygems/test_gem_package.rb152
-rw-r--r--test/rubygems/test_gem_package_old.rb20
-rw-r--r--test/rubygems/test_gem_package_tar_header.rb12
-rw-r--r--test/rubygems/test_gem_package_tar_reader.rb2
-rw-r--r--test/rubygems/test_gem_package_tar_reader_entry.rb18
-rw-r--r--test/rubygems/test_gem_package_tar_writer.rb40
-rw-r--r--test/rubygems/test_gem_package_task.rb6
-rw-r--r--test/rubygems/test_gem_path_support.rb4
-rw-r--r--test/rubygems/test_gem_platform.rb3
-rw-r--r--test/rubygems/test_gem_rdoc.rb20
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb101
-rw-r--r--test/rubygems/test_gem_request.rb64
-rw-r--r--test/rubygems/test_gem_request_connection_pools.rb4
-rw-r--r--test/rubygems/test_gem_request_set.rb31
-rw-r--r--test/rubygems/test_gem_request_set_gem_dependency_api.rb34
-rw-r--r--test/rubygems/test_gem_request_set_lockfile.rb8
-rw-r--r--test/rubygems/test_gem_request_set_lockfile_parser.rb8
-rw-r--r--test/rubygems/test_gem_request_set_lockfile_tokenizer.rb10
-rw-r--r--test/rubygems/test_gem_requirement.rb62
-rw-r--r--test/rubygems/test_gem_resolver.rb26
-rw-r--r--test/rubygems/test_gem_resolver_activation_request.rb2
-rw-r--r--test/rubygems/test_gem_resolver_api_set.rb2
-rw-r--r--test/rubygems/test_gem_resolver_api_specification.rb2
-rw-r--r--test/rubygems/test_gem_resolver_best_set.rb4
-rw-r--r--test/rubygems/test_gem_resolver_composed_set.rb2
-rw-r--r--test/rubygems/test_gem_resolver_conflict.rb2
-rw-r--r--test/rubygems/test_gem_resolver_dependency_request.rb2
-rw-r--r--test/rubygems/test_gem_resolver_git_set.rb4
-rw-r--r--test/rubygems/test_gem_resolver_git_specification.rb7
-rw-r--r--test/rubygems/test_gem_resolver_index_set.rb2
-rw-r--r--test/rubygems/test_gem_resolver_index_specification.rb4
-rw-r--r--test/rubygems/test_gem_resolver_installed_specification.rb2
-rw-r--r--test/rubygems/test_gem_resolver_installer_set.rb28
-rw-r--r--test/rubygems/test_gem_resolver_local_specification.rb4
-rw-r--r--test/rubygems/test_gem_resolver_lock_set.rb2
-rw-r--r--test/rubygems/test_gem_resolver_lock_specification.rb2
-rw-r--r--test/rubygems/test_gem_resolver_requirement_list.rb2
-rw-r--r--test/rubygems/test_gem_resolver_specification.rb4
-rw-r--r--test/rubygems/test_gem_resolver_vendor_set.rb6
-rw-r--r--test/rubygems/test_gem_resolver_vendor_specification.rb2
-rw-r--r--test/rubygems/test_gem_security.rb48
-rw-r--r--test/rubygems/test_gem_security_policy.rb56
-rw-r--r--test/rubygems/test_gem_security_signer.rb16
-rw-r--r--test/rubygems/test_gem_security_trust_dir.rb10
-rw-r--r--test/rubygems/test_gem_server.rb14
-rw-r--r--test/rubygems/test_gem_silent_ui.rb20
-rw-r--r--test/rubygems/test_gem_source.rb6
-rw-r--r--test/rubygems/test_gem_source_fetch_problem.rb14
-rw-r--r--test/rubygems/test_gem_source_git.rb29
-rw-r--r--test/rubygems/test_gem_source_installed.rb2
-rw-r--r--test/rubygems/test_gem_source_list.rb2
-rw-r--r--test/rubygems/test_gem_source_local.rb2
-rw-r--r--test/rubygems/test_gem_source_lock.rb2
-rw-r--r--test/rubygems/test_gem_source_specific_file.rb4
-rw-r--r--test/rubygems/test_gem_source_subpath_problem.rb2
-rw-r--r--test/rubygems/test_gem_source_vendor.rb2
-rw-r--r--test/rubygems/test_gem_spec_fetcher.rb4
-rw-r--r--test/rubygems/test_gem_specification.rb825
-rw-r--r--test/rubygems/test_gem_stream_ui.rb4
-rw-r--r--test/rubygems/test_gem_stub_specification.rb10
-rw-r--r--test/rubygems/test_gem_text.rb2
-rw-r--r--test/rubygems/test_gem_uninstaller.rb45
-rw-r--r--test/rubygems/test_gem_unsatisfiable_dependency_error.rb2
-rw-r--r--test/rubygems/test_gem_uri.rb39
-rw-r--r--test/rubygems/test_gem_uri_formatter.rb2
-rw-r--r--test/rubygems/test_gem_util.rb16
-rw-r--r--test/rubygems/test_gem_validator.rb2
-rw-r--r--test/rubygems/test_gem_version.rb19
-rw-r--r--test/rubygems/test_gem_version_option.rb2
-rw-r--r--test/rubygems/test_kernel.rb12
-rw-r--r--test/rubygems/test_project_sanity.rb4
-rw-r--r--test/rubygems/test_remote_fetch_error.rb4
-rw-r--r--test/rubygems/test_require.rb62
-rw-r--r--test/rubygems/test_rubygems.rb44
-rw-r--r--test/rubygems/utilities.rb (renamed from lib/rubygems/test_utilities.rb)4
-rw-r--r--test/socket/test_tcp.rb25
-rw-r--r--test/stringio/test_stringio.rb52
-rw-r--r--test/strscan/test_ractor.rb2
-rw-r--r--test/strscan/test_stringscanner.rb17
-rw-r--r--test/test_time.rb9
-rw-r--r--test/test_tmpdir.rb6
-rw-r--r--test/uri/test_common.rb11
-rw-r--r--test/uri/test_parser.rb29
-rw-r--r--test/zlib/test_zlib.rb88
-rw-r--r--thread.c246
-rw-r--r--thread_pthread.c1
-rw-r--r--thread_sync.c6
-rw-r--r--tool/bundler/rubocop_gems.rb11
-rw-r--r--tool/bundler/rubocop_gems.rb.lock63
-rw-r--r--tool/bundler/standard_gems.rb11
-rw-r--r--tool/bundler/standard_gems.rb.lock69
-rw-r--r--tool/bundler/test_gems.rb3
-rw-r--r--tool/bundler/test_gems.rb.lock12
-rw-r--r--tool/fake.rb1
-rwxr-xr-xtool/leaked-globals32
-rw-r--r--tool/lib/minitest/unit.rb4
-rw-r--r--tool/lib/test/unit.rb3
-rw-r--r--tool/lib/test/unit/core_assertions.rb33
-rw-r--r--tool/lib/vcs.rb3
-rw-r--r--tool/m4/ruby_check_builtin_setjmp.m48
-rw-r--r--tool/m4/ruby_check_printf_prefix.m49
-rw-r--r--tool/m4/ruby_check_setjmp.m46
-rw-r--r--tool/m4/ruby_check_sysconf.m46
-rw-r--r--tool/m4/ruby_cppoutfile.m44
-rw-r--r--tool/m4/ruby_decl_attribute.m44
-rw-r--r--tool/m4/ruby_dtrace_available.m42
-rw-r--r--tool/m4/ruby_dtrace_postprocess.m42
-rw-r--r--tool/m4/ruby_mingw32.m44
-rw-r--r--tool/m4/ruby_stack_grow_direction.m44
-rw-r--r--tool/m4/ruby_try_cflags.m42
-rw-r--r--tool/m4/ruby_try_cxxflags.m42
-rw-r--r--tool/m4/ruby_try_ldflags.m42
-rwxr-xr-xtool/rbinstall.rb5
-rw-r--r--tool/ruby_vm/views/_mjit_compile_getinlinecache.erb14
-rw-r--r--tool/sync_default_gems.rb5
-rw-r--r--tool/test-bundled-gems.rb2
-rw-r--r--transcode.c6
-rw-r--r--util.c1
-rw-r--r--version.h10
-rw-r--r--vm.c58
-rw-r--r--vm_args.c2
-rw-r--r--vm_backtrace.c67
-rw-r--r--vm_callinfo.h23
-rw-r--r--vm_core.h13
-rw-r--r--vm_dump.c64
-rw-r--r--vm_eval.c298
-rw-r--r--vm_insnhelper.c145
-rw-r--r--vm_insnhelper.h2
-rw-r--r--vm_method.c68
-rw-r--r--vm_sync.c4
-rw-r--r--vm_sync.h6
-rw-r--r--vm_trace.c18
-rw-r--r--win32/win32.c146
900 files changed, 24407 insertions, 8845 deletions
diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml
index bdb9011f25..16d1e4a85a 100644
--- a/.github/workflows/baseruby.yml
+++ b/.github/workflows/baseruby.yml
@@ -25,7 +25,7 @@ jobs:
bundler: none
- run: echo "make=make -sj$((1 + $(nproc --all)))" >> $GITHUB_ENV
- run: sudo apt-get install build-essential autoconf bison
- - run: autoconf
+ - run: ./autogen.sh
- run: ./configure --disable-install-doc
- run: $make update-unicode
- run: $make common-srcs
diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml
index c878caddeb..26d2ca473d 100644
--- a/.github/workflows/check_dependencies.yml
+++ b/.github/workflows/check_dependencies.yml
@@ -17,14 +17,14 @@ jobs:
if: "contains(matrix.os, 'ubuntu')"
- name: Install libraries
run: |
- brew upgrade
+ brew update
brew install gdbm gmp libffi openssl@1.1 zlib autoconf automake libtool readline
if: "contains(matrix.os, 'macos')"
- name: git config
run: |
git config --global advice.detachedHead 0
- uses: actions/checkout@v2
- - run: autoconf
+ - run: ./autogen.sh
- name: Run configure
run: ./configure -C --disable-install-doc --disable-rubygems --with-gcc 'optflags=-O0' 'debugflags=-save-temps=obj -g'
- run: make all golf
diff --git a/.github/workflows/check_misc.yml b/.github/workflows/check_misc.yml
index 2f4bbc7093..576341bf88 100644
--- a/.github/workflows/check_misc.yml
+++ b/.github/workflows/check_misc.yml
@@ -3,7 +3,7 @@ on: [push, pull_request]
jobs:
checks:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Check if C-sources are US-ASCII
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 9fbabd35c6..2dc47c0683 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -9,8 +9,8 @@ on:
jobs:
CodeQL-Build:
- # CodeQL runs on ubuntu-latest and windows-latest
- runs-on: ubuntu-latest
+ # CodeQL runs on ubuntu-20.04 and windows-latest
+ runs-on: ubuntu-20.04
steps:
- name: Install libraries
@@ -31,13 +31,13 @@ jobs:
run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ uses: github/codeql-action/init@v2
with:
languages: cpp
config-file: ./.github/codeql/codeql-config.yml
- name: Autobuild
- uses: github/codeql-action/autobuild@v1
+ uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml
index 532e4589e2..47b73fb907 100644
--- a/.github/workflows/compilers.yml
+++ b/.github/workflows/compilers.yml
@@ -1,17 +1,31 @@
name: Compilations
-on: [push, pull_request]
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**.md'
+ - '**.rdoc'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**.md'
+ - '**.rdoc'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
# Github actions does not support YAML anchors. This creative use of
-# environment variables (plus the "echo ::set-env" hack) is to reroute that
+# environment variables (plus the "echo $GITHUB_ENV" hack) is to reroute that
# restriction.
env:
- default_cc: clang-11
+ default_cc: clang-14
append_cc: ''
crosshost: ''
# -O1 is faster than -O3 in our tests... Majority of time are consumed trying
- # to optimize binaries. Also Github Actions runs on a relatively modern CPU
+ # to optimize binaries. Also Github Actions run on relatively modern CPUs
# compared to, say, GCC 4 or Clang 3. We don't specify `-march=native`
# because compilers tend not understand what the CPU is.
optflags: '-O1'
@@ -47,37 +61,52 @@ jobs:
fail-fast: false
matrix:
entry:
- - { key: default_cc, name: gcc-10, value: gcc-10 }
-# - { key: default_cc, name: gcc-9, value: gcc-9 }
-# - { key: default_cc, name: gcc-8, value: gcc-8 }
-# - { key: default_cc, name: gcc-7, value: gcc-7 }
-# - { key: default_cc, name: gcc-6, value: gcc-6 }
-# - { key: default_cc, name: gcc-5, value: gcc-5 }
- - { key: default_cc, name: gcc-4.8, value: gcc-4.8 }
- - { key: default_cc, name: clang-12, value: clang-12 }
-# - { key: default_cc, name: clang-11, value: clang-11 }
-# - { key: default_cc, name: clang-10, value: clang-10 }
-# - { key: default_cc, name: clang-9, value: clang-9 }
-# - { key: default_cc, name: clang-8, value: clang-8 }
-# - { key: default_cc, name: clang-7, value: clang-7 }
-# - { key: default_cc, name: clang-6.0, value: clang-6.0 }
-# - { key: default_cc, name: clang-5.0, value: clang-5.0 }
-# - { key: default_cc, name: clang-4.0, value: clang-4.0 }
- - { key: default_cc, name: clang-3.9, value: clang-3.9 }
-
- - { key: crosshost, name: aarch64-linux-gnu, value: aarch64-linux-gnu }
+ - { key: default_cc, name: gcc-11, value: gcc-11, container: gcc-11 }
+ - { key: default_cc, name: gcc-10, value: gcc-10, container: gcc-10 }
+ - { key: default_cc, name: gcc-9, value: gcc-9, container: gcc-9 }
+ - { key: default_cc, name: gcc-8, value: gcc-8, container: gcc-8 }
+ - { key: default_cc, name: gcc-7, value: gcc-7, container: gcc-7 }
+ - { key: default_cc, name: gcc-6, value: gcc-6, container: gcc-6 }
+ - { key: default_cc, name: gcc-5, value: gcc-5, container: gcc-5 }
+ - { key: default_cc, name: gcc-4.8, value: gcc-4.8, container: gcc-4.8 }
+ - key: default_cc
+ name: 'gcc-11 LTO'
+ value: 'gcc-11 -O2 -flto=auto -ffat-lto-objects'
+ container: gcc-11
+ shared: '--disable-shared'
+ # check: true
+ - { key: default_cc, name: clang-14, value: clang-14, container: clang-14 }
+ - { key: default_cc, name: clang-13, value: clang-13, container: clang-13 }
+ - { key: default_cc, name: clang-12, value: clang-12, container: clang-12 }
+ - { key: default_cc, name: clang-11, value: clang-11, container: clang-11 }
+ - { key: default_cc, name: clang-10, value: clang-10, container: clang-10 }
+ - { key: default_cc, name: clang-9, value: clang-9, container: clang-9 }
+ - { key: default_cc, name: clang-8, value: clang-8, container: clang-8 }
+ - { key: default_cc, name: clang-7, value: clang-7, container: clang-7 }
+ - { key: default_cc, name: clang-6.0, value: clang-6.0, container: clang-6.0 }
+ - { key: default_cc, name: clang-5.0, value: clang-5.0, container: clang-5.0 }
+ - { key: default_cc, name: clang-4.0, value: clang-4.0, container: clang-4.0 }
+ - { key: default_cc, name: clang-3.9, value: clang-3.9, container: clang-3.9 }
+ - key: default_cc
+ name: 'clang-14 LTO'
+ value: 'clang-14 -O2 -flto=auto'
+ container: clang-14
+ shared: '--disable-shared'
+ # check: true
+
+ - { key: crosshost, name: aarch64-linux-gnu, value: aarch64-linux-gnu, container: crossbuild-essential-arm64 }
# - { key: crosshost, name: arm-linux-gnueabi, value: arm-linux-gnueabi }
# - { key: crosshost, name: arm-linux-gnueabihf, value: arm-linux-gnueabihf }
# - { key: crosshost, name: i686-w64-mingw32, value: i686-w64-mingw32 }
# - { key: crosshost, name: powerpc-linux-gnu, value: powerpc-linux-gnu }
- - { key: crosshost, name: powerpc64le-linux-gnu, value: powerpc64le-linux-gnu }
- - { key: crosshost, name: s390x-linux-gnu, value: s390x-linux-gnu }
- - { key: crosshost, name: x86_64-w64-mingw32, value: x86_64-w64-mingw32 }
+ - { key: crosshost, name: powerpc64le-linux-gnu, value: powerpc64le-linux-gnu, container: crossbuild-essential-ppc64el }
+ - { key: crosshost, name: s390x-linux-gnu, value: s390x-linux-gnu, container: crossbuild-essential-s390x }
+ - { key: crosshost, name: x86_64-w64-mingw32, value: x86_64-w64-mingw32, container: mingw-w64 }
- { key: append_cc, name: c99, value: '-std=c99 -Werror=pedantic -pedantic-errors' }
# - { key: append_cc, name: c11, value: '-std=c11 -Werror=pedantic -pedantic-errors' }
# - { key: append_cc, name: c17, value: '-std=c17 -Werror=pedantic -pedantic-errors' }
- - { key: append_cc, name: c2x, value: '-std=c2x -Werror=pedantic -pedantic-errors' }
+# - { key: append_cc, name: c2x, value: '-std=c2x -Werror=pedantic -pedantic-errors' }
- { key: CXXFLAGS, name: c++98, value: '-std=c++98 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' }
# - { key: CXXFLAGS, name: c++11, value: '-std=c++11 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' }
# - { key: CXXFLAGS, name: c++14, value: '-std=c++14 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' }
@@ -85,7 +114,7 @@ jobs:
- { key: CXXFLAGS, name: c++2a, value: '-std=c++2a -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' }
- { key: optflags, name: '-O0', value: '-O0 -march=x86-64 -mtune=generic' }
-# - { key: optflags, name: '-O3', value: '-O3 -march=x86-64 -mtune=generic' }
+# - { key: optflags, name: '-O3', value: '-O3 -march=x86-64 -mtune=generic', check: true }
- { key: append_configure, name: gmp, value: '--with-gmp' }
- { key: append_configure, name: jemalloc, value: '--with-jemalloc' }
@@ -130,7 +159,7 @@ jobs:
- { key: cppflags, name: DEBUG_FIND_TIME_NUMGUESS, value: '-DDEBUG_FIND_TIME_NUMGUESS' }
- { key: cppflags, name: DEBUG_INTEGER_PACK, value: '-DDEBUG_INTEGER_PACK' }
- - { key: cppflags, name: ENABLE_PATH_CHECK, value: '-DENABLE_PATH_CHECK' }
+# - { key: cppflags, name: ENABLE_PATH_CHECK, value: '-DENABLE_PATH_CHECK' }
- { key: cppflags, name: GC_DEBUG_STRESS_TO_CLASS, value: '-DGC_DEBUG_STRESS_TO_CLASS' }
# - { key: cppflags, name: GC_ENABLE_LAZY_SWEEP=0, value: '-DGC_ENABLE_LAZY_SWEEP=0' }
@@ -152,50 +181,50 @@ jobs:
# - { key: cppflags, name: VM_DEBUG_VERIFY_METHOD_CACHE, value: '-DVM_DEBUG_VERIFY_METHOD_CACHE' }
- { key: cppflags, name: MJIT_FORCE_ENABLE, value: '-DMJIT_FORCE_ENABLE' }
+ - { key: cppflags, name: YJIT_FORCE_ENABLE, value: '-DYJIT_FORCE_ENABLE' }
name: ${{ matrix.entry.name }}
runs-on: ubuntu-latest
- container: ghcr.io/ruby/ruby-ci-image:latest
- if: "!contains(github.event.head_commit.message, '[ci skip]')"
+ container:
+ image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || 'clang-14' }}
+ options: --user root
+ if: ${{ !startsWith(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
steps:
+ - run: id
+ working-directory:
- run: mkdir build
working-directory:
- name: setenv
run: |
echo "${{ matrix.entry.key }}=${{ matrix.entry.value }}" >> $GITHUB_ENV
- echo "make=make -sj$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - uses: actions/checkout@v2
+ echo "GNUMAKEFLAGS=-sj$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ - uses: actions/checkout@v3
with:
path: src
- - run: autoconf
+ - uses: actions/cache@v3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - run: ./autogen.sh
working-directory: src
- name: Run configure
- run: |
- if [ -n "${crosshost}" ]; then
- ../src/configure -C \
- ${default_configure} \
- ${append_configure} \
- --host="${crosshost}"
- else
- ../src/configure -C \
- ${default_configure} \
- ${append_configure} \
- --with-gcc="${default_cc} ${append_cc}"
- fi
- - run: $make extract-extlibs
- - run: $make incs
- - run: $make
- - run: $make test
- - run: $make install
- if: "matrix.entry.name == '-O3'"
- - run: /usr/local/bin/gem install --no-doc timezone tzinfo
- if: "matrix.entry.name == '-O3'"
- - run: $make test-tool
- if: "matrix.entry.name == '-O3'"
- - run: $make test-all TESTS='-- ruby -ext-'
- if: "matrix.entry.name == '-O3'"
- - run: $make test-spec
- if: "matrix.entry.name == '-O3'"
+ run: >
+ ../src/configure -C ${default_configure} ${append_configure}
+ ${{ matrix.entry.key == 'crosshost' && '--host="${crosshost}"' || '--with-gcc="${default_cc} ${append_cc}"' }}
+ ${{ matrix.entry.shared || '--enable-shared' }}
+ - run: make extract-extlibs
+ - run: make incs
+ - run: make
+# - run: make leaked-globals
+ - run: make test
+ - run: make install
+ if: ${{ matrix.entry.check }}
+ - run: make test-tool
+ if: ${{ matrix.entry.check }}
+ - run: make test-all TESTS='-- ruby -ext-'
+ if: ${{ matrix.entry.check }}
+ - run: make test-spec
+ if: ${{ matrix.entry.check }}
- uses: k0kubun/action-slack@v2.0.0
with:
@@ -209,7 +238,7 @@ jobs:
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
- if: failure() && github.event_name == 'push'
+ if: ${{ failure() && github.event_name == 'push' }}
defaults:
run:
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index f0355258f1..1da94630fe 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -5,7 +5,7 @@ jobs:
runs-on: macos-latest
strategy:
matrix:
- test_task: [ "check", "test-bundler-parallel", "test-bundled-gems", "leaked-globals" ]
+ test_task: [ "check" ] #, "test-bundler-parallel", "test-bundled-gems" ] matrix tests has unknown issues
fail-fast: false
env:
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
@@ -26,18 +26,20 @@ jobs:
- name: Install libraries
run: |
export WAITS='5 60'
- tool/travis_retry.sh brew upgrade
+ tool/travis_retry.sh brew update
tool/travis_retry.sh brew install gdbm gmp libffi openssl@1.1 zlib autoconf automake libtool readline
working-directory: src
- name: Set ENV
run: |
echo "JOBS=-j$((1 + $(sysctl -n hw.activecpu)))" >> $GITHUB_ENV
- - run: autoconf
+ - run: ./autogen.sh
working-directory: src
- name: Run configure
run: ../src/configure -C --disable-install-doc --with-openssl-dir=$(brew --prefix openssl@1.1) --with-readline-dir=$(brew --prefix readline)
- run: make $JOBS incs
- run: make $JOBS
+ - run: make leaked-globals
+ if: matrix.test_task == 'check'
- run: make prepare-gems
if: matrix.test_task == 'check'
- run: make $JOBS -s ${{ matrix.test_task }}
diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml
index c0366f2942..a5d64c7b2d 100644
--- a/.github/workflows/mingw.yml
+++ b/.github/workflows/mingw.yml
@@ -6,7 +6,8 @@ on: [push, pull_request]
#
jobs:
make:
- runs-on: windows-2019
+ runs-on: windows-2022
+ name: ${{ github.workflow }} (${{ matrix.msystem }})
env:
MSYSTEM: MINGW64
MSYSTEM_PREFIX: /mingw64
@@ -35,7 +36,7 @@ jobs:
with:
path: src
- name: Set up Ruby & MSYS2
- uses: MSP-Greg/setup-ruby-pkgs@v1
+ uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
mingw: _upgrade_ gdbm gmp libffi libyaml openssl ragel readline
@@ -50,13 +51,12 @@ jobs:
if ($rslt.contains($e)) { Write-Host $rslt }
else { Write-Host "`nCan't find $e" }
}
- - name: misc setup, autoreconf
+
+ - name: autogen
run: |
- mkdir install
- mkdir temp
- cd src
- sh -c "autoreconf -fi"
- working-directory:
+ ./autogen.sh
+ working-directory: src
+ shell: sh
- name: configure
run: |
@@ -66,7 +66,7 @@ jobs:
[Console]::InputEncoding = [System.Text.Encoding]::GetEncoding("IBM437")
$config_args = "--build=$env:CHOST --host=$env:CHOST --target=$env:CHOST"
Write-Host $config_args
- sh -c "../src/configure --disable-install-doc --prefix=/install $config_args"
+ sh -c "../src/configure --disable-install-doc --prefix=/. $config_args"
# Write-Host "-------------------------------------- config.log"
# Get-Content ./config.log | foreach {Write-Output $_}
@@ -92,7 +92,7 @@ jobs:
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("IBM437")
[Console]::InputEncoding = [System.Text.Encoding]::GetEncoding("IBM437")
- make DESTDIR=.. install-nodoc
+ make DESTDIR=../install install-nodoc
- name: test
timeout-minutes: 5
@@ -127,7 +127,7 @@ jobs:
payload: |
{
"ci": "GitHub Actions",
- "env": "${{ github.workflow }} / ${{ matrix.test_task }}",
+ "env": "${{ github.workflow }} ${{ matrix.msystem }} / ${{ matrix.test_task }}",
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"commit": "${{ github.sha }}",
"branch": "${{ github.ref }}".split('/').reverse()[0]
diff --git a/.github/workflows/mjit.yml b/.github/workflows/mjit.yml
index 68889ecf3d..8dcbcf7760 100644
--- a/.github/workflows/mjit.yml
+++ b/.github/workflows/mjit.yml
@@ -4,10 +4,10 @@ jobs:
make:
strategy:
matrix:
- test_task: [ "check" ] # to make job names consistent
- jit_opts: [ "--jit", "--jit-wait" ]
+ test_task: [check] # to make job names consistent
+ jit_opts: [--jit-wait]
fail-fast: false
- runs-on: ubuntu-latest
+ runs-on: ubuntu-20.04
if: "!contains(github.event.head_commit.message, '[ci skip]')"
env:
TESTOPTS: '-q --tty=no'
@@ -34,29 +34,29 @@ jobs:
sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
- name: Set ENV
run: |
- echo "JOBS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - run: autoconf
+ echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ - run: ./autogen.sh
working-directory: src
- name: Run configure
- run: ../src/configure -C --disable-install-doc
- - run: make $JOBS incs
- - run: make $JOBS
- - run: sudo make $JOBS -s install
+ run: ../src/configure -C --disable-install-doc cppflags=-DVM_CHECK_MODE
+ - run: make incs
+ - run: make
+ - run: sudo make -s install
- run: sudo apt-get install gdb # used by test / test-all failure
- name: Run test
run: |
ulimit -c unlimited
- make $JOBS -s test RUN_OPTS="$RUN_OPTS"
- timeout-minutes: 60
- - name: Run test-all
- run: |
- ulimit -c unlimited
- make $JOBS -s test-all RUN_OPTS="$RUN_OPTS"
+ make -s test RUN_OPTS="$RUN_OPTS"
timeout-minutes: 60
+ # - name: Run test-all
+ # run: |
+ # ulimit -c unlimited
+ # make -s test-all RUN_OPTS="$RUN_OPTS"
+ # timeout-minutes: 60
- name: Run test-spec
run: |
ulimit -c unlimited
- make $JOBS -s test-spec RUN_OPTS="$RUN_OPTS"
+ make -s test-spec RUN_OPTS="$RUN_OPTS"
timeout-minutes: 60
- uses: k0kubun/action-slack@v2.0.0
with:
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index fe517d2750..93187699be 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -4,7 +4,7 @@ jobs:
make:
strategy:
matrix:
- test_task: [ "check", "test-bundler-parallel", "test-bundled-gems", "test-all TESTS=--repeat-count=2", "leaked-globals" ]
+ test_task: [ "check", "test-bundler-parallel", "test-bundled-gems", "test-all TESTS=--repeat-count=2" ]
os:
- ubuntu-20.04
# - ubuntu-18.04
@@ -17,14 +17,10 @@ jobs:
os: ubuntu-16.04
- test_task: "test-all TESTS=--repeat-count=2"
os: ubuntu-16.04
- - test_task: leaked-globals
- os: ubuntu-16.04
- os: ubuntu-16.04
debug: -DRUBY_DEBUG
- test_task: "test-all TESTS=--repeat-count=2"
debug: -DRUBY_DEBUG
- - test_task: leaked-globals
- debug: -DRUBY_DEBUG
fail-fast: false
env:
GITPULLOPTIONS: --no-tags origin ${{github.ref}}
@@ -53,12 +49,14 @@ jobs:
- name: Set ENV
run: |
echo "JOBS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
- - run: autoconf
+ - run: ./autogen.sh
working-directory: src
- name: Run configure
run: ../src/configure -C --disable-install-doc cppflags=${{ matrix.debug }}
- run: make $JOBS incs
- run: make $JOBS
+ - run: make leaked-globals
+ if: matrix.test_task == 'check'
- run: make prepare-gems
if: matrix.test_task == 'check'
- name: Create dummy files in build dir
diff --git a/README.ja.md b/README.ja.md
index bee6433c62..6663b94680 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -52,11 +52,11 @@ Rubyリãƒã‚¸ãƒˆãƒªã®æœ¬æ¥ã®master㯠https://git.ruby-lang.org/ruby.git ã«ã
### Subversion
-å¤ã„Rubyã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã¯æ¬¡ã®ã‚³ãƒžãƒ³ãƒ‰ã§å–å¾—ã§ãã¾ã™ï¼Ž
+å¤ã„Rubyã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã¯æ¬¡ã®ã‚³ãƒžãƒ³ãƒ‰ã§ã‚‚å–å¾—ã§ãã¾ã™ï¼Ž
$ svn co https://svn.ruby-lang.org/repos/ruby/branches/ruby_2_6/ ruby
-ä»–ã«é–‹ç™ºä¸­ã®ãƒ–ランãƒã®ä¸€è¦§ã¯æ¬¡ã®ã‚³ãƒžãƒ³ãƒ‰ã§è¦‹ã‚‰ã‚Œã¾ã™ï¼Ž
+ä»–ã®ãƒ–ランãƒã®ä¸€è¦§ã¯æ¬¡ã®ã‚³ãƒžãƒ³ãƒ‰ã§è¦‹ã‚‰ã‚Œã¾ã™ï¼Ž
$ svn ls https://svn.ruby-lang.org/repos/ruby/branches/
@@ -71,31 +71,26 @@ https://www.ruby-lang.org/
## メーリングリスト
-Rubyã®ãƒ¡ãƒ¼ãƒªãƒ³ã‚°ãƒªã‚¹ãƒˆãŒã‚ã‚Šã¾ã™ï¼Žå‚加希望ã®æ–¹ã¯
-
-mailto:ruby-list-request@ruby-lang.org
-
-ã¾ã§æœ¬æ–‡ã«
+Rubyã®ãƒ¡ãƒ¼ãƒªãƒ³ã‚°ãƒªã‚¹ãƒˆãŒã‚ã‚Šã¾ã™ï¼Žå‚加希望ã®æ–¹ã¯ [ruby-list-request@ruby-lang.org] ã¾ã§æœ¬æ–‡ã«
subscribe
ã¨æ›¸ã„ã¦é€ã£ã¦ä¸‹ã•ã„.
-Ruby開発者å‘ã‘メーリングリストもã‚ã‚Šã¾ã™ï¼Žã“ã¡ã‚‰ã§ã¯rubyã®ãƒã‚°ï¼Œå°†æ¥ã®ä»•æ§˜æ‹¡å¼µãªã©å®Ÿè£…上ã®å•é¡Œã«ã¤ã„ã¦è­°è«–ã•ã‚Œã¦ã„ã¾ã™ï¼Ž å‚加希望ã®æ–¹ã¯
-
-mailto:ruby-dev-request@ruby-lang.org
-
-ã¾ã§ruby-listã¨åŒæ§˜ã®æ–¹æ³•ã§ãƒ¡ãƒ¼ãƒ«ã—ã¦ãã ã•ã„.
+Ruby開発者å‘ã‘メーリングリストもã‚ã‚Šã¾ã™ï¼Žã“ã¡ã‚‰ã§ã¯rubyã®ãƒã‚°ï¼Œå°†æ¥ã®ä»•æ§˜æ‹¡å¼µãªã©å®Ÿè£…上ã®å•é¡Œã«ã¤ã„ã¦è­°è«–ã•ã‚Œã¦ã„ã¾ã™ï¼Ž
+å‚加希望ã®æ–¹ã¯ [ruby-dev-request@ruby-lang.org] ã¾ã§ruby-listã¨åŒæ§˜ã®æ–¹æ³•ã§ãƒ¡ãƒ¼ãƒ«ã—ã¦ãã ã•ã„.
Ruby拡張モジュールã«ã¤ã„ã¦è©±ã—åˆã†ruby-extメーリングリストã¨æ•°å­¦é–¢ä¿‚ã®è©±é¡Œã«ã¤ã„ã¦è©±ã—åˆã†ruby-mathメーリングリストã¨
英語ã§rubyã«ã¤ã„ã¦è©±ã—åˆã†ruby-talkメーリングリストもã‚ã‚Šã¾ã™ï¼Žå‚加方法ã¯ã©ã‚Œã‚‚åŒã˜ã§ã™ï¼Ž
+[ruby-list-request@ruby-lang.org]: mailto:ruby-list-request@ruby-lang.org?subject=Join%20Ruby%20Mailing%20List&body=subscribe
+[ruby-dev-request@ruby-lang.org]: mailto:ruby-dev-request@ruby-lang.org?subject=Join%20Ruby%20Mailing%20List&body=subscribe
+
## コンパイル・インストール
以下ã®æ‰‹é †ã§è¡Œã£ã¦ãã ã•ã„.
-1. ã‚‚ã— `configure` ファイルãŒè¦‹ã¤ã‹ã‚‰ãªã„,もã—ã㯠`configure.ac` よりå¤ã„よã†ãªã‚‰ï¼Œ `autoconf` を実行ã—ã¦
- æ–°ã—ã `configure` を生æˆã™ã‚‹
+1. (Gitリãƒã‚¸ãƒˆãƒªã‹ã‚‰å–å¾—ã—ãŸã‚½ãƒ¼ã‚¹ã‚’ビルドã™ã‚‹å ´åˆ) `./autogen.sh` を実行ã—ã¦æ–°ã—ã `configure` を生æˆã™ã‚‹
2. `configure` を実行ã—㦠`Makefile` ãªã©ã‚’生æˆã™ã‚‹
diff --git a/README.md b/README.md
index 350b560905..bd7c7813a7 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,8 @@ if you are a committer.
### Subversion
-Stable branches for older Ruby versions can be checked out with the following command:
+Stable branches for older Ruby versions can be checked out with also the
+following command:
$ svn co https://svn.ruby-lang.org/repos/ruby/branches/ruby_2_6/ ruby
@@ -71,16 +72,17 @@ send the following phrase:
subscribe
-in the mail body (not subject) to the address
-[ruby-talk-request@ruby-lang.org](mailto:ruby-talk-request@ruby-lang.org?subject=Join%20Ruby%20Mailing%20List&body=subscribe).
+in the mail body (not subject) to the address [ruby-talk-request@ruby-lang.org].
+
+[ruby-talk-request@ruby-lang.org]: mailto:ruby-talk-request@ruby-lang.org?subject=Join%20Ruby%20Mailing%20List&body=subscribe
## How to compile and install
1. If you want to use Microsoft Visual C++ to compile Ruby, read
[win32/README.win32](win32/README.win32) instead of this document.
-2. If `./configure` does not exist or is older than `configure.ac`, run
- `autoconf` to (re)generate configure.
+2. Run `./autogen.sh` to generate configure, when you build the source checked
+ out from the Git repository.
3. Run `./configure`, which will generate `config.h` and `Makefile`.
diff --git a/aclocal.m4 b/aclocal.m4
index 940d91e83f..e69de29bb2 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,48 +0,0 @@
-# generated automatically by aclocal 1.16.2 -*- Autoconf -*-
-
-# Copyright (C) 1996-2020 Free Software Foundation, Inc.
-
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
-m4_include([tool/m4/_colorize_result_prepare.m4])
-m4_include([tool/m4/ac_msg_result.m4])
-m4_include([tool/m4/colorize_result.m4])
-m4_include([tool/m4/ruby_append_option.m4])
-m4_include([tool/m4/ruby_append_options.m4])
-m4_include([tool/m4/ruby_check_builtin_func.m4])
-m4_include([tool/m4/ruby_check_builtin_setjmp.m4])
-m4_include([tool/m4/ruby_check_printf_prefix.m4])
-m4_include([tool/m4/ruby_check_setjmp.m4])
-m4_include([tool/m4/ruby_check_signedness.m4])
-m4_include([tool/m4/ruby_check_sizeof.m4])
-m4_include([tool/m4/ruby_check_sysconf.m4])
-m4_include([tool/m4/ruby_cppoutfile.m4])
-m4_include([tool/m4/ruby_decl_attribute.m4])
-m4_include([tool/m4/ruby_default_arch.m4])
-m4_include([tool/m4/ruby_define_if.m4])
-m4_include([tool/m4/ruby_defint.m4])
-m4_include([tool/m4/ruby_dtrace_available.m4])
-m4_include([tool/m4/ruby_dtrace_postprocess.m4])
-m4_include([tool/m4/ruby_func_attribute.m4])
-m4_include([tool/m4/ruby_mingw32.m4])
-m4_include([tool/m4/ruby_prepend_option.m4])
-m4_include([tool/m4/ruby_prog_gnu_ld.m4])
-m4_include([tool/m4/ruby_replace_funcs.m4])
-m4_include([tool/m4/ruby_replace_type.m4])
-m4_include([tool/m4/ruby_rm_recursive.m4])
-m4_include([tool/m4/ruby_setjmp_type.m4])
-m4_include([tool/m4/ruby_stack_grow_direction.m4])
-m4_include([tool/m4/ruby_try_cflags.m4])
-m4_include([tool/m4/ruby_try_cxxflags.m4])
-m4_include([tool/m4/ruby_try_ldflags.m4])
-m4_include([tool/m4/ruby_type_attribute.m4])
-m4_include([tool/m4/ruby_universal_arch.m4])
-m4_include([tool/m4/ruby_werror_flag.m4])
diff --git a/addr2line.c b/addr2line.c
index 9669427d36..0029cffbca 100644
--- a/addr2line.c
+++ b/addr2line.c
@@ -437,7 +437,8 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
addr += a;
break;
case DW_LNS_fixed_advance_pc:
- a = *(unsigned char *)p++;
+ a = *(uint16_t *)p;
+ p += sizeof(uint16_t);
addr += a;
break;
case DW_LNS_set_prologue_end:
@@ -1625,6 +1626,7 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
static unsigned long
uncompress_debug_section(ElfW(Shdr) *shdr, char *file, char **ptr)
{
+ *ptr = NULL;
#ifdef SUPPORT_COMPRESSED_DEBUG_LINE
ElfW(Chdr) *chdr = (ElfW(Chdr) *)(file + shdr->sh_offset);
unsigned long destsize = chdr->ch_size;
@@ -1645,6 +1647,7 @@ uncompress_debug_section(ElfW(Shdr) *shdr, char *file, char **ptr)
fail:
free(*ptr);
+ *ptr = NULL;
#endif
return 0;
}
diff --git a/appveyor.yml b/appveyor.yml
index d31d4b2df9..78e3102008 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,12 +8,11 @@ platform:
- x64
environment:
ruby_version: "24-%Platform%"
- zlib_version: "1.2.11"
matrix:
- build: vs
vs: 120
ssl: OpenSSL
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
GEMS_FOR_TEST: ""
- build: vs
vs: 140
@@ -22,6 +21,8 @@ environment:
GEMS_FOR_TEST: ""
RELINE_TEST_ENCODING: "Windows-31J"
UPDATE_UNICODE: "UNICODE_FILES=. UNICODE_PROPERTY_FILES=. UNICODE_AUXILIARY_FILES=. UNICODE_EMOJI_FILES=."
+cache:
+ - c:\Tools\vcpkg\installed\
for:
-
matrix:
@@ -33,6 +34,12 @@ for:
- SET BITS=%Platform:x86=32%
- SET BITS=%BITS:x=%
- SET OPENSSL_DIR=C:\%ssl%-Win%BITS%
+ - cd C:\Tools\vcpkg
+ - git pull -q
+ - .\bootstrap-vcpkg.bat
+ - cd %APPVEYOR_BUILD_FOLDER%
+ - vcpkg --triplet %Platform%-windows install libffi libyaml zlib
+ - set PATH=c:\Tools\vcpkg\installed\%Platform%-windows\bin;%PATH%
- CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat
- SET vcvars
- '"%vcvars%" %Platform:x64=amd64%'
@@ -53,16 +60,17 @@ for:
- mkdir \usr\local\bin
- mkdir \usr\local\include
- mkdir \usr\local\lib
- - SET ZLIB_ZIP=.downloaded-cache\zlib%zlib_version:.=%.zip
- - if not exist %ZLIB_ZIP% curl -fsSL -o %ZLIB_ZIP% --retry 10 https://zlib.net/zlib%zlib_version:.=%.zip
- - 7z x -aos -o%APPVEYOR_BUILD_FOLDER%\ext\zlib %ZLIB_ZIP%
- for %%I in (%OPENSSL_DIR%\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I
- attrib +r /s /d
- mkdir %Platform%-mswin_%vs%
build_script:
- cd %APPVEYOR_BUILD_FOLDER%
- cd %Platform%-mswin_%vs%
- - ..\win32\configure.bat --without-ext=+,dbm,gdbm,readline --with-opt-dir=/usr/local --with-openssl-dir=%OPENSSL_DIR:\=/%
+ - >-
+ ..\win32\configure.bat
+ --without-ext=+,dbm,gdbm,readline
+ --with-opt-dir="/usr/local;c:/Tools/vcpkg/installed/%Platform%-windows"
+ --with-openssl-dir=%OPENSSL_DIR:\=/%
- nmake -l
- nmake install-nodoc
- \usr\bin\ruby -v -e "p :locale => Encoding.find('locale'), :filesystem => Encoding.find('filesystem')"
diff --git a/array.c b/array.c
index ea84473fdf..b48a6431e7 100644
--- a/array.c
+++ b/array.c
@@ -3838,6 +3838,7 @@ select_bang_ensure(VALUE a)
if (i2 < len && i2 < i1) {
long tail = 0;
+ rb_ary_modify(ary);
if (i1 < len) {
tail = len - i1;
RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
@@ -4071,7 +4072,7 @@ ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len)
else if (orig_len < pos) {
return Qnil;
}
- else if (orig_len < pos + len) {
+ if (orig_len < pos + len) {
len = orig_len - pos;
}
if (len == 0) {
@@ -4820,6 +4821,7 @@ ary_append(VALUE x, VALUE y)
if (n > 0) {
rb_ary_splice(x, RARRAY_LEN(x), 0, RARRAY_CONST_PTR_TRANSIENT(y), n);
}
+ RB_GC_GUARD(y);
return x;
}
@@ -8120,7 +8122,7 @@ Init_Array(void)
rb_define_method(rb_cArray, "each_index", rb_ary_each_index, 0);
rb_define_method(rb_cArray, "reverse_each", rb_ary_reverse_each, 0);
rb_define_method(rb_cArray, "length", rb_ary_length, 0);
- rb_define_alias(rb_cArray, "size", "length");
+ rb_define_method(rb_cArray, "size", rb_ary_length, 0);
rb_define_method(rb_cArray, "empty?", rb_ary_empty_p, 0);
rb_define_method(rb_cArray, "find_index", rb_ary_index, -1);
rb_define_method(rb_cArray, "index", rb_ary_index, -1);
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000000..44a1922cb0
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+PWD=
+case "$0" in
+*/*) srcdir=`dirname $0`;;
+*) srcdir="";;
+esac
+
+exec ${AUTORECONF:-autoreconf} --install --symlink "$@" ${srcdir:+"$srcdir"}
diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb
index 1c29a2b799..7d920c31b5 100644
--- a/bootstraptest/test_ractor.rb
+++ b/bootstraptest/test_ractor.rb
@@ -8,6 +8,24 @@ assert_equal 'Ractor', %q{
Ractor.new{}.class
}
+# Ractor.allocate is not supported
+assert_equal "[:ok, :ok]", %q{
+ rs = []
+ begin
+ Ractor.allocate
+ rescue => e
+ rs << :ok if e.message == 'allocator undefined for Ractor'
+ end
+
+ begin
+ Ractor.new{}.dup
+ rescue
+ rs << :ok if e.message == 'allocator undefined for Ractor'
+ end
+
+ rs
+}
+
# A Ractor can have a name
assert_equal 'test-name', %q{
r = Ractor.new name: 'test-name' do
@@ -158,6 +176,39 @@ assert_equal '[[:e1, 1], [:e2, 2]]', %q{
a #
}
+# dtoa race condition
+assert_equal '[:ok, :ok, :ok]', %q{
+ n = 3
+ n.times.map{
+ Ractor.new{
+ 10_000.times{ rand.to_s }
+ :ok
+ }
+ }.map(&:take)
+}
+
+# Ractor.make_shareable issue for locals in proc [Bug #18023]
+assert_equal '[:a, :b, :c, :d, :e]', %q{
+ v1, v2, v3, v4, v5 = :a, :b, :c, :d, :e
+ closure = Proc.new { [v1, v2, v3, v4, v5] }
+
+ Ractor.make_shareable(closure).call
+}
+
+# Ractor.make_shareable issue for locals in proc [Bug #18023]
+assert_equal '[:a, :b, :c, :d, :e, :f, :g]', %q{
+ a = :a
+ closure = -> {
+ b, c, d = :b, :c, :d
+ -> {
+ e, f, g = :e, :f, :g
+ -> { [a, b, c, d, e, f, g] }
+ }.call
+ }.call
+
+ Ractor.make_shareable(closure).call
+}
+
###
###
# Ractor still has several memory corruption so skip huge number of tests
@@ -709,6 +760,31 @@ assert_equal 'hello', %q{
end
}
+# yield/move should not make moved object when the yield is not succeeded
+assert_equal '"str"', %q{
+ R = Ractor.new{}
+ M = Ractor.current
+ r = Ractor.new do
+ s = 'str'
+ selected_r, v = Ractor.select R, yield_value: s, move: true
+ raise if selected_r != R # taken from R
+ M.send s.inspect # s should not be a moved object
+ end
+
+ Ractor.receive
+}
+
+# yield/move can fail
+assert_equal "allocator undefined for Thread", %q{
+ r = Ractor.new do
+ obj = Thread.new{}
+ Ractor.yield obj
+ rescue => e
+ e.message
+ end
+ r.take
+}
+
# Access to global-variables are prohibited
assert_equal 'can not access global variables $gv from non-main Ractors', %q{
$gv = 1
@@ -751,6 +827,18 @@ assert_equal 'ok', %q{
'ok'
}
+# $stdin,out,err belong to Ractor
+assert_equal 'ok', %q{
+ r = Ractor.new do
+ $stdin.itself
+ $stdout.itself
+ $stderr.itself
+ 'ok'
+ end
+
+ r.take
+}
+
# $DEBUG, $VERBOSE are Ractor local
assert_equal 'true', %q{
$DEBUG = true
@@ -1338,4 +1426,19 @@ assert_equal "ok", %q{
end
}
+# Can yield back values while GC is sweeping [Bug #18117]
+assert_equal "ok", %q{
+ workers = (0...8).map do
+ Ractor.new do
+ loop do
+ 10_000.times.map { Object.new }
+ Ractor.yield Time.now
+ end
+ end
+ end
+
+ 1_000.times { idle_worker, tmp_reporter = Ractor.select(*workers) }
+ "ok"
+}
+
end # if !ENV['GITHUB_WORKFLOW']
diff --git a/class.c b/class.c
index 47f35b1f90..682a21d488 100644
--- a/class.c
+++ b/class.c
@@ -26,6 +26,7 @@
#include "ruby/internal/config.h"
#include <ctype.h>
+#include "gc.h"
#include "constant.h"
#include "id_table.h"
#include "internal.h"
@@ -973,17 +974,22 @@ rb_include_module(VALUE klass, VALUE module)
int do_include = 1;
while (iclass) {
VALUE check_class = iclass->klass;
- while (check_class) {
- if (RB_TYPE_P(check_class, T_ICLASS) &&
- (RBASIC(check_class)->klass == module)) {
- do_include = 0;
+ /* During lazy sweeping, iclass->klass could be a dead object that
+ * has not yet been swept. */
+ if (!rb_objspace_garbage_object_p(check_class)) {
+ while (check_class) {
+ if (RB_TYPE_P(check_class, T_ICLASS) &&
+ (RBASIC(check_class)->klass == module)) {
+ do_include = 0;
+ }
+ check_class = RCLASS_SUPER(check_class);
}
- check_class = RCLASS_SUPER(check_class);
- }
- if (do_include) {
- include_modules_at(iclass->klass, RCLASS_ORIGIN(iclass->klass), module, TRUE);
+ if (do_include) {
+ include_modules_at(iclass->klass, RCLASS_ORIGIN(iclass->klass), module, TRUE);
+ }
}
+
iclass = iclass->next;
}
}
@@ -1134,10 +1140,12 @@ cache_clear_refined_method(ID key, VALUE value, void *data)
{
rb_method_entry_t *me = (rb_method_entry_t *) value;
- if (me->def->type == VM_METHOD_TYPE_REFINED) {
+ if (me->def->type == VM_METHOD_TYPE_REFINED && me->def->body.refined.orig_me) {
VALUE klass = (VALUE)data;
rb_clear_method_cache(klass, me->called_id);
}
+ // Refined method entries without an orig_me is going to stay in the method
+ // table of klass, like before the move, so no need to clear the cache.
return ID_TABLE_CONTINUE;
}
@@ -1178,17 +1186,22 @@ rb_prepend_module(VALUE klass, VALUE module)
struct rb_id_table *klass_m_tbl = RCLASS_M_TBL(klass);
struct rb_id_table *klass_origin_m_tbl = RCLASS_M_TBL(klass_origin);
while (iclass) {
- if (klass_had_no_origin && klass_origin_m_tbl == RCLASS_M_TBL(iclass->klass)) {
- // backfill an origin iclass to handle refinements and future prepends
- rb_id_table_foreach(RCLASS_M_TBL(iclass->klass), clear_module_cache_i, (void *)iclass->klass);
- RCLASS_M_TBL(iclass->klass) = klass_m_tbl;
- VALUE origin = rb_include_class_new(klass_origin, RCLASS_SUPER(iclass->klass));
- RCLASS_SET_SUPER(iclass->klass, origin);
- RCLASS_SET_INCLUDER(origin, RCLASS_INCLUDER(iclass->klass));
- RCLASS_SET_ORIGIN(iclass->klass, origin);
- RICLASS_SET_ORIGIN_SHARED_MTBL(origin);
+ /* During lazy sweeping, iclass->klass could be a dead object that
+ * has not yet been swept. */
+ if (!rb_objspace_garbage_object_p(iclass->klass)) {
+ if (klass_had_no_origin && klass_origin_m_tbl == RCLASS_M_TBL(iclass->klass)) {
+ // backfill an origin iclass to handle refinements and future prepends
+ rb_id_table_foreach(RCLASS_M_TBL(iclass->klass), clear_module_cache_i, (void *)iclass->klass);
+ RCLASS_M_TBL(iclass->klass) = klass_m_tbl;
+ VALUE origin = rb_include_class_new(klass_origin, RCLASS_SUPER(iclass->klass));
+ RCLASS_SET_SUPER(iclass->klass, origin);
+ RCLASS_SET_INCLUDER(origin, RCLASS_INCLUDER(iclass->klass));
+ RCLASS_SET_ORIGIN(iclass->klass, origin);
+ RICLASS_SET_ORIGIN_SHARED_MTBL(origin);
+ }
+ include_modules_at(iclass->klass, iclass->klass, module, FALSE);
}
- include_modules_at(iclass->klass, iclass->klass, module, FALSE);
+
iclass = iclass->next;
}
}
diff --git a/common.mk b/common.mk
index 763305082d..6372cbf92c 100644
--- a/common.mk
+++ b/common.mk
@@ -1344,7 +1344,7 @@ test-bundled-gems-prepare: $(TEST_RUNNABLE)-test-bundled-gems-prepare
no-test-bundled-gems-prepare: no-test-bundled-gems-precheck
yes-test-bundled-gems-prepare: yes-test-bundled-gems-precheck
$(XRUBY) -C "$(srcdir)" bin/gem install --no-document \
- --install-dir .bundle --conservative "bundler" "minitest:~> 5" "test-unit" "rake" "hoe" "yard" "pry" "packnga" "rexml" "json-schema" "rbs"
+ --install-dir .bundle --conservative "bundler" "minitest:~> 5" "test-unit" "rake" "hoe" "rexml" "json-schema" "rbs:~> 1.6.2"
PREPARE_BUNDLED_GEMS = test-bundled-gems-prepare
test-bundled-gems: $(TEST_RUNNABLE)-test-bundled-gems
@@ -2454,6 +2454,7 @@ class.$(OBJEXT): {$(VPATH)}config.h
class.$(OBJEXT): {$(VPATH)}constant.h
class.$(OBJEXT): {$(VPATH)}defines.h
class.$(OBJEXT): {$(VPATH)}encoding.h
+class.$(OBJEXT): {$(VPATH)}gc.h
class.$(OBJEXT): {$(VPATH)}id.h
class.$(OBJEXT): {$(VPATH)}id_table.h
class.$(OBJEXT): {$(VPATH)}intern.h
@@ -2803,6 +2804,7 @@ compile.$(OBJEXT): $(top_srcdir)/internal/hash.h
compile.$(OBJEXT): $(top_srcdir)/internal/imemo.h
compile.$(OBJEXT): $(top_srcdir)/internal/numeric.h
compile.$(OBJEXT): $(top_srcdir)/internal/object.h
+compile.$(OBJEXT): $(top_srcdir)/internal/rational.h
compile.$(OBJEXT): $(top_srcdir)/internal/re.h
compile.$(OBJEXT): $(top_srcdir)/internal/serial.h
compile.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
@@ -8428,6 +8430,7 @@ mjit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
mjit.$(OBJEXT): $(hdrdir)/ruby/version.h
mjit.$(OBJEXT): $(top_srcdir)/internal/array.h
mjit.$(OBJEXT): $(top_srcdir)/internal/class.h
+mjit.$(OBJEXT): $(top_srcdir)/internal/compile.h
mjit.$(OBJEXT): $(top_srcdir)/internal/compilers.h
mjit.$(OBJEXT): $(top_srcdir)/internal/cont.h
mjit.$(OBJEXT): $(top_srcdir)/internal/file.h
@@ -8449,6 +8452,7 @@ mjit.$(OBJEXT): {$(VPATH)}backward/2/limits.h
mjit.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
mjit.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
mjit.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+mjit.$(OBJEXT): {$(VPATH)}builtin.h
mjit.$(OBJEXT): {$(VPATH)}config.h
mjit.$(OBJEXT): {$(VPATH)}constant.h
mjit.$(OBJEXT): {$(VPATH)}debug.h
@@ -8459,6 +8463,9 @@ mjit.$(OBJEXT): {$(VPATH)}encoding.h
mjit.$(OBJEXT): {$(VPATH)}gc.h
mjit.$(OBJEXT): {$(VPATH)}id.h
mjit.$(OBJEXT): {$(VPATH)}id_table.h
+mjit.$(OBJEXT): {$(VPATH)}insns.def
+mjit.$(OBJEXT): {$(VPATH)}insns.inc
+mjit.$(OBJEXT): {$(VPATH)}insns_info.inc
mjit.$(OBJEXT): {$(VPATH)}intern.h
mjit.$(OBJEXT): {$(VPATH)}internal.h
mjit.$(OBJEXT): {$(VPATH)}internal/anyargs.h
@@ -8601,6 +8608,7 @@ mjit.$(OBJEXT): {$(VPATH)}internal/value_type.h
mjit.$(OBJEXT): {$(VPATH)}internal/variable.h
mjit.$(OBJEXT): {$(VPATH)}internal/warning_push.h
mjit.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+mjit.$(OBJEXT): {$(VPATH)}iseq.h
mjit.$(OBJEXT): {$(VPATH)}method.h
mjit.$(OBJEXT): {$(VPATH)}missing.h
mjit.$(OBJEXT): {$(VPATH)}mjit.c
@@ -14800,6 +14808,7 @@ util.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
util.$(OBJEXT): $(top_srcdir)/internal/util.h
util.$(OBJEXT): $(top_srcdir)/internal/warnings.h
util.$(OBJEXT): {$(VPATH)}assert.h
+util.$(OBJEXT): {$(VPATH)}atomic.h
util.$(OBJEXT): {$(VPATH)}backward/2/assume.h
util.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
util.$(OBJEXT): {$(VPATH)}backward/2/bool.h
@@ -14955,6 +14964,7 @@ util.$(OBJEXT): {$(VPATH)}internal/variable.h
util.$(OBJEXT): {$(VPATH)}internal/warning_push.h
util.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
util.$(OBJEXT): {$(VPATH)}missing.h
+util.$(OBJEXT): {$(VPATH)}ruby_atomic.h
util.$(OBJEXT): {$(VPATH)}st.h
util.$(OBJEXT): {$(VPATH)}subst.h
util.$(OBJEXT): {$(VPATH)}util.c
diff --git a/compile.c b/compile.c
index a5e082f46d..9844c145bc 100644
--- a/compile.c
+++ b/compile.c
@@ -28,6 +28,7 @@
#include "internal/hash.h"
#include "internal/numeric.h"
#include "internal/object.h"
+#include "internal/rational.h"
#include "internal/re.h"
#include "internal/symbol.h"
#include "internal/thread.h"
@@ -1985,6 +1986,16 @@ cdhash_cmp(VALUE val, VALUE lit)
else if (tlit == T_FLOAT) {
return rb_float_cmp(lit, val);
}
+ else if (tlit == T_RATIONAL) {
+ const struct RRational *rat1 = RRATIONAL(val);
+ const struct RRational *rat2 = RRATIONAL(lit);
+ return cdhash_cmp(rat1->num, rat2->num) || cdhash_cmp(rat1->den, rat2->den);
+ }
+ else if (tlit == T_COMPLEX) {
+ const struct RComplex *comp1 = RCOMPLEX(val);
+ const struct RComplex *comp2 = RCOMPLEX(lit);
+ return cdhash_cmp(comp1->real, comp2->real) || cdhash_cmp(comp1->imag, comp2->imag);
+ }
else {
UNREACHABLE_RETURN(-1);
}
@@ -2003,6 +2014,10 @@ cdhash_hash(VALUE a)
return FIX2LONG(rb_big_hash(a));
case T_FLOAT:
return rb_dbl_long_hash(RFLOAT_VALUE(a));
+ case T_RATIONAL:
+ return rb_rational_hash(a);
+ case T_COMPLEX:
+ return rb_complex_hash(a);
default:
UNREACHABLE_RETURN(0);
}
@@ -2884,7 +2899,8 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
else if (iobj != diobj && IS_INSN(&diobj->link) &&
IS_INSN_ID(diobj, jump) &&
- OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0)) {
+ OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0) &&
+ diobj->insn_info.events == 0) {
/*
* useless jump elimination:
* jump LABEL1
@@ -9901,7 +9917,7 @@ typedef unsigned int ibf_offset_t;
#define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
#if RUBY_DEVEL
-#define IBF_DEVEL_VERSION 2
+#define IBF_DEVEL_VERSION 3
#define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION)
#else
#define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
@@ -10735,6 +10751,33 @@ ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
return offset;
}
+static enum rb_id_table_iterator_result
+dump_outer_variable(ID id, VALUE val, void *dump)
+{
+ ibf_dump_write_small_value(dump, ibf_dump_id(dump, id));
+ ibf_dump_write_small_value(dump, val);
+
+ return ID_TABLE_CONTINUE;
+}
+
+static ibf_offset_t
+ibf_dump_outer_variables(struct ibf_dump *dump, const rb_iseq_t *iseq)
+{
+ struct rb_id_table * ovs = iseq->body->outer_variables;
+
+ ibf_offset_t offset = ibf_dump_pos(dump);
+
+ if (ovs) {
+ ibf_dump_write_small_value(dump, (VALUE)rb_id_table_size(ovs));
+ rb_id_table_foreach(ovs, dump_outer_variable, (void *)dump);
+ }
+ else {
+ ibf_dump_write_small_value(dump, (VALUE)0);
+ }
+
+ return offset;
+}
+
/* note that we dump out rb_call_info but load back rb_call_data */
static void
ibf_load_ci_entries(const struct ibf_load *load,
@@ -10779,6 +10822,28 @@ ibf_load_ci_entries(const struct ibf_load *load,
}
}
+static struct rb_id_table *
+ibf_load_outer_variables(const struct ibf_load * load, ibf_offset_t outer_variables_offset)
+{
+ ibf_offset_t reading_pos = outer_variables_offset;
+
+ struct rb_id_table *tbl = NULL;
+
+ size_t table_size = (size_t)ibf_load_small_value(load, &reading_pos);
+
+ if (table_size > 0) {
+ tbl = rb_id_table_create(table_size);
+ }
+
+ for (size_t i = 0; i < table_size; i++) {
+ ID key = ibf_load_id(load, (ID)ibf_load_small_value(load, &reading_pos));
+ VALUE value = ibf_load_small_value(load, &reading_pos);
+ rb_id_table_insert(tbl, key, value);
+ }
+
+ return tbl;
+}
+
static ibf_offset_t
ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
{
@@ -10818,6 +10883,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
const int parent_iseq_index = ibf_dump_iseq(dump, iseq->body->parent_iseq);
const int local_iseq_index = ibf_dump_iseq(dump, iseq->body->local_iseq);
const ibf_offset_t ci_entries_offset = ibf_dump_ci_entries(dump, iseq);
+ const ibf_offset_t outer_variables_offset = ibf_dump_outer_variables(dump, iseq);
#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
ibf_offset_t local_obj_list_offset;
@@ -10879,6 +10945,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
ibf_dump_write_small_value(dump, parent_iseq_index);
ibf_dump_write_small_value(dump, local_iseq_index);
ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(ci_entries_offset));
+ ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(outer_variables_offset));
ibf_dump_write_small_value(dump, body->variable.flip_count);
ibf_dump_write_small_value(dump, body->local_table_size);
ibf_dump_write_small_value(dump, body->is_size);
@@ -10985,6 +11052,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
const int parent_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
const int local_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
const ibf_offset_t ci_entries_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
+ const ibf_offset_t outer_variables_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
const rb_snum_t variable_flip_count = (rb_snum_t)ibf_load_small_value(load, &reading_pos);
const unsigned int local_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
const unsigned int is_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
@@ -11034,6 +11102,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, is_size);
ibf_load_ci_entries(load, ci_entries_offset, ci_size, &load_body->call_data);
+ load_body->outer_variables = ibf_load_outer_variables(load, outer_variables_offset);
load_body->param.opt_table = ibf_load_param_opt_table(load, param_opt_table_offset, param_opt_num);
load_body->param.keyword = ibf_load_param_keyword(load, param_keyword_offset);
load_body->param.flags.has_kw = (param_flags >> 4) & 1;
diff --git a/complex.c b/complex.c
index 560d9788b2..27c1f131e3 100644
--- a/complex.c
+++ b/complex.c
@@ -1328,8 +1328,8 @@ nucomp_numerator(VALUE self)
}
/* :nodoc: */
-static VALUE
-nucomp_hash(VALUE self)
+st_index_t
+rb_complex_hash(VALUE self)
{
st_index_t v, h[2];
VALUE n;
@@ -1340,7 +1340,13 @@ nucomp_hash(VALUE self)
n = rb_hash(dat->imag);
h[1] = NUM2LONG(n);
v = rb_memhash(h, sizeof(h));
- return ST2FIX(v);
+ return v;
+}
+
+static VALUE
+nucomp_hash(VALUE self)
+{
+ return ST2FIX(rb_complex_hash(self));
}
/* :nodoc: */
diff --git a/configure.ac b/configure.ac
index c143c23d3a..28fdfefbbc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,22 +1,48 @@
dnl Process this file with autoconf to produce a configure script.
-AC_INIT()
+AC_INIT
{
AC_CONFIG_AUX_DIR(tool)
-AC_CONFIG_MACRO_DIRS(tool/m4)
AC_PREREQ(2.67)
tooldir="$srcdir/tool"
-dnl override AC_CHECKING
-dnl placed here due to aclocal(1)'s
-dnl ignoring this definition in separate files
-AC_DEFUN([AC_CHECKING],[dnl
-AC_REQUIRE([_COLORIZE_RESULT_PREPARE])dnl
-AS_MESSAGE([checking ${msg_checking}$1${msg_reset}...])])dnl
-
AC_DISABLE_OPTION_CHECKING
+m4_include([tool/m4/_colorize_result_prepare.m4])
+m4_include([tool/m4/ac_msg_result.m4])
+m4_include([tool/m4/colorize_result.m4])
+m4_include([tool/m4/ruby_append_option.m4])
+m4_include([tool/m4/ruby_append_options.m4])
+m4_include([tool/m4/ruby_check_builtin_func.m4])
+m4_include([tool/m4/ruby_check_builtin_setjmp.m4])
+m4_include([tool/m4/ruby_check_printf_prefix.m4])
+m4_include([tool/m4/ruby_check_setjmp.m4])
+m4_include([tool/m4/ruby_check_signedness.m4])
+m4_include([tool/m4/ruby_check_sizeof.m4])
+m4_include([tool/m4/ruby_check_sysconf.m4])
+m4_include([tool/m4/ruby_cppoutfile.m4])
+m4_include([tool/m4/ruby_decl_attribute.m4])
+m4_include([tool/m4/ruby_default_arch.m4])
+m4_include([tool/m4/ruby_define_if.m4])
+m4_include([tool/m4/ruby_defint.m4])
+m4_include([tool/m4/ruby_dtrace_available.m4])
+m4_include([tool/m4/ruby_dtrace_postprocess.m4])
+m4_include([tool/m4/ruby_func_attribute.m4])
+m4_include([tool/m4/ruby_mingw32.m4])
+m4_include([tool/m4/ruby_prepend_option.m4])
+m4_include([tool/m4/ruby_prog_gnu_ld.m4])
+m4_include([tool/m4/ruby_replace_funcs.m4])
+m4_include([tool/m4/ruby_replace_type.m4])
+m4_include([tool/m4/ruby_rm_recursive.m4])
+m4_include([tool/m4/ruby_setjmp_type.m4])
+m4_include([tool/m4/ruby_stack_grow_direction.m4])
+m4_include([tool/m4/ruby_try_cflags.m4])
+m4_include([tool/m4/ruby_try_cxxflags.m4])
+m4_include([tool/m4/ruby_try_ldflags.m4])
+m4_include([tool/m4/ruby_universal_arch.m4])
+m4_include([tool/m4/ruby_werror_flag.m4])
+
AC_ARG_VAR([cflags], [additional CFLAGS (ignored when CFLAGS is given)])
AC_ARG_VAR([cppflags], [additional CPPFLAGS (ignored when CPPFLAGS is given)])
AC_ARG_VAR([cxxflags], [additional CXXFLAGS (ignored when CXXFLAGS is given)])
@@ -175,7 +201,10 @@ rb_test_CFLAGS=${CFLAGS+yes}
rb_test_CXXFLAGS=${CXXFLAGS+yes}
# BSD's ports and MacPorts prefix GNU binutils with 'g'
-AC_PROG_CC_C99
+
+dnl Seems necessarily in order to add -std=gnu99 option for gcc 4.9.
+m4_version_prereq([2.70], [], [AC_PROG_CC_C99])
+
AC_PROG_CXX
AC_PROG_CPP
AC_PROG_RANLIB
@@ -244,6 +273,9 @@ AC_ARG_ENABLE(load-relative,
AS_HELP_STRING([--enable-load-relative], [resolve load paths at run time]),
[load_relative=$enableval])
+# checks for UNIX variants that set C preprocessor variables
+AC_USE_SYSTEM_EXTENSIONS
+
dnl Checks for programs.
cflagspat=
@@ -283,12 +315,12 @@ AS_CASE(["$host_os:$build_os"],
AS_CASE(["$target_os"],
[darwin*], [
AC_MSG_CHECKING(if minimum required OS X version is supported)
- AC_TRY_CPP([@%:@include <AvailabilityMacros.h>
+ AC_PREPROC_IFELSE([AC_LANG_SOURCE([[@%:@include <AvailabilityMacros.h>
@%:@if MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5
@%:@error pre OS X 10.5
[!<===== pre OS X 10.5 =====>]
@%:@endif
- ],
+ ]])],
[macosx_min_required=yes],
[AC_MSG_RESULT(no)
AC_MSG_ERROR([Unsupported OS X version is required])])
@@ -316,6 +348,14 @@ AS_IF([test "$GCC" = yes -a "$gcc_major" -lt 3 ], [
AC_MSG_ERROR([too old GCC])
])
+
+AC_CACHE_CHECK([if thread-local storage is supported], [rb_cv_tls_supported],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[int __thread conftest;]])],
+ [rb_cv_tls_supported=yes],
+ [rb_cv_tls_supported=no])])
+AS_IF([test x"$rb_cv_tls_supported" != xyes],
+ [AC_DEFINE(RB_THREAD_LOCAL_SPECIFIER_IS_UNSUPPORTED)])
+
RUBY_PROG_GNU_LD
RUBY_CPPOUTFILE
@@ -399,8 +439,8 @@ AS_CASE(["$target_os"],
[mingw*], [
test "$rb_cv_msvcrt" = "" && unset rb_cv_msvcrt
AC_CACHE_CHECK(for mingw32 runtime DLL, rb_cv_msvcrt, [
- AC_TRY_LINK([@%:@include <stdio.h>],
- [FILE* volatile f = stdin; return 0;],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[@%:@include <stdio.h>]],
+ [[FILE* volatile f = stdin; return 0;]])],
[rb_cv_msvcrt=`$OBJDUMP -p conftest$ac_exeext |
tr A-Z a-z |
sed -n '/^[[ ]]*dll name: \(msvc.*\)\.dll$/{s//\1/p;q;}'`],
@@ -444,15 +484,12 @@ for prog in ${ac_tool_prefix:+${ac_tool_prefix}pkg-config} pkg-config; do
test -z "${PKG_CONFIG}" || break
done
-# checks for UNIX variants that set C preprocessor variables
-AC_USE_SYSTEM_EXTENSIONS
-
AC_MSG_CHECKING([whether it is Android])
-AC_TRY_COMPILE([
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@ifdef __ANDROID__
@%:@error android
@%:@endif
-], [],
+]], [[]])],
[AC_MSG_RESULT(no)],
[
AC_MSG_RESULT(yes)
@@ -499,7 +536,7 @@ AC_SUBST(CHDIR)
: "compiler section" && {
RUBY_WERROR_FLAG([
AC_MSG_CHECKING([whether CFLAGS is valid])
- AC_TRY_COMPILE([], [],
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
[AC_MSG_RESULT(yes)],
[
AC_MSG_RESULT(no)
@@ -515,7 +552,7 @@ RUBY_WERROR_FLAG([
echo '<?xml?><plist><dict><key>CFBundleIdentifier</key><string></string></dict></plist>' > Info.plist &&
:
} || AC_MSG_ERROR([failed to make temporary directory])
- AC_TRY_LINK([], [],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
[AC_MSG_RESULT(yes)],
[
cd .. && rm -fr tmp.$$.try_link
@@ -720,13 +757,13 @@ AS_IF([test "$GCC" = yes], [
], [
CFLAGS="$CFLAGS -Werror -Wuninitialized"
])
- AC_TRY_COMPILE([@%:@include <math.h>
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <math.h>
int foo(double x)
{
int exp;
frexp(x, &exp);
return exp;
- }], [if (foo(0.0)) return 1;],
+ }]], [[if (foo(0.0)) return 1;]])],
[rb_cv_mingw64_broken_frexp_modf=no],
[rb_cv_mingw64_broken_frexp_modf=yes])
CFLAGS="$save_CFLAGS"
@@ -816,13 +853,13 @@ AS_IF([test "$GCC" = yes], [
AS_CASE(["$target_cpu"], [[i[3-6]86*]], [
AC_CACHE_CHECK([for __sync_val_compare_and_swap], [rb_cv_gcc_compiler_cas], [
- AC_TRY_LINK([unsigned long atomic_var;],
- [__sync_val_compare_and_swap(&atomic_var, 0, 1);],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[unsigned long atomic_var;]],
+ [[__sync_val_compare_and_swap(&atomic_var, 0, 1);]])],
[rb_cv_gcc_compiler_cas=yes],
[
save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -march=i486"
- AC_TRY_LINK([unsigned long atomic_var;],
- [__sync_val_compare_and_swap(&atomic_var, 0, 1);],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[unsigned long atomic_var;]],
+ [[__sync_val_compare_and_swap(&atomic_var, 0, 1);]])],
[rb_cv_gcc_compiler_cas=i486],
[rb_cv_gcc_compiler_cas=no])
CFLAGS="$save_CFLAGS"
@@ -849,7 +886,7 @@ test -z "${ac_env_CXXFLAGS_set}" -a -n "${cxxflags+set}" && eval CXXFLAGS="\"$cx
AC_CACHE_CHECK([whether compiler has statement and declarations in expressions],
rb_cv_have_stmt_and_decl_in_expr,
- [AC_TRY_COMPILE([],[ __extension__ ({ int a = 0; a; }); ],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[ __extension__ ({ int a = 0; a; }); ]])],
[rb_cv_have_stmt_and_decl_in_expr=yes],
[rb_cv_have_stmt_and_decl_in_expr=no])])
AS_IF([test "$rb_cv_have_stmt_and_decl_in_expr" = yes], [
@@ -870,12 +907,12 @@ AS_CASE(["$target_os"],
[freebsd*], [
AC_CACHE_CHECK([whether pthread should be enabled by default],
rb_cv_enable_pthread_default,
- [AC_TRY_CPP([
+ [AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
#include <osreldate.h>
#if __FreeBSD_version < 502102
#error pthread should be disabled on this platform
#endif
- ],
+ ]])],
rb_cv_enable_pthread_default=yes,
rb_cv_enable_pthread_default=no)])
enable_pthread=$rb_cv_enable_pthread_default
@@ -903,8 +940,8 @@ AS_CASE(["$target_os"],
RUBY_APPEND_OPTIONS(CPPFLAGS, -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT)
AC_CACHE_CHECK([whether syscall(2) is deprecated], rb_cv_syscall_deprecated,
[RUBY_WERROR_FLAG([
- AC_TRY_COMPILE([@%:@include <unistd.h>],
- [if (syscall(0)) return 1;],
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <unistd.h>]],
+ [[if (syscall(0)) return 1;]])],
[rb_cv_syscall_deprecated=no],
[rb_cv_syscall_deprecated=yes])])])
AS_IF([test $rb_cv_syscall_deprecated = yes], [
@@ -931,7 +968,7 @@ AS_CASE(["$target_os"],
])
with_setjmp_type=sigsetjmp # to hijack SIGCHLD handler
AC_CACHE_CHECK(for broken crypt with 8bit chars, rb_cv_broken_crypt,
- [AC_TRY_RUN([
+ [AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <stdio.h>
#include <unistd.h>
#include <string.h>
@@ -968,7 +1005,7 @@ main()
}
return 0;
}
-],
+]])],
rb_cv_broken_crypt=no,
rb_cv_broken_crypt=yes,
rb_cv_broken_crypt=yes)])
@@ -997,11 +1034,11 @@ main()
[solaris*], [ LIBS="-lm $LIBS"
ac_cv_func_vfork=no
AC_MSG_CHECKING(whether _XOPEN_SOURCE is already given)
- AC_TRY_COMPILE([#include <unistd.h>
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>
#ifndef _XOPEN_SOURCE
#error _XOPEN_SOURCE is not defined
#endif
- ], [],
+ ]], [[]])],
[given_xopen_source=yes], [given_xopen_source=no])
AC_MSG_RESULT($given_xopen_source)
AS_IF([test $given_xopen_source = no], [
@@ -1011,13 +1048,13 @@ main()
AS_IF([test x"$define_xopen_source" != x], [
break
])
- RUBY_WERROR_FLAG([AC_TRY_COMPILE([
+ RUBY_WERROR_FLAG([AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#define _XOPEN_SOURCE ${tmp_xpg}00
#include <unistd.h>
#ifndef _XPG${tmp_xpg}
#error _XPG${tmp_xpg} should be defined by _XOPEN_SOURCE=${tmp_xpg}00
#endif
- ], [],
+ ]], [[]])],
[define_xopen_source=${tmp_xpg}00], [])
])
done
@@ -1135,7 +1172,8 @@ AS_IF([test -n "${rb_there_is_in_fact_no_gplusplus_but_autoconf_is_cheating_us}"
RUBY_WERROR_FLAG([
AC_MSG_CHECKING([whether CXXFLAGS is valid])
AC_LANG_PUSH(C++)
- AC_TRY_COMPILE([@%:@include <cstdio>], [], [AC_MSG_RESULT(yes)], [
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <cstdio>]], [[]])],
+ [AC_MSG_RESULT(yes)],[
AC_MSG_RESULT(no)
# The message mentions CXXFLAGS, but CPPFLAGS might also affects.
AC_MSG_WARN([something wrong with CXXFLAGS="$CXXFLAGS"])
@@ -1333,8 +1371,8 @@ AC_CACHE_CHECK(packed struct attribute, rb_cv_packed_struct,
"__pragma(pack(push, 1)) x __pragma(pack(pop))" \
"x __attribute__((packed))" \
; do
- AC_TRY_COMPILE([@%:@define PACKED_STRUCT(x) $mac
- PACKED_STRUCT(struct { int a; });], [],
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@define PACKED_STRUCT(x) $mac
+ PACKED_STRUCT(struct { int a; });]], [[]])],
[rb_cv_packed_struct=$mac; break])
done])
AS_IF([test "$rb_cv_packed_struct" != no], [
@@ -1377,10 +1415,10 @@ RUBY_REPLACE_TYPE(clockid_t, [], CLOCKID, [@%:@ifdef HAVE_TIME_H
# 2006). The check below is redundant and should always success. Remain not
# deleted for backward compat.
AC_CACHE_CHECK(for variable length macro, rb_cv_va_args_macro,
- [AC_TRY_COMPILE([
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
int foo(const char*);
@%:@define FOO(...) foo(@%:@__VA_ARGS__)
-], [FOO(1);FOO(1,2);FOO(1,2,3);],
+]], [[FOO(1);FOO(1,2);FOO(1,2,3);]])],
rb_cv_va_args_macro=yes,
rb_cv_va_args_macro=no)])
AS_IF([test "$rb_cv_va_args_macro" = yes], [
@@ -1398,7 +1436,7 @@ AS_IF([test "$rb_cv_va_args_macro" = yes], [
AC_CACHE_CHECK([if _Alignof() works], rb_cv_have__alignof,[
rb_cv_have__alignof=no
RUBY_WERROR_FLAG([
- AC_TRY_COMPILE([
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@ifdef HAVE_STDALIGN_H
@%:@include <stdalign.h>
@%:@endif
@@ -1408,7 +1446,7 @@ AC_CACHE_CHECK([if _Alignof() works], rb_cv_have__alignof,[
@%:@ifndef __GNUC__
@%:@define __extension__
@%:@endif
- ], [
+ ]], [[
typedef struct conftest_tag {
char _;
double d;
@@ -1418,9 +1456,9 @@ AC_CACHE_CHECK([if _Alignof() works], rb_cv_have__alignof,[
? 1 : -1
@:>@;
return conftest_ary@<:@0@:>@;
- ], [
+ ]])],[
rb_cv_have__alignof=yes
- ])
+ ],[])
])
])
AS_IF([test "$rb_cv_have__alignof" != no], [
@@ -1483,8 +1521,8 @@ AS_IF([test "$GCC" = yes], [
AC_CACHE_CHECK([for function alias], [rb_cv_gcc_function_alias],
[rb_cv_gcc_function_alias=no
for a in alias weak,alias; do
- AC_TRY_LINK([void foo(void) {}
- void bar(void) __attribute__(($a("foo")));], [bar()],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[void foo(void) {}
+ void bar(void) __attribute__(($a("foo")));]], [[bar()]])],
[rb_cv_gcc_function_alias=$a; break])
done])
AS_IF([test "$rb_cv_gcc_function_alias" != no], [
@@ -1496,14 +1534,14 @@ AS_IF([test "$GCC" = yes], [
])
AC_CACHE_CHECK([for __atomic builtins], [rb_cv_gcc_atomic_builtins], [
- AC_TRY_LINK([unsigned int atomic_var;],
- [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[unsigned int atomic_var;]],
+ [[
__atomic_exchange_n(&atomic_var, 0, __ATOMIC_SEQ_CST);
__atomic_exchange_n(&atomic_var, 1, __ATOMIC_SEQ_CST);
__atomic_fetch_add(&atomic_var, 1, __ATOMIC_SEQ_CST);
__atomic_fetch_sub(&atomic_var, 1, __ATOMIC_SEQ_CST);
__atomic_or_fetch(&atomic_var, 1, __ATOMIC_SEQ_CST);
- ],
+ ]])],
[rb_cv_gcc_atomic_builtins=yes],
[rb_cv_gcc_atomic_builtins=no])])
AS_IF([test "$rb_cv_gcc_atomic_builtins" = yes], [
@@ -1511,15 +1549,15 @@ AS_IF([test "$GCC" = yes], [
])
AC_CACHE_CHECK([for __sync builtins], [rb_cv_gcc_sync_builtins], [
- AC_TRY_LINK([unsigned int atomic_var;],
- [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[unsigned int atomic_var;]],
+ [[
__sync_lock_test_and_set(&atomic_var, 0);
__sync_lock_test_and_set(&atomic_var, 1);
__sync_fetch_and_add(&atomic_var, 1);
__sync_fetch_and_sub(&atomic_var, 1);
__sync_or_and_fetch(&atomic_var, 1);
__sync_val_compare_and_swap(&atomic_var, 0, 1);
- ],
+ ]])],
[rb_cv_gcc_sync_builtins=yes],
[rb_cv_gcc_sync_builtins=no])])
AS_IF([test "$rb_cv_gcc_sync_builtins" = yes], [
@@ -1529,8 +1567,8 @@ AS_IF([test "$GCC" = yes], [
AC_CACHE_CHECK(for __builtin_unreachable, rb_cv_func___builtin_unreachable,
[RUBY_WERROR_FLAG(
- [AC_TRY_LINK([volatile int zero;],
- [if (zero) __builtin_unreachable();],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[volatile int zero;]],
+ [[if (zero) __builtin_unreachable();]])],
[rb_cv_func___builtin_unreachable=yes],
[rb_cv_func___builtin_unreachable=no])
])
@@ -1543,8 +1581,8 @@ AC_CACHE_CHECK(for exported function attribute, rb_cv_func_exported, [
rb_cv_func_exported=no
RUBY_WERROR_FLAG([
for mac in '__attribute__ ((__visibility__("default")))' '__declspec(dllexport)'; do
- AC_TRY_COMPILE([@%:@define RUBY_FUNC_EXPORTED $mac extern
- RUBY_FUNC_EXPORTED void conftest_attribute_check(void);], [],
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@define RUBY_FUNC_EXPORTED $mac extern
+ RUBY_FUNC_EXPORTED void conftest_attribute_check(void);]], [[]])],
[rb_cv_func_exported="$mac"; break])
done
])])
@@ -1571,8 +1609,8 @@ AC_CACHE_CHECK(for function name string predefined identifier,
rb_cv_function_name_string=no
RUBY_WERROR_FLAG([
for func in __func__ __FUNCTION__; do
- AC_TRY_LINK([@%:@include <stdio.h>],
- [puts($func);],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[@%:@include <stdio.h>]],
+ [[puts($func);]])],
[rb_cv_function_name_string=$func
break])
done
@@ -1729,9 +1767,9 @@ AS_IF([test "x$rb_cv_type_int64_t" != xno], [
AC_CACHE_CHECK(for stack end address, rb_cv_stack_end_address,
[rb_cv_stack_end_address=no
- AC_TRY_LINK(
- [extern void *__libc_stack_end;],
- [if (!__libc_stack_end) return 1;],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[extern void *__libc_stack_end;]],
+ [[if (!__libc_stack_end) return 1;]])],
[rb_cv_stack_end_address="__libc_stack_end"])
])
AS_IF([test $rb_cv_stack_end_address != no], [
@@ -1759,14 +1797,14 @@ AS_CASE(["${target_cpu}-${target_os}:${target_archs}"],
AS_IF([test "x$ALLOCA" = "x"], [
AC_CACHE_CHECK([for dynamic size alloca], rb_cv_dynamic_alloca, [
for chk in ok __chkstk; do
- AC_TRY_LINK([
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
@%:@ifdef HAVE_ALLOCA_H
@%:@include <alloca.h>
@%:@endif
void $chk() {}
int dynamic_alloca_test;
- int dynamic_alloca_result;],
- [dynamic_alloca_result = alloca(dynamic_alloca_test) != 0;],
+ int dynamic_alloca_result;]],
+ [[dynamic_alloca_result = alloca(dynamic_alloca_test) != 0;]])],
[rb_cv_dynamic_alloca=$chk; break])
done])
AS_IF([test "x$rb_cv_dynamic_alloca" = "x__chkstk"], [
@@ -1818,9 +1856,9 @@ AC_CHECK_HEADERS(sys/pstat.h)
AC_CACHE_CHECK(for signbit, rb_cv_have_signbit,
- [AC_TRY_LINK([
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#include <math.h>
-], [int v = signbit(-0.0);],
+]], [[int v = signbit(-0.0);]])],
rb_cv_have_signbit=yes,
rb_cv_have_signbit=no)])
AS_IF([test "$rb_cv_have_signbit" = yes], [
@@ -1978,7 +2016,7 @@ AS_CASE(["$ac_cv_func_memset_s:$ac_cv_func_qsort_s"], [*yes*],
AS_IF([test "$ac_cv_func_getcwd" = yes], [
AC_CACHE_CHECK(if getcwd allocates buffer if NULL is given, [rb_cv_getcwd_malloc],
- [AC_TRY_RUN([
+ [AC_RUN_IFELSE([AC_LANG_SOURCE([[
@%:@include <stddef.h>
@%:@include <stdio.h>
@%:@ifdef HAVE_UNISTD_H
@@ -1997,7 +2035,7 @@ main(int argc, char **argv)
if (!getcwd(NULL, 0)) return EXIT_FAILURE;
return EXIT_SUCCESS;
}
-],
+]])],
rb_cv_getcwd_malloc=yes,
rb_cv_getcwd_malloc=no,
AS_CASE($target_os,
@@ -2045,21 +2083,21 @@ RUBY_CHECK_BUILTIN_FUNC(__builtin_trap, [__builtin_trap()])
AS_IF([test "$ac_cv_func_qsort_r" != no], [
AC_CACHE_CHECK(whether qsort_r is GNU version, rb_cv_gnu_qsort_r,
- [AC_TRY_COMPILE([
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@include <stdlib.h>
void (qsort_r)(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *, void *),
void *arg);
-],[ ],
+]], [[ ]])],
[rb_cv_gnu_qsort_r=yes],
[rb_cv_gnu_qsort_r=no])
])
AC_CACHE_CHECK(whether qsort_r is BSD version, rb_cv_bsd_qsort_r,
- [AC_TRY_COMPILE([
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@include <stdlib.h>
void (qsort_r)(void *base, size_t nmemb, size_t size,
void *arg, int (*compar)(void *, const void *, const void *));
-],[ ],
+]], [[ ]])],
[rb_cv_bsd_qsort_r=yes],
[rb_cv_bsd_qsort_r=no])
])
@@ -2074,7 +2112,7 @@ void (qsort_r)(void *base, size_t nmemb, size_t size,
AC_CACHE_CHECK(whether atan2 handles Inf as C99, rb_cv_atan2_inf_c99, [
AS_IF([test $ac_cv_func_atan2f:$ac_cv_func_atan2l = yes:yes], [
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
@%:@include <math.h>
@%:@ifdef HAVE_UNISTD_H
@%:@include <unistd.h>
@@ -2092,7 +2130,7 @@ main(int argc, char **argv)
if (fabs(atan2(INFINITY, INFINITY) - M_PI_4) <= 0.01) return EXIT_SUCCESS;
return EXIT_FAILURE;
}
-],
+]])],
[rb_cv_atan2_inf_c99=yes],
[rb_cv_atan2_inf_c99=no],
[AS_CASE($target_os, [mingw*|mswin*], [rb_cv_atan2_inf_c99=no], [rb_cv_atan2_inf_c99=yes])]
@@ -2121,9 +2159,9 @@ AS_IF([test x"$ac_cv_lib_rt_timer_settime" = xyes], [
])
AC_CACHE_CHECK(for unsetenv returns a value, rb_cv_unsetenv_return_value,
- [AC_TRY_COMPILE([
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <stdlib.h>
-], [int v = unsetenv("foo");],
+]], [[int v = unsetenv("foo");]])],
rb_cv_unsetenv_return_value=yes,
rb_cv_unsetenv_return_value=no)])
AS_IF([test "$rb_cv_unsetenv_return_value" = no], [
@@ -2141,21 +2179,21 @@ AS_IF([test "$use_setreuid" = yes], [
])
AC_STRUCT_TIMEZONE
AC_CACHE_CHECK(for struct tm.tm_gmtoff, rb_cv_member_struct_tm_tm_gmtoff,
- [AC_TRY_COMPILE([
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@define _BSD_SOURCE
@%:@define _DEFAULT_SOURCE
@%:@include <time.h>
- ],
- [struct tm t; t.tm_gmtoff = 3600;],
+ ]],
+ [[struct tm t; t.tm_gmtoff = 3600;]])],
[rb_cv_member_struct_tm_tm_gmtoff=yes],
[rb_cv_member_struct_tm_tm_gmtoff=no])])
AS_IF([test "$rb_cv_member_struct_tm_tm_gmtoff" = yes], [
AC_DEFINE(HAVE_STRUCT_TM_TM_GMTOFF)
])
AC_CACHE_CHECK(for external int daylight, rb_cv_have_daylight,
- [AC_TRY_LINK([#include <time.h>
- int i;],
- [i = daylight;],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <time.h>
+ int i;]],
+ [[i = daylight;]])],
rb_cv_have_daylight=yes,
rb_cv_have_daylight=no)])
AS_IF([test "$rb_cv_have_daylight" = yes], [
@@ -2163,7 +2201,7 @@ AS_IF([test "$rb_cv_have_daylight" = yes], [
])
AC_CACHE_CHECK(for negative time_t for gmtime(3), rb_cv_negative_time_t,
- [AC_TRY_RUN([
+ [AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <stdlib.h>
#include <time.h>
@@ -2193,7 +2231,7 @@ main()
check(gmtime(&t), 1, 12, 13, 20, 52);
return 0;
}
-],
+]])],
rb_cv_negative_time_t=yes,
rb_cv_negative_time_t=no,
rb_cv_negative_time_t=yes)])
@@ -2204,7 +2242,7 @@ AS_IF([test "$rb_cv_negative_time_t" = yes], [
# [ruby-dev:40910] overflow of time on FreeBSD
# http://www.freebsd.org/cgi/query-pr.cgi?pr=145341
AC_CACHE_CHECK(for localtime(3) overflow correctly, rb_cv_localtime_overflow,
- [AC_TRY_RUN([
+ [AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <stdlib.h>
#include <time.h>
@@ -2236,7 +2274,7 @@ main()
check(t);
return 0;
}
-],
+]])],
rb_cv_localtime_overflow=yes,
rb_cv_localtime_overflow=no,
rb_cv_localtime_overflow=no)])
@@ -2249,7 +2287,7 @@ AS_IF([test "$ac_cv_func_sigprocmask" = yes && test "$ac_cv_func_sigaction" = ye
], [
AC_CHECK_FUNCS(sigsetmask)
AC_CACHE_CHECK(for BSD signal semantics, rb_cv_bsd_signal,
- [AC_TRY_RUN([
+ [AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <stdio.h>
#include <signal.h>
@@ -2267,7 +2305,7 @@ main()
kill(getpid(), SIGINT);
return 0;
}
-],
+]])],
rb_cv_bsd_signal=yes,
rb_cv_bsd_signal=no,
rb_cv_bsd_signal=$ac_cv_func_sigsetmask)])
@@ -2313,7 +2351,7 @@ AS_IF([test "$rb_cv_rshift_sign" = yes], [
AS_IF([test "$ac_cv_func_copy_file_range" = no], [
AC_CACHE_CHECK([for copy_file_range],
rb_cv_use_copy_file_range,
- [AC_TRY_RUN([
+ [AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
@@ -2340,7 +2378,7 @@ main()
return 1;
#endif
}
- ],
+ ]])],
[rb_cv_use_copy_file_range=yes],
[rb_cv_use_copy_file_range=no],
[rb_cv_use_copy_file_range=no])])
@@ -2481,13 +2519,13 @@ AS_IF([test x"$enable_pthread" = xyes], [
AC_MSG_WARN("Don't know how to find pthread library on your system -- thread support disabled")
])
AC_CACHE_CHECK([whether pthread_t is scalar type], [rb_cv_scalar_pthread_t], [
- AC_TRY_COMPILE([
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@include <pthread.h>
- ], [
+ ]], [[
pthread_t thread_id;
thread_id = 0;
if (!thread_id) return 0;
- ], [rb_cv_scalar_pthread_t=yes], [rb_cv_scalar_pthread_t=no])
+ ]])],[rb_cv_scalar_pthread_t=yes],[rb_cv_scalar_pthread_t=no])
])
AS_IF([test x"$rb_cv_scalar_pthread_t" = xyes], [
: # RUBY_CHECK_SIZEOF(pthread_t, [void* int long], [], [@%:@include <pthread.h>])
@@ -2513,14 +2551,14 @@ AS_IF([test x"$enable_pthread" = xyes], [
"(pthread_self(), \"%s\", name)" \
"(name)" \
; do
- AC_TRY_COMPILE([
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@include <pthread.h>
@%:@ifdef HAVE_PTHREAD_NP_H
@%:@include <pthread_np.h>
@%:@endif
@%:@define SET_THREAD_NAME(name) pthread_setname_np${mac}
- ],
- [if (SET_THREAD_NAME("conftest")) return 1;],
+ ]],
+ [[if (SET_THREAD_NAME("conftest")) return 1;]])],
[rb_cv_func_pthread_setname_np_arguments="${mac}"
break])
done
@@ -2544,8 +2582,8 @@ AS_IF([test x"$enable_pthread" = xyes], [
AS_IF([test x"$ac_cv_header_ucontext_h" = xno], [
AC_CACHE_CHECK([if signal.h defines ucontext_t], [rb_cv_ucontext_in_signal_h],
- [AC_TRY_COMPILE([@%:@include <signal.h>],
- [size_t size = sizeof(ucontext_t);],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <signal.h>]],
+ [[size_t size = sizeof(ucontext_t);]])],
[rb_cv_ucontext_in_signal_h=yes], [rb_cv_ucontext_in_signal_h=no])])
AS_IF([test x"$rb_cv_ucontext_in_signal_h" = xyes], [
AC_DEFINE_UNQUOTED(UCONTEXT_IN_SIGNAL_H, 1)
@@ -2553,14 +2591,14 @@ AS_IF([test x"$ac_cv_header_ucontext_h" = xno], [
])
AS_IF([test x"$ac_cv_header_ucontext_h" = xyes -o x"$rb_cv_ucontext_in_signal_h" = xyes], [
AC_CACHE_CHECK([if mcontext_t is a pointer], [rb_cv_mcontext_t_ptr],
- [AC_TRY_COMPILE([
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@include <signal.h>
@%:@ifdef HAVE_UCONTEXT_H
@%:@include <ucontext.h>
@%:@endif
mcontext_t test(mcontext_t mc) {return mc+1;}
- ],
- [test(0);],
+ ]],
+ [[test(0);]])],
[rb_cv_mcontext_t_ptr=yes], [rb_cv_mcontext_t_ptr=no])])
AS_IF([test x"$rb_cv_mcontext_t_ptr" = xyes], [
AC_DEFINE_UNQUOTED(DEFINE_MCONTEXT_PTR(mc, uc), mcontext_t mc = (uc)->uc_mcontext)
@@ -2574,7 +2612,7 @@ AS_IF([test x"$ac_cv_header_ucontext_h" = xyes -o x"$rb_cv_ucontext_in_signal_h"
AS_IF([test "$ac_cv_func_fork_works" = "yes" -a "$rb_with_pthread" = "yes"], [
AC_CACHE_CHECK([if fork works with pthread], rb_cv_fork_with_pthread,
- [AC_TRY_RUN([
+ [AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
@@ -2630,7 +2668,7 @@ main(int argc, char *argv[])
}
return EXIT_SUCCESS;
-}],
+}]])],
rb_cv_fork_with_pthread=yes,
rb_cv_fork_with_pthread=no,
rb_cv_fork_with_pthread=yes)])
@@ -2653,7 +2691,7 @@ AC_ARG_WITH(dln-a-out,
with_dln_a_out=no])], [with_dln_a_out=no])
AC_CACHE_CHECK(whether ELF binaries are produced, rb_cv_binary_elf,
-[AC_TRY_LINK([],[], [
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[
AS_CASE(["`head -1 conftest$EXEEXT | tr -dc '\177ELF' | tr '\177' .`"],
[.ELF*], [rb_cv_binary_elf=yes], [rb_cv_binary_elf=no])],
rb_cv_binary_elf=no)])
@@ -2810,6 +2848,14 @@ AS_IF([test "$with_dln_a_out" != yes], [
: ${LDFLAGS=""}
: ${LIBPATHENV=DYLD_FALLBACK_LIBRARY_PATH}
: ${PRELOADENV=DYLD_INSERT_LIBRARIES}
+ AS_IF([test x"$enable_shared" = xyes], [
+ # Resolve symbols from libruby.dylib when --enable-shared
+ EXTDLDFLAGS='$(LIBRUBYARG_SHARED)'
+ ], [test "x$EXTSTATIC" = x], [
+ # When building exts as bundles, a mach-o bundle needs to know its loader
+ # program to bind symbols from the ruby executable
+ EXTDLDFLAGS="-bundle_loader '\$(BUILTRUBY)'"
+ ])
rb_cv_dlopen=yes],
[aix*], [ : ${LDSHARED='$(CC)'}
AS_IF([test "$GCC" = yes], [
@@ -2846,15 +2892,26 @@ AS_IF([test "$with_dln_a_out" != yes], [
AS_IF([test "$rb_cv_dlopen" = yes], [
AS_CASE(["$target_os"],
[darwin*], [
+ AC_SUBST(ADDITIONAL_DLDFLAGS, "")
for flag in \
- "-undefined dynamic_lookup" \
"-multiply_defined suppress" \
+ "-undefined dynamic_lookup" \
; do
- test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`"
- RUBY_TRY_LDFLAGS([$flag], [], [flag=])
- AS_IF([test "x$flag" != x], [
- RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag])
- ])
+ test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`"
+ RUBY_TRY_LDFLAGS([$flag], [], [flag=])
+ AS_IF([test x"$flag" = x], [continue])
+
+ AC_MSG_CHECKING([whether $flag is accepted for bundle])
+ : > conftest.c
+ AS_IF([${LDSHARED/'$(CC)'/$CC} -o conftest.bundle $flag conftest.c >/dev/null 2>conftest.err &&
+ test ! -s conftest.err], [
+ AC_MSG_RESULT([yes])
+ RUBY_APPEND_OPTIONS(DLDFLAGS, [$flag])
+ ], [
+ AC_MSG_RESULT([no])
+ RUBY_APPEND_OPTIONS(ADDITIONAL_DLDFLAGS, [$flag])
+ ])
+ rm -fr conftest.*
done
])
])
@@ -2930,7 +2987,7 @@ AC_CHECK_FUNCS(backtrace)
AS_IF([test "x$ac_cv_func_backtrace" = xyes], [
AC_CACHE_CHECK(for broken backtrace, rb_cv_broken_backtrace,
- [AC_TRY_RUN([
+ [AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -2979,7 +3036,7 @@ main(void)
a[0] = 1;
return EXIT_SUCCESS;
}
-],
+]])],
rb_cv_broken_backtrace=no,
rb_cv_broken_backtrace=yes,
rb_cv_broken_backtrace=no)])
@@ -2999,11 +3056,10 @@ AS_IF([test "$ac_cv_header_a_out_h" = yes], [
AS_IF([test "$with_dln_a_out" = yes || test "$rb_cv_dlopen" = unknown], [
cat confdefs.h > config.h
AC_CACHE_CHECK(whether matz's dln works, rb_cv_dln_a_out,
- [AC_TRY_COMPILE([
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#define USE_DLN_A_OUT
#include "dln.c"
-],
- [],
+]], [[]])],
rb_cv_dln_a_out=yes,
rb_cv_dln_a_out=no)])
AS_IF([test "$rb_cv_dln_a_out" = yes], [
@@ -3076,10 +3132,10 @@ AS_IF([test "$with_dln_a_out" = yes], [
AC_ARG_WITH(ext,
- AC_HELP_STRING([--with-ext=EXTS],
+ AS_HELP_STRING([--with-ext=EXTS],
[pass to --with-ext option of extmk.rb]))
AC_ARG_WITH(out-ext,
- AC_HELP_STRING([--with-out-ext=EXTS],
+ AS_HELP_STRING([--with-out-ext=EXTS],
[pass to --without-ext option of extmk.rb]))
EXTSTATIC=
AC_SUBST(EXTSTATIC)dnl
@@ -3263,6 +3319,11 @@ AS_CASE("$enable_shared", [yes], [
AC_DEFINE_UNQUOTED(LIBDIR_BASENAME, ["${libdir_basename}"])
libdir_basename="${libdir_basename}"${multiarch+'/${arch}'}
+ RUBY_TRY_LDFLAGS([${linker_flag}--no-as-needed], [no_as_needed=yes], [no_as_needed=no])
+ AS_IF([test "$no_as_needed" = yes], [
+ RUBY_APPEND_OPTIONS(LDFLAGS, [${linker_flag}--no-as-needed])
+ ])
+
AS_CASE(["$target_os"],
[freebsd*|dragonfly*], [],
[
@@ -3541,7 +3602,7 @@ AS_IF([test "$rb_with_pthread" = "yes"], [
THREAD_MODEL=pthread
])
AC_CACHE_CHECK([for prefix of external symbols], rb_cv_symbol_prefix, [
- AC_TRY_COMPILE([extern void conftest_external(void) {}], [], [
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[extern void conftest_external(void) {}]], [[]])],[
rb_cv_symbol_prefix=`$NM conftest.$ac_objext |
sed -n ['/.*T[ ]\([^ ]*\)conftest_external.*/!d;s//\1/p;q']`
],
@@ -3552,7 +3613,7 @@ SYMBOL_PREFIX="$rb_cv_symbol_prefix"
test "x$SYMBOL_PREFIX" = xNONE && SYMBOL_PREFIX=''
DLNOBJ=dln.o
AC_ARG_ENABLE(dln,
- AC_HELP_STRING([--disable-dln], [disable dynamic link feature]),
+ AS_HELP_STRING([--disable-dln], [disable dynamic link feature]),
[test "$enableval" = yes || DLNOBJ=dmydln.o])
AC_SUBST(DLNOBJ)
MINIDLNOBJ=dmydln.o
@@ -3684,13 +3745,12 @@ AS_IF([test "${universal_binary-no}" = yes ], [
AC_CACHE_CHECK([for architecture macros], rb_cv_architecture_macros, [
mv confdefs.h confdefs1.h
: > confdefs.h
- AC_TRY_COMPILE([@%:@if defined __`echo ${universal_archnames} |
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@if defined __`echo ${universal_archnames} |
sed 's/=[^ ]*//g;s/ /__ || defined __/g'`__
@%:@else
@%:@error
>>>>>><<<<<<
-@%:@endif], [],
-[
+@%:@endif]], [[]])],[
rb_cv_architecture_macros=yes
mv -f confdefs1.h confdefs.h
], [
@@ -3703,16 +3763,17 @@ AS_IF([test "${universal_binary-no}" = yes ], [
CFLAGS="$new_cflags -arch $archs"
archs="__${archs}__"
AC_MSG_CHECKING([for macro ${archs} on ${cpu}])
- AC_TRY_COMPILE([@%:@ifndef ${archs}
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@ifndef ${archs}
@%:@error
-@%:@endif], [], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])])
+@%:@endif]], [[]])],
+ [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])])
done
mv -f confdefs1.h confdefs.h
AC_MSG_ERROR([failed])
])])
AC_CACHE_CHECK(whether __ARCHITECTURE__ is available, rb_cv_architecture_available,
- AC_TRY_COMPILE([@%:@include <stdio.h>
- const char arch[[]] = __ARCHITECTURE__;], [puts(arch);],
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <stdio.h>
+ const char arch[[]] = __ARCHITECTURE__;]], [[puts(arch);]])],
[rb_cv_architecture_available=yes], [rb_cv_architecture_available=no]))
])
@@ -4115,7 +4176,7 @@ AC_CONFIG_FILES(Makefile:template/Makefile.in, [
[EXEEXT='$EXEEXT' MAKE='${MAKE-make}' gnumake='$gnumake' GIT='$GIT'])
AC_ARG_WITH([ruby-pc],
- AC_HELP_STRING([--with-ruby-pc=FILENAME], [pc file basename]),
+ AS_HELP_STRING([--with-ruby-pc=FILENAME], [pc file basename]),
[ruby_pc="$withval"],
[ruby_pc="${RUBY_BASE_NAME}-${MAJOR}.${MINOR}.pc"])
AC_SUBST(ruby_pc)
diff --git a/cont.c b/cont.c
index a8250c3273..a7103a087e 100644
--- a/cont.c
+++ b/cont.c
@@ -1155,6 +1155,11 @@ VALUE rb_fiberptr_self(struct rb_fiber_struct *fiber)
return fiber->cont.self;
}
+unsigned int rb_fiberptr_blocking(struct rb_fiber_struct *fiber)
+{
+ return fiber->blocking;
+}
+
// This is used for root_fiber because other fibers call cont_init_mjit_cont through cont_new.
void
rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber)
@@ -2003,7 +2008,7 @@ rb_fiber_set_scheduler(VALUE klass, VALUE scheduler)
return rb_scheduler_set(scheduler);
}
-static void rb_fiber_terminate(rb_fiber_t *fiber, int need_interrupt);
+NORETURN(static void rb_fiber_terminate(rb_fiber_t *fiber, int need_interrupt));
void
rb_fiber_start(void)
@@ -2379,6 +2384,7 @@ rb_fiber_terminate(rb_fiber_t *fiber, int need_interrupt)
next_fiber = return_fiber(true);
if (need_interrupt) RUBY_VM_SET_INTERRUPT(&next_fiber->cont.saved_ec);
fiber_switch(next_fiber, 1, &value, RB_NO_KEYWORDS, Qfalse, false);
+ ruby_stop(0);
}
VALUE
diff --git a/debug_counter.h b/debug_counter.h
index 6dc66ef988..3c20821db6 100644
--- a/debug_counter.h
+++ b/debug_counter.h
@@ -62,6 +62,9 @@ RB_DEBUG_COUNTER(ccs_not_found) // count for not found corresponding ccs on met
// vm_eval.c
RB_DEBUG_COUNTER(call0_public)
RB_DEBUG_COUNTER(call0_other)
+RB_DEBUG_COUNTER(gccct_hit)
+RB_DEBUG_COUNTER(gccct_miss)
+RB_DEBUG_COUNTER(gccct_null)
// iseq
RB_DEBUG_COUNTER(iseq_num) // number of total created iseq
diff --git a/dir.c b/dir.c
index bedbdd9f83..c19ef637a1 100644
--- a/dir.c
+++ b/dir.c
@@ -2273,6 +2273,8 @@ glob_helper(
int escape = !(flags & FNM_NOESCAPE);
size_t pathlen = baselen + namelen;
+ rb_check_stack_overflow();
+
for (cur = beg; cur < end; ++cur) {
struct glob_pattern *p = *cur;
if (p->type == RECURSIVE) {
diff --git a/enc/Makefile.in b/enc/Makefile.in
index 9203874386..3854f66de6 100644
--- a/enc/Makefile.in
+++ b/enc/Makefile.in
@@ -21,6 +21,7 @@ TRANSSODIR = $(ENCSODIR)/trans
DLEXT = @DLEXT@
OBJEXT = @OBJEXT@
LIBEXT = @LIBEXT@
+EXEEXT = @EXEEXT@
TIMESTAMPDIR = $(EXTOUT)/.timestamp
ENC_TRANS_D = $(TIMESTAMPDIR)/.enc-trans.time
ENC_TRANS_SO_D = $(TIMESTAMPDIR)/.enc-trans.so.time
@@ -34,6 +35,7 @@ RUBY_SO_NAME = @RUBY_SO_NAME@
LIBRUBY = @LIBRUBY@
LIBRUBYARG_SHARED = @LIBRUBYARG_SHARED@
LIBRUBYARG_STATIC = $(LIBRUBYARG_SHARED)
+BUILTRUBY = $(topdir)/miniruby$(EXEEXT)
empty =
AR = @AR@
diff --git a/encoding.c b/encoding.c
index 330be29f4b..32d5a349eb 100644
--- a/encoding.c
+++ b/encoding.c
@@ -101,8 +101,6 @@ static rb_encoding *global_enc_ascii,
#define ENCODING_NAMELEN_MAX 63
#define valid_encoding_name_p(name) ((name) && strlen(name) <= ENCODING_NAMELEN_MAX)
-#define enc_autoload_p(enc) (!rb_enc_mbmaxlen(enc))
-
static const rb_data_type_t encoding_data_type = {
"encoding",
{0, 0, 0,},
@@ -207,16 +205,14 @@ rb_enc_dummy_p(rb_encoding *enc)
return ENC_DUMMY_P(enc) != 0;
}
-static int enc_autoload(rb_encoding *);
-
static int
check_encoding(rb_encoding *enc)
{
int index = rb_enc_to_index(enc);
if (rb_enc_from_index(index) != enc)
return -1;
- if (enc_autoload_p(enc)) {
- index = enc_autoload(enc);
+ if (rb_enc_autoload_p(enc)) {
+ index = rb_enc_autoload(enc);
}
return index;
}
@@ -260,7 +256,7 @@ must_encindex(int index)
rb_raise(rb_eEncodingError, "wrong encoding index %d for %s (expected %d)",
index, rb_enc_name(enc), ENC_TO_ENCINDEX(enc));
}
- if (enc_autoload_p(enc) && enc_autoload(enc) == -1) {
+ if (rb_enc_autoload_p(enc) && rb_enc_autoload(enc) == -1) {
rb_loaderror("failed to load encoding (%s)",
rb_enc_name(enc));
}
@@ -444,7 +440,7 @@ rb_enc_register(const char *name, rb_encoding *encoding)
if (STRCASECMP(name, rb_enc_name(oldenc))) {
index = enc_register(enc_table, name, encoding);
}
- else if (enc_autoload_p(oldenc) || !ENC_DUMMY_P(oldenc)) {
+ else if (rb_enc_autoload_p(oldenc) || !ENC_DUMMY_P(oldenc)) {
enc_register_at(enc_table, index, name, encoding);
}
else {
@@ -834,7 +830,7 @@ load_encoding(const char *name)
else if ((idx = enc_registered(enc_table, name)) < 0) {
idx = -1;
}
- else if (enc_autoload_p(enc_table->list[idx].enc)) {
+ else if (rb_enc_autoload_p(enc_table->list[idx].enc)) {
idx = -1;
}
}
@@ -853,8 +849,8 @@ enc_autoload_body(struct enc_table *enc_table, rb_encoding *enc)
do {
if (i >= enc_table->count) return -1;
} while (enc_table->list[i].enc != base && (++i, 1));
- if (enc_autoload_p(base)) {
- if (enc_autoload(base) < 0) return -1;
+ if (rb_enc_autoload_p(base)) {
+ if (rb_enc_autoload(base) < 0) return -1;
}
i = enc->ruby_encoding_index;
enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base);
@@ -867,8 +863,8 @@ enc_autoload_body(struct enc_table *enc_table, rb_encoding *enc)
}
}
-static int
-enc_autoload(rb_encoding *enc)
+int
+rb_enc_autoload(rb_encoding *enc)
{
int i;
GLOBAL_ENC_TABLE_EVAL(enc_table, i = enc_autoload_body(enc_table, enc));
@@ -895,8 +891,8 @@ rb_enc_find_index(const char *name)
rb_raise(rb_eArgError, "encoding %s is not registered", name);
}
}
- else if (enc_autoload_p(enc)) {
- if (enc_autoload(enc) < 0) {
+ else if (rb_enc_autoload_p(enc)) {
+ if (rb_enc_autoload(enc) < 0) {
rb_warn("failed to load encoding (%s); use ASCII-8BIT instead",
name);
return 0;
@@ -1340,7 +1336,7 @@ enc_inspect(VALUE self)
"#<%"PRIsVALUE":%s%s%s>", rb_obj_class(self),
rb_enc_name(enc),
(ENC_DUMMY_P(enc) ? " (dummy)" : ""),
- enc_autoload_p(enc) ? " (autoload)" : "");
+ rb_enc_autoload_p(enc) ? " (autoload)" : "");
}
/*
diff --git a/enum.c b/enum.c
index da9f4348fa..b1a617d585 100644
--- a/enum.c
+++ b/enum.c
@@ -682,7 +682,7 @@ enum_to_a(int argc, VALUE *argv, VALUE obj)
{
VALUE ary = rb_ary_new();
- rb_block_call(obj, id_each, argc, argv, collect_all, ary);
+ rb_block_call_kw(obj, id_each, argc, argv, collect_all, ary, RB_PASS_CALLED_KEYWORDS);
return ary;
}
@@ -805,7 +805,7 @@ ary_inject_op(VALUE ary, VALUE init, VALUE op)
if (FIXNUM_P(e)) {
n += FIX2LONG(e); /* should not overflow long type */
if (!FIXABLE(n)) {
- v = rb_big_plus(ULONG2NUM(n), v);
+ v = rb_big_plus(LONG2NUM(n), v);
n = 0;
}
}
diff --git a/enumerator.c b/enumerator.c
index b4a7cb588e..90d2ec433c 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -2678,8 +2678,13 @@ lazy_with_index_proc(VALUE proc_entry, struct MEMO* result, VALUE memos, long me
return result;
}
+static VALUE
+lazy_with_index_size(VALUE proc, VALUE receiver) {
+ return receiver;
+}
+
static const lazyenum_funcs lazy_with_index_funcs = {
- lazy_with_index_proc, 0,
+ lazy_with_index_proc, lazy_with_index_size,
};
/*
diff --git a/error.c b/error.c
index d1dfd03a9d..d614a9c0a2 100644
--- a/error.c
+++ b/error.c
@@ -306,7 +306,8 @@ rb_warning_warn(VALUE mod, VALUE str)
static int
rb_warning_warn_arity(void)
{
- return rb_method_entry_arity(rb_method_entry(rb_singleton_class(rb_mWarning), id_warn));
+ const rb_method_entry_t *me = rb_method_entry(rb_singleton_class(rb_mWarning), id_warn);
+ return me ? rb_method_entry_arity(me) : 1;
}
static VALUE
@@ -1950,8 +1951,10 @@ name_err_mesg_to_str(VALUE obj)
d = rb_protect(name_err_mesg_receiver_name, obj, &state);
if (state || d == Qundef || d == Qnil)
d = rb_protect(rb_inspect, obj, &state);
- if (state)
+ if (state) {
rb_set_errinfo(Qnil);
+ }
+ d = rb_check_string_type(d);
if (NIL_P(d)) {
d = rb_any_to_s(obj);
}
diff --git a/eval.c b/eval.c
index 55ac8b4eac..839518185a 100644
--- a/eval.c
+++ b/eval.c
@@ -1021,7 +1021,7 @@ rb_vrescue2(VALUE (* b_proc) (VALUE), VALUE data1,
else if (result) {
/* escape from r_proc */
if (state == TAG_RETRY) {
- state = 0;
+ state = TAG_NONE;
ec->errinfo = Qnil;
result = Qfalse;
goto retry_entry;
@@ -1033,17 +1033,21 @@ rb_vrescue2(VALUE (* b_proc) (VALUE), VALUE data1,
if (state == TAG_RAISE) {
int handle = FALSE;
VALUE eclass;
+ va_list ap;
- while ((eclass = va_arg(args, VALUE)) != 0) {
+ result = Qnil;
+ /* reuses args when raised again after retrying in r_proc */
+ va_copy(ap, args);
+ while ((eclass = va_arg(ap, VALUE)) != 0) {
if (rb_obj_is_kind_of(ec->errinfo, eclass)) {
handle = TRUE;
break;
}
}
+ va_end(ap);
if (handle) {
- result = Qnil;
- state = 0;
+ state = TAG_NONE;
if (r_proc) {
result = (*r_proc) (data2, ec->errinfo);
}
diff --git a/eval_intern.h b/eval_intern.h
index 34489777a2..9fa9031189 100644
--- a/eval_intern.h
+++ b/eval_intern.h
@@ -302,7 +302,16 @@ VALUE rb_ec_backtrace_location_ary(const rb_execution_context_t *ec, long lev, l
#ifndef CharNext /* defined as CharNext[AW] on Windows. */
# ifdef HAVE_MBLEN
-# define CharNext(p) ((p) + mblen((p), RUBY_MBCHAR_MAXSIZE))
+# define CharNext(p) rb_char_next(p)
+static inline const char *
+rb_char_next(const char *p)
+{
+ if (p) {
+ int len = mblen(p, RUBY_MBCHAR_MAXSIZE);
+ p += len > 0 ? len : 1;
+ }
+ return p;
+}
# else
# define CharNext(p) ((p) + 1)
# endif
diff --git a/ext/-test-/array/concat/depend b/ext/-test-/array/concat/depend
new file mode 100644
index 0000000000..4f2ba01f71
--- /dev/null
+++ b/ext/-test-/array/concat/depend
@@ -0,0 +1,322 @@
+# AUTOGENERATED DEPENDENCIES START
+resize.o: $(RUBY_EXTCONF_H)
+resize.o: $(arch_hdrdir)/ruby/config.h
+resize.o: $(hdrdir)/ruby/assert.h
+resize.o: $(hdrdir)/ruby/backward.h
+resize.o: $(hdrdir)/ruby/backward/2/assume.h
+resize.o: $(hdrdir)/ruby/backward/2/attributes.h
+resize.o: $(hdrdir)/ruby/backward/2/bool.h
+resize.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h
+resize.o: $(hdrdir)/ruby/backward/2/inttypes.h
+resize.o: $(hdrdir)/ruby/backward/2/limits.h
+resize.o: $(hdrdir)/ruby/backward/2/long_long.h
+resize.o: $(hdrdir)/ruby/backward/2/stdalign.h
+resize.o: $(hdrdir)/ruby/backward/2/stdarg.h
+resize.o: $(hdrdir)/ruby/defines.h
+resize.o: $(hdrdir)/ruby/intern.h
+resize.o: $(hdrdir)/ruby/internal/anyargs.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+resize.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+resize.o: $(hdrdir)/ruby/internal/assume.h
+resize.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+resize.o: $(hdrdir)/ruby/internal/attr/artificial.h
+resize.o: $(hdrdir)/ruby/internal/attr/cold.h
+resize.o: $(hdrdir)/ruby/internal/attr/const.h
+resize.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+resize.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+resize.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+resize.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+resize.o: $(hdrdir)/ruby/internal/attr/error.h
+resize.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+resize.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+resize.o: $(hdrdir)/ruby/internal/attr/format.h
+resize.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+resize.o: $(hdrdir)/ruby/internal/attr/noalias.h
+resize.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+resize.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+resize.o: $(hdrdir)/ruby/internal/attr/noinline.h
+resize.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+resize.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+resize.o: $(hdrdir)/ruby/internal/attr/pure.h
+resize.o: $(hdrdir)/ruby/internal/attr/restrict.h
+resize.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+resize.o: $(hdrdir)/ruby/internal/attr/warning.h
+resize.o: $(hdrdir)/ruby/internal/attr/weakref.h
+resize.o: $(hdrdir)/ruby/internal/cast.h
+resize.o: $(hdrdir)/ruby/internal/compiler_is.h
+resize.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+resize.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+resize.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+resize.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+resize.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+resize.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+resize.o: $(hdrdir)/ruby/internal/compiler_since.h
+resize.o: $(hdrdir)/ruby/internal/config.h
+resize.o: $(hdrdir)/ruby/internal/constant_p.h
+resize.o: $(hdrdir)/ruby/internal/core.h
+resize.o: $(hdrdir)/ruby/internal/core/rarray.h
+resize.o: $(hdrdir)/ruby/internal/core/rbasic.h
+resize.o: $(hdrdir)/ruby/internal/core/rbignum.h
+resize.o: $(hdrdir)/ruby/internal/core/rclass.h
+resize.o: $(hdrdir)/ruby/internal/core/rdata.h
+resize.o: $(hdrdir)/ruby/internal/core/rfile.h
+resize.o: $(hdrdir)/ruby/internal/core/rhash.h
+resize.o: $(hdrdir)/ruby/internal/core/robject.h
+resize.o: $(hdrdir)/ruby/internal/core/rregexp.h
+resize.o: $(hdrdir)/ruby/internal/core/rstring.h
+resize.o: $(hdrdir)/ruby/internal/core/rstruct.h
+resize.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+resize.o: $(hdrdir)/ruby/internal/ctype.h
+resize.o: $(hdrdir)/ruby/internal/dllexport.h
+resize.o: $(hdrdir)/ruby/internal/dosish.h
+resize.o: $(hdrdir)/ruby/internal/error.h
+resize.o: $(hdrdir)/ruby/internal/eval.h
+resize.o: $(hdrdir)/ruby/internal/event.h
+resize.o: $(hdrdir)/ruby/internal/fl_type.h
+resize.o: $(hdrdir)/ruby/internal/gc.h
+resize.o: $(hdrdir)/ruby/internal/glob.h
+resize.o: $(hdrdir)/ruby/internal/globals.h
+resize.o: $(hdrdir)/ruby/internal/has/attribute.h
+resize.o: $(hdrdir)/ruby/internal/has/builtin.h
+resize.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+resize.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+resize.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+resize.o: $(hdrdir)/ruby/internal/has/extension.h
+resize.o: $(hdrdir)/ruby/internal/has/feature.h
+resize.o: $(hdrdir)/ruby/internal/has/warning.h
+resize.o: $(hdrdir)/ruby/internal/intern/array.h
+resize.o: $(hdrdir)/ruby/internal/intern/bignum.h
+resize.o: $(hdrdir)/ruby/internal/intern/class.h
+resize.o: $(hdrdir)/ruby/internal/intern/compar.h
+resize.o: $(hdrdir)/ruby/internal/intern/complex.h
+resize.o: $(hdrdir)/ruby/internal/intern/cont.h
+resize.o: $(hdrdir)/ruby/internal/intern/dir.h
+resize.o: $(hdrdir)/ruby/internal/intern/enum.h
+resize.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+resize.o: $(hdrdir)/ruby/internal/intern/error.h
+resize.o: $(hdrdir)/ruby/internal/intern/eval.h
+resize.o: $(hdrdir)/ruby/internal/intern/file.h
+resize.o: $(hdrdir)/ruby/internal/intern/gc.h
+resize.o: $(hdrdir)/ruby/internal/intern/hash.h
+resize.o: $(hdrdir)/ruby/internal/intern/io.h
+resize.o: $(hdrdir)/ruby/internal/intern/load.h
+resize.o: $(hdrdir)/ruby/internal/intern/marshal.h
+resize.o: $(hdrdir)/ruby/internal/intern/numeric.h
+resize.o: $(hdrdir)/ruby/internal/intern/object.h
+resize.o: $(hdrdir)/ruby/internal/intern/parse.h
+resize.o: $(hdrdir)/ruby/internal/intern/proc.h
+resize.o: $(hdrdir)/ruby/internal/intern/process.h
+resize.o: $(hdrdir)/ruby/internal/intern/random.h
+resize.o: $(hdrdir)/ruby/internal/intern/range.h
+resize.o: $(hdrdir)/ruby/internal/intern/rational.h
+resize.o: $(hdrdir)/ruby/internal/intern/re.h
+resize.o: $(hdrdir)/ruby/internal/intern/ruby.h
+resize.o: $(hdrdir)/ruby/internal/intern/select.h
+resize.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+resize.o: $(hdrdir)/ruby/internal/intern/signal.h
+resize.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+resize.o: $(hdrdir)/ruby/internal/intern/string.h
+resize.o: $(hdrdir)/ruby/internal/intern/struct.h
+resize.o: $(hdrdir)/ruby/internal/intern/thread.h
+resize.o: $(hdrdir)/ruby/internal/intern/time.h
+resize.o: $(hdrdir)/ruby/internal/intern/variable.h
+resize.o: $(hdrdir)/ruby/internal/intern/vm.h
+resize.o: $(hdrdir)/ruby/internal/interpreter.h
+resize.o: $(hdrdir)/ruby/internal/iterator.h
+resize.o: $(hdrdir)/ruby/internal/memory.h
+resize.o: $(hdrdir)/ruby/internal/method.h
+resize.o: $(hdrdir)/ruby/internal/module.h
+resize.o: $(hdrdir)/ruby/internal/newobj.h
+resize.o: $(hdrdir)/ruby/internal/rgengc.h
+resize.o: $(hdrdir)/ruby/internal/scan_args.h
+resize.o: $(hdrdir)/ruby/internal/special_consts.h
+resize.o: $(hdrdir)/ruby/internal/static_assert.h
+resize.o: $(hdrdir)/ruby/internal/stdalign.h
+resize.o: $(hdrdir)/ruby/internal/stdbool.h
+resize.o: $(hdrdir)/ruby/internal/symbol.h
+resize.o: $(hdrdir)/ruby/internal/value.h
+resize.o: $(hdrdir)/ruby/internal/value_type.h
+resize.o: $(hdrdir)/ruby/internal/variable.h
+resize.o: $(hdrdir)/ruby/internal/warning_push.h
+resize.o: $(hdrdir)/ruby/internal/xmalloc.h
+resize.o: $(hdrdir)/ruby/missing.h
+resize.o: $(hdrdir)/ruby/ruby.h
+resize.o: $(hdrdir)/ruby/st.h
+resize.o: $(hdrdir)/ruby/subst.h
+resize.o: resize.c
+to_ary_conact.o: $(RUBY_EXTCONF_H)
+to_ary_conact.o: $(arch_hdrdir)/ruby/config.h
+to_ary_conact.o: $(hdrdir)/ruby.h
+to_ary_conact.o: $(hdrdir)/ruby/assert.h
+to_ary_conact.o: $(hdrdir)/ruby/backward.h
+to_ary_conact.o: $(hdrdir)/ruby/backward/2/assume.h
+to_ary_conact.o: $(hdrdir)/ruby/backward/2/attributes.h
+to_ary_conact.o: $(hdrdir)/ruby/backward/2/bool.h
+to_ary_conact.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h
+to_ary_conact.o: $(hdrdir)/ruby/backward/2/inttypes.h
+to_ary_conact.o: $(hdrdir)/ruby/backward/2/limits.h
+to_ary_conact.o: $(hdrdir)/ruby/backward/2/long_long.h
+to_ary_conact.o: $(hdrdir)/ruby/backward/2/stdalign.h
+to_ary_conact.o: $(hdrdir)/ruby/backward/2/stdarg.h
+to_ary_conact.o: $(hdrdir)/ruby/defines.h
+to_ary_conact.o: $(hdrdir)/ruby/intern.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/anyargs.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/char.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/double.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/int.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/long.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/short.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/assume.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/alloc_size.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/artificial.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/cold.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/const.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/constexpr.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/deprecated.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/error.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/flag_enum.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/forceinline.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/format.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/noalias.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/nodiscard.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/noexcept.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/noinline.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/nonnull.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/noreturn.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/pure.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/restrict.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/warning.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/attr/weakref.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/cast.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/compiler_is.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/compiler_is/apple.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/compiler_is/clang.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/compiler_is/intel.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/compiler_since.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/config.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/constant_p.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rarray.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rbasic.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rbignum.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rclass.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rdata.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rfile.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rhash.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/robject.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rregexp.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rstring.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rstruct.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/core/rtypeddata.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/ctype.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/dllexport.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/dosish.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/error.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/eval.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/event.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/fl_type.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/gc.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/glob.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/globals.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/has/attribute.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/has/builtin.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/has/c_attribute.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/has/extension.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/has/feature.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/has/warning.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/array.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/bignum.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/class.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/compar.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/complex.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/cont.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/dir.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/enum.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/enumerator.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/error.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/eval.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/file.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/gc.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/hash.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/io.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/load.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/marshal.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/numeric.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/object.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/parse.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/proc.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/process.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/random.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/range.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/rational.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/re.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/ruby.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/select.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/select/largesize.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/signal.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/sprintf.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/string.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/struct.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/thread.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/time.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/variable.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/intern/vm.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/interpreter.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/iterator.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/memory.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/method.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/module.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/newobj.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/rgengc.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/scan_args.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/special_consts.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/static_assert.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/stdalign.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/stdbool.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/symbol.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/token_paste.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/value.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/value_type.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/variable.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/warning_push.h
+to_ary_conact.o: $(hdrdir)/ruby/internal/xmalloc.h
+to_ary_conact.o: $(hdrdir)/ruby/missing.h
+to_ary_conact.o: $(hdrdir)/ruby/ruby.h
+to_ary_conact.o: $(hdrdir)/ruby/st.h
+to_ary_conact.o: $(hdrdir)/ruby/subst.h
+to_ary_conact.o: to_ary_conact.c
+# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/-test-/array/concat/extconf.rb b/ext/-test-/array/concat/extconf.rb
new file mode 100644
index 0000000000..cdd79126c9
--- /dev/null
+++ b/ext/-test-/array/concat/extconf.rb
@@ -0,0 +1,2 @@
+# frozen_string_literal: false
+create_makefile("-test-/array/to_ary_concat")
diff --git a/ext/-test-/array/concat/to_ary_conact.c b/ext/-test-/array/concat/to_ary_conact.c
new file mode 100644
index 0000000000..ec1fd321ce
--- /dev/null
+++ b/ext/-test-/array/concat/to_ary_conact.c
@@ -0,0 +1,34 @@
+#include "ruby.h"
+
+// Bar
+
+typedef struct {
+ int dummy;
+} Bar;
+
+static rb_data_type_t Bar_type = {
+ "Bar",
+ {NULL, RUBY_TYPED_DEFAULT_FREE, NULL },
+};
+
+static VALUE
+Bar_alloc(VALUE klass)
+{
+ return TypedData_Wrap_Struct(klass, &Bar_type, NULL);
+}
+
+VALUE Bar_to_ary(VALUE _self) {
+ VALUE ary = rb_ary_new2(2);
+ VALUE foo = rb_ary_new2(0);
+ rb_ary_push(ary, foo);
+ rb_ary_push(ary, foo);
+ rb_ary_push(ary, foo);
+ return ary;
+}
+
+void Init_to_ary_concat() {
+ VALUE mBug = rb_define_module("Bug");
+ VALUE bar = rb_define_class_under(mBug, "Bar", rb_cObject);
+ rb_define_alloc_func(bar, Bar_alloc);
+ rb_define_method(bar, "to_ary", Bar_to_ary, 0);
+}
diff --git a/ext/-test-/postponed_job/postponed_job.c b/ext/-test-/postponed_job/postponed_job.c
index d8684d475a..fa57bef6f5 100644
--- a/ext/-test-/postponed_job/postponed_job.c
+++ b/ext/-test-/postponed_job/postponed_job.c
@@ -58,6 +58,34 @@ pjob_call_direct(VALUE self, VALUE obj)
return self;
}
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+
+static void *
+pjob_register_in_c_thread_i(void *obj)
+{
+ rb_postponed_job_register_one(0, pjob_one_callback, (void *)obj);
+ rb_postponed_job_register_one(0, pjob_one_callback, (void *)obj);
+ rb_postponed_job_register_one(0, pjob_one_callback, (void *)obj);
+ return NULL;
+}
+
+static VALUE
+pjob_register_in_c_thread(VALUE self, VALUE obj)
+{
+ pthread_t thread;
+ if (pthread_create(&thread, NULL, pjob_register_in_c_thread_i, (void *)obj)) {
+ return Qfalse;
+ }
+
+ if (pthread_join(thread, NULL)) {
+ return Qfalse;
+ }
+
+ return Qtrue;
+}
+#endif
+
void
Init_postponed_job(VALUE self)
{
@@ -65,5 +93,8 @@ Init_postponed_job(VALUE self)
rb_define_module_function(mBug, "postponed_job_register", pjob_register, 1);
rb_define_module_function(mBug, "postponed_job_register_one", pjob_register_one, 1);
rb_define_module_function(mBug, "postponed_job_call_direct", pjob_call_direct, 1);
+#ifdef HAVE_PTHREAD_H
+ rb_define_module_function(mBug, "postponed_job_register_in_c_thread", pjob_register_in_c_thread, 1);
+#endif
}
diff --git a/ext/-test-/string/depend b/ext/-test-/string/depend
index 67dfd2289f..7db4465bf9 100644
--- a/ext/-test-/string/depend
+++ b/ext/-test-/string/depend
@@ -1000,6 +1000,7 @@ fstring.o: $(hdrdir)/ruby/backward/2/long_long.h
fstring.o: $(hdrdir)/ruby/backward/2/stdalign.h
fstring.o: $(hdrdir)/ruby/backward/2/stdarg.h
fstring.o: $(hdrdir)/ruby/defines.h
+fstring.o: $(hdrdir)/ruby/encoding.h
fstring.o: $(hdrdir)/ruby/intern.h
fstring.o: $(hdrdir)/ruby/internal/anyargs.h
fstring.o: $(hdrdir)/ruby/internal/arithmetic.h
@@ -1142,6 +1143,8 @@ fstring.o: $(hdrdir)/ruby/internal/variable.h
fstring.o: $(hdrdir)/ruby/internal/warning_push.h
fstring.o: $(hdrdir)/ruby/internal/xmalloc.h
fstring.o: $(hdrdir)/ruby/missing.h
+fstring.o: $(hdrdir)/ruby/onigmo.h
+fstring.o: $(hdrdir)/ruby/oniguruma.h
fstring.o: $(hdrdir)/ruby/ruby.h
fstring.o: $(hdrdir)/ruby/st.h
fstring.o: $(hdrdir)/ruby/subst.h
diff --git a/ext/-test-/string/enc_str_buf_cat.c b/ext/-test-/string/enc_str_buf_cat.c
index 9ac4a298be..4c1b262e1e 100644
--- a/ext/-test-/string/enc_str_buf_cat.c
+++ b/ext/-test-/string/enc_str_buf_cat.c
@@ -7,8 +7,22 @@ enc_str_buf_cat(VALUE str, VALUE str2)
return rb_enc_str_buf_cat(str, RSTRING_PTR(str2), RSTRING_LEN(str2), rb_enc_get(str2));
}
+static VALUE
+str_conv_enc_opts(VALUE str, VALUE from, VALUE to, VALUE ecflags, VALUE ecopts)
+{
+ rb_encoding *from_enc = NIL_P(from) ? NULL : rb_to_encoding(from);
+ rb_encoding *to_enc = NIL_P(to) ? NULL : rb_to_encoding(to);
+ int flags = NUM2INT(ecflags);
+ if (!NIL_P(ecopts)) {
+ Check_Type(ecopts, T_HASH);
+ OBJ_FREEZE(ecopts);
+ }
+ return rb_str_conv_enc_opts(str, from_enc, to_enc, flags, ecopts);
+}
+
void
Init_string_enc_str_buf_cat(VALUE klass)
{
rb_define_method(klass, "enc_str_buf_cat", enc_str_buf_cat, 1);
+ rb_define_method(klass, "str_conv_enc_opts", str_conv_enc_opts, 4);
}
diff --git a/ext/-test-/string/fstring.c b/ext/-test-/string/fstring.c
index 30120b42f6..2374319fe3 100644
--- a/ext/-test-/string/fstring.c
+++ b/ext/-test-/string/fstring.c
@@ -1,4 +1,5 @@
#include "ruby.h"
+#include "ruby/encoding.h"
VALUE rb_fstring(VALUE str);
@@ -8,8 +9,22 @@ bug_s_fstring(VALUE self, VALUE str)
return rb_fstring(str);
}
+VALUE
+bug_s_rb_enc_interned_str(VALUE self, VALUE encoding)
+{
+ return rb_enc_interned_str("foo", 3, RDATA(encoding)->data);
+}
+
+VALUE
+bug_s_rb_enc_str_new(VALUE self, VALUE encoding)
+{
+ return rb_enc_str_new("foo", 3, RDATA(encoding)->data);
+}
+
void
Init_string_fstring(VALUE klass)
{
rb_define_singleton_method(klass, "fstring", bug_s_fstring, 1);
+ rb_define_singleton_method(klass, "rb_enc_interned_str", bug_s_rb_enc_interned_str, 1);
+ rb_define_singleton_method(klass, "rb_enc_str_new", bug_s_rb_enc_str_new, 1);
}
diff --git a/ext/cgi/escape/escape.c b/ext/cgi/escape/escape.c
index 77627e2f03..809f95ef4c 100644
--- a/ext/cgi/escape/escape.c
+++ b/ext/cgi/escape/escape.c
@@ -36,7 +36,8 @@ static VALUE
optimized_escape_html(VALUE str)
{
VALUE vbuf;
- char *buf = ALLOCV_N(char, vbuf, RSTRING_LEN(str) * HTML_ESCAPE_MAX_LEN);
+ typedef char escape_buf[HTML_ESCAPE_MAX_LEN];
+ char *buf = *ALLOCV_N(escape_buf, vbuf, RSTRING_LEN(str));
const char *cstr = RSTRING_PTR(str);
const char *end = cstr + RSTRING_LEN(str);
@@ -388,7 +389,7 @@ cgiesc_unescape(int argc, VALUE *argv, VALUE self)
void
Init_escape(void)
{
-#if HAVE_RB_EXT_RACTOR_SAFE
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
rb_ext_ractor_safe(true);
#endif
diff --git a/ext/date/date.gemspec b/ext/date/date.gemspec
index 44282759c6..cf07696976 100644
--- a/ext/date/date.gemspec
+++ b/ext/date/date.gemspec
@@ -1,7 +1,12 @@
# frozen_string_literal: true
+
+version = File.foreach(File.expand_path("../lib/date.rb", __FILE__)).find do |line|
+ /^\s*VERSION\s*=\s*["'](.*)["']/ =~ line and break $1
+end
+
Gem::Specification.new do |s|
s.name = "date"
- s.version = '3.1.0'
+ s.version = version
s.summary = "A subclass of Object includes Comparable module for handling dates."
s.description = "A subclass of Object includes Comparable module for handling dates."
diff --git a/ext/date/date_core.c b/ext/date/date_core.c
index 7e9bf16a07..66ec0aa92a 100644
--- a/ext/date/date_core.c
+++ b/ext/date/date_core.c
@@ -4328,12 +4328,40 @@ date_s_strptime(int argc, VALUE *argv, VALUE klass)
VALUE date__parse(VALUE str, VALUE comp);
+static size_t
+get_limit(VALUE opt)
+{
+ if (!NIL_P(opt)) {
+ VALUE limit = rb_hash_aref(opt, ID2SYM(rb_intern("limit")));
+ if (NIL_P(limit)) return SIZE_MAX;
+ return NUM2SIZET(limit);
+ }
+ return 128;
+}
+
+static void
+check_limit(VALUE str, VALUE opt)
+{
+ if (NIL_P(str)) return;
+ if (SYMBOL_P(str)) str = rb_sym2str(str);
+
+ StringValue(str);
+ size_t slen = RSTRING_LEN(str);
+ size_t limit = get_limit(opt);
+ if (slen > limit) {
+ rb_raise(rb_eArgError,
+ "string length (%"PRI_SIZE_PREFIX"u) exceeds the limit %"PRI_SIZE_PREFIX"u", slen, limit);
+ }
+}
+
static VALUE
date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
{
- VALUE vstr, vcomp, hash;
+ VALUE vstr, vcomp, hash, opt;
- rb_scan_args(argc, argv, "11", &vstr, &vcomp);
+ rb_scan_args(argc, argv, "11:", &vstr, &vcomp, &opt);
+ if (!NIL_P(opt)) argc--;
+ check_limit(vstr, opt);
StringValue(vstr);
if (!rb_enc_str_asciicompat_p(vstr))
rb_raise(rb_eArgError,
@@ -4348,7 +4376,7 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * Date._parse(string[, comp=true]) -> hash
+ * Date._parse(string[, comp=true], limit: 128) -> hash
*
* Parses the given representation of date and time, and returns a
* hash of parsed elements.
@@ -4363,6 +4391,10 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
* it full.
*
* Date._parse('2001-02-03') #=> {:year=>2001, :mon=>2, :mday=>3}
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s__parse(int argc, VALUE *argv, VALUE klass)
@@ -4372,7 +4404,7 @@ date_s__parse(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * Date.parse(string='-4712-01-01'[, comp=true[, start=Date::ITALY]]) -> date
+ * Date.parse(string='-4712-01-01'[, comp=true[, start=Date::ITALY]], limit: 128) -> date
*
* Parses the given representation of date and time, and creates a
* date object.
@@ -4389,13 +4421,18 @@ date_s__parse(int argc, VALUE *argv, VALUE klass)
* Date.parse('2001-02-03') #=> #<Date: 2001-02-03 ...>
* Date.parse('20010203') #=> #<Date: 2001-02-03 ...>
* Date.parse('3rd Feb 2001') #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_parse(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, comp, sg;
+ VALUE str, comp, sg, opt;
- rb_scan_args(argc, argv, "03", &str, &comp, &sg);
+ rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4407,11 +4444,12 @@ date_s_parse(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE argv2[2], hash;
-
- argv2[0] = str;
- argv2[1] = comp;
- hash = date_s__parse(2, argv2, klass);
+ int argc2 = 2;
+ VALUE argv2[3];
+ argv2[0] = str;
+ argv2[1] = comp;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__parse(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
@@ -4425,19 +4463,28 @@ VALUE date__jisx0301(VALUE);
/*
* call-seq:
- * Date._iso8601(string) -> hash
+ * Date._iso8601(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__iso8601(VALUE klass, VALUE str)
+date_s__iso8601(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__iso8601(str);
}
/*
* call-seq:
- * Date.iso8601(string='-4712-01-01'[, start=Date::ITALY]) -> date
+ * Date.iso8601(string='-4712-01-01'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some typical ISO 8601 formats.
@@ -4445,13 +4492,18 @@ date_s__iso8601(VALUE klass, VALUE str)
* Date.iso8601('2001-02-03') #=> #<Date: 2001-02-03 ...>
* Date.iso8601('20010203') #=> #<Date: 2001-02-03 ...>
* Date.iso8601('2001-W05-6') #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_iso8601(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4461,38 +4513,56 @@ date_s_iso8601(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__iso8601(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__iso8601(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._rfc3339(string) -> hash
+ * Date._rfc3339(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__rfc3339(VALUE klass, VALUE str)
+date_s__rfc3339(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__rfc3339(str);
}
/*
* call-seq:
- * Date.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> date
+ * Date.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some typical RFC 3339 formats.
*
* Date.rfc3339('2001-02-03T04:05:06+07:00') #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_rfc3339(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4502,38 +4572,56 @@ date_s_rfc3339(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc3339(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__rfc3339(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._xmlschema(string) -> hash
+ * Date._xmlschema(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__xmlschema(VALUE klass, VALUE str)
+date_s__xmlschema(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__xmlschema(str);
}
/*
* call-seq:
- * Date.xmlschema(string='-4712-01-01'[, start=Date::ITALY]) -> date
+ * Date.xmlschema(string='-4712-01-01'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some typical XML Schema formats.
*
* Date.xmlschema('2001-02-03') #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_xmlschema(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4543,41 +4631,58 @@ date_s_xmlschema(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__xmlschema(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__xmlschema(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._rfc2822(string) -> hash
- * Date._rfc822(string) -> hash
+ * Date._rfc2822(string, limit: 128) -> hash
+ * Date._rfc822(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__rfc2822(VALUE klass, VALUE str)
+date_s__rfc2822(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__rfc2822(str);
}
/*
* call-seq:
- * Date.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> date
- * Date.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> date
+ * Date.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> date
+ * Date.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some typical RFC 2822 formats.
*
* Date.rfc2822('Sat, 3 Feb 2001 00:00:00 +0000')
* #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_rfc2822(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
switch (argc) {
case 0:
@@ -4587,39 +4692,56 @@ date_s_rfc2822(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc2822(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__rfc2822(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._httpdate(string) -> hash
+ * Date._httpdate(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__httpdate(VALUE klass, VALUE str)
+date_s__httpdate(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__httpdate(str);
}
/*
* call-seq:
- * Date.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=Date::ITALY]) -> date
+ * Date.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some RFC 2616 format.
*
* Date.httpdate('Sat, 03 Feb 2001 00:00:00 GMT')
* #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_httpdate(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
switch (argc) {
case 0:
@@ -4629,26 +4751,39 @@ date_s_httpdate(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__httpdate(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__httpdate(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._jisx0301(string) -> hash
+ * Date._jisx0301(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__jisx0301(VALUE klass, VALUE str)
+date_s__jisx0301(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__jisx0301(str);
}
/*
* call-seq:
- * Date.jisx0301(string='-4712-01-01'[, start=Date::ITALY]) -> date
+ * Date.jisx0301(string='-4712-01-01'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some typical JIS X 0301 formats.
@@ -4658,13 +4793,18 @@ date_s__jisx0301(VALUE klass, VALUE str)
* For no-era year, legacy format, Heisei is assumed.
*
* Date.jisx0301('13.02.03') #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_jisx0301(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4674,7 +4814,11 @@ date_s_jisx0301(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__jisx0301(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__jisx0301(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
@@ -8013,7 +8157,7 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=Date::ITALY]]) -> datetime
+ * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=Date::ITALY]], limit: 128) -> datetime
*
* Parses the given representation of date and time, and creates a
* DateTime object.
@@ -8032,13 +8176,18 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
* DateTime.parse('3rd Feb 2001 04:05:06 PM')
* #=> #<DateTime: 2001-02-03T16:05:06+00:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_parse(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, comp, sg;
+ VALUE str, comp, sg, opt;
- rb_scan_args(argc, argv, "03", &str, &comp, &sg);
+ rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8050,18 +8199,20 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE argv2[2], hash;
-
- argv2[0] = str;
- argv2[1] = comp;
- hash = date_s__parse(2, argv2, klass);
+ int argc2 = 2;
+ VALUE argv2[3];
+ argv2[0] = str;
+ argv2[1] = comp;
+ argv2[2] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__parse(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime
+ * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
* Creates a new DateTime object by parsing from a string according to
* some typical ISO 8601 formats.
@@ -8072,13 +8223,18 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
* DateTime.iso8601('2001-W05-6T04:05:06+07:00')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_iso8601(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8088,27 +8244,37 @@ datetime_s_iso8601(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__iso8601(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2--;
+ VALUE hash = date_s__iso8601(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime
+ * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
* Creates a new DateTime object by parsing from a string according to
* some typical RFC 3339 formats.
*
* DateTime.rfc3339('2001-02-03T04:05:06+07:00')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8118,27 +8284,37 @@ datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc3339(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__rfc3339(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime
+ * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
* Creates a new DateTime object by parsing from a string according to
* some typical XML Schema formats.
*
* DateTime.xmlschema('2001-02-03T04:05:06+07:00')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8148,28 +8324,38 @@ datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__xmlschema(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__xmlschema(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> datetime
- * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> datetime
+ * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> datetime
+ * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> datetime
*
* Creates a new DateTime object by parsing from a string according to
* some typical RFC 2822 formats.
*
* DateTime.rfc2822('Sat, 3 Feb 2001 04:05:06 +0700')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8179,7 +8365,12 @@ datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc2822(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__rfc2822(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
@@ -8193,13 +8384,18 @@ datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass)
*
* DateTime.httpdate('Sat, 03 Feb 2001 04:05:06 GMT')
* #=> #<DateTime: 2001-02-03T04:05:06+00:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_httpdate(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8209,14 +8405,19 @@ datetime_s_httpdate(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__httpdate(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__httpdate(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime
+ * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
* Creates a new DateTime object by parsing from a string according to
* some typical JIS X 0301 formats.
@@ -8228,13 +8429,18 @@ datetime_s_httpdate(int argc, VALUE *argv, VALUE klass)
*
* DateTime.jisx0301('13.02.03T04:05:06+07:00')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8244,7 +8450,12 @@ datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__jisx0301(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__jisx0301(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
@@ -9403,19 +9614,19 @@ Init_date_core(void)
rb_define_singleton_method(cDate, "strptime", date_s_strptime, -1);
rb_define_singleton_method(cDate, "_parse", date_s__parse, -1);
rb_define_singleton_method(cDate, "parse", date_s_parse, -1);
- rb_define_singleton_method(cDate, "_iso8601", date_s__iso8601, 1);
+ rb_define_singleton_method(cDate, "_iso8601", date_s__iso8601, -1);
rb_define_singleton_method(cDate, "iso8601", date_s_iso8601, -1);
- rb_define_singleton_method(cDate, "_rfc3339", date_s__rfc3339, 1);
+ rb_define_singleton_method(cDate, "_rfc3339", date_s__rfc3339, -1);
rb_define_singleton_method(cDate, "rfc3339", date_s_rfc3339, -1);
- rb_define_singleton_method(cDate, "_xmlschema", date_s__xmlschema, 1);
+ rb_define_singleton_method(cDate, "_xmlschema", date_s__xmlschema, -1);
rb_define_singleton_method(cDate, "xmlschema", date_s_xmlschema, -1);
- rb_define_singleton_method(cDate, "_rfc2822", date_s__rfc2822, 1);
- rb_define_singleton_method(cDate, "_rfc822", date_s__rfc2822, 1);
+ rb_define_singleton_method(cDate, "_rfc2822", date_s__rfc2822, -1);
+ rb_define_singleton_method(cDate, "_rfc822", date_s__rfc2822, -1);
rb_define_singleton_method(cDate, "rfc2822", date_s_rfc2822, -1);
rb_define_singleton_method(cDate, "rfc822", date_s_rfc2822, -1);
- rb_define_singleton_method(cDate, "_httpdate", date_s__httpdate, 1);
+ rb_define_singleton_method(cDate, "_httpdate", date_s__httpdate, -1);
rb_define_singleton_method(cDate, "httpdate", date_s_httpdate, -1);
- rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, 1);
+ rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, -1);
rb_define_singleton_method(cDate, "jisx0301", date_s_jisx0301, -1);
rb_define_method(cDate, "initialize", date_initialize, -1);
diff --git a/ext/date/lib/date.rb b/ext/date/lib/date.rb
index 65c34ace49..0a49076ab6 100644
--- a/ext/date/lib/date.rb
+++ b/ext/date/lib/date.rb
@@ -4,6 +4,7 @@
require 'date_core'
class Date
+ VERSION = '3.1.3' # :nodoc:
def infinite?
false
diff --git a/ext/etc/etc.c b/ext/etc/etc.c
index 477423c9ed..737d295abc 100644
--- a/ext/etc/etc.c
+++ b/ext/etc/etc.c
@@ -52,7 +52,7 @@ char *getenv();
#endif
char *getlogin();
-#define RUBY_ETC_VERSION "1.2.0"
+#define RUBY_ETC_VERSION "1.3.0"
#ifdef HAVE_RB_DEPRECATE_CONSTANT
void rb_deprecate_constant(VALUE mod, const char *name);
@@ -68,6 +68,15 @@ void rb_deprecate_constant(VALUE mod, const char *name);
typedef int rb_atomic_t;
# define RUBY_ATOMIC_CAS(var, oldval, newval) \
((var) == (oldval) ? ((var) = (newval), (oldval)) : (var))
+# define RUBY_ATOMIC_EXCHANGE(var, newval) \
+ atomic_exchange(&var, newval)
+static inline rb_atomic_t
+atomic_exchange(volatile rb_atomic_t *var, rb_atomic_t newval)
+{
+ rb_atomic_t oldval = *var;
+ *var = newval;
+ return oldval;
+}
#endif
/* call-seq:
@@ -253,7 +262,9 @@ static VALUE
passwd_ensure(VALUE _)
{
endpwent();
- passwd_blocking = 0;
+ if (RUBY_ATOMIC_EXCHANGE(passwd_blocking, 0) != 1) {
+ rb_raise(rb_eRuntimeError, "unexpected passwd_blocking");
+ }
return Qnil;
}
@@ -495,7 +506,9 @@ static VALUE
group_ensure(VALUE _)
{
endgrent();
- group_blocking = 0;
+ if (RUBY_ATOMIC_EXCHANGE(group_blocking, 0) != 1) {
+ rb_raise(rb_eRuntimeError, "unexpected group_blocking");
+ }
return Qnil;
}
@@ -944,11 +957,13 @@ io_pathconf(VALUE io, VALUE arg)
static int
etc_nprocessors_affin(void)
{
- cpu_set_t *cpuset;
+ cpu_set_t *cpuset, cpuset_buff[1024 / sizeof(cpu_set_t)];
size_t size;
int ret;
int n;
+ CPU_ZERO_S(sizeof(cpuset_buff), cpuset_buff);
+
/*
* XXX:
* man page says CPU_ALLOC takes number of cpus. But it is not accurate
@@ -967,13 +982,12 @@ etc_nprocessors_affin(void)
*/
for (n=64; n <= 16384; n *= 2) {
size = CPU_ALLOC_SIZE(n);
- if (size >= 1024) {
+ if (size >= sizeof(cpuset_buff)) {
cpuset = xcalloc(1, size);
if (!cpuset)
return -1;
} else {
- cpuset = alloca(size);
- CPU_ZERO_S(size, cpuset);
+ cpuset = cpuset_buff;
}
ret = sched_getaffinity(0, size, cpuset);
@@ -982,10 +996,10 @@ etc_nprocessors_affin(void)
ret = CPU_COUNT_S(size, cpuset);
}
- if (size >= 1024) {
+ if (size >= sizeof(cpuset_buff)) {
xfree(cpuset);
}
- if (ret > 0) {
+ if (ret > 0 || errno != EINVAL) {
return ret;
}
}
diff --git a/ext/etc/extconf.rb b/ext/etc/extconf.rb
index b6ae7700da..6e7810a5e8 100644
--- a/ext/etc/extconf.rb
+++ b/ext/etc/extconf.rb
@@ -47,10 +47,7 @@ if !File.exist?("#{srcdir}/depend")
%x[#{RbConfig.ruby} #{srcdir}/mkconstants.rb -o #{srcdir}/constdefs.h]
end
-decl = [
- "void rb_deprecate_constant(VALUE, const char *);",
-]
-have_func('rb_deprecate_constant(Qnil, "None")', [decl])
+have_func('rb_deprecate_constant(Qnil, "None")')
$distcleanfiles << "constdefs.h"
diff --git a/ext/extmk.rb b/ext/extmk.rb
index 80a0a1208d..97f1ad9c39 100755
--- a/ext/extmk.rb
+++ b/ext/extmk.rb
@@ -408,8 +408,10 @@ if CROSS_COMPILING
$ruby = $mflags.defined?("MINIRUBY") || CONFIG['MINIRUBY']
elsif sep = config_string('BUILD_FILE_SEPARATOR')
$ruby = "$(topdir:/=#{sep})#{sep}miniruby" + EXEEXT
-else
+elsif CONFIG['EXTSTATIC']
$ruby = '$(topdir)/miniruby' + EXEEXT
+else
+ $ruby = '$(topdir)/ruby' + EXEEXT
end
$ruby = [$ruby]
$ruby << "-I'$(topdir)'"
@@ -421,6 +423,7 @@ end
topruby = $ruby
$ruby = topruby.join(' ')
$mflags << "ruby=#$ruby"
+$builtruby = '$(topdir)/miniruby' + EXEEXT # Must be an executable path
MTIMES = [__FILE__, 'rbconfig.rb', srcdir+'/lib/mkmf.rb'].collect {|f| File.mtime(f)}
diff --git a/ext/fcntl/fcntl.gemspec b/ext/fcntl/fcntl.gemspec
index 645c507a4d..048e101aa5 100644
--- a/ext/fcntl/fcntl.gemspec
+++ b/ext/fcntl/fcntl.gemspec
@@ -3,7 +3,7 @@
Gem::Specification.new do |spec|
spec.name = "fcntl"
- spec.version = "1.0.0"
+ spec.version = "1.0.1"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c
index 40cee55e9a..3679e5c9ad 100644
--- a/ext/fiddle/closure.c
+++ b/ext/fiddle/closure.c
@@ -130,6 +130,10 @@ with_gvl_callback(void *ptr)
rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i]));
break;
#endif
+ case TYPE_CONST_STRING:
+ rb_ary_push(params,
+ rb_str_new_cstr(*((const char **)(x->args[i]))));
+ break;
default:
rb_raise(rb_eRuntimeError, "closure args: %d", type);
}
@@ -175,6 +179,10 @@ with_gvl_callback(void *ptr)
*(unsigned LONG_LONG *)x->resp = NUM2ULL(ret);
break;
#endif
+ case TYPE_CONST_STRING:
+ /* Dangerous. Callback must keep reference of the String. */
+ *((const char **)(x->resp)) = StringValueCStr(ret);
+ break;
default:
rb_raise(rb_eRuntimeError, "closure retval: %d", type);
}
@@ -221,6 +229,7 @@ initialize(int rbargc, VALUE argv[], VALUE self)
{
VALUE ret;
VALUE args;
+ VALUE normalized_args;
VALUE abi;
fiddle_closure * cl;
ffi_cif * cif;
@@ -239,21 +248,26 @@ initialize(int rbargc, VALUE argv[], VALUE self)
cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
+ normalized_args = rb_ary_new_capa(argc);
for (i = 0; i < argc; i++) {
- int type = NUM2INT(RARRAY_AREF(args, i));
- cl->argv[i] = INT2FFI_TYPE(type);
+ VALUE arg = rb_fiddle_type_ensure(RARRAY_AREF(args, i));
+ rb_ary_push(normalized_args, arg);
+ cl->argv[i] = rb_fiddle_int_to_ffi_type(NUM2INT(arg));
}
cl->argv[argc] = NULL;
+ ret = rb_fiddle_type_ensure(ret);
rb_iv_set(self, "@ctype", ret);
- rb_iv_set(self, "@args", args);
+ rb_iv_set(self, "@args", normalized_args);
cif = &cl->cif;
pcl = cl->pcl;
- result = ffi_prep_cif(cif, NUM2INT(abi), argc,
- INT2FFI_TYPE(NUM2INT(ret)),
- cl->argv);
+ result = ffi_prep_cif(cif,
+ NUM2INT(abi),
+ argc,
+ rb_fiddle_int_to_ffi_type(NUM2INT(ret)),
+ cl->argv);
if (FFI_OK != result)
rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
diff --git a/ext/fiddle/conversions.h b/ext/fiddle/conversions.h
index 1de956e90c..c7c12a9234 100644
--- a/ext/fiddle/conversions.h
+++ b/ext/fiddle/conversions.h
@@ -24,7 +24,6 @@ typedef union
void * pointer; /* ffi_type_pointer */
} fiddle_generic;
-/* Deprecated. Use rb_fiddle_*() version. */
VALUE rb_fiddle_type_ensure(VALUE type);
ffi_type * rb_fiddle_int_to_ffi_type(int type);
void rb_fiddle_value_to_generic(int type, VALUE *src, fiddle_generic *dst);
diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb
index 6d1d5104a1..6ca685317e 100644
--- a/ext/fiddle/extconf.rb
+++ b/ext/fiddle/extconf.rb
@@ -3,6 +3,47 @@ require 'mkmf'
# :stopdoc:
+def gcc?
+ RbConfig::CONFIG["GCC"] == "yes"
+end
+
+def disable_optimization_build_flag(flags)
+ if gcc?
+ expanded_flags = RbConfig.expand(flags.dup)
+ optimization_option_pattern = /(^|\s)?-O\d(\s|$)?/
+ if optimization_option_pattern.match?(expanded_flags)
+ expanded_flags.gsub(optimization_option_pattern, '\\1-Og\\2')
+ else
+ flags + " -Og"
+ end
+ else
+ flags
+ end
+end
+
+def enable_debug_build_flag(flags)
+ if gcc?
+ expanded_flags = RbConfig.expand(flags.dup)
+ debug_option_pattern = /(^|\s)-g(?:gdb)?\d?(\s|$)/
+ if debug_option_pattern.match?(expanded_flags)
+ expanded_flags.gsub(debug_option_pattern, '\\1-ggdb3\\2')
+ else
+ flags + " -ggdb3"
+ end
+ else
+ flags
+ end
+end
+
+checking_for(checking_message("--enable-debug-build option")) do
+ enable_debug_build = enable_config("debug-build", false)
+ if enable_debug_build
+ $CFLAGS = disable_optimization_build_flag($CFLAGS)
+ $CFLAGS = enable_debug_build_flag($CFLAGS)
+ end
+ enable_debug_build
+end
+
libffi_version = nil
have_libffi = false
bundle = enable_config('bundled-libffi')
@@ -159,6 +200,8 @@ elsif have_header "windows.h"
%w{ LoadLibrary FreeLibrary GetProcAddress }.each do |func|
abort "missing function #{func}" unless have_func(func)
end
+
+ have_library "ws2_32"
end
have_const('FFI_STDCALL', ffi_header)
diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c
index 1d82bc8a3e..d15a54bfa6 100644
--- a/ext/fiddle/function.c
+++ b/ext/fiddle/function.c
@@ -375,10 +375,17 @@ function_call(int argc, VALUE argv[], VALUE self)
(void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0);
}
- rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
+ {
+ int errno_keep = errno;
#if defined(_WIN32)
- rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
+ int socket_error = WSAGetLastError();
+ rb_funcall(mFiddle, rb_intern("win32_last_error="), 1,
+ INT2NUM(errno_keep));
+ rb_funcall(mFiddle, rb_intern("win32_last_socket_error="), 1,
+ INT2NUM(socket_error));
#endif
+ rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno_keep));
+ }
ALLOCV_END(alloc_buffer);
diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb
index 3fdf525b4c..4512989310 100644
--- a/ext/fiddle/lib/fiddle.rb
+++ b/ext/fiddle/lib/fiddle.rb
@@ -17,6 +17,18 @@ module Fiddle
def self.win32_last_error= error
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
end
+
+ # Returns the last win32 socket +Error+ of the current executing
+ # +Thread+ or nil if none
+ def self.win32_last_socket_error
+ Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__]
+ end
+
+ # Sets the last win32 socket +Error+ of the current executing
+ # +Thread+ to +error+
+ def self.win32_last_socket_error= error
+ Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] = error
+ end
end
# Returns the last +Error+ of the current executing +Thread+ or nil if none
diff --git a/ext/fiddle/lib/fiddle/cparser.rb b/ext/fiddle/lib/fiddle/cparser.rb
index 8a269393c6..93a05513c9 100644
--- a/ext/fiddle/lib/fiddle/cparser.rb
+++ b/ext/fiddle/lib/fiddle/cparser.rb
@@ -148,9 +148,11 @@ module Fiddle
#
def parse_ctype(ty, tymap=nil)
tymap ||= {}
- case ty
- when Array
+ if ty.is_a?(Array)
return [parse_ctype(ty[0], tymap), ty[1]]
+ end
+ ty = ty.gsub(/\Aconst\s+/, "")
+ case ty
when 'void'
return TYPE_VOID
when /\A(?:(?:signed\s+)?long\s+long(?:\s+int\s+)?|int64_t)(?:\s+\w+)?\z/
diff --git a/ext/fiddle/lib/fiddle/types.rb b/ext/fiddle/lib/fiddle/types.rb
index 8dc811d3e4..7baf31ec9e 100644
--- a/ext/fiddle/lib/fiddle/types.rb
+++ b/ext/fiddle/lib/fiddle/types.rb
@@ -27,28 +27,29 @@ module Fiddle
# * WORD
module Win32Types
def included(m) # :nodoc:
+ # https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
m.module_eval{
- typealias "DWORD", "unsigned long"
- typealias "PDWORD", "unsigned long *"
- typealias "DWORD32", "unsigned long"
- typealias "DWORD64", "unsigned long long"
- typealias "WORD", "unsigned short"
- typealias "PWORD", "unsigned short *"
+ typealias "ATOM", "WORD"
typealias "BOOL", "int"
- typealias "ATOM", "int"
typealias "BYTE", "unsigned char"
- typealias "PBYTE", "unsigned char *"
+ typealias "DWORD", "unsigned long"
+ typealias "DWORD32", "uint32_t"
+ typealias "DWORD64", "uint64_t"
+ typealias "HANDLE", "PVOID"
+ typealias "HDC", "HANDLE"
+ typealias "HINSTANCE", "HANDLE"
+ typealias "HWND", "HANDLE"
+ typealias "LPCSTR", "const char *"
+ typealias "LPSTR", "char *"
+ typealias "PBYTE", "BYTE *"
+ typealias "PDWORD", "DWORD *"
+ typealias "PHANDLE", "HANDLE *"
+ typealias "PVOID", "void *"
+ typealias "PWORD", "WORD *"
+ typealias "UCHAR", "unsigned char"
typealias "UINT", "unsigned int"
typealias "ULONG", "unsigned long"
- typealias "UCHAR", "unsigned char"
- typealias "HANDLE", "uintptr_t"
- typealias "PHANDLE", "void*"
- typealias "PVOID", "void*"
- typealias "LPCSTR", "char*"
- typealias "LPSTR", "char*"
- typealias "HINSTANCE", "unsigned int"
- typealias "HDC", "unsigned int"
- typealias "HWND", "unsigned int"
+ typealias "WORD", "unsigned short"
}
end
module_function :included
diff --git a/ext/fiddle/lib/fiddle/version.rb b/ext/fiddle/lib/fiddle/version.rb
index d80cb7078f..a699371ee4 100644
--- a/ext/fiddle/lib/fiddle/version.rb
+++ b/ext/fiddle/lib/fiddle/version.rb
@@ -1,3 +1,3 @@
module Fiddle
- VERSION = "1.0.6"
+ VERSION = "1.0.8"
end
diff --git a/ext/io/console/console.c b/ext/io/console/console.c
index ff4df73693..2e2467036d 100644
--- a/ext/io/console/console.c
+++ b/ext/io/console/console.c
@@ -77,7 +77,7 @@ getattr(int fd, conmode *t)
static ID id_getc, id_console, id_close, id_min, id_time, id_intr;
#if ENABLE_IO_GETPASS
-static ID id_gets;
+static ID id_gets, id_chomp_bang;
#endif
#ifdef HAVE_RB_SCHEDULER_TIMEOUT
@@ -1223,8 +1223,8 @@ console_key_pressed_p(VALUE io, VALUE k)
}
#else
struct query_args {
- const char *qstr;
- int opt;
+ char qstr[6];
+ unsigned char opt;
};
static int
@@ -1562,7 +1562,7 @@ static VALUE
str_chomp(VALUE str)
{
if (!NIL_P(str)) {
- str = rb_funcallv(str, rb_intern("chomp!"), 0, 0);
+ rb_funcallv(str, id_chomp_bang, 0, 0);
}
return str;
}
@@ -1574,6 +1574,10 @@ str_chomp(VALUE str)
* Reads and returns a line without echo back.
* Prints +prompt+ unless it is +nil+.
*
+ * The newline character that terminates the
+ * read line is removed from the returned string,
+ * see String#chomp!.
+ *
* You must require 'io/console' to use this method.
*/
static VALUE
@@ -1618,6 +1622,7 @@ Init_console(void)
id_getc = rb_intern("getc");
#if ENABLE_IO_GETPASS
id_gets = rb_intern("gets");
+ id_chomp_bang = rb_intern("chomp!");
#endif
id_console = rb_intern("console");
id_close = rb_intern("close");
diff --git a/ext/io/console/io-console.gemspec b/ext/io/console/io-console.gemspec
index 743e5e965d..8a0df83b2c 100644
--- a/ext/io/console/io-console.gemspec
+++ b/ext/io/console/io-console.gemspec
@@ -1,5 +1,5 @@
# -*- ruby -*-
-_VERSION = "0.5.6"
+_VERSION = "0.5.7"
Gem::Specification.new do |s|
s.name = "io-console"
diff --git a/ext/io/wait/io-wait.gemspec b/ext/io/wait/io-wait.gemspec
index 1c6c2d5705..ec23699def 100644
--- a/ext/io/wait/io-wait.gemspec
+++ b/ext/io/wait/io-wait.gemspec
@@ -1,6 +1,8 @@
+_VERSION = "0.2.0"
+
Gem::Specification.new do |spec|
spec.name = "io-wait"
- spec.version = "0.1.0"
+ spec.version = _VERSION
spec.authors = ["Nobu Nakada"]
spec.email = ["nobu@ruby-lang.org"]
@@ -8,7 +10,7 @@ Gem::Specification.new do |spec|
spec.description = %q{Waits until IO is readable or writable without blocking.}
spec.homepage = "https://github.com/ruby/io-wait"
spec.licenses = ["Ruby", "BSD-2-Clause"]
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
@@ -20,6 +22,6 @@ Gem::Specification.new do |spec|
end
spec.extensions = %w[ext/io/wait/extconf.rb]
spec.bindir = "exe"
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.executables = []
spec.require_paths = ["lib"]
end
diff --git a/ext/io/wait/wait.c b/ext/io/wait/wait.c
index 73bc77a294..512e4f6a80 100644
--- a/ext/io/wait/wait.c
+++ b/ext/io/wait/wait.c
@@ -211,7 +211,7 @@ wait_mode_sym(VALUE mode)
/*
* call-seq:
* io.wait(events, timeout) -> event mask or false.
- * io.wait(timeout = nil, mode = :read) -> event mask or false (deprecated)
+ * io.wait(timeout = nil, mode = :read) -> event mask or false.
*
* Waits until the IO becomes ready for the specified events and returns the
* subset of events that become ready, or +false+ when times out.
@@ -222,34 +222,32 @@ wait_mode_sym(VALUE mode)
* Returns +true+ immediately when buffered data is available.
*
* Optional parameter +mode+ is one of +:read+, +:write+, or
- * +:read_write+ (deprecated).
+ * +:read_write+.
*/
static VALUE
io_wait(int argc, VALUE *argv, VALUE io)
{
- VALUE timeout = Qnil;
+ VALUE timeout = Qundef;
rb_io_event_t events = 0;
- if (argc < 2 || (argc >= 2 && RB_SYMBOL_P(argv[1]))) {
- if (argc > 0) {
- timeout = argv[0];
- }
-
- for (int i = 1; i < argc; i += 1) {
- events |= wait_mode_sym(argv[i]);
+ if (argc != 2 || (RB_SYMBOL_P(argv[0]) || RB_SYMBOL_P(argv[1]))) {
+ for (int i = 0; i < argc; i += 1) {
+ if (RB_SYMBOL_P(argv[i])) {
+ events |= wait_mode_sym(argv[i]);
+ }
+ else if (timeout == Qundef) {
+ rb_time_interval(timeout = argv[i]);
+ }
+ else {
+ rb_raise(rb_eArgError, "timeout given more than once");
+ }
}
+ if (timeout == Qundef) timeout = Qnil;
}
- else if (argc == 2) {
+ else /* argc == 2 */ {
events = RB_NUM2UINT(argv[0]);
-
- if (argv[1] != Qnil) {
- timeout = argv[1];
- }
- }
- else {
- // TODO error
- return Qnil;
+ timeout = argv[1];
}
if (events == 0) {
@@ -275,6 +273,10 @@ io_wait(int argc, VALUE *argv, VALUE io)
void
Init_wait(void)
{
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
+ RB_EXT_RACTOR_SAFE(true);
+#endif
+
rb_define_method(rb_cIO, "nread", io_nread, 0);
rb_define_method(rb_cIO, "ready?", io_ready_p, 0);
diff --git a/ext/monitor/monitor.c b/ext/monitor/monitor.c
index 627d8211d6..a3efe96bb3 100644
--- a/ext/monitor/monitor.c
+++ b/ext/monitor/monitor.c
@@ -53,7 +53,7 @@ monitor_ptr(VALUE monitor)
static int
mc_owner_p(struct rb_monitor *mc)
{
- return mc->owner == rb_thread_current();
+ return mc->owner == rb_fiber_current();
}
static VALUE
@@ -65,7 +65,7 @@ monitor_try_enter(VALUE monitor)
if (!rb_mutex_trylock(mc->mutex)) {
return Qfalse;
}
- RB_OBJ_WRITE(monitor, &mc->owner, rb_thread_current());
+ RB_OBJ_WRITE(monitor, &mc->owner, rb_fiber_current());
mc->count = 0;
}
mc->count += 1;
@@ -78,7 +78,7 @@ monitor_enter(VALUE monitor)
struct rb_monitor *mc = monitor_ptr(monitor);
if (!mc_owner_p(mc)) {
rb_mutex_lock(mc->mutex);
- RB_OBJ_WRITE(monitor, &mc->owner, rb_thread_current());
+ RB_OBJ_WRITE(monitor, &mc->owner, rb_fiber_current());
mc->count = 0;
}
mc->count++;
@@ -90,7 +90,7 @@ monitor_check_owner(VALUE monitor)
{
struct rb_monitor *mc = monitor_ptr(monitor);
if (!mc_owner_p(mc)) {
- rb_raise(rb_eThreadError, "current thread not owner");
+ rb_raise(rb_eThreadError, "current fiber not owner");
}
return Qnil;
}
@@ -161,7 +161,7 @@ monitor_enter_for_cond(VALUE v)
struct wait_for_cond_data *data = (struct wait_for_cond_data *)v;
struct rb_monitor *mc = monitor_ptr(data->monitor);
- RB_OBJ_WRITE(data->monitor, &mc->owner, rb_thread_current());
+ RB_OBJ_WRITE(data->monitor, &mc->owner, rb_fiber_current());
mc->count = NUM2LONG(data->count);
return Qnil;
}
@@ -203,7 +203,7 @@ monitor_synchronize(VALUE monitor)
void
Init_monitor(void)
{
-#if HAVE_RB_EXT_RACTOR_SAFE
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
rb_ext_ractor_safe(true);
#endif
diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c
index 4973a7535b..66d6baa491 100644
--- a/ext/objspace/object_tracing.c
+++ b/ext/objspace/object_tracing.c
@@ -208,7 +208,8 @@ allocation_info_tracer_compact(void *ptr)
{
struct traceobj_arg *trace_arg = (struct traceobj_arg *)ptr;
- if (st_foreach_with_replace(trace_arg->object_table, hash_foreach_should_replace_key, hash_replace_key, 0)) {
+ if (trace_arg->object_table &&
+ st_foreach_with_replace(trace_arg->object_table, hash_foreach_should_replace_key, hash_replace_key, 0)) {
rb_raise(rb_eRuntimeError, "hash modified during iteration");
}
}
diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c
index 7afdfc1f6b..3fa4fd279b 100644
--- a/ext/objspace/objspace.c
+++ b/ext/objspace/objspace.c
@@ -996,6 +996,7 @@ Init_objspace(void)
* You can use the #type method to check the type of the internal object.
*/
rb_cInternalObjectWrapper = rb_define_class_under(rb_mObjSpace, "InternalObjectWrapper", rb_cObject);
+ rb_undef_alloc_func(rb_cInternalObjectWrapper);
rb_define_method(rb_cInternalObjectWrapper, "type", iow_type, 0);
rb_define_method(rb_cInternalObjectWrapper, "inspect", iow_inspect, 0);
rb_define_method(rb_cInternalObjectWrapper, "internal_object_id", iow_internal_object_id, 0);
diff --git a/ext/openssl/History.md b/ext/openssl/History.md
index a4a82a146c..60b9dd8825 100644
--- a/ext/openssl/History.md
+++ b/ext/openssl/History.md
@@ -1,3 +1,26 @@
+Version 2.2.2
+=============
+
+Merged changes in 2.1.4.
+
+
+Version 2.2.1
+=============
+
+Merged changes in 2.1.3. Additionally, the following issues are fixed by this
+release.
+
+Bug fixes
+---------
+
+* Fix crash in `OpenSSL::Timestamp::{Request,Response,TokenInfo}.new` when
+ invalid arguments are given.
+ [[GitHub #407]](https://github.com/ruby/openssl/pull/407)
+* Fix `OpenSSL::Timestamp::Factory#create_timestamp` with LibreSSL on platforms
+ where `time_t` has a different size from `long`.
+ [[GitHub #454]](https://github.com/ruby/openssl/pull/454)
+
+
Version 2.2.0
=============
@@ -75,6 +98,52 @@ Notable changes
[[GitHub #297]](https://github.com/ruby/openssl/pull/297)
+Version 2.1.4
+=============
+
+Bug fixes
+---------
+
+* Do not use pkg-config if --with-openssl-dir option is specified.
+ [[GitHub #486]](https://github.com/ruby/openssl/pull/486)
+
+
+Version 2.1.3
+=============
+
+Bug fixes
+---------
+
+* Fix deprecation warnings on Ruby 3.0.
+* Add ".include" directive support in `OpenSSL::Config`.
+ [[GitHub #216]](https://github.com/ruby/openssl/pull/216)
+* Fix handling of IPv6 address SANs.
+ [[GitHub #185]](https://github.com/ruby/openssl/pull/185)
+* Hostname verification failure with `OpenSSL::SSL::SSLContext#verify_hostname=`
+ sets a proper error code.
+ [[GitHub #350]](https://github.com/ruby/openssl/pull/350)
+* Fix crash with `OpenSSL::BN.new(nil, 2)`.
+ [[Bug #15760]](https://bugs.ruby-lang.org/issues/15760)
+* `OpenSSL::SSL::SSLSocket#sys{read,write}` prevent internal string buffers from
+ being modified by another thread.
+ [[GitHub #453]](https://github.com/ruby/openssl/pull/453)
+* Fix misuse of input record separator in `OpenSSL::Buffering` where it was
+ for output.
+* Fix wrong interger casting in `OpenSSL::PKey::EC#dsa_verify_asn1`.
+ [[GitHub #460]](https://github.com/ruby/openssl/pull/460)
+* `extconf.rb` explicitly checks that OpenSSL's version number is 1.0.1 or
+ newer but also less than 3.0. Ruby/OpenSSL v2.1.x and v2.2.x will not support
+ OpenSSL 3.0 API.
+ [[GitHub #458]](https://github.com/ruby/openssl/pull/458)
+* Activate `digest` gem correctly. `digest` library could go into an
+ inconsistent state if there are multiple versions of `digest` is installed
+ and `openssl` is `require`d before `digest`.
+ [[GitHub #463]](https://github.com/ruby/openssl/pull/463)
+* Fix GC.compact compatibility.
+ [[GitHub #464]](https://github.com/ruby/openssl/issues/464)
+ [[GitHub #465]](https://github.com/ruby/openssl/pull/465)
+
+
Version 2.1.2
=============
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index 693e55cd97..0dc1a5eb43 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -13,7 +13,7 @@
require "mkmf"
-dir_config("openssl")
+dir_config_given = dir_config("openssl").any?
dir_config("kerberos")
Logging::message "=== OpenSSL for Ruby configurator ===\n"
@@ -33,9 +33,6 @@ if $mswin || $mingw
have_library("ws2_32")
end
-Logging::message "=== Checking for required stuff... ===\n"
-result = pkg_config("openssl") && have_header("openssl/ssl.h")
-
if $mingw
append_cflags '-D_FORTIFY_SOURCE=2'
append_ldflags '-fstack-protector'
@@ -92,19 +89,33 @@ def find_openssl_library
return false
end
-unless result
- unless find_openssl_library
- Logging::message "=== Checking for required stuff failed. ===\n"
- Logging::message "Makefile wasn't created. Fix the errors above.\n"
- raise "OpenSSL library could not be found. You might want to use " \
- "--with-openssl-dir=<dir> option to specify the prefix where OpenSSL " \
- "is installed."
- end
+Logging::message "=== Checking for required stuff... ===\n"
+pkg_config_found = !dir_config_given && pkg_config("openssl") && have_header("openssl/ssl.h")
+
+if !pkg_config_found && !find_openssl_library
+ Logging::message "=== Checking for required stuff failed. ===\n"
+ Logging::message "Makefile wasn't created. Fix the errors above.\n"
+ raise "OpenSSL library could not be found. You might want to use " \
+ "--with-openssl-dir=<dir> option to specify the prefix where OpenSSL " \
+ "is installed."
end
-unless checking_for("OpenSSL version is 1.0.1 or later") {
- try_static_assert("OPENSSL_VERSION_NUMBER >= 0x10001000L", "openssl/opensslv.h") }
- raise "OpenSSL >= 1.0.1 or LibreSSL is required"
+version_ok = if have_macro("LIBRESSL_VERSION_NUMBER", "openssl/opensslv.h")
+ is_libressl = true
+ checking_for("LibreSSL version >= 2.5.0") {
+ try_static_assert("LIBRESSL_VERSION_NUMBER >= 0x20500000L", "openssl/opensslv.h") }
+else
+ checking_for("OpenSSL version >= 1.0.1 and < 3.0.0") {
+ try_static_assert("OPENSSL_VERSION_NUMBER >= 0x10001000L", "openssl/opensslv.h") &&
+ !try_static_assert("OPENSSL_VERSION_MAJOR >= 3", "openssl/opensslv.h") }
+end
+unless version_ok
+ raise "OpenSSL >= 1.0.1, < 3.0.0 or LibreSSL >= 2.5.0 is required"
+end
+
+# Prevent wincrypt.h from being included, which defines conflicting macro with openssl/x509.h
+if is_libressl && ($mswin || $mingw)
+ $defs.push("-DNOCRYPT")
end
Logging::message "=== Checking for OpenSSL features... ===\n"
@@ -116,10 +127,6 @@ engines.each { |name|
have_func("ENGINE_load_#{name}()", "openssl/engine.h")
}
-if ($mswin || $mingw) && have_macro("LIBRESSL_VERSION_NUMBER", "openssl/opensslv.h")
- $defs.push("-DNOCRYPT")
-end
-
# added in 1.0.2
have_func("EC_curve_nist2nid")
have_func("X509_REVOKED_dup")
diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb
index 9c7515ba0f..89e052069e 100644
--- a/ext/openssl/lib/openssl/version.rb
+++ b/ext/openssl/lib/openssl/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module OpenSSL
- VERSION = "2.2.0"
+ VERSION = "2.2.2"
end
diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec
index 471a3c4265..c8e3cc3fc3 100644
--- a/ext/openssl/openssl.gemspec
+++ b/ext/openssl/openssl.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "openssl"
- spec.version = "2.2.0"
+ spec.version = "2.2.2"
spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"]
spec.email = ["ruby-core@ruby-lang.org"]
spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.}
@@ -17,7 +17,8 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = ">= 2.3.0"
- spec.add_development_dependency "rake"
+ spec.add_runtime_dependency "ipaddr"
+ spec.add_development_dependency "rake", ">= 11.2.0"
spec.add_development_dependency "rake-compiler"
spec.add_development_dependency "test-unit", "~> 3.0"
spec.add_development_dependency "rdoc"
diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c
index d94b8e375c..bec37299f7 100644
--- a/ext/openssl/ossl_bn.c
+++ b/ext/openssl/ossl_bn.c
@@ -453,7 +453,7 @@ ossl_bn_is_negative(VALUE self)
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
- if (!BN_##func(result, bn, ossl_bn_ctx)) { \
+ if (BN_##func(result, bn, ossl_bn_ctx) <= 0) { \
BN_free(result); \
ossl_raise(eBNError, NULL); \
} \
@@ -479,7 +479,7 @@ BIGNUM_1c(sqr)
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
- if (!BN_##func(result, bn1, bn2)) { \
+ if (BN_##func(result, bn1, bn2) <= 0) { \
BN_free(result); \
ossl_raise(eBNError, NULL); \
} \
@@ -512,7 +512,7 @@ BIGNUM_2(sub)
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
- if (!BN_##func(result, bn1, bn2, ossl_bn_ctx)) { \
+ if (BN_##func(result, bn1, bn2, ossl_bn_ctx) <= 0) { \
BN_free(result); \
ossl_raise(eBNError, NULL); \
} \
@@ -556,11 +556,21 @@ BIGNUM_2c(gcd)
BIGNUM_2c(mod_sqr)
/*
- * Document-method: OpenSSL::BN#mod_inverse
* call-seq:
- * bn.mod_inverse(bn2) => aBN
+ * bn.mod_inverse(bn2) => aBN
*/
-BIGNUM_2c(mod_inverse)
+static VALUE
+ossl_bn_mod_inverse(VALUE self, VALUE other)
+{
+ BIGNUM *bn1, *bn2 = GetBNPtr(other), *result;
+ VALUE obj;
+ GetBN(self, bn1);
+ obj = NewBN(rb_obj_class(self));
+ if (!(result = BN_mod_inverse(NULL, bn1, bn2, ossl_bn_ctx)))
+ ossl_raise(eBNError, "BN_mod_inverse");
+ SetBN(obj, result);
+ return obj;
+}
/*
* call-seq:
@@ -609,7 +619,7 @@ ossl_bn_div(VALUE self, VALUE other)
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
- if (!BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx)) { \
+ if (BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx) <= 0) { \
BN_free(result); \
ossl_raise(eBNError, NULL); \
} \
@@ -651,7 +661,7 @@ BIGNUM_3c(mod_exp)
{ \
BIGNUM *bn; \
GetBN(self, bn); \
- if (!BN_##func(bn, NUM2INT(bit))) { \
+ if (BN_##func(bn, NUM2INT(bit)) <= 0) { \
ossl_raise(eBNError, NULL); \
} \
return self; \
@@ -711,7 +721,7 @@ ossl_bn_is_bit_set(VALUE self, VALUE bit)
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
- if (!BN_##func(result, bn, b)) { \
+ if (BN_##func(result, bn, b) <= 0) { \
BN_free(result); \
ossl_raise(eBNError, NULL); \
} \
@@ -741,7 +751,7 @@ BIGNUM_SHIFT(rshift)
int b; \
b = NUM2INT(bits); \
GetBN(self, bn); \
- if (!BN_##func(bn, bn, b)) \
+ if (BN_##func(bn, bn, b) <= 0) \
ossl_raise(eBNError, NULL); \
return self; \
}
@@ -780,7 +790,7 @@ BIGNUM_SELF_SHIFT(rshift)
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
- if (!BN_##func(result, b, top, bottom)) { \
+ if (BN_##func(result, b, top, bottom) <= 0) { \
BN_free(result); \
ossl_raise(eBNError, NULL); \
} \
@@ -809,7 +819,7 @@ BIGNUM_RAND(pseudo_rand)
if (!(result = BN_new())) { \
ossl_raise(eBNError, NULL); \
} \
- if (!BN_##func##_range(result, bn)) { \
+ if (BN_##func##_range(result, bn) <= 0) { \
BN_free(result); \
ossl_raise(eBNError, NULL); \
} \
diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c
index 0b78f40b72..5b92fc39f0 100644
--- a/ext/openssl/ossl_cipher.c
+++ b/ext/openssl/ossl_cipher.c
@@ -814,6 +814,31 @@ ossl_cipher_block_size(VALUE self)
}
/*
+ * call-seq:
+ * cipher.ccm_data_len = integer -> integer
+ *
+ * Sets the length of the plaintext / ciphertext message that will be
+ * processed in CCM mode. Make sure to call this method after #key= and
+ * #iv= have been set, and before #auth_data=.
+ *
+ * Only call this method after calling Cipher#encrypt or Cipher#decrypt.
+ */
+static VALUE
+ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len)
+{
+ int in_len, out_len;
+ EVP_CIPHER_CTX *ctx;
+
+ in_len = NUM2INT(data_len);
+
+ GetCipher(self, ctx);
+ if (EVP_CipherUpdate(ctx, NULL, &out_len, NULL, in_len) != 1)
+ ossl_raise(eCipherError, NULL);
+
+ return data_len;
+}
+
+/*
* INIT
*/
void
@@ -1043,6 +1068,7 @@ Init_ossl_cipher(void)
rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0);
rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0);
rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1);
+ rb_define_method(cCipher, "ccm_data_len=", ossl_cipher_set_ccm_data_len, 1);
id_auth_tag_len = rb_intern_const("auth_tag_len");
id_key_set = rb_intern_const("key_set");
diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c
index e2157cb02f..6294fa2acf 100644
--- a/ext/openssl/ossl_digest.c
+++ b/ext/openssl/ossl_digest.c
@@ -313,8 +313,6 @@ ossl_digest_block_length(VALUE self)
void
Init_ossl_digest(void)
{
- rb_require("digest");
-
#if 0
mOSSL = rb_define_module("OpenSSL");
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
@@ -398,6 +396,12 @@ Init_ossl_digest(void)
* digest2 = sha256.digest(data2)
*
*/
+
+ /*
+ * Digest::Class is defined by the digest library. rb_require() cannot be
+ * used here because it bypasses RubyGems.
+ */
+ rb_funcall(Qnil, rb_intern_const("require"), 1, rb_str_new_cstr("digest"));
cDigest = rb_define_class_under(mOSSL, "Digest", rb_path2class("Digest::Class"));
/* Document-class: OpenSSL::Digest::DigestError
*
diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c
index fc2bc6c815..1d105abd35 100644
--- a/ext/openssl/ossl_pkey_ec.c
+++ b/ext/openssl/ossl_pkey_ec.c
@@ -653,15 +653,15 @@ static VALUE ossl_ec_key_dsa_verify_asn1(VALUE self, VALUE data, VALUE sig)
StringValue(data);
StringValue(sig);
- switch (ECDSA_verify(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(sig), (int)RSTRING_LEN(sig), ec)) {
- case 1: return Qtrue;
- case 0: return Qfalse;
- default: break;
+ switch (ECDSA_verify(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data),
+ (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), ec)) {
+ case 1:
+ return Qtrue;
+ case 0:
+ return Qfalse;
+ default:
+ ossl_raise(eECError, "ECDSA_verify");
}
-
- ossl_raise(eECError, "ECDSA_verify");
-
- UNREACHABLE;
}
/*
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index 4b7efa39f5..7654be12ee 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -13,6 +13,12 @@
#define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0]))
+#if !defined(TLS1_3_VERSION) && \
+ defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER >= 0x3020000fL
+# define TLS1_3_VERSION 0x0304
+#endif
+
#ifdef _WIN32
# define TO_SOCKET(s) _get_osfhandle(s)
#else
@@ -33,7 +39,7 @@ static VALUE eSSLErrorWaitReadable;
static VALUE eSSLErrorWaitWritable;
static ID id_call, ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback,
- id_npn_protocols_encoded;
+ id_npn_protocols_encoded, id_each;
static VALUE sym_exception, sym_wait_readable, sym_wait_writable;
static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
@@ -54,6 +60,13 @@ static int ossl_sslctx_ex_store_p;
#endif
static void
+ossl_sslctx_mark(void *ptr)
+{
+ SSL_CTX *ctx = ptr;
+ rb_gc_mark((VALUE)SSL_CTX_get_ex_data(ctx, ossl_sslctx_ex_ptr_idx));
+}
+
+static void
ossl_sslctx_free(void *ptr)
{
SSL_CTX *ctx = ptr;
@@ -67,7 +80,7 @@ ossl_sslctx_free(void *ptr)
static const rb_data_type_t ossl_sslctx_type = {
"OpenSSL/SSL/CTX",
{
- 0, ossl_sslctx_free,
+ ossl_sslctx_mark, ossl_sslctx_free,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
};
@@ -616,7 +629,7 @@ static VALUE
ssl_encode_npn_protocols(VALUE protocols)
{
VALUE encoded = rb_str_new(NULL, 0);
- rb_iterate(rb_each, protocols, ssl_npn_encode_protocol_i, encoded);
+ rb_block_call(protocols, id_each, 0, 0, ssl_npn_encode_protocol_i, encoded);
return encoded;
}
@@ -686,7 +699,7 @@ static int
ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen,
void *arg)
{
- VALUE protocols = (VALUE)arg;
+ VALUE protocols = rb_attr_get((VALUE)arg, id_npn_protocols_encoded);
*out = (const unsigned char *) RSTRING_PTR(protocols);
*outlen = RSTRING_LENINT(protocols);
@@ -908,7 +921,7 @@ ossl_sslctx_setup(VALUE self)
if (!NIL_P(val)) {
VALUE encoded = ssl_encode_npn_protocols(val);
rb_ivar_set(self, id_npn_protocols_encoded, encoded);
- SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)encoded);
+ SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)self);
OSSL_Debug("SSL NPN advertise callback added");
}
if (RTEST(rb_attr_get(self, id_i_npn_select_cb))) {
@@ -1527,6 +1540,14 @@ ssl_started(SSL *ssl)
}
static void
+ossl_ssl_mark(void *ptr)
+{
+ SSL *ssl = ptr;
+ rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx));
+ rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx));
+}
+
+static void
ossl_ssl_free(void *ssl)
{
SSL_free(ssl);
@@ -1535,7 +1556,7 @@ ossl_ssl_free(void *ssl)
const rb_data_type_t ossl_ssl_type = {
"OpenSSL/SSL",
{
- 0, ossl_ssl_free,
+ ossl_ssl_mark, ossl_ssl_free,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
};
@@ -1691,6 +1712,11 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts)
rb_io_wait_readable(fptr->fd);
continue;
case SSL_ERROR_SYSCALL:
+#ifdef __APPLE__
+ /* See ossl_ssl_write_internal() */
+ if (errno == EPROTOTYPE)
+ continue;
+#endif
if (errno) rb_sys_fail(funcname);
ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl));
#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED)
@@ -1847,26 +1873,36 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
io = rb_attr_get(self, id_i_io);
GetOpenFile(io, fptr);
if (ssl_started(ssl)) {
- for (;;){
+ rb_str_locktmp(str);
+ for (;;) {
nread = SSL_read(ssl, RSTRING_PTR(str), ilen);
switch(ssl_get_error(ssl, nread)){
case SSL_ERROR_NONE:
+ rb_str_unlocktmp(str);
goto end;
case SSL_ERROR_ZERO_RETURN:
+ rb_str_unlocktmp(str);
if (no_exception_p(opts)) { return Qnil; }
rb_eof_error();
case SSL_ERROR_WANT_WRITE:
- if (no_exception_p(opts)) { return sym_wait_writable; }
- write_would_block(nonblock);
+ if (nonblock) {
+ rb_str_unlocktmp(str);
+ if (no_exception_p(opts)) { return sym_wait_writable; }
+ write_would_block(nonblock);
+ }
rb_io_wait_writable(fptr->fd);
continue;
case SSL_ERROR_WANT_READ:
- if (no_exception_p(opts)) { return sym_wait_readable; }
- read_would_block(nonblock);
+ if (nonblock) {
+ rb_str_unlocktmp(str);
+ if (no_exception_p(opts)) { return sym_wait_readable; }
+ read_would_block(nonblock);
+ }
rb_io_wait_readable(fptr->fd);
continue;
case SSL_ERROR_SYSCALL:
if (!ERR_peek_error()) {
+ rb_str_unlocktmp(str);
if (errno)
rb_sys_fail(0);
else {
@@ -1883,6 +1919,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
}
/* fall through */
default:
+ rb_str_unlocktmp(str);
ossl_raise(eSSLError, "SSL_read");
}
}
@@ -1953,21 +1990,21 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
int nwrite = 0;
rb_io_t *fptr;
int nonblock = opts != Qfalse;
- VALUE io;
+ VALUE tmp, io;
- StringValue(str);
+ tmp = rb_str_new_frozen(StringValue(str));
GetSSL(self, ssl);
io = rb_attr_get(self, id_i_io);
GetOpenFile(io, fptr);
if (ssl_started(ssl)) {
- for (;;){
- int num = RSTRING_LENINT(str);
+ for (;;) {
+ int num = RSTRING_LENINT(tmp);
/* SSL_write(3ssl) manpage states num == 0 is undefined */
if (num == 0)
goto end;
- nwrite = SSL_write(ssl, RSTRING_PTR(str), num);
+ nwrite = SSL_write(ssl, RSTRING_PTR(tmp), num);
switch(ssl_get_error(ssl, nwrite)){
case SSL_ERROR_NONE:
goto end;
@@ -1982,6 +2019,16 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
rb_io_wait_readable(fptr->fd);
continue;
case SSL_ERROR_SYSCALL:
+#ifdef __APPLE__
+ /*
+ * It appears that send syscall can return EPROTOTYPE if the
+ * socket is being torn down. Retry to get a proper errno to
+ * make the error handling in line with the socket library.
+ * [Bug #14713] https://bugs.ruby-lang.org/issues/14713
+ */
+ if (errno == EPROTOTYPE)
+ continue;
+#endif
if (errno) rb_sys_fail(0);
default:
ossl_raise(eSSLError, "SSL_write");
@@ -2997,6 +3044,7 @@ Init_ossl_ssl(void)
id_tmp_dh_callback = rb_intern_const("tmp_dh_callback");
id_tmp_ecdh_callback = rb_intern_const("tmp_ecdh_callback");
id_npn_protocols_encoded = rb_intern_const("npn_protocols_encoded");
+ id_each = rb_intern_const("each");
#define DefIVarID(name) do \
id_i_##name = rb_intern_const("@"#name); while (0)
diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c
index d3209c3d40..cff9b7bfff 100644
--- a/ext/openssl/ossl_ts.c
+++ b/ext/openssl/ossl_ts.c
@@ -68,9 +68,9 @@ static VALUE cTimestampRequest;
static VALUE cTimestampResponse;
static VALUE cTimestampTokenInfo;
static VALUE cTimestampFactory;
-static ID sBAD_ALG, sBAD_REQUEST, sBAD_DATA_FORMAT, sTIME_NOT_AVAILABLE;
-static ID sUNACCEPTED_POLICY, sUNACCEPTED_EXTENSION, sADD_INFO_NOT_AVAILABLE;
-static ID sSYSTEM_FAILURE;
+static VALUE sBAD_ALG, sBAD_REQUEST, sBAD_DATA_FORMAT, sTIME_NOT_AVAILABLE;
+static VALUE sUNACCEPTED_POLICY, sUNACCEPTED_EXTENSION, sADD_INFO_NOT_AVAILABLE;
+static VALUE sSYSTEM_FAILURE;
static void
ossl_ts_req_free(void *ptr)
@@ -205,8 +205,10 @@ ossl_ts_req_initialize(int argc, VALUE *argv, VALUE self)
in = ossl_obj2bio(&arg);
ts_req = d2i_TS_REQ_bio(in, &ts_req);
BIO_free(in);
- if (!ts_req)
+ if (!ts_req) {
+ DATA_PTR(self) = NULL;
ossl_raise(eTimestampError, "Error when decoding the timestamp request");
+ }
DATA_PTR(self) = ts_req;
return self;
@@ -529,8 +531,10 @@ ossl_ts_resp_initialize(VALUE self, VALUE der)
in = ossl_obj2bio(&der);
ts_resp = d2i_TS_RESP_bio(in, &ts_resp);
BIO_free(in);
- if (!ts_resp)
+ if (!ts_resp) {
+ DATA_PTR(self) = NULL;
ossl_raise(eTimestampError, "Error when decoding the timestamp response");
+ }
DATA_PTR(self) = ts_resp;
return self;
@@ -871,8 +875,10 @@ ossl_ts_token_info_initialize(VALUE self, VALUE der)
in = ossl_obj2bio(&der);
info = d2i_TS_TST_INFO_bio(in, &info);
BIO_free(in);
- if (!info)
+ if (!info) {
+ DATA_PTR(self) = NULL;
ossl_raise(eTimestampError, "Error when decoding the timestamp token info");
+ }
DATA_PTR(self) = info;
return self;
@@ -1074,7 +1080,11 @@ ossl_tsfac_serial_cb(struct TS_resp_ctx *ctx, void *data)
}
static int
+#if !defined(LIBRESSL_VERSION_NUMBER)
ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, long *sec, long *usec)
+#else
+ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, time_t *sec, long *usec)
+#endif
{
*sec = *((long *)data);
*usec = 0;
@@ -1247,24 +1257,24 @@ Init_ossl_ts(void)
* timestamp server rejects the message imprint algorithm used in the
* +Request+
*/
- sBAD_ALG = rb_intern_const("BAD_ALG");
+ sBAD_ALG = ID2SYM(rb_intern_const("BAD_ALG"));
/*
* Possible return value for +Response#failure_info+. Indicates that the
* timestamp server was not able to process the +Request+ properly.
*/
- sBAD_REQUEST = rb_intern_const("BAD_REQUEST");
+ sBAD_REQUEST = ID2SYM(rb_intern_const("BAD_REQUEST"));
/*
* Possible return value for +Response#failure_info+. Indicates that the
* timestamp server was not able to parse certain data in the +Request+.
*/
- sBAD_DATA_FORMAT = rb_intern_const("BAD_DATA_FORMAT");
+ sBAD_DATA_FORMAT = ID2SYM(rb_intern_const("BAD_DATA_FORMAT"));
- sTIME_NOT_AVAILABLE = rb_intern_const("TIME_NOT_AVAILABLE");
- sUNACCEPTED_POLICY = rb_intern_const("UNACCEPTED_POLICY");
- sUNACCEPTED_EXTENSION = rb_intern_const("UNACCEPTED_EXTENSION");
- sADD_INFO_NOT_AVAILABLE = rb_intern_const("ADD_INFO_NOT_AVAILABLE");
- sSYSTEM_FAILURE = rb_intern_const("SYSTEM_FAILURE");
+ sTIME_NOT_AVAILABLE = ID2SYM(rb_intern_const("TIME_NOT_AVAILABLE"));
+ sUNACCEPTED_POLICY = ID2SYM(rb_intern_const("UNACCEPTED_POLICY"));
+ sUNACCEPTED_EXTENSION = ID2SYM(rb_intern_const("UNACCEPTED_EXTENSION"));
+ sADD_INFO_NOT_AVAILABLE = ID2SYM(rb_intern_const("ADD_INFO_NOT_AVAILABLE"));
+ sSYSTEM_FAILURE = ID2SYM(rb_intern_const("SYSTEM_FAILURE"));
/* Document-class: OpenSSL::Timestamp
* Provides classes and methods to request, create and validate
diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c
index 61543d44f6..9035a70aa9 100644
--- a/ext/openssl/ossl_x509store.c
+++ b/ext/openssl/ossl_x509store.c
@@ -106,6 +106,13 @@ VALUE cX509StoreContext;
VALUE eX509StoreError;
static void
+ossl_x509store_mark(void *ptr)
+{
+ X509_STORE *store = ptr;
+ rb_gc_mark((VALUE)X509_STORE_get_ex_data(store, store_ex_verify_cb_idx));
+}
+
+static void
ossl_x509store_free(void *ptr)
{
X509_STORE_free(ptr);
@@ -114,7 +121,7 @@ ossl_x509store_free(void *ptr)
static const rb_data_type_t ossl_x509store_type = {
"OpenSSL/X509/STORE",
{
- 0, ossl_x509store_free,
+ ossl_x509store_mark, ossl_x509store_free,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
};
@@ -457,23 +464,16 @@ ossl_x509store_verify(int argc, VALUE *argv, VALUE self)
}
/*
- * Public Functions
- */
-static void ossl_x509stctx_free(void*);
-
-
-static const rb_data_type_t ossl_x509stctx_type = {
- "OpenSSL/X509/STORE_CTX",
- {
- 0, ossl_x509stctx_free,
- },
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
-};
-
-/*
* Private functions
*/
static void
+ossl_x509stctx_mark(void *ptr)
+{
+ X509_STORE_CTX *ctx = ptr;
+ rb_gc_mark((VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx));
+}
+
+static void
ossl_x509stctx_free(void *ptr)
{
X509_STORE_CTX *ctx = ptr;
@@ -484,6 +484,14 @@ ossl_x509stctx_free(void *ptr)
X509_STORE_CTX_free(ctx);
}
+static const rb_data_type_t ossl_x509stctx_type = {
+ "OpenSSL/X509/STORE_CTX",
+ {
+ ossl_x509stctx_mark, ossl_x509stctx_free,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+};
+
static VALUE
ossl_x509stctx_alloc(VALUE klass)
{
@@ -517,7 +525,9 @@ static VALUE ossl_x509stctx_set_time(VALUE, VALUE);
/*
* call-seq:
- * StoreContext.new(store, cert = nil, chain = nil)
+ * StoreContext.new(store, cert = nil, untrusted = nil)
+ *
+ * Sets up a StoreContext for a verification of the X.509 certificate _cert_.
*/
static VALUE
ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self)
@@ -527,15 +537,24 @@ ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self)
X509_STORE *x509st;
X509 *x509 = NULL;
STACK_OF(X509) *x509s = NULL;
+ int state;
rb_scan_args(argc, argv, "12", &store, &cert, &chain);
GetX509StCtx(self, ctx);
GetX509Store(store, x509st);
- if(!NIL_P(cert)) x509 = DupX509CertPtr(cert); /* NEED TO DUP */
- if(!NIL_P(chain)) x509s = ossl_x509_ary2sk(chain);
- if(X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){
+ if (!NIL_P(cert))
+ x509 = DupX509CertPtr(cert); /* NEED TO DUP */
+ if (!NIL_P(chain)) {
+ x509s = ossl_protect_x509_ary2sk(chain, &state);
+ if (state) {
+ X509_free(x509);
+ rb_jump_tag(state);
+ }
+ }
+ if (X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){
+ X509_free(x509);
sk_X509_pop_free(x509s, X509_free);
- ossl_raise(eX509StoreError, NULL);
+ ossl_raise(eX509StoreError, "X509_STORE_CTX_init");
}
if (!NIL_P(t = rb_iv_get(store, "@time")))
ossl_x509stctx_set_time(self, t);
diff --git a/ext/psych/lib/psych.rb b/ext/psych/lib/psych.rb
index cedf0a4ad6..34d2218549 100644
--- a/ext/psych/lib/psych.rb
+++ b/ext/psych/lib/psych.rb
@@ -271,7 +271,7 @@ module Psych
# YAML documents that are supplied via user input. Instead, please use the
# safe_load method.
#
- def self.load yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: false, symbolize_names: false, freeze: false
+ def self.unsafe_load yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: false, symbolize_names: false, freeze: false
if legacy_filename != NOT_GIVEN
warn_with_uplevel 'Passing filename with the 2nd argument of Psych.load is deprecated. Use keyword argument like Psych.load(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
filename = legacy_filename
@@ -281,6 +281,7 @@ module Psych
return fallback unless result
result.to_ruby(symbolize_names: symbolize_names, freeze: freeze)
end
+ class << self; alias :load :unsafe_load; end
###
# Safely load the yaml string in +yaml+. By default, only the following
@@ -577,11 +578,12 @@ module Psych
# NOTE: This method *should not* be used to parse untrusted documents, such as
# YAML documents that are supplied via user input. Instead, please use the
# safe_load_file method.
- def self.load_file filename, **kwargs
+ def self.unsafe_load_file filename, **kwargs
File.open(filename, 'r:bom|utf-8') { |f|
- self.load f, filename: filename, **kwargs
+ self.unsafe_load f, filename: filename, **kwargs
}
end
+ class << self; alias :load_file :unsafe_load_file; end
###
# Safely loads the document contained in +filename+. Returns the yaml contained in
diff --git a/ext/psych/lib/psych/handler.rb b/ext/psych/lib/psych/handler.rb
index 8f23e366fa..ad7249ff77 100644
--- a/ext/psych/lib/psych/handler.rb
+++ b/ext/psych/lib/psych/handler.rb
@@ -119,7 +119,7 @@ module Psych
# +tag+ is an associated tag or nil
# +plain+ is a boolean value
# +quoted+ is a boolean value
- # +style+ is an integer idicating the string style
+ # +style+ is an integer indicating the string style
#
# See the constants in Psych::Nodes::Scalar for the possible values of
# +style+
diff --git a/ext/psych/lib/psych/nodes/scalar.rb b/ext/psych/lib/psych/nodes/scalar.rb
index e2616b6a84..5550b616a3 100644
--- a/ext/psych/lib/psych/nodes/scalar.rb
+++ b/ext/psych/lib/psych/nodes/scalar.rb
@@ -50,7 +50,7 @@ module Psych
# +tag+ is an associated tag or nil
# +plain+ is a boolean value
# +quoted+ is a boolean value
- # +style+ is an integer idicating the string style
+ # +style+ is an integer indicating the string style
#
# == See Also
#
diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb
index 488f86e0b4..acb21336c4 100644
--- a/ext/psych/lib/psych/versions.rb
+++ b/ext/psych/lib/psych/versions.rb
@@ -1,10 +1,10 @@
-
# frozen_string_literal: true
+
module Psych
# The version of Psych you are using
- VERSION = '3.3.0'
+ VERSION = '3.3.2'
if RUBY_ENGINE == 'jruby'
- DEFAULT_SNAKEYAML_VERSION = '1.26'.freeze
+ DEFAULT_SNAKEYAML_VERSION = '1.28'.freeze
end
end
diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb
index ec80701917..4de7f80d33 100644
--- a/ext/psych/lib/psych/visitors/to_ruby.rb
+++ b/ext/psych/lib/psych/visitors/to_ruby.rb
@@ -24,6 +24,7 @@ module Psych
super()
@st = {}
@ss = ss
+ @load_tags = Psych.load_tags
@domain_types = Psych.domain_types
@class_loader = class_loader
@symbolize_names = symbolize_names
@@ -48,7 +49,7 @@ module Psych
end
def deserialize o
- if klass = resolve_class(Psych.load_tags[o.tag])
+ if klass = resolve_class(@load_tags[o.tag])
instance = klass.allocate
if instance.respond_to?(:init_with)
@@ -128,7 +129,7 @@ module Psych
end
def visit_Psych_Nodes_Sequence o
- if klass = resolve_class(Psych.load_tags[o.tag])
+ if klass = resolve_class(@load_tags[o.tag])
instance = klass.allocate
if instance.respond_to?(:init_with)
@@ -160,8 +161,8 @@ module Psych
end
def visit_Psych_Nodes_Mapping o
- if Psych.load_tags[o.tag]
- return revive(resolve_class(Psych.load_tags[o.tag]), o)
+ if @load_tags[o.tag]
+ return revive(resolve_class(@load_tags[o.tag]), o)
end
return revive_hash(register(o, {}), o) unless o.tag
@@ -326,6 +327,7 @@ module Psych
end
private
+
def register node, object
@st[node.anchor] = object if node.anchor
object
@@ -337,7 +339,7 @@ module Psych
list
end
- def revive_hash hash, o
+ def revive_hash hash, o, tagged= false
o.children.each_slice(2) { |k,v|
key = accept(k)
val = accept(v)
@@ -364,7 +366,7 @@ module Psych
hash[key] = val
end
else
- if @symbolize_names
+ if !tagged && @symbolize_names && key.is_a?(String)
key = key.to_sym
elsif !@freeze
key = deduplicate(key)
@@ -402,7 +404,7 @@ module Psych
def revive klass, node
s = register(node, klass.allocate)
- init_with(s, revive_hash({}, node), node)
+ init_with(s, revive_hash({}, node, true), node)
end
def init_with o, h, node
diff --git a/ext/psych/lib/psych/visitors/visitor.rb b/ext/psych/lib/psych/visitors/visitor.rb
index e2585c0c77..21052aa66f 100644
--- a/ext/psych/lib/psych/visitors/visitor.rb
+++ b/ext/psych/lib/psych/visitors/visitor.rb
@@ -17,7 +17,7 @@ module Psych
if defined?(Ractor)
def dispatch
- Ractor.current[:Psych_Visitors_Visitor] ||= Visitor.dispatch_cache
+ @dispatch_cache ||= (Ractor.current[:Psych_Visitors_Visitor] ||= Visitor.dispatch_cache)
end
else
DISPATCH = dispatch_cache
diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb
index ac6777aeb5..bf7c0bb8ca 100644
--- a/ext/psych/lib/psych/visitors/yaml_tree.rb
+++ b/ext/psych/lib/psych/visitors/yaml_tree.rb
@@ -509,9 +509,9 @@ module Psych
def emit_coder c, o
case c.type
when :scalar
- @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, Nodes::Scalar::ANY
+ @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, c.style
when :seq
- @emitter.start_sequence nil, c.tag, c.tag.nil?, Nodes::Sequence::BLOCK
+ @emitter.start_sequence nil, c.tag, c.tag.nil?, c.style
c.seq.each do |thing|
accept thing
end
diff --git a/ext/psych/yaml/loader.c b/ext/psych/yaml/loader.c
index 78b87e6f6b..bcf3aee8cb 100644
--- a/ext/psych/yaml/loader.c
+++ b/ext/psych/yaml/loader.c
@@ -541,4 +541,4 @@ yaml_parser_load_mapping_end(yaml_parser_t *parser, yaml_event_t *event,
(void)POP(parser, *ctx);
return 1;
-} \ No newline at end of file
+}
diff --git a/ext/psych/yaml/scanner.c b/ext/psych/yaml/scanner.c
index 6acee7d465..bb5d201274 100644
--- a/ext/psych/yaml/scanner.c
+++ b/ext/psych/yaml/scanner.c
@@ -273,7 +273,7 @@
* The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation
* increase that precedes a block collection (cf. the INDENT token in Python).
* The token BLOCK-END denote indentation decrease that ends a block collection
- * (cf. the DEDENT token in Python). However YAML has some syntax pecularities
+ * (cf. the DEDENT token in Python). However YAML has some syntax peculiarities
* that makes detections of these tokens more complex.
*
* The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators
@@ -3287,7 +3287,7 @@ yaml_parser_scan_flow_scalar(yaml_parser_t *parser, yaml_token_t *token,
/* Check if we are at the end of the scalar. */
- /* Fix for crash unitialized value crash
+ /* Fix for crash uninitialized value crash
* Credit for the bug and input is to OSS Fuzz
* Credit for the fix to Alex Gaynor
*/
diff --git a/ext/psych/yaml/yaml.h b/ext/psych/yaml/yaml.h
index 4f84957d8f..f1b7bfde20 100644
--- a/ext/psych/yaml/yaml.h
+++ b/ext/psych/yaml/yaml.h
@@ -1095,7 +1095,7 @@ typedef struct yaml_parser_s {
yaml_error_type_t error;
/** Error description. */
const char *problem;
- /** The byte about which the problem occured. */
+ /** The byte about which the problem occurred. */
size_t problem_offset;
/** The problematic value (@c -1 is none). */
int problem_value;
@@ -1335,7 +1335,7 @@ yaml_parser_delete(yaml_parser_t *parser);
* Set a string input.
*
* Note that the @a input pointer must be valid while the @a parser object
- * exists. The application is responsible for destroing @a input after
+ * exists. The application is responsible for destroying @a input after
* destroying the @a parser.
*
* @param[in,out] parser A parser object.
@@ -1734,7 +1734,7 @@ typedef struct yaml_emitter_s {
size_t length;
/** Does the scalar contain line breaks? */
int multiline;
- /** Can the scalar be expessed in the flow plain style? */
+ /** Can the scalar be expressed in the flow plain style? */
int flow_plain_allowed;
/** Can the scalar be expressed in the block plain style? */
int block_plain_allowed;
@@ -1950,7 +1950,7 @@ yaml_emitter_close(yaml_emitter_t *emitter);
/**
* Emit a YAML document.
*
- * The documen object may be generated using the yaml_parser_load() function
+ * The document object may be generated using the yaml_parser_load() function
* or the yaml_document_initialize() function. The emitter takes the
* responsibility for the document object and destroys its content after
* it is emitted. The document object is destroyed even if the function fails.
diff --git a/ext/psych/yaml/yaml_private.h b/ext/psych/yaml/yaml_private.h
index 6c674de147..266a6bd3a7 100644
--- a/ext/psych/yaml/yaml_private.h
+++ b/ext/psych/yaml/yaml_private.h
@@ -2,7 +2,7 @@
#include RUBY_EXTCONF_H
#endif
-#if HAVE_CONFIG_H
+#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
diff --git a/ext/racc/cparse/cparse.c b/ext/racc/cparse/cparse.c
index 8614c10e09..f71ed2bba9 100644
--- a/ext/racc/cparse/cparse.c
+++ b/ext/racc/cparse/cparse.c
@@ -819,7 +819,7 @@ reduce0(RB_BLOCK_CALL_FUNC_ARGLIST(_, data))
void
Init_cparse(void)
{
-#if HAVE_RB_EXT_RACTOR_SAFE
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
rb_ext_ractor_safe(true);
#endif
diff --git a/ext/ripper/lib/ripper/lexer.rb b/ext/ripper/lib/ripper/lexer.rb
index 06db264497..1df1c022c7 100644
--- a/ext/ripper/lib/ripper/lexer.rb
+++ b/ext/ripper/lib/ripper/lexer.rb
@@ -136,7 +136,7 @@ class Ripper
end
@buf.flatten!
unless (result = @buf).empty?
- result.concat(@buf) until (@buf = []; super(); @buf.empty?)
+ result.concat(@buf) until (@buf = []; super(); @buf.flatten!; @buf.empty?)
end
result
end
diff --git a/ext/socket/ifaddr.c b/ext/socket/ifaddr.c
index da013256cb..1da259bd6f 100644
--- a/ext/socket/ifaddr.c
+++ b/ext/socket/ifaddr.c
@@ -460,6 +460,7 @@ rsock_init_sockifaddr(void)
* Socket::Ifaddr represents a result of getifaddrs() function.
*/
rb_cSockIfaddr = rb_define_class_under(rb_cSocket, "Ifaddr", rb_cObject);
+ rb_undef_alloc_func(rb_cSockIfaddr);
rb_define_method(rb_cSockIfaddr, "inspect", ifaddr_inspect, 0);
rb_define_method(rb_cSockIfaddr, "name", ifaddr_name, 0);
rb_define_method(rb_cSockIfaddr, "ifindex", ifaddr_ifindex, 0);
diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c
index 6c86e8964d..12930b3575 100644
--- a/ext/stringio/stringio.c
+++ b/ext/stringio/stringio.c
@@ -1,3 +1,4 @@
+/* -*- mode: c; indent-tabs-mode: t -*- */
/**********************************************************************
stringio.c -
@@ -11,7 +12,7 @@
**********************************************************************/
-#define STRINGIO_VERSION "3.0.0"
+#define STRINGIO_VERSION "3.0.1.1"
#include "ruby.h"
#include "ruby/io.h"
@@ -64,7 +65,7 @@ strio_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash,
n = strchr(n, '|');
}
e = strchr(++n, ':');
- len = e ? e - n : strlen(n);
+ len = e ? e - n : (long)strlen(n);
if (len > 0 && len <= ENCODING_MAXNAMELEN) {
if (e) {
memcpy(encname, n, len);
@@ -599,6 +600,14 @@ strio_closed_write(VALUE self)
return Qtrue;
}
+static struct StringIO *
+strio_to_read(VALUE self)
+{
+ struct StringIO *ptr = readable(self);
+ if (ptr->pos < RSTRING_LEN(ptr->string)) return ptr;
+ return NULL;
+}
+
/*
* call-seq:
* strio.eof -> true or false
@@ -610,8 +619,7 @@ strio_closed_write(VALUE self)
static VALUE
strio_eof(VALUE self)
{
- struct StringIO *ptr = readable(self);
- if (ptr->pos < RSTRING_LEN(ptr->string)) return Qfalse;
+ if (strio_to_read(self)) return Qfalse;
return Qtrue;
}
@@ -821,11 +829,11 @@ strio_get_sync(VALUE self)
static VALUE
strio_each_byte(VALUE self)
{
- struct StringIO *ptr = readable(self);
+ struct StringIO *ptr;
RETURN_ENUMERATOR(self, 0, 0);
- while (ptr->pos < RSTRING_LEN(ptr->string)) {
+ while ((ptr = strio_to_read(self)) != NULL) {
char c = RSTRING_PTR(ptr->string)[ptr->pos++];
rb_yield(CHR2FIX(c));
}
@@ -976,7 +984,7 @@ strio_unget_bytes(struct StringIO *ptr, const char *cp, long cl)
len = RSTRING_LEN(str);
rest = pos - len;
if (cl > pos) {
- long ex = (rest < 0 ? cl-pos : cl+rest);
+ long ex = cl - (rest < 0 ? pos : len);
rb_str_modify_expand(str, ex);
rb_str_set_len(str, len + ex);
s = RSTRING_PTR(str);
@@ -1064,11 +1072,7 @@ strio_each_codepoint(VALUE self)
ptr = readable(self);
enc = get_enc(ptr);
- for (;;) {
- if (ptr->pos >= RSTRING_LEN(ptr->string)) {
- return self;
- }
-
+ while ((ptr = strio_to_read(self)) != NULL) {
c = rb_enc_codepoint_len(RSTRING_PTR(ptr->string)+ptr->pos,
RSTRING_END(ptr->string), &n, enc);
ptr->pos += n;
diff --git a/ext/stringio/stringio.gemspec b/ext/stringio/stringio.gemspec
index d12c648a7d..524d976cfb 100644
--- a/ext/stringio/stringio.gemspec
+++ b/ext/stringio/stringio.gemspec
@@ -28,7 +28,4 @@ Gem::Specification.new do |s|
# s.cert_chain = %w[certs/nobu.pem]
# s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
-
- s.add_development_dependency 'rake-compiler'
- s.add_development_dependency 'test-unit'
end
diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c
index 88074a0a26..e1426380b4 100644
--- a/ext/strscan/strscan.c
+++ b/ext/strscan/strscan.c
@@ -22,7 +22,7 @@ extern size_t onig_region_memsize(const struct re_registers *regs);
#include <stdbool.h>
-#define STRSCAN_VERSION "3.0.0"
+#define STRSCAN_VERSION "3.0.1"
/* =======================================================================
Data Type Definitions
@@ -445,13 +445,10 @@ static VALUE
strscan_get_charpos(VALUE self)
{
struct strscanner *p;
- VALUE substr;
GET_SCANNER(self, p);
- substr = rb_funcall(p->str, id_byteslice, 2, INT2FIX(0), LONG2NUM(p->curr));
-
- return rb_str_length(substr);
+ return LONG2NUM(rb_enc_strlen(S_PBEG(p), CURPTR(p), rb_enc_get(p->str)));
}
/*
@@ -984,7 +981,7 @@ strscan_unscan(VALUE self)
}
/*
- * Returns +true+ iff the scan pointer is at the beginning of the line.
+ * Returns +true+ if and only if the scan pointer is at the beginning of the line.
*
* s = StringScanner.new("test\ntest\n")
* s.bol? # => true
@@ -1037,7 +1034,7 @@ strscan_empty_p(VALUE self)
}
/*
- * Returns true iff there is more data in the string. See #eos?.
+ * Returns true if and only if there is more data in the string. See #eos?.
* This method is obsolete; use #eos? instead.
*
* s = StringScanner.new('test string')
@@ -1054,7 +1051,7 @@ strscan_rest_p(VALUE self)
}
/*
- * Returns +true+ iff the last match was successful.
+ * Returns +true+ if and only if the last match was successful.
*
* s = StringScanner.new('test string')
* s.match?(/\w+/) # => 4
@@ -1537,7 +1534,7 @@ strscan_fixed_anchor_p(VALUE self)
*
* === Finding Where we Are
*
- * - #beginning_of_line? (#bol?)
+ * - #beginning_of_line? (<tt>#bol?</tt>)
* - #eos?
* - #rest?
* - #rest_size
@@ -1554,13 +1551,13 @@ strscan_fixed_anchor_p(VALUE self)
* - #matched
* - #matched?
* - #matched_size
- * - []
+ * - <tt>#[]</tt>
* - #pre_match
* - #post_match
*
* === Miscellaneous
*
- * - <<
+ * - <tt><<</tt>
* - #concat
* - #string
* - #string=
diff --git a/ext/strscan/strscan.gemspec b/ext/strscan/strscan.gemspec
index fa9b895a9c..5d8119ea4c 100644
--- a/ext/strscan/strscan.gemspec
+++ b/ext/strscan/strscan.gemspec
@@ -25,8 +25,4 @@ Gem::Specification.new do |s|
s.email = [nil, "kou@cozmixng.org"]
s.homepage = "https://github.com/ruby/strscan"
s.licenses = ["Ruby", "BSD-2-Clause"]
-
- s.add_development_dependency "rake-compiler"
- s.add_development_dependency "benchmark-driver"
- s.add_development_dependency "test-unit"
end
diff --git a/ext/zlib/extlibs b/ext/zlib/extlibs
index a64b37ba5f..556d1f4a6f 100644
--- a/ext/zlib/extlibs
+++ b/ext/zlib/extlibs
@@ -1,8 +1,6 @@
-ver = 1.2.11
+ver = 1.2.13
pkg = zlib-$(ver)
-https://zlib.net/$(pkg).tar.gz \
- md5:1c9f62f0778697a09d36121ead88e08e \
- sha512:73fd3fff4adeccd4894084c15ddac89890cd10ef105dd5e1835e1e9bbb6a49ff229713bd197d203edfa17c2727700fce65a2a235f07568212d820dca88b528ae \
+https://github.com/madler/zlib/releases/download/v$(ver)/$(pkg).tar.gz \
+ sha256:b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30 \
#
- win32/$(pkg)-mswin.patch -p0
diff --git a/ext/zlib/win32/zlib-1.2.11-mswin.patch b/ext/zlib/win32/zlib-1.2.11-mswin.patch
deleted file mode 100644
index 8810b4403c..0000000000
--- a/ext/zlib/win32/zlib-1.2.11-mswin.patch
+++ /dev/null
@@ -1,95 +0,0 @@
-diff -ru zlib-1.2.11/gzread.c zlib-1.2.11/gzread.c
---- zlib-1.2.11/gzread.c 2016-12-31 23:37:10.000000000 +0900
-+++ zlib-1.2.11/gzread.c 2020-11-23 19:35:00.550987184 +0900
-@@ -316,7 +316,7 @@
- /* set n to the maximum amount of len that fits in an unsigned int */
- n = -1;
- if (n > len)
-- n = len;
-+ n = (unsigned)len;
-
- /* first just try copying data from the output buffer */
- if (state->x.have) {
-@@ -397,7 +397,7 @@
- }
-
- /* read len or fewer bytes to buf */
-- len = gz_read(state, buf, len);
-+ len = (unsigned)gz_read(state, buf, len);
-
- /* check for an error */
- if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR)
-@@ -469,7 +469,7 @@
- }
-
- /* nothing there -- try gz_read() */
-- ret = gz_read(state, buf, 1);
-+ ret = (int)gz_read(state, buf, 1);
- return ret < 1 ? -1 : buf[0];
- }
-
-diff -ru zlib-1.2.11/gzwrite.c zlib-1.2.11/gzwrite.c
---- zlib-1.2.11/gzwrite.c 2017-01-15 09:29:40.000000000 +0900
-+++ zlib-1.2.11/gzwrite.c 2020-11-23 19:35:41.530494030 +0900
-@@ -209,7 +209,7 @@
- state->in);
- copy = state->size - have;
- if (copy > len)
-- copy = len;
-+ copy = (unsigned)len;
- memcpy(state->in + have, buf, copy);
- state->strm.avail_in += copy;
- state->x.pos += copy;
-@@ -229,7 +229,7 @@
- do {
- unsigned n = (unsigned)-1;
- if (n > len)
-- n = len;
-+ n = (unsigned)len;
- state->strm.avail_in = n;
- state->x.pos += n;
- if (gz_comp(state, Z_NO_FLUSH) == -1)
-@@ -368,7 +368,7 @@
-
- /* write string */
- len = strlen(str);
-- ret = gz_write(state, str, len);
-+ ret = (int)gz_write(state, str, len);
- return ret == 0 && len != 0 ? -1 : ret;
- }
-
-diff -ru zlib-1.2.11/win32/Makefile.msc zlib-1.2.11/win32/Makefile.msc
---- zlib-1.2.11/win32/Makefile.msc 2017-01-15 09:07:08.000000000 +0900
-+++ zlib-1.2.11/win32/Makefile.msc 2020-11-23 22:37:19.746500208 +0900
-@@ -37,6 +37,22 @@
- gzwrite.obj infback.obj inflate.obj inftrees.obj inffast.obj trees.obj uncompr.obj zutil.obj
- OBJA =
-
-+!ifdef USE_ASM
-+LOC = -DASMV -DASMINF
-+!if "$(ARCH)" == "i386"
-+OBJA = inffas32.obj match686.obj
-+!else if "$(ARCH)" == "x64"
-+AS = ml64
-+LOC = $(LOC) -I.
-+OBJA = inffasx64.obj gvmat64.obj inffas8664.obj
-+!endif
-+!endif
-+
-+!if "$(ARCH)" == "x64"
-+ZBASE = 0x5A4C000000
-+!else
-+ZBASE = 0x5A4C0000
-+!endif
-
- # targets
- all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) \
-@@ -49,7 +65,7 @@
-
- $(SHAREDLIB): $(TOP)/win32/zlib.def $(OBJS) $(OBJA) zlib1.res
- $(LD) $(LDFLAGS) -def:$(TOP)/win32/zlib.def -dll -implib:$(IMPLIB) \
-- -out:$@ -base:0x5A4C0000 $(OBJS) $(OBJA) zlib1.res
-+ -out:$@ -base:$(ZBASE) $(OBJS) $(OBJA) zlib1.res
- if exist $@.manifest \
- mt -nologo -manifest $@.manifest -outputresource:$@;2
-
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index 90fa5a61ef..5dbeba6943 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -25,7 +25,7 @@
# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
#endif
-#define RUBY_ZLIB_VERSION "1.1.0"
+#define RUBY_ZLIB_VERSION "2.0.0"
#ifndef RB_PASS_CALLED_KEYWORDS
# define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass)
@@ -288,6 +288,7 @@ static VALUE rb_gzreader_readlines(int, VALUE*, VALUE);
* - Zlib::MemError
* - Zlib::BufError
* - Zlib::VersionError
+ * - Zlib::InProgressError
*
* (if you have GZIP_SUPPORT)
* - Zlib::GzipReader
@@ -304,7 +305,7 @@ void Init_zlib(void);
/*--------- Exceptions --------*/
static VALUE cZError, cStreamEnd, cNeedDict;
-static VALUE cStreamError, cDataError, cMemError, cBufError, cVersionError;
+static VALUE cStreamError, cDataError, cMemError, cBufError, cVersionError, cInProgressError;
static void
raise_zlib_error(int err, const char *msg)
@@ -546,6 +547,7 @@ struct zstream {
unsigned long flags;
VALUE buf;
VALUE input;
+ VALUE mutex;
z_stream stream;
const struct zstream_funcs {
int (*reset)(z_streamp);
@@ -554,14 +556,15 @@ struct zstream {
} *func;
};
-#define ZSTREAM_FLAG_READY 0x1
-#define ZSTREAM_FLAG_IN_STREAM 0x2
-#define ZSTREAM_FLAG_FINISHED 0x4
-#define ZSTREAM_FLAG_CLOSING 0x8
-#define ZSTREAM_FLAG_GZFILE 0x10 /* disallows yield from expand_buffer for
+#define ZSTREAM_FLAG_READY (1 << 0)
+#define ZSTREAM_FLAG_IN_STREAM (1 << 1)
+#define ZSTREAM_FLAG_FINISHED (1 << 2)
+#define ZSTREAM_FLAG_CLOSING (1 << 3)
+#define ZSTREAM_FLAG_GZFILE (1 << 4) /* disallows yield from expand_buffer for
gzip*/
-#define ZSTREAM_REUSE_BUFFER 0x20
-#define ZSTREAM_FLAG_UNUSED 0x40
+#define ZSTREAM_REUSE_BUFFER (1 << 5)
+#define ZSTREAM_IN_PROGRESS (1 << 6)
+#define ZSTREAM_FLAG_UNUSED (1 << 7)
#define ZSTREAM_READY(z) ((z)->flags |= ZSTREAM_FLAG_READY)
#define ZSTREAM_IS_READY(z) ((z)->flags & ZSTREAM_FLAG_READY)
@@ -590,7 +593,9 @@ static const struct zstream_funcs inflate_funcs = {
};
struct zstream_run_args {
- struct zstream * z;
+ struct zstream *const z;
+ Bytef *src;
+ long len;
int flush; /* stream flush value for inflate() or deflate() */
int interrupt; /* stop processing the stream and return to ruby */
int jump_state; /* for buffer expansion block break or exception */
@@ -621,6 +626,7 @@ zstream_init(struct zstream *z, const struct zstream_funcs *func)
z->flags = 0;
z->buf = Qnil;
z->input = Qnil;
+ z->mutex = rb_mutex_new();
z->stream.zalloc = zlib_mem_alloc;
z->stream.zfree = zlib_mem_free;
z->stream.opaque = Z_NULL;
@@ -652,7 +658,9 @@ zstream_expand_buffer(struct zstream *z)
rb_obj_reveal(z->buf, rb_cString);
}
+ rb_mutex_unlock(z->mutex);
rb_protect(rb_yield, z->buf, &state);
+ rb_mutex_lock(z->mutex);
if (ZSTREAM_REUSE_BUFFER_P(z)) {
rb_str_modify(z->buf);
@@ -1053,19 +1061,18 @@ zstream_unblock_func(void *ptr)
args->interrupt = 1;
}
-static void
-zstream_run(struct zstream *z, Bytef *src, long len, int flush)
+static VALUE
+zstream_run_try(VALUE value_arg)
{
- struct zstream_run_args args;
+ struct zstream_run_args *args = (struct zstream_run_args *)value_arg;
+ struct zstream *z = args->z;
+ Bytef *src = args->src;
+ long len = args->len;
+ int flush = args->flush;
+
int err;
VALUE old_input = Qnil;
- args.z = z;
- args.flush = flush;
- args.interrupt = 0;
- args.jump_state = 0;
- args.stream_output = !ZSTREAM_IS_GZFILE(z) && rb_block_given_p();
-
if (NIL_P(z->input) && len == 0) {
z->stream.next_in = (Bytef*)"";
z->stream.avail_in = 0;
@@ -1087,14 +1094,20 @@ zstream_run(struct zstream *z, Bytef *src, long len, int flush)
loop:
#ifndef RB_NOGVL_UBF_ASYNC_SAFE
- err = (int)(VALUE)rb_thread_call_without_gvl(zstream_run_func, (void *)&args,
- zstream_unblock_func, (void *)&args);
+ err = (int)(VALUE)rb_thread_call_without_gvl(zstream_run_func, (void *)args,
+ zstream_unblock_func, (void *)args);
#else
- err = (int)(VALUE)rb_nogvl(zstream_run_func, (void *)&args,
- zstream_unblock_func, (void *)&args,
+ err = (int)(VALUE)rb_nogvl(zstream_run_func, (void *)args,
+ zstream_unblock_func, (void *)args,
RB_NOGVL_UBF_ASYNC_SAFE);
#endif
+ /* retry if no exception is thrown */
+ if (err == Z_OK && args->interrupt) {
+ args->interrupt = 0;
+ goto loop;
+ }
+
if (flush != Z_FINISH && err == Z_BUF_ERROR
&& z->stream.avail_out > 0) {
z->flags |= ZSTREAM_FLAG_IN_STREAM;
@@ -1128,8 +1141,52 @@ loop:
rb_gc_force_recycle(old_input);
}
- if (args.jump_state)
- rb_jump_tag(args.jump_state);
+ if (args->jump_state)
+ rb_jump_tag(args->jump_state);
+
+ return Qnil;
+}
+
+static VALUE
+zstream_run_ensure(VALUE value_arg)
+{
+ struct zstream_run_args *args = (struct zstream_run_args *)value_arg;
+
+ /* Remove ZSTREAM_IN_PROGRESS flag to signal that this zstream is not in use. */
+ args->z->flags &= ~ZSTREAM_IN_PROGRESS;
+
+ return Qnil;
+}
+
+static VALUE
+zstream_run_synchronized(VALUE value_arg)
+{
+ struct zstream_run_args *args = (struct zstream_run_args *)value_arg;
+
+ /* Cannot start zstream while it is in progress. */
+ if (args->z->flags & ZSTREAM_IN_PROGRESS) {
+ rb_raise(cInProgressError, "zlib stream is in progress");
+ }
+ args->z->flags |= ZSTREAM_IN_PROGRESS;
+
+ rb_ensure(zstream_run_try, value_arg, zstream_run_ensure, value_arg);
+
+ return Qnil;
+}
+
+static void
+zstream_run(struct zstream *z, Bytef *src, long len, int flush)
+{
+ struct zstream_run_args args = {
+ .z = z,
+ .src = src,
+ .len = len,
+ .flush = flush,
+ .interrupt = 0,
+ .jump_state = 0,
+ .stream_output = !ZSTREAM_IS_GZFILE(z) && rb_block_given_p(),
+ };
+ rb_mutex_synchronize(z->mutex, zstream_run_synchronized, (VALUE)&args);
}
static VALUE
@@ -1177,6 +1234,7 @@ zstream_mark(void *p)
struct zstream *z = p;
rb_gc_mark(z->buf);
rb_gc_mark(z->input);
+ rb_gc_mark(z->mutex);
}
static void
@@ -3514,6 +3572,16 @@ rb_gzfile_path(VALUE obj)
return gz->path;
}
+static VALUE
+gzfile_initialize_path_partial(VALUE obj)
+{
+ struct gzfile* gz;
+ TypedData_Get_Struct(obj, struct gzfile, &gzfile_data_type, gz);
+ gz->path = rb_funcall(gz->io, id_path, 0);
+ rb_define_singleton_method(obj, "path", rb_gzfile_path, 0);
+ return Qnil;
+}
+
static void
rb_gzfile_ecopts(struct gzfile *gz, VALUE opts)
{
@@ -3622,8 +3690,8 @@ rb_gzwriter_initialize(int argc, VALUE *argv, VALUE obj)
rb_gzfile_ecopts(gz, opt);
if (rb_respond_to(io, id_path)) {
- gz->path = rb_funcall(gz->io, id_path, 0);
- rb_define_singleton_method(obj, "path", rb_gzfile_path, 0);
+ /* File#path may raise IOError in case when a path is unavailable */
+ rb_rescue2(gzfile_initialize_path_partial, obj, NULL, Qnil, rb_eIOError, (VALUE)0);
}
return obj;
@@ -3884,8 +3952,8 @@ rb_gzreader_initialize(int argc, VALUE *argv, VALUE obj)
rb_gzfile_ecopts(gz, opt);
if (rb_respond_to(io, id_path)) {
- gz->path = rb_funcall(gz->io, id_path, 0);
- rb_define_singleton_method(obj, "path", rb_gzfile_path, 0);
+ /* File#path may raise IOError in case when a path is unavailable */
+ rb_rescue2(gzfile_initialize_path_partial, obj, NULL, Qnil, rb_eIOError, (VALUE)0);
}
return obj;
@@ -4548,7 +4616,7 @@ zlib_gunzip_run(VALUE arg)
void
Init_zlib(void)
{
-#if HAVE_RB_EXT_RACTOR_SAFE
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
rb_ext_ractor_safe(true);
#endif
@@ -4570,6 +4638,7 @@ Init_zlib(void)
cMemError = rb_define_class_under(mZlib, "MemError", cZError);
cBufError = rb_define_class_under(mZlib, "BufError", cZError);
cVersionError = rb_define_class_under(mZlib, "VersionError", cZError);
+ cInProgressError = rb_define_class_under(mZlib, "InProgressError", cZError);
rb_define_module_function(mZlib, "zlib_version", rb_zlib_version, 0);
rb_define_module_function(mZlib, "adler32", rb_zlib_adler32, -1);
@@ -4877,6 +4946,7 @@ Init_zlib(void)
* - Zlib::MemError
* - Zlib::BufError
* - Zlib::VersionError
+ * - Zlib::InProgressError
*
*/
@@ -4952,6 +5022,20 @@ Init_zlib(void)
*/
/*
+ * Document-class: Zlib::InProgressError
+ *
+ * Subclass of Zlib::Error. This error is raised when the zlib
+ * stream is currently in progress.
+ *
+ * For example:
+ *
+ * inflater = Zlib::Inflate.new
+ * inflater.inflate(compressed) do
+ * inflater.inflate(compressed) # Raises Zlib::InProgressError
+ * end
+ */
+
+/*
* Document-class: Zlib::GzipFile::Error
*
* Base class of errors that occur when processing GZIP files.
diff --git a/ext/zlib/zlib.gemspec b/ext/zlib/zlib.gemspec
index ae1f24143d..1d35065921 100644
--- a/ext/zlib/zlib.gemspec
+++ b/ext/zlib/zlib.gemspec
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
spec.homepage = "https://github.com/ruby/zlib"
spec.licenses = ["Ruby", "BSD-2-Clause"]
- spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "ext/zlib/extconf.rb", "ext/zlib/zlib.c", "zlib.gemspec"]
+ spec.files = [".gitignore", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "ext/zlib/extconf.rb", "ext/zlib/zlib.c", "zlib.gemspec"]
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
diff --git a/file.c b/file.c
index 0c599b0895..dc832a8d9c 100644
--- a/file.c
+++ b/file.c
@@ -268,6 +268,46 @@ rb_str_encode_ospath(VALUE path)
#ifdef __APPLE__
# define NORMALIZE_UTF8PATH 1
+
+# ifdef HAVE_WORKING_FORK
+static void
+rb_CFString_class_initialize_before_fork(void)
+{
+ /*
+ * Since macOS 13, CFString family API used in
+ * rb_str_append_normalized_ospath may internally use Objective-C classes
+ * (NSTaggedPointerString and NSPlaceholderMutableString) for small strings.
+ *
+ * On the other hand, Objective-C classes should not be used for the first
+ * time in a fork()'ed but not exec()'ed process. Violations for this rule
+ * can result deadlock during class initialization, so Objective-C runtime
+ * conservatively crashes on such cases by default.
+ *
+ * Therefore, we need to use CFString API to initialize Objective-C classes
+ * used internally *before* fork().
+ *
+ * For future changes, please note that this initialization process cannot
+ * be done in ctor because NSTaggedPointerString in CoreFoundation is enabled
+ * after CFStringInitializeTaggedStrings(), which is called during loading
+ * Objective-C runtime after ctor.
+ * For more details, see https://bugs.ruby-lang.org/issues/18912
+ */
+
+ /* Enough small but non-empty ASCII string to fit in NSTaggedPointerString. */
+ const char small_str[] = "/";
+ long len = sizeof(small_str) - 1;
+
+ const CFAllocatorRef alloc = kCFAllocatorDefault;
+ CFStringRef s = CFStringCreateWithBytesNoCopy(alloc,
+ (const UInt8 *)small_str,
+ len, kCFStringEncodingUTF8,
+ FALSE, kCFAllocatorNull);
+ CFMutableStringRef m = CFStringCreateMutableCopy(alloc, len, s);
+ CFRelease(m);
+ CFRelease(s);
+}
+# endif
+
static VALUE
rb_str_append_normalized_ospath(VALUE str, const char *ptr, long len)
{
@@ -1253,7 +1293,7 @@ statx_birthtime(const struct statx *stx, VALUE fname)
/* birthtime is not supported on the filesystem */
statx_notimplement("birthtime");
}
- return rb_time_nano_new(stx->stx_btime.tv_sec, stx->stx_btime.tv_nsec);
+ return rb_time_nano_new((time_t)stx->stx_btime.tv_sec, stx->stx_btime.tv_nsec);
}
typedef struct statx statx_data;
@@ -2173,7 +2213,7 @@ rb_file_sticky_p(VALUE obj, VALUE fname)
#ifdef S_ISVTX
return check3rdbyte(fname, S_ISVTX);
#else
- return Qnil;
+ return Qfalse;
#endif
}
@@ -6462,6 +6502,10 @@ const char ruby_null_device[] =
void
Init_File(void)
{
+#if defined(__APPLE__) && defined(HAVE_WORKING_FORK)
+ rb_CFString_class_initialize_before_fork();
+#endif
+
VALUE separator;
rb_mFileTest = rb_define_module("FileTest");
diff --git a/gc.c b/gc.c
index e363dc158a..5d0c342206 100644
--- a/gc.c
+++ b/gc.c
@@ -679,7 +679,7 @@ typedef struct rb_objspace {
unsigned int dont_gc : 1;
unsigned int dont_incremental : 1;
unsigned int during_gc : 1;
- unsigned int during_compacting : 2;
+ unsigned int during_compacting : 1;
unsigned int gc_stressful: 1;
unsigned int has_hook: 1;
unsigned int during_minor_gc : 1;
@@ -1202,6 +1202,14 @@ tick(void)
#define MEASURE_LINE(expr) expr
#endif /* USE_TICK_T */
+static inline void *
+asan_unpoison_object_temporary(VALUE obj)
+{
+ void *ptr = asan_poisoned_object_p(obj);
+ asan_unpoison_object(obj, false);
+ return ptr;
+}
+
#define FL_CHECK2(name, x, pred) \
((RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) ? \
(rb_bug(name": SPECIAL_CONST (%p)", (void *)(x)), 0) : (pred))
@@ -1239,99 +1247,102 @@ RVALUE_FLAGS_AGE(VALUE flags)
static int
check_rvalue_consistency_force(const VALUE obj, int terminate)
{
- rb_objspace_t *objspace = &rb_objspace;
int err = 0;
+ rb_objspace_t *objspace = &rb_objspace;
- if (SPECIAL_CONST_P(obj)) {
- fprintf(stderr, "check_rvalue_consistency: %p is a special const.\n", (void *)obj);
- err++;
- }
- else if (!is_pointer_to_heap(objspace, (void *)obj)) {
- /* check if it is in tomb_pages */
- struct heap_page *page = NULL;
- list_for_each(&heap_tomb->pages, page, page_node) {
- if (&page->start[0] <= (RVALUE *)obj &&
- (RVALUE *)obj < &page->start[page->total_slots]) {
- fprintf(stderr, "check_rvalue_consistency: %p is in a tomb_heap (%p).\n",
- (void *)obj, (void *)page);
- err++;
- goto skip;
- }
- }
- bp();
- fprintf(stderr, "check_rvalue_consistency: %p is not a Ruby object.\n", (void *)obj);
- err++;
- skip:
- ;
- }
- else {
- const int wb_unprotected_bit = RVALUE_WB_UNPROTECTED_BITMAP(obj) != 0;
- const int uncollectible_bit = RVALUE_UNCOLLECTIBLE_BITMAP(obj) != 0;
- const int mark_bit = RVALUE_MARK_BITMAP(obj) != 0;
- const int marking_bit = RVALUE_MARKING_BITMAP(obj) != 0, remembered_bit = marking_bit;
- const int age = RVALUE_FLAGS_AGE(RBASIC(obj)->flags);
-
- if (GET_HEAP_PAGE(obj)->flags.in_tomb) {
- fprintf(stderr, "check_rvalue_consistency: %s is in tomb page.\n", obj_info(obj));
- err++;
- }
- if (BUILTIN_TYPE(obj) == T_NONE) {
- fprintf(stderr, "check_rvalue_consistency: %s is T_NONE.\n", obj_info(obj));
- err++;
- }
- if (BUILTIN_TYPE(obj) == T_ZOMBIE) {
- fprintf(stderr, "check_rvalue_consistency: %s is T_ZOMBIE.\n", obj_info(obj));
+ RB_VM_LOCK_ENTER_NO_BARRIER();
+ {
+ if (SPECIAL_CONST_P(obj)) {
+ fprintf(stderr, "check_rvalue_consistency: %p is a special const.\n", (void *)obj);
err++;
}
-
- obj_memsize_of((VALUE)obj, FALSE);
-
- /* check generation
- *
- * OLD == age == 3 && old-bitmap && mark-bit (except incremental marking)
- */
- if (age > 0 && wb_unprotected_bit) {
- fprintf(stderr, "check_rvalue_consistency: %s is not WB protected, but age is %d > 0.\n", obj_info(obj), age);
+ else if (!is_pointer_to_heap(objspace, (void *)obj)) {
+ /* check if it is in tomb_pages */
+ struct heap_page *page = NULL;
+ list_for_each(&heap_tomb->pages, page, page_node) {
+ if (&page->start[0] <= (RVALUE *)obj &&
+ (RVALUE *)obj < &page->start[page->total_slots]) {
+ fprintf(stderr, "check_rvalue_consistency: %p is in a tomb_heap (%p).\n",
+ (void *)obj, (void *)page);
+ err++;
+ goto skip;
+ }
+ }
+ bp();
+ fprintf(stderr, "check_rvalue_consistency: %p is not a Ruby object.\n", (void *)obj);
err++;
+ skip:
+ ;
}
+ else {
+ const int wb_unprotected_bit = RVALUE_WB_UNPROTECTED_BITMAP(obj) != 0;
+ const int uncollectible_bit = RVALUE_UNCOLLECTIBLE_BITMAP(obj) != 0;
+ const int mark_bit = RVALUE_MARK_BITMAP(obj) != 0;
+ const int marking_bit = RVALUE_MARKING_BITMAP(obj) != 0, remembered_bit = marking_bit;
+ const int age = RVALUE_FLAGS_AGE(RBASIC(obj)->flags);
+
+ if (GET_HEAP_PAGE(obj)->flags.in_tomb) {
+ fprintf(stderr, "check_rvalue_consistency: %s is in tomb page.\n", obj_info(obj));
+ err++;
+ }
+ if (BUILTIN_TYPE(obj) == T_NONE) {
+ fprintf(stderr, "check_rvalue_consistency: %s is T_NONE.\n", obj_info(obj));
+ err++;
+ }
+ if (BUILTIN_TYPE(obj) == T_ZOMBIE) {
+ fprintf(stderr, "check_rvalue_consistency: %s is T_ZOMBIE.\n", obj_info(obj));
+ err++;
+ }
- if (!is_marking(objspace) && uncollectible_bit && !mark_bit) {
- fprintf(stderr, "check_rvalue_consistency: %s is uncollectible, but is not marked while !gc.\n", obj_info(obj));
- err++;
- }
+ obj_memsize_of((VALUE)obj, FALSE);
- if (!is_full_marking(objspace)) {
- if (uncollectible_bit && age != RVALUE_OLD_AGE && !wb_unprotected_bit) {
- fprintf(stderr, "check_rvalue_consistency: %s is uncollectible, but not old (age: %d) and not WB unprotected.\n",
- obj_info(obj), age);
+ /* check generation
+ *
+ * OLD == age == 3 && old-bitmap && mark-bit (except incremental marking)
+ */
+ if (age > 0 && wb_unprotected_bit) {
+ fprintf(stderr, "check_rvalue_consistency: %s is not WB protected, but age is %d > 0.\n", obj_info(obj), age);
err++;
}
- if (remembered_bit && age != RVALUE_OLD_AGE) {
- fprintf(stderr, "check_rvalue_consistency: %s is remembered, but not old (age: %d).\n",
- obj_info(obj), age);
+
+ if (!is_marking(objspace) && uncollectible_bit && !mark_bit) {
+ fprintf(stderr, "check_rvalue_consistency: %s is uncollectible, but is not marked while !gc.\n", obj_info(obj));
err++;
}
- }
- /*
- * check coloring
- *
- * marking:false marking:true
- * marked:false white *invalid*
- * marked:true black grey
- */
- if (is_incremental_marking(objspace) && marking_bit) {
- if (!is_marking(objspace) && !mark_bit) {
- fprintf(stderr, "check_rvalue_consistency: %s is marking, but not marked.\n", obj_info(obj));
- err++;
+ if (!is_full_marking(objspace)) {
+ if (uncollectible_bit && age != RVALUE_OLD_AGE && !wb_unprotected_bit) {
+ fprintf(stderr, "check_rvalue_consistency: %s is uncollectible, but not old (age: %d) and not WB unprotected.\n",
+ obj_info(obj), age);
+ err++;
+ }
+ if (remembered_bit && age != RVALUE_OLD_AGE) {
+ fprintf(stderr, "check_rvalue_consistency: %s is remembered, but not old (age: %d).\n",
+ obj_info(obj), age);
+ err++;
+ }
+ }
+
+ /*
+ * check coloring
+ *
+ * marking:false marking:true
+ * marked:false white *invalid*
+ * marked:true black grey
+ */
+ if (is_incremental_marking(objspace) && marking_bit) {
+ if (!is_marking(objspace) && !mark_bit) {
+ fprintf(stderr, "check_rvalue_consistency: %s is marking, but not marked.\n", obj_info(obj));
+ err++;
+ }
}
}
}
+ RB_VM_LOCK_LEAVE_NO_BARRIER();
if (err > 0 && terminate) {
rb_bug("check_rvalue_consistency_force: there is %d errors.", err);
}
-
return err;
}
@@ -1710,45 +1721,34 @@ heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj
gc_report(3, objspace, "heap_page_add_freeobj: add %p to freelist\n", (void *)obj);
}
-static inline bool
+static inline void
heap_add_freepage(rb_heap_t *heap, struct heap_page *page)
{
asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
GC_ASSERT(page->free_slots != 0);
+ GC_ASSERT(page->freelist != NULL);
- if (page->freelist) {
- page->free_next = heap->free_pages;
- heap->free_pages = page;
+ page->free_next = heap->free_pages;
+ heap->free_pages = page;
- RUBY_DEBUG_LOG("page:%p freelist:%p", page, page->freelist);
+ RUBY_DEBUG_LOG("page:%p freelist:%p", page, page->freelist);
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
- return true;
- }
- else {
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
- return false;
- }
+ asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
}
#if GC_ENABLE_INCREMENTAL_MARK
-static inline int
+static inline void
heap_add_poolpage(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page)
{
asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
- if (page->freelist) {
- page->free_next = heap->pooled_pages;
- heap->pooled_pages = page;
- objspace->rincgc.pooled_slots += page->free_slots;
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+ GC_ASSERT(page->free_slots != 0);
+ GC_ASSERT(page->freelist != NULL);
- return TRUE;
- }
- else {
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+ page->free_next = heap->pooled_pages;
+ heap->pooled_pages = page;
+ objspace->rincgc.pooled_slots += page->free_slots;
- return FALSE;
- }
+ asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
}
#endif
@@ -2071,12 +2071,15 @@ gc_event_hook_body(rb_execution_context_t *ec, rb_objspace_t *objspace, const rb
#define gc_event_hook_available_p(objspace) ((objspace)->flags.has_hook)
#define gc_event_hook_needed_p(objspace, event) ((objspace)->hook_events & (event))
-#define gc_event_hook(objspace, event, data) do { \
+#define gc_event_hook_prep(objspace, event, data, prep) do { \
if (UNLIKELY(gc_event_hook_needed_p(objspace, event))) { \
+ prep; \
gc_event_hook_body(GET_EC(), (objspace), (event), (data)); \
} \
} while (0)
+#define gc_event_hook(objspace, event, data) gc_event_hook_prep(objspace, event, data, (void)0)
+
static inline VALUE
newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace, VALUE obj)
{
@@ -2220,6 +2223,16 @@ ractor_cache_slots(rb_objspace_t *objspace, rb_ractor_t *cr)
GC_ASSERT(RB_TYPE_P((VALUE)cr->newobj_cache.freelist, T_NONE));
}
+static inline VALUE
+newobj_fill(VALUE obj, VALUE v1, VALUE v2, VALUE v3)
+{
+ RVALUE *p = (RVALUE *)obj;
+ p->as.values.v1 = v1;
+ p->as.values.v2 = v2;
+ p->as.values.v3 = v3;
+ return obj;
+}
+
ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, int wb_protected));
static inline VALUE
@@ -2250,7 +2263,7 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *
}
GC_ASSERT(obj != 0);
newobj_init(klass, flags, wb_protected, objspace, obj);
- gc_event_hook(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj);
+ gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_fill(obj, 0, 0, 0));
}
RB_VM_LOCK_LEAVE_CR_LEV(cr, &lev);
@@ -2312,16 +2325,6 @@ newobj_of0(VALUE klass, VALUE flags, int wb_protected, rb_ractor_t *cr)
}
static inline VALUE
-newobj_fill(VALUE obj, VALUE v1, VALUE v2, VALUE v3)
-{
- RVALUE *p = (RVALUE *)obj;
- p->as.values.v1 = v1;
- p->as.values.v2 = v2;
- p->as.values.v3 = v3;
- return obj;
-}
-
-static inline VALUE
newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, int wb_protected)
{
VALUE obj = newobj_of0(klass, flags, wb_protected, GET_RACTOR());
@@ -3858,16 +3861,6 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
ATOMIC_SET(finalizing, 0);
}
-PUREFUNC(static inline int is_id_value(rb_objspace_t *objspace, VALUE ptr));
-static inline int
-is_id_value(rb_objspace_t *objspace, VALUE ptr)
-{
- if (!is_pointer_to_heap(objspace, (void *)ptr)) return FALSE;
- if (BUILTIN_TYPE(ptr) > T_FIXNUM) return FALSE;
- if (BUILTIN_TYPE(ptr) == T_ICLASS) return FALSE;
- return TRUE;
-}
-
static inline int
heap_is_swept_object(rb_objspace_t *objspace, rb_heap_t *heap, VALUE ptr)
{
@@ -4484,11 +4477,6 @@ static VALUE gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free);
static void
lock_page_body(rb_objspace_t *objspace, struct heap_page_body *body)
{
- /* If this is an explicit compaction (GC.compact), we don't need a read
- * barrier, so just return early. */
- if (objspace->flags.during_compacting >> 1) {
- return;
- }
#if defined(_WIN32)
DWORD old_protect;
@@ -4505,11 +4493,6 @@ lock_page_body(rb_objspace_t *objspace, struct heap_page_body *body)
static void
unlock_page_body(rb_objspace_t *objspace, struct heap_page_body *body)
{
- /* If this is an explicit compaction (GC.compact), we don't need a read
- * barrier, so just return early. */
- if (objspace->flags.during_compacting >> 1) {
- return;
- }
#if defined(_WIN32)
DWORD old_protect;
@@ -4641,7 +4624,7 @@ static void read_barrier_handler(intptr_t address)
}
#if defined(_WIN32)
-LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
+static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
typedef void (*signal_handler)(int);
static signal_handler old_sigsegv_handler;
@@ -4660,13 +4643,15 @@ static LONG WINAPI read_barrier_signal(EXCEPTION_POINTERS * info)
}
}
-static void uninstall_handlers(void)
+static void
+uninstall_handlers(void)
{
signal(SIGSEGV, old_sigsegv_handler);
SetUnhandledExceptionFilter(old_handler);
}
-static void install_handlers(void)
+static void
+install_handlers(void)
{
/* Remove SEGV handler so that the Unhandled Exception Filter handles it */
old_sigsegv_handler = signal(SIGSEGV, NULL);
@@ -4702,13 +4687,15 @@ read_barrier_signal(int sig, siginfo_t * info, void * data)
sigprocmask(SIG_SETMASK, &prev_set, NULL);
}
-static void uninstall_handlers(void)
+static void
+uninstall_handlers(void)
{
sigaction(SIGBUS, &old_sigbus_handler, NULL);
sigaction(SIGSEGV, &old_sigsegv_handler, NULL);
}
-static void install_handlers(void)
+static void
+install_handlers(void)
{
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
@@ -4747,12 +4734,8 @@ gc_compact_finish(rb_objspace_t *objspace, rb_heap_t *heap)
{
GC_ASSERT(heap->sweeping_page == heap->compact_cursor);
- /* If this is an explicit compaction (GC.compact), no read barrier was set
- * so we don't need to unprotect pages or uninstall the SEGV handler */
- if (!(objspace->flags.during_compacting >> 1)) {
- gc_unprotect_pages(objspace, heap);
- uninstall_handlers();
- }
+ gc_unprotect_pages(objspace, heap);
+ uninstall_handlers();
/* The mutator is allowed to run during incremental sweeping. T_MOVED
* objects can get pushed on the stack and when the compaction process
@@ -5054,20 +5037,7 @@ gc_sweep_start_heap(rb_objspace_t *objspace, rb_heap_t *heap)
rb_ractor_t *r = NULL;
list_for_each(&GET_VM()->ractor.set, r, vmlr_node) {
- struct heap_page *page = r->newobj_cache.using_page;
- RVALUE *freelist = r->newobj_cache.freelist;
- RUBY_DEBUG_LOG("ractor using_page:%p freelist:%p", page, freelist);
-
- if (page && freelist) {
- RVALUE **p = &page->freelist;
- while (*p) {
- p = &(*p)->as.free.next;
- }
- *p = freelist;
- }
-
- r->newobj_cache.using_page = NULL;
- r->newobj_cache.freelist = NULL;
+ rb_gc_ractor_newobj_cache_clear(&r->newobj_cache);
}
}
@@ -5140,21 +5110,18 @@ gc_sweep_step(rb_objspace_t *objspace, rb_heap_t *heap)
else if (free_slots > 0) {
#if GC_ENABLE_INCREMENTAL_MARK
if (need_pool) {
- if (heap_add_poolpage(objspace, heap, sweep_page)) {
- need_pool = FALSE;
- }
+ heap_add_poolpage(objspace, heap, sweep_page);
+ need_pool = FALSE;
}
else {
- if (heap_add_freepage(heap, sweep_page)) {
- swept_slots += free_slots;
- if (swept_slots > 2048) {
- break;
- }
+ heap_add_freepage(heap, sweep_page);
+ swept_slots += free_slots;
+ if (swept_slots > 2048) {
+ break;
}
}
#else
- heap_add_freepage(heap, sweep_page);
- break;
+ heap_add_freepage(heap, sweep_page);
#endif
}
else {
@@ -5272,12 +5239,6 @@ gc_compact_start(rb_objspace_t *objspace, rb_heap_t *heap)
memset(objspace->rcompactor.considered_count_table, 0, T_MASK * sizeof(size_t));
memset(objspace->rcompactor.moved_count_table, 0, T_MASK * sizeof(size_t));
- /* If this is an explicit compaction (GC.compact), we don't need a read
- * barrier, so just return early. */
- if (objspace->flags.during_compacting >> 1) {
- return;
- }
-
/* Set up read barrier for pages containing MOVED objects */
install_handlers();
}
@@ -6181,12 +6142,16 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
case imemo_env:
{
const rb_env_t *env = (const rb_env_t *)obj;
- GC_ASSERT(env->ep[VM_ENV_DATA_INDEX_ENV] == obj);
- GC_ASSERT(VM_ENV_ESCAPED_P(env->ep));
- gc_mark_values(objspace, (long)env->env_size, env->env);
- VM_ENV_FLAGS_SET(env->ep, VM_ENV_FLAG_WB_REQUIRED);
- gc_mark(objspace, (VALUE)rb_vm_env_prev_env(env));
- gc_mark(objspace, (VALUE)env->iseq);
+
+ if (LIKELY(env->ep)) {
+ // just after newobj() can be NULL here.
+ GC_ASSERT(env->ep[VM_ENV_DATA_INDEX_ENV] == obj);
+ GC_ASSERT(VM_ENV_ESCAPED_P(env->ep));
+ gc_mark_values(objspace, (long)env->env_size, env->env);
+ VM_ENV_FLAGS_SET(env->ep, VM_ENV_FLAG_WB_REQUIRED);
+ gc_mark(objspace, (VALUE)rb_vm_env_prev_env(env));
+ gc_mark(objspace, (VALUE)env->iseq);
+ }
}
return;
case imemo_cref:
@@ -7969,6 +7934,37 @@ rb_obj_gc_flags(VALUE obj, ID* flags, size_t max)
/* GC */
void
+rb_gc_ractor_newobj_cache_clear(rb_ractor_newobj_cache_t *newobj_cache)
+{
+ struct heap_page *page = newobj_cache->using_page;
+ RVALUE *freelist = newobj_cache->freelist;
+ RUBY_DEBUG_LOG("ractor using_page:%p freelist:%p", page, freelist);
+
+ if (page && freelist) {
+ asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
+ if (page->freelist) {
+ RVALUE *p = page->freelist;
+ asan_unpoison_object((VALUE)p, false);
+ while (p->as.free.next) {
+ RVALUE *prev = p;
+ p = p->as.free.next;
+ asan_poison_object((VALUE)prev);
+ asan_unpoison_object((VALUE)p, false);
+ }
+ p->as.free.next = freelist;
+ asan_poison_object((VALUE)p);
+ }
+ else {
+ page->freelist = freelist;
+ }
+ asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
+ }
+
+ newobj_cache->using_page = NULL;
+ newobj_cache->freelist = NULL;
+}
+
+void
rb_gc_force_recycle(VALUE obj)
{
rb_objspace_t *objspace = &rb_objspace;
@@ -7997,7 +7993,7 @@ rb_gc_force_recycle(VALUE obj)
}
else {
#endif
- if (is_old || !GET_HEAP_PAGE(obj)->flags.before_sweep) {
+ if (is_old || GET_HEAP_PAGE(obj)->flags.before_sweep) {
CLEAR_IN_BITMAP(GET_HEAP_MARK_BITS(obj), obj);
}
CLEAR_IN_BITMAP(GET_HEAP_MARKING_BITS(obj), obj);
@@ -8218,7 +8214,7 @@ gc_start(rb_objspace_t *objspace, int reason)
objspace->flags.immediate_sweep = !!((unsigned)reason & GPR_FLAG_IMMEDIATE_SWEEP);
/* Explicitly enable compaction (GC.compact) */
- objspace->flags.during_compacting = (!!((unsigned)reason & GPR_FLAG_COMPACT) << 1);
+ objspace->flags.during_compacting = !!(reason & GPR_FLAG_COMPACT);
if (!heap_allocated_pages) return FALSE; /* heap is not ready */
if (!(reason & GPR_FLAG_METHOD) && !ready_to_gc(objspace)) return TRUE; /* GC is not allowed */
@@ -8457,6 +8453,9 @@ gc_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_
RB_VM_LOCK_ENTER_LEV(lock_lev);
switch (event) {
+ case gc_enter_event_rest:
+ if (!is_marking(objspace)) break;
+ // fall through
case gc_enter_event_start:
case gc_enter_event_mark_continue:
// stop other ractors
@@ -9451,11 +9450,7 @@ heap_check_moved_i(void *vstart, void *vend, size_t stride, void *data)
static VALUE
gc_compact(rb_execution_context_t *ec, VALUE self)
{
- /* Clear the heap. */
- gc_start_internal(ec, self, Qtrue, Qtrue, Qtrue, Qfalse);
-
- /* At this point, all references are live and the mutator is not allowed
- * to run, so we don't need a read barrier. */
+ /* Run GC with compaction enabled */
gc_start_internal(ec, self, Qtrue, Qtrue, Qtrue, Qtrue);
return gc_compact_stats(ec, self);
@@ -11181,9 +11176,20 @@ wmap_allocate(VALUE klass)
static int
wmap_live_p(rb_objspace_t *objspace, VALUE obj)
{
- if (!FL_ABLE(obj)) return TRUE;
- if (!is_id_value(objspace, obj)) return FALSE;
- if (!is_live_object(objspace, obj)) return FALSE;
+ if (SPECIAL_CONST_P(obj)) return TRUE;
+ if (is_pointer_to_heap(objspace, (void *)obj)) {
+ void *poisoned = asan_unpoison_object_temporary(obj);
+
+ enum ruby_value_type t = BUILTIN_TYPE(obj);
+ int ret = (!(t == T_NONE || t >= T_FIXNUM || t == T_ICLASS) &&
+ is_live_object(objspace, obj));
+
+ if (poisoned) {
+ asan_poison_object(obj);
+ }
+
+ return ret;
+ }
return TRUE;
}
@@ -11249,10 +11255,26 @@ struct wmap_iter_arg {
VALUE value;
};
+static VALUE
+wmap_inspect_append(rb_objspace_t *objspace, VALUE str, VALUE obj)
+{
+ if (SPECIAL_CONST_P(obj)) {
+ return rb_str_append(str, rb_inspect(obj));
+ }
+ else if (wmap_live_p(objspace, obj)) {
+ return rb_str_append(str, rb_any_to_s(obj));
+ }
+ else {
+ return rb_str_catf(str, "#<collected:%p>", (void*)obj);
+ }
+}
+
static int
wmap_inspect_i(st_data_t key, st_data_t val, st_data_t arg)
{
- VALUE str = (VALUE)arg;
+ struct wmap_iter_arg *argp = (struct wmap_iter_arg *)arg;
+ rb_objspace_t *objspace = argp->objspace;
+ VALUE str = argp->value;
VALUE k = (VALUE)key, v = (VALUE)val;
if (RSTRING_PTR(str)[0] == '#') {
@@ -11262,11 +11284,9 @@ wmap_inspect_i(st_data_t key, st_data_t val, st_data_t arg)
rb_str_cat2(str, ": ");
RSTRING_PTR(str)[0] = '#';
}
- k = SPECIAL_CONST_P(k) ? rb_inspect(k) : rb_any_to_s(k);
- rb_str_append(str, k);
+ wmap_inspect_append(objspace, str, k);
rb_str_cat2(str, " => ");
- v = SPECIAL_CONST_P(v) ? rb_inspect(v) : rb_any_to_s(v);
- rb_str_append(str, v);
+ wmap_inspect_append(objspace, str, v);
return ST_CONTINUE;
}
@@ -11277,11 +11297,14 @@ wmap_inspect(VALUE self)
VALUE str;
VALUE c = rb_class_name(CLASS_OF(self));
struct weakmap *w;
+ struct wmap_iter_arg args;
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
str = rb_sprintf("-<%"PRIsVALUE":%p", c, (void *)self);
if (w->wmap2obj) {
- st_foreach(w->wmap2obj, wmap_inspect_i, str);
+ args.objspace = &rb_objspace;
+ args.value = str;
+ st_foreach(w->wmap2obj, wmap_inspect_i, (st_data_t)&args);
}
RSTRING_PTR(str)[0] = '#';
rb_str_cat2(str, ">");
diff --git a/gems/bundled_gems b/gems/bundled_gems
index f8fe130552..445d876df8 100644
--- a/gems/bundled_gems
+++ b/gems/bundled_gems
@@ -1,9 +1,9 @@
# gem-name version-to-bundle repository-url [optional-commit-hash-to-test-or-defaults-to-v-version]
minitest 5.14.2 https://github.com/seattlerb/minitest
-power_assert 1.2.0 https://github.com/ruby/power_assert
+power_assert 1.2.1 https://github.com/ruby/power_assert
rake 13.0.3 https://github.com/ruby/rake
test-unit 3.3.7 https://github.com/test-unit/test-unit 3.3.7
-rexml 3.2.4 https://github.com/ruby/rexml
+rexml 3.2.5 https://github.com/ruby/rexml
rss 0.2.9 https://github.com/ruby/rss 0.2.9
-rbs 1.0.4 https://github.com/ruby/rbs
-typeprof 0.12.0 https://github.com/ruby/typeprof
+typeprof 0.15.2 https://github.com/ruby/typeprof
+rbs 1.4.0 https://github.com/ruby/rbs
diff --git a/hash.c b/hash.c
index 0ed2692f24..17d4186acf 100644
--- a/hash.c
+++ b/hash.c
@@ -2476,6 +2476,7 @@ static int
delete_if_i(VALUE key, VALUE value, VALUE hash)
{
if (RTEST(rb_yield_values(2, key, value))) {
+ rb_hash_modify(hash);
return ST_DELETE;
}
return ST_CONTINUE;
@@ -2753,6 +2754,7 @@ static int
keep_if_i(VALUE key, VALUE value, VALUE hash)
{
if (!RTEST(rb_yield_values(2, key, value))) {
+ rb_hash_modify(hash);
return ST_DELETE;
}
return ST_CONTINUE;
@@ -3249,7 +3251,7 @@ rb_hash_transform_keys(int argc, VALUE *argv, VALUE hash)
return result;
}
-static VALUE rb_hash_flatten(int argc, VALUE *argv, VALUE hash);
+static int flatten_i(VALUE key, VALUE val, VALUE ary);
/*
* call-seq:
@@ -3278,8 +3280,9 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
long i;
- VALUE pairs = rb_hash_flatten(0, NULL, hash);
- rb_hash_clear(hash);
+ VALUE new_keys = hash_alloc(0);
+ VALUE pairs = rb_ary_tmp_new(RHASH_SIZE(hash) * 2);
+ rb_hash_foreach(hash, flatten_i, pairs);
for (i = 0; i < RARRAY_LEN(pairs); i += 2) {
VALUE key = RARRAY_AREF(pairs, i), new_key, val;
@@ -3296,8 +3299,14 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
new_key = key;
}
val = RARRAY_AREF(pairs, i+1);
+ if (!hash_stlike_lookup(new_keys, key, NULL)) {
+ rb_hash_stlike_delete(hash, &key, NULL);
+ }
rb_hash_aset(hash, new_key, val);
+ rb_hash_aset(new_keys, new_key, Qnil);
}
+ rb_ary_clear(pairs);
+ rb_hash_clear(new_keys);
}
return hash;
}
@@ -3313,6 +3322,7 @@ transform_values_foreach_replace(st_data_t *key, st_data_t *value, st_data_t arg
{
VALUE new_value = rb_yield((VALUE)*value);
VALUE hash = (VALUE)argp;
+ rb_hash_modify(hash);
RB_OBJ_WRITE(hash, value, new_value);
return ST_CONTINUE;
}
diff --git a/include/ruby/internal/intern/select/posix.h b/include/ruby/internal/intern/select/posix.h
index 6c1092b39d..0bf68ae204 100644
--- a/include/ruby/internal/intern/select/posix.h
+++ b/include/ruby/internal/intern/select/posix.h
@@ -55,7 +55,7 @@ rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int n)
}
static inline void
-rb_fd_dup(rb_fdset_t *dst, const fd_set *src, int n)
+rb_fd_dup(rb_fdset_t *dst, const fd_set *src)
{
*dst = *src;
}
diff --git a/include/ruby/internal/memory.h b/include/ruby/internal/memory.h
index 7d24df4945..46dfcadd10 100644
--- a/include/ruby/internal/memory.h
+++ b/include/ruby/internal/memory.h
@@ -110,18 +110,18 @@ extern void *alloca();
((var) = RBIMPL_CAST((type *)ruby_xrealloc2((void *)(var), (n), sizeof(type))))
#define ALLOCA_N(type,n) \
- RBIMPL_CAST((type *)alloca(rbimpl_size_mul_or_raise(sizeof(type), (n))))
+ RBIMPL_CAST((type *)(!(n) ? NULL : alloca(rbimpl_size_mul_or_raise(sizeof(type), (n)))))
/* allocates _n_ bytes temporary buffer and stores VALUE including it
* in _v_. _n_ may be evaluated twice. */
#define RB_ALLOCV(v, n) \
((n) < RUBY_ALLOCV_LIMIT ? \
- ((v) = 0, alloca(n)) : \
+ ((v) = 0, !(n) ? NULL : alloca(n)) : \
rb_alloc_tmp_buffer(&(v), (n)))
#define RB_ALLOCV_N(type, v, n) \
RBIMPL_CAST((type *) \
(((size_t)(n) < RUBY_ALLOCV_LIMIT / sizeof(type)) ? \
- ((v) = 0, alloca((n) * sizeof(type))) : \
+ ((v) = 0, !(n) ? NULL : alloca((n) * sizeof(type))) : \
rb_alloc_tmp_buffer2(&(v), (n), sizeof(type))))
#define RB_ALLOCV_END(v) rb_free_tmp_buffer(&(v))
@@ -256,6 +256,7 @@ rb_alloc_tmp_buffer2(volatile VALUE *store, long count, size_t elsize)
}
#ifndef __MINGW32__
+RBIMPL_SYMBOL_EXPORT_BEGIN()
RBIMPL_ATTR_NOALIAS()
RBIMPL_ATTR_NONNULL((1))
RBIMPL_ATTR_RETURNS_NONNULL()
@@ -272,6 +273,7 @@ ruby_nonempty_memcpy(void *dest, const void *src, size_t n)
return dest;
}
}
+RBIMPL_SYMBOL_EXPORT_END()
#undef memcpy
#define memcpy ruby_nonempty_memcpy
#endif
diff --git a/internal/bits.h b/internal/bits.h
index a7ddcaeb78..746947bfc2 100644
--- a/internal/bits.h
+++ b/internal/bits.h
@@ -284,7 +284,7 @@ nlz_int64(uint64_t x)
}
else {
/* :FIXME: Is there a way to make this branch a compile-time error? */
- __builtin_unreachable();
+ UNREACHABLE_RETURN(~0);
}
#else
@@ -419,7 +419,7 @@ rb_popcount64(uint64_t x)
}
else {
/* :FIXME: Is there a way to make this branch a compile-time error? */
- __builtin_unreachable();
+ UNREACHABLE_RETURN(~0);
}
#else
@@ -492,7 +492,7 @@ ntz_int64(uint64_t x)
}
else {
/* :FIXME: Is there a way to make this branch a compile-time error? */
- __builtin_unreachable();
+ UNREACHABLE_RETURN(~0);
}
#else
diff --git a/internal/complex.h b/internal/complex.h
index 6f63a9ebeb..9eae804ddb 100644
--- a/internal/complex.h
+++ b/internal/complex.h
@@ -25,5 +25,6 @@ struct RComplex {
/* complex.c */
VALUE rb_dbl_complex_new_polar_pi(double abs, double ang);
+st_index_t rb_complex_hash(VALUE comp);
#endif /* INTERNAL_COMPLEX_H */
diff --git a/internal/cont.h b/internal/cont.h
index a365cbe978..9e49dd3c8e 100644
--- a/internal/cont.h
+++ b/internal/cont.h
@@ -21,5 +21,6 @@ void ruby_register_rollback_func_for_ensure(VALUE (*ensure_func)(VALUE), VALUE (
void rb_fiber_init_mjit_cont(struct rb_fiber_struct *fiber);
VALUE rb_fiberptr_self(struct rb_fiber_struct *fiber);
+unsigned int rb_fiberptr_blocking(struct rb_fiber_struct *fiber);
#endif /* INTERNAL_CONT_H */
diff --git a/internal/encoding.h b/internal/encoding.h
index af236daeaf..c0cf061bd4 100644
--- a/internal/encoding.h
+++ b/internal/encoding.h
@@ -12,12 +12,15 @@
#include "ruby/ruby.h" /* for ID */
#include "ruby/encoding.h" /* for rb_encoding */
+#define rb_enc_autoload_p(enc) (!rb_enc_mbmaxlen(enc))
+
/* encoding.c */
ID rb_id_encoding(void);
rb_encoding *rb_enc_get_from_index(int index);
rb_encoding *rb_enc_check_str(VALUE str1, VALUE str2);
int rb_encdb_replicate(const char *alias, const char *orig);
int rb_encdb_alias(const char *alias, const char *orig);
+int rb_enc_autoload(rb_encoding *enc);
int rb_encdb_dummy(const char *name);
void rb_encdb_declare(const char *name);
void rb_enc_set_base(const char *name, const char *orig);
diff --git a/internal/gc.h b/internal/gc.h
index a602f0c9b3..d439f6d8f6 100644
--- a/internal/gc.h
+++ b/internal/gc.h
@@ -61,6 +61,11 @@ struct rb_objspace; /* in vm_core.h */
rb_obj_write((VALUE)(a), UNALIGNED_MEMBER_ACCESS((VALUE *)(slot)), \
(VALUE)(b), __FILE__, __LINE__)
+typedef struct ractor_newobj_cache {
+ struct RVALUE *freelist;
+ struct heap_page *using_page;
+} rb_ractor_newobj_cache_t;
+
/* gc.c */
extern VALUE *ruby_initial_gc_stress_ptr;
extern int ruby_disable_gc;
@@ -100,6 +105,7 @@ void rb_gc_mark_vm_stack_values(long n, const VALUE *values);
void *ruby_sized_xrealloc(void *ptr, size_t new_size, size_t old_size) RUBY_ATTR_RETURNS_NONNULL RUBY_ATTR_ALLOC_SIZE((2));
void *ruby_sized_xrealloc2(void *ptr, size_t new_count, size_t element_size, size_t old_count) RUBY_ATTR_RETURNS_NONNULL RUBY_ATTR_ALLOC_SIZE((2, 3));
void ruby_sized_xfree(void *x, size_t size);
+void rb_gc_ractor_newobj_cache_clear(rb_ractor_newobj_cache_t *newobj_cache);
RUBY_SYMBOL_EXPORT_END
MJIT_SYMBOL_EXPORT_BEGIN
diff --git a/internal/rational.h b/internal/rational.h
index e53ee7b499..6bbd2a9810 100644
--- a/internal/rational.h
+++ b/internal/rational.h
@@ -33,6 +33,7 @@ VALUE rb_rational_div(VALUE self, VALUE other);
VALUE rb_lcm(VALUE x, VALUE y);
VALUE rb_rational_reciprocal(VALUE x);
VALUE rb_cstr_to_rat(const char *, int);
+VALUE rb_rational_hash(VALUE self);
VALUE rb_rational_abs(VALUE self);
VALUE rb_rational_cmp(VALUE self, VALUE other);
VALUE rb_rational_pow(VALUE self, VALUE other);
diff --git a/internal/string.h b/internal/string.h
index 8907a1a6e6..adc8385cb6 100644
--- a/internal/string.h
+++ b/internal/string.h
@@ -43,6 +43,7 @@ size_t rb_str_memsize(VALUE);
char *rb_str_to_cstr(VALUE str);
const char *ruby_escaped_char(int c);
void rb_str_make_independent(VALUE str);
+int rb_enc_str_coderange_scan(VALUE str, rb_encoding *enc);
static inline bool STR_EMBED_P(VALUE str);
static inline bool STR_SHARED_P(VALUE str);
diff --git a/internal/vm.h b/internal/vm.h
index 874eb2d881..689b4fa61f 100644
--- a/internal/vm.h
+++ b/internal/vm.h
@@ -80,12 +80,7 @@ VALUE rb_yield_force_blockarg(VALUE values);
VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv,
rb_block_call_func_t bl_proc, int min_argc, int max_argc,
VALUE data2);
-
-MJIT_SYMBOL_EXPORT_BEGIN
-VALUE rb_vm_call0(struct rb_execution_context_struct *ec, VALUE recv, ID id, int argc, const VALUE *argv, const struct rb_callable_method_entry_struct *me, int kw_splat);
-VALUE rb_vm_call_kw(struct rb_execution_context_struct *ec, VALUE recv, VALUE id, int argc, const VALUE *argv, const struct rb_callable_method_entry_struct *me, int kw_splat);
-VALUE rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj, int argc, const VALUE *argv, int priv);
-MJIT_SYMBOL_EXPORT_END
+void rb_check_stack_overflow(void);
/* vm_insnhelper.c */
VALUE rb_equal_opt(VALUE obj1, VALUE obj2);
diff --git a/io.c b/io.c
index 48592ac51a..caa4dd28f7 100644
--- a/io.c
+++ b/io.c
@@ -1306,71 +1306,77 @@ rb_io_from_fd(int fd)
int
rb_io_wait_readable(int f)
{
- VALUE scheduler = rb_scheduler_current();
- if (scheduler != Qnil) {
- return RTEST(
- rb_scheduler_io_wait_readable(scheduler, rb_io_from_fd(f))
- );
- }
+ VALUE scheduler;
io_fd_check_closed(f);
+
+ scheduler = rb_scheduler_current();
switch (errno) {
case EINTR:
#if defined(ERESTART)
case ERESTART:
#endif
- rb_thread_check_ints();
- return TRUE;
+ rb_thread_check_ints();
+ return TRUE;
case EAGAIN:
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
- rb_thread_wait_fd(f);
- return TRUE;
+ if (scheduler != Qnil) {
+ return RTEST(
+ rb_scheduler_io_wait_readable(scheduler, rb_io_from_fd(f))
+ );
+ } else {
+ rb_thread_wait_fd(f);
+ }
+ return TRUE;
default:
- return FALSE;
+ return FALSE;
}
}
int
rb_io_wait_writable(int f)
{
- VALUE scheduler = rb_scheduler_current();
- if (scheduler != Qnil) {
- return RTEST(
- rb_scheduler_io_wait_writable(scheduler, rb_io_from_fd(f))
- );
- }
+ VALUE scheduler;
io_fd_check_closed(f);
+
+ scheduler = rb_scheduler_current();
switch (errno) {
case EINTR:
#if defined(ERESTART)
case ERESTART:
#endif
- /*
- * In old Linux, several special files under /proc and /sys don't handle
- * select properly. Thus we need avoid to call if don't use O_NONBLOCK.
- * Otherwise, we face nasty hang up. Sigh.
- * e.g. http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=31b07093c44a7a442394d44423e21d783f5523b8
- * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=31b07093c44a7a442394d44423e21d783f5523b8
- * In EINTR case, we only need to call RUBY_VM_CHECK_INTS_BLOCKING().
- * Then rb_thread_check_ints() is enough.
- */
- rb_thread_check_ints();
- return TRUE;
+ /*
+ * In old Linux, several special files under /proc and /sys don't handle
+ * select properly. Thus we need avoid to call if don't use O_NONBLOCK.
+ * Otherwise, we face nasty hang up. Sigh.
+ * e.g. http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=31b07093c44a7a442394d44423e21d783f5523b8
+ * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=31b07093c44a7a442394d44423e21d783f5523b8
+ * In EINTR case, we only need to call RUBY_VM_CHECK_INTS_BLOCKING().
+ * Then rb_thread_check_ints() is enough.
+ */
+ rb_thread_check_ints();
+ return TRUE;
case EAGAIN:
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
- rb_thread_fd_writable(f);
- return TRUE;
+ if (scheduler != Qnil) {
+ return RTEST(
+ rb_scheduler_io_wait_writable(scheduler, rb_io_from_fd(f))
+ );
+ } else {
+ rb_thread_fd_writable(f);
+ }
+ return TRUE;
default:
- return FALSE;
+ return FALSE;
}
}
@@ -2976,8 +2982,10 @@ io_getpartial(int argc, VALUE *argv, VALUE io, int no_exception, int nonblock)
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
- if (len == 0)
+ if (len == 0) {
+ io_set_read_length(str, 0, shrinkable);
return str;
+ }
if (!nonblock)
READ_CHECK(fptr);
@@ -3120,8 +3128,10 @@ io_read_nonblock(rb_execution_context_t *ec, VALUE io, VALUE length, VALUE str,
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
- if (len == 0)
+ if (len == 0) {
+ io_set_read_length(str, 0, shrinkable);
return str;
+ }
n = read_buffered_data(RSTRING_PTR(str), len, fptr);
if (n <= 0) {
@@ -3988,9 +3998,9 @@ rb_io_each_byte(VALUE io)
char *p = fptr->rbuf.ptr + fptr->rbuf.off++;
fptr->rbuf.len--;
rb_yield(INT2FIX(*p & 0xff));
+ rb_io_check_byte_readable(fptr);
errno = 0;
}
- rb_io_check_byte_readable(fptr);
READ_CHECK(fptr);
} while (io_fillbuf(fptr) >= 0);
return io;
@@ -4209,6 +4219,7 @@ rb_io_each_codepoint(VALUE io)
fptr->cbuf.off += n;
fptr->cbuf.len -= n;
rb_yield(UINT2NUM(c));
+ rb_io_check_byte_readable(fptr);
}
}
NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
@@ -4246,6 +4257,7 @@ rb_io_each_codepoint(VALUE io)
else {
continue;
}
+ rb_io_check_byte_readable(fptr);
}
return io;
@@ -11238,6 +11250,13 @@ nogvl_fcopyfile(struct copy_stream_struct *stp)
return 0;
if (lseek(stp->dst_fd, 0, SEEK_CUR) > (off_t)0) /* if dst IO was already written */
return 0;
+ if (fcntl(stp->dst_fd, F_GETFL) & O_APPEND) {
+ /* fcopyfile(3) appends src IO to dst IO and then truncates
+ * dst IO to src IO's original size. */
+ off_t end = lseek(stp->dst_fd, 0, SEEK_END);
+ lseek(stp->dst_fd, 0, SEEK_SET);
+ if (end > (off_t)0) return 0;
+ }
if (src_offset > (off_t)0) {
off_t r;
diff --git a/iseq.c b/iseq.c
index 096d456f11..2d83bc3f06 100644
--- a/iseq.c
+++ b/iseq.c
@@ -359,8 +359,12 @@ rb_iseq_mark(const rb_iseq_t *iseq)
rb_gc_mark_movable((VALUE)ci);
}
if (cc && vm_cc_markable(cc)) {
- rb_gc_mark_movable((VALUE)cc);
- // TODO: check enable
+ if (!vm_cc_invalidated_p(cc)) {
+ rb_gc_mark_movable((VALUE)cc);
+ }
+ else {
+ cds[i].cc = rb_vm_empty_cc();
+ }
}
}
}
diff --git a/lib/bundler.rb b/lib/bundler.rb
index d370d8a53a..1ab87ac299 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -37,12 +37,13 @@ module Bundler
environment_preserver = EnvironmentPreserver.from_env
ORIGINAL_ENV = environment_preserver.restore
environment_preserver.replace_with_backup
- SUDO_MUTEX = Mutex.new
+ SUDO_MUTEX = Thread::Mutex.new
autoload :Definition, File.expand_path("bundler/definition", __dir__)
autoload :Dependency, File.expand_path("bundler/dependency", __dir__)
autoload :DepProxy, File.expand_path("bundler/dep_proxy", __dir__)
autoload :Deprecate, File.expand_path("bundler/deprecate", __dir__)
+ autoload :Digest, File.expand_path("bundler/digest", __dir__)
autoload :Dsl, File.expand_path("bundler/dsl", __dir__)
autoload :EndpointSpecification, File.expand_path("bundler/endpoint_specification", __dir__)
autoload :Env, File.expand_path("bundler/env", __dir__)
@@ -69,6 +70,7 @@ module Bundler
autoload :SharedHelpers, File.expand_path("bundler/shared_helpers", __dir__)
autoload :Source, File.expand_path("bundler/source", __dir__)
autoload :SourceList, File.expand_path("bundler/source_list", __dir__)
+ autoload :SourceMap, File.expand_path("bundler/source_map", __dir__)
autoload :SpecSet, File.expand_path("bundler/spec_set", __dir__)
autoload :StubSpecification, File.expand_path("bundler/stub_specification", __dir__)
autoload :UI, File.expand_path("bundler/ui", __dir__)
@@ -197,7 +199,7 @@ module Bundler
def frozen_bundle?
frozen = settings[:deployment]
- frozen ||= settings[:frozen] unless feature_flag.deployment_means_frozen?
+ frozen ||= settings[:frozen]
frozen
end
@@ -235,8 +237,9 @@ module Bundler
end
if warning
- user_home = tmp_home_path(warning)
- Bundler.ui.warn "#{warning}\nBundler will use `#{user_home}' as your home directory temporarily.\n"
+ Bundler.ui.warn "#{warning}\n"
+ user_home = tmp_home_path
+ Bundler.ui.warn "Bundler will use `#{user_home}' as your home directory temporarily.\n"
user_home
else
Pathname.new(home)
@@ -634,6 +637,12 @@ EOF
@rubygems = nil
end
+ def configure_gem_home_and_path(path = bundle_path)
+ configure_gem_path
+ configure_gem_home(path)
+ Bundler.rubygems.clear_paths
+ end
+
private
def eval_yaml_gemspec(path, contents)
@@ -651,47 +660,29 @@ EOF
rescue ScriptError, StandardError => e
msg = "There was an error while loading `#{path.basename}`: #{e.message}"
- if e.is_a?(LoadError)
- msg += "\nDoes it try to require a relative path? That's been removed in Ruby 1.9"
- end
-
raise GemspecError, Dsl::DSLError.new(msg, path, e.backtrace, contents)
end
- def configure_gem_home_and_path
- configure_gem_path
- configure_gem_home
- bundle_path
- end
-
- def configure_gem_path(env = ENV)
- blank_home = env["GEM_HOME"].nil? || env["GEM_HOME"].empty?
- if !use_system_gems?
+ def configure_gem_path
+ unless use_system_gems?
# this needs to be empty string to cause
# PathSupport.split_gem_path to only load up the
# Bundler --path setting as the GEM_PATH.
- env["GEM_PATH"] = ""
- elsif blank_home
- possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path]
- paths = possibles.flatten.compact.uniq.reject(&:empty?)
- env["GEM_PATH"] = paths.join(File::PATH_SEPARATOR)
+ Bundler::SharedHelpers.set_env "GEM_PATH", ""
end
end
- def configure_gem_home
- Bundler::SharedHelpers.set_env "GEM_HOME", File.expand_path(bundle_path, root)
- Bundler.rubygems.clear_paths
+ def configure_gem_home(path)
+ Bundler::SharedHelpers.set_env "GEM_HOME", path.to_s
end
- def tmp_home_path(warning)
+ def tmp_home_path
Kernel.send(:require, "tmpdir")
SharedHelpers.filesystem_access(Dir.tmpdir) do
path = Bundler.tmp
at_exit { Bundler.rm_rf(path) }
path
end
- rescue RuntimeError => e
- raise e.exception("#{warning}\nBundler also failed to create a temporary home directory':\n#{e}")
end
# @param env [Hash]
diff --git a/lib/bundler/bundler.gemspec b/lib/bundler/bundler.gemspec
index 48410e76f4..38c533b0c1 100644
--- a/lib/bundler/bundler.gemspec
+++ b/lib/bundler/bundler.gemspec
@@ -34,9 +34,10 @@ Gem::Specification.new do |s|
s.required_ruby_version = ">= 2.3.0"
s.required_rubygems_version = ">= 2.5.2"
- s.files = (Dir.glob("lib/bundler/**/*", File::FNM_DOTMATCH) + Dir.glob("man/bundle*") + Dir.glob("man/gemfile*") + Dir.glob("libexec/bundle*")).reject {|f| File.directory?(f) }
+ s.files = Dir.glob("lib/bundler{.rb,/**/*}", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
- s.files += ["lib/bundler.rb"]
+ # include the gemspec itself because warbler breaks w/o it
+ s.files += %w[lib/bundler/bundler.gemspec]
s.bindir = "libexec"
s.executables = %w[bundle bundler]
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index 421f42cb52..d271086b25 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -14,6 +14,7 @@ module Bundler
COMMAND_ALIASES = {
"check" => "c",
"install" => "i",
+ "plugin" => "",
"list" => "ls",
"exec" => ["e", "ex", "exe"],
"cache" => ["package", "pack"],
@@ -72,14 +73,6 @@ module Bundler
Bundler.ui = UI::Shell.new(options)
Bundler.ui.level = "debug" if options["verbose"]
unprinted_warnings.each {|w| Bundler.ui.warn(w) }
-
- if ENV["RUBYGEMS_GEMDEPS"] && !ENV["RUBYGEMS_GEMDEPS"].empty?
- Bundler.ui.warn(
- "The RUBYGEMS_GEMDEPS environment variable is set. This enables RubyGems' " \
- "experimental Gemfile mode, which may conflict with Bundler and cause unexpected errors. " \
- "To remove this warning, unset RUBYGEMS_GEMDEPS.", :wrap => true
- )
- end
end
check_unknown_options!(:except => [:config, :exec])
@@ -191,6 +184,7 @@ module Bundler
method_option "install", :type => :boolean, :banner =>
"Runs 'bundle install' after removing the gems from the Gemfile"
def remove(*gems)
+ SharedHelpers.major_deprecation(2, "The `--install` flag has been deprecated. `bundle install` is triggered by default.") if ARGV.include?("--install")
require_relative "cli/remove"
Remove.new(gems, options).run
end
@@ -308,39 +302,19 @@ module Bundler
end
end
- unless Bundler.feature_flag.bundler_3_mode?
- desc "show GEM [OPTIONS]", "Shows all gems that are part of the bundle, or the path to a given gem"
- long_desc <<-D
- Show lists the names and versions of all gems that are required by your Gemfile.
- Calling show with [GEM] will list the exact location of that gem on your machine.
- D
- method_option "paths", :type => :boolean,
- :banner => "List the paths of all gems that are required by your Gemfile."
- method_option "outdated", :type => :boolean,
- :banner => "Show verbose output including whether gems are outdated."
- def show(gem_name = nil)
- if ARGV[0] == "show"
- rest = ARGV[1..-1]
-
- if flag = rest.find{|arg| ["--verbose", "--outdated"].include?(arg) }
- Bundler::SharedHelpers.major_deprecation(2, "the `#{flag}` flag to `bundle show` was undocumented and will be removed without replacement")
- else
- new_command = rest.find {|arg| !arg.start_with?("--") } ? "info" : "list"
-
- new_arguments = rest.map do |arg|
- next arg if arg != "--paths"
- next "--path" if new_command == "info"
- end
-
- old_argv = ARGV.join(" ")
- new_argv = [new_command, *new_arguments.compact].join(" ")
-
- Bundler::SharedHelpers.major_deprecation(2, "use `bundle #{new_argv}` instead of `bundle #{old_argv}`")
- end
- end
- require_relative "cli/show"
- Show.new(options, gem_name).run
- end
+ desc "show GEM [OPTIONS]", "Shows all gems that are part of the bundle, or the path to a given gem"
+ long_desc <<-D
+ Show lists the names and versions of all gems that are required by your Gemfile.
+ Calling show with [GEM] will list the exact location of that gem on your machine.
+ D
+ method_option "paths", :type => :boolean,
+ :banner => "List the paths of all gems that are required by your Gemfile."
+ method_option "outdated", :type => :boolean,
+ :banner => "Show verbose output including whether gems are outdated."
+ def show(gem_name = nil)
+ SharedHelpers.major_deprecation(2, "the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement") if ARGV.include?("--outdated")
+ require_relative "cli/show"
+ Show.new(options, gem_name).run
end
desc "list", "List all gems in the bundle"
@@ -357,6 +331,7 @@ module Bundler
desc "info GEM [OPTIONS]", "Show information for the given gem"
method_option "path", :type => :boolean, :banner => "Print full path to gem"
+ method_option "version", :type => :boolean, :banner => "Print gem version"
def info(gem_name)
require_relative "cli/info"
Info.new(options, gem_name).run
@@ -475,6 +450,12 @@ module Bundler
"do in future versions. Instead please use `bundle config set cache_all true`, " \
"and stop using this flag" if ARGV.include?("--all")
+ SharedHelpers.major_deprecation 2,
+ "The `--path` flag is deprecated because its semantics are unclear. " \
+ "Use `bundle config cache_path` to configure the path of your cache of gems, " \
+ "and `bundle config path` to configure the path where your gems are installed, " \
+ "and stop using this flag" if ARGV.include?("--path")
+
require_relative "cli/cache"
Cache.new(options).run
end
@@ -482,7 +463,7 @@ module Bundler
map aliases_for("cache")
desc "exec [OPTIONS]", "Run the command in context of the bundle"
- method_option :keep_file_descriptors, :type => :boolean, :default => false
+ method_option :keep_file_descriptors, :type => :boolean, :default => true
method_option :gemfile, :type => :string, :required => false
long_desc <<-D
Exec runs a command, providing it access to the gems in the bundle. While using
@@ -490,6 +471,10 @@ module Bundler
into the system wide RubyGems repository.
D
def exec(*args)
+ if ARGV.include?("--no-keep-file-descriptors")
+ SharedHelpers.major_deprecation(2, "The `--no-keep-file-descriptors` has been deprecated. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to")
+ end
+
require_relative "cli/exec"
Exec.new(options, args).run
end
@@ -504,8 +489,8 @@ module Bundler
By default, setting a configuration value sets it for all projects
on the machine.
- If a global setting is superceded by local configuration, this command
- will show the current value, as well as any superceded values and
+ If a global setting is superseded by local configuration, this command
+ will show the current value, as well as any superseded values and
where they were specified.
D
require_relative "cli/config"
@@ -568,7 +553,7 @@ module Bundler
method_option :version, :type => :boolean, :default => false, :aliases => "-v", :desc => "Set to show each gem version."
method_option :without, :type => :array, :default => [], :aliases => "-W", :banner => "GROUP[ GROUP...]", :desc => "Exclude gems that are part of the specified named group."
def viz
- SharedHelpers.major_deprecation 2, "The `viz` command has been moved to the `bundle-viz` gem, see https://github.com/bundler/bundler-viz"
+ SharedHelpers.major_deprecation 2, "The `viz` command has been renamed to `graph` and moved to a plugin. See https://github.com/rubygems/bundler-graph"
require_relative "cli/viz"
Viz.new(options.dup).run
end
@@ -591,6 +576,9 @@ module Bundler
:desc => "Generate a test directory for your library, either rspec, minitest or test-unit. Set a default with `bundle config set --global gem.test (rspec|minitest|test-unit)`."
method_option :ci, :type => :string, :lazy_default => Bundler.settings["gem.ci"] || "",
:desc => "Generate CI configuration, either GitHub Actions, Travis CI, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|travis|gitlab|circle)`"
+ method_option :linter, :type => :string, :lazy_default => Bundler.settings["gem.linter"] || "",
+ :desc => "Add a linter and code formatter, either RuboCop or Standard. Set a default with `bundle config set --global gem.linter (rubocop|standard)`"
+ method_option :github_username, :type => :string, :default => Bundler.settings["gem.github_username"], :banner => "Set your username on GitHub", :desc => "Fill in GitHub username on README so that you don't have to do it manually. Set a default with `bundle config set --global gem.github_username <your_username>`."
def gem(name)
end
diff --git a/lib/bundler/cli/cache.rb b/lib/bundler/cli/cache.rb
index 9cd6133879..c8698ed7e3 100644
--- a/lib/bundler/cli/cache.rb
+++ b/lib/bundler/cli/cache.rb
@@ -9,7 +9,7 @@ module Bundler
end
def run
- Bundler.ui.level = "error" if options[:quiet]
+ Bundler.ui.level = "warn" if options[:quiet]
Bundler.settings.set_command_option_if_given :path, options[:path]
Bundler.settings.set_command_option_if_given :cache_path, options["cache-path"]
diff --git a/lib/bundler/cli/check.rb b/lib/bundler/cli/check.rb
index 19c0aaea06..65c51337d2 100644
--- a/lib/bundler/cli/check.rb
+++ b/lib/bundler/cli/check.rb
@@ -11,9 +11,11 @@ module Bundler
def run
Bundler.settings.set_command_option_if_given :path, options[:path]
+ definition = Bundler.definition
+ definition.validate_runtime!
+
begin
- definition = Bundler.definition
- definition.validate_runtime!
+ definition.resolve_only_locally!
not_installed = definition.missing_specs
rescue GemNotFound, VersionConflict
Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies."
diff --git a/lib/bundler/cli/common.rb b/lib/bundler/cli/common.rb
index 23ac78a103..ba259143b7 100644
--- a/lib/bundler/cli/common.rb
+++ b/lib/bundler/cli/common.rb
@@ -36,10 +36,15 @@ module Bundler
def self.without_groups_message(command)
command_in_past_tense = command == :install ? "installed" : "updated"
groups = Bundler.settings[:without]
+ "Gems in the #{verbalize_groups(groups)} were not #{command_in_past_tense}."
+ end
+
+ def self.verbalize_groups(groups)
+ groups.map!{|g| "'#{g}'" }
group_list = [groups[0...-1].join(", "), groups[-1..-1]].
reject {|s| s.to_s.empty? }.join(" and ")
group_str = groups.size == 1 ? "group" : "groups"
- "Gems in the #{group_str} #{group_list} were not #{command_in_past_tense}."
+ "#{group_str} #{group_list}"
end
def self.select_spec(name, regex_match = nil)
@@ -53,7 +58,13 @@ module Bundler
case specs.count
when 0
- raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
+ dep_in_other_group = Bundler.definition.current_dependencies.find {|dep|dep.name == name }
+
+ if dep_in_other_group
+ raise GemNotFound, "Could not find gem '#{name}', because it's in the #{verbalize_groups(dep_in_other_group.groups)}, configured to be ignored."
+ else
+ raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
+ end
when 1
specs.first
else
@@ -83,6 +94,8 @@ module Bundler
end
def self.ensure_all_gems_in_lockfile!(names, locked_gems = Bundler.locked_gems)
+ return unless locked_gems
+
locked_names = locked_gems.specs.map(&:name).uniq
names.-(locked_names).each do |g|
raise GemNotFound, gem_not_found_message(g, locked_names)
diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb
index 2986ddbc99..43f1ca92e2 100644
--- a/lib/bundler/cli/doctor.rb
+++ b/lib/bundler/cli/doctor.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "rbconfig"
+require "shellwords"
module Bundler
class CLI::Doctor
@@ -22,14 +23,14 @@ module Bundler
end
def dylibs_darwin(path)
- output = `/usr/bin/otool -L "#{path}"`.chomp
+ output = `/usr/bin/otool -L #{path.shellescape}`.chomp
dylibs = output.split("\n")[1..-1].map {|l| l.match(DARWIN_REGEX).captures[0] }.uniq
# ignore @rpath and friends
dylibs.reject {|dylib| dylib.start_with? "@" }
end
def dylibs_ldd(path)
- output = `/usr/bin/ldd "#{path}"`.chomp
+ output = `/usr/bin/ldd #{path.shellescape}`.chomp
output.split("\n").map do |l|
match = l.match(LDD_REGEX)
next if match.nil?
@@ -61,7 +62,7 @@ module Bundler
end
def run
- Bundler.ui.level = "error" if options[:quiet]
+ Bundler.ui.level = "warn" if options[:quiet]
Bundler.settings.validate!
check!
@@ -100,8 +101,11 @@ module Bundler
files_not_readable_or_writable = []
files_not_rw_and_owned_by_different_user = []
files_not_owned_by_current_user_but_still_rw = []
+ broken_symlinks = []
Find.find(Bundler.bundle_path.to_s).each do |f|
- if !File.writable?(f) || !File.readable?(f)
+ if !File.exist?(f)
+ broken_symlinks << f
+ elsif !File.writable?(f) || !File.readable?(f)
if File.stat(f).uid != Process.uid
files_not_rw_and_owned_by_different_user << f
else
@@ -113,6 +117,13 @@ module Bundler
end
ok = true
+
+ if broken_symlinks.any?
+ Bundler.ui.warn "Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{broken_symlinks.join("\n - ")}"
+
+ ok = false
+ end
+
if files_not_owned_by_current_user_but_still_rw.any?
Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
"user, but are still readable/writable. These files are:\n - #{files_not_owned_by_current_user_but_still_rw.join("\n - ")}"
diff --git a/lib/bundler/cli/exec.rb b/lib/bundler/cli/exec.rb
index 318d57fb06..42b602a055 100644
--- a/lib/bundler/cli/exec.rb
+++ b/lib/bundler/cli/exec.rb
@@ -12,12 +12,7 @@ module Bundler
@options = options
@cmd = args.shift
@args = args
-
- if !Bundler.current_ruby.jruby?
- @args << { :close_others => !options.keep_file_descriptors? }
- elsif options.keep_file_descriptors?
- Bundler.ui.warn "Ruby version #{RUBY_VERSION} defaults to keeping non-standard file descriptors on Kernel#exec."
- end
+ @args << { :close_others => !options.keep_file_descriptors? } unless Bundler.current_ruby.jruby?
end
def run
diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb
index 0d773579e5..2a74325fde 100644
--- a/lib/bundler/cli/gem.rb
+++ b/lib/bundler/cli/gem.rb
@@ -39,11 +39,19 @@ module Bundler
constant_name = name.gsub(/-[_-]*(?![_-]|$)/) { "::" }.gsub(/([_-]+|(::)|^)(.|$)/) { $2.to_s + $3.upcase }
constant_array = constant_name.split("::")
- git_installed = Bundler.git_present?
+ use_git = Bundler.git_present? && options[:git]
- git_author_name = git_installed ? `git config user.name`.chomp : ""
- github_username = git_installed ? `git config github.user`.chomp : ""
- git_user_email = git_installed ? `git config user.email`.chomp : ""
+ git_author_name = use_git ? `git config user.name`.chomp : ""
+ git_username = use_git ? `git config github.user`.chomp : ""
+ git_user_email = use_git ? `git config user.email`.chomp : ""
+
+ github_username = if options[:github_username].nil?
+ git_username
+ elsif options[:github_username] == false
+ ""
+ else
+ options[:github_username]
+ end
config = {
:name => name,
@@ -58,8 +66,9 @@ module Bundler
:ext => options[:ext],
:exe => options[:exe],
:bundler_version => bundler_dependency_version,
+ :git => use_git,
:github_username => github_username.empty? ? "[USERNAME]" : github_username,
- :required_ruby_version => Gem.ruby_version < Gem::Version.new("2.4.a") ? "2.3.0" : "2.4.0",
+ :required_ruby_version => required_ruby_version,
}
ensure_safe_gem_name(name, constant_array)
@@ -67,6 +76,7 @@ module Bundler
"#{Bundler.preferred_gemfile_name}.tt" => Bundler.preferred_gemfile_name,
"lib/newgem.rb.tt" => "lib/#{namespaced_path}.rb",
"lib/newgem/version.rb.tt" => "lib/#{namespaced_path}/version.rb",
+ "sig/newgem.rbs.tt" => "sig/#{namespaced_path}.rbs",
"newgem.gemspec.tt" => "#{name}.gemspec",
"Rakefile.tt" => "Rakefile",
"README.md.tt" => "README.md",
@@ -79,7 +89,7 @@ module Bundler
bin/setup
]
- templates.merge!("gitignore.tt" => ".gitignore") if Bundler.git_present?
+ templates.merge!("gitignore.tt" => ".gitignore") if use_git
if test_framework = ask_and_set_test_framework
config[:test] = test_framework
@@ -154,15 +164,16 @@ module Bundler
templates.merge!("CHANGELOG.md.tt" => "CHANGELOG.md")
end
- if ask_and_set(:rubocop, "Do you want to add rubocop as a dependency for gems you generate?",
- "RuboCop is a static code analyzer that has out-of-the-box rules for many " \
- "of the guidelines in the community style guide. " \
- "For more information, see the RuboCop docs (https://docs.rubocop.org/en/stable/) " \
- "and the Ruby Style Guides (https://github.com/rubocop-hq/ruby-style-guide).")
- config[:rubocop] = true
- config[:rubocop_version] = Gem.ruby_version < Gem::Version.new("2.4.a") ? "0.81.0" : "1.7"
+ config[:linter] = ask_and_set_linter
+ case config[:linter]
+ when "rubocop"
+ config[:linter_version] = rubocop_version
Bundler.ui.info "RuboCop enabled in config"
templates.merge!("rubocop.yml.tt" => ".rubocop.yml")
+ when "standard"
+ config[:linter_version] = standard_version
+ Bundler.ui.info "Standard enabled in config"
+ templates.merge!("standard.yml.tt" => ".standard.yml")
end
templates.merge!("exe/newgem.tt" => "exe/#{name}") if config[:exe]
@@ -175,24 +186,32 @@ module Bundler
)
end
+ if target.exist? && !target.directory?
+ Bundler.ui.error "Couldn't create a new gem named `#{gem_name}` because there's an existing file named `#{gem_name}`."
+ exit Bundler::BundlerError.all_errors[Bundler::GenericSystemCallError]
+ end
+
+ if use_git
+ Bundler.ui.info "Initializing git repo in #{target}"
+ require "shellwords"
+ `git init #{target.to_s.shellescape}`
+
+ config[:git_default_branch] = File.read("#{target}/.git/HEAD").split("/").last.chomp
+ end
+
templates.each do |src, dst|
destination = target.join(dst)
- SharedHelpers.filesystem_access(destination) do
- thor.template("newgem/#{src}", destination, config)
- end
+ thor.template("newgem/#{src}", destination, config)
end
executables.each do |file|
- SharedHelpers.filesystem_access(target.join(file)) do |path|
- executable = (path.stat.mode | 0o111)
- path.chmod(executable)
- end
+ path = target.join(file)
+ executable = (path.stat.mode | 0o111)
+ path.chmod(executable)
end
- if Bundler.git_present? && options[:git]
- Bundler.ui.info "Initializing git repo in #{target}"
+ if use_git
Dir.chdir(target) do
- `git init`
`git add .`
end
end
@@ -202,8 +221,6 @@ module Bundler
Bundler.ui.info "Gem '#{name}' was successfully created. " \
"For more information on making a RubyGem visit https://bundler.io/guides/creating_gem.html"
- rescue Errno::EEXIST => e
- raise GenericSystemCallError.new(e, "There was a conflict while creating the new gem.")
end
private
@@ -302,6 +319,58 @@ module Bundler
ci_template
end
+ def ask_and_set_linter
+ linter_template = options[:linter] || Bundler.settings["gem.linter"]
+ linter_template = deprecated_rubocop_option if linter_template.nil?
+
+ if linter_template.to_s.empty?
+ Bundler.ui.confirm "Do you want to add a code linter and formatter to your gem? " \
+ "Supported Linters:\n" \
+ "* RuboCop: https://rubocop.org\n" \
+ "* Standard: https://github.com/testdouble/standard\n" \
+ "\n"
+ Bundler.ui.info hint_text("linter")
+
+ result = Bundler.ui.ask "Enter a linter. rubocop/standard/(none):"
+ if result =~ /rubocop|standard/
+ linter_template = result
+ else
+ linter_template = false
+ end
+ end
+
+ if Bundler.settings["gem.linter"].nil?
+ Bundler.settings.set_global("gem.linter", linter_template)
+ end
+
+ # Once gem.linter safely set, unset the deprecated gem.rubocop
+ unless Bundler.settings["gem.rubocop"].nil?
+ Bundler.settings.set_global("gem.rubocop", nil)
+ end
+
+ if options[:linter] == Bundler.settings["gem.linter"]
+ Bundler.ui.info "#{options[:linter]} is already configured, ignoring --linter flag."
+ end
+
+ linter_template
+ end
+
+ def deprecated_rubocop_option
+ if !options[:rubocop].nil?
+ if options[:rubocop]
+ Bundler::SharedHelpers.major_deprecation 2, "--rubocop is deprecated, use --linter=rubocop"
+ "rubocop"
+ else
+ Bundler::SharedHelpers.major_deprecation 2, "--no-rubocop is deprecated, use --linter"
+ false
+ end
+ elsif !Bundler.settings["gem.rubocop"].nil?
+ Bundler::SharedHelpers.major_deprecation 2,
+ "config gem.rubocop is deprecated; we've updated your config to use gem.linter instead"
+ Bundler.settings["gem.rubocop"] ? "rubocop" : false
+ end
+ end
+
def bundler_dependency_version
v = Gem::Version.new(Bundler::VERSION)
req = v.segments[0..1]
@@ -335,5 +404,30 @@ module Bundler
def open_editor(editor, file)
thor.run(%(#{editor} "#{file}"))
end
+
+ def required_ruby_version
+ if Gem.ruby_version < Gem::Version.new("2.4.a") then "2.3.0"
+ elsif Gem.ruby_version < Gem::Version.new("2.5.a") then "2.4.0"
+ elsif Gem.ruby_version < Gem::Version.new("2.6.a") then "2.5.0"
+ else
+ "2.6.0"
+ end
+ end
+
+ def rubocop_version
+ if Gem.ruby_version < Gem::Version.new("2.4.a") then "0.81.0"
+ elsif Gem.ruby_version < Gem::Version.new("2.5.a") then "1.12"
+ else
+ "1.21"
+ end
+ end
+
+ def standard_version
+ if Gem.ruby_version < Gem::Version.new("2.4.a") then "0.2.5"
+ elsif Gem.ruby_version < Gem::Version.new("2.5.a") then "1.0"
+ else
+ "1.3"
+ end
+ end
end
end
diff --git a/lib/bundler/cli/info.rb b/lib/bundler/cli/info.rb
index 3111b64a33..76c8cf60c0 100644
--- a/lib/bundler/cli/info.rb
+++ b/lib/bundler/cli/info.rb
@@ -18,6 +18,7 @@ module Bundler
if spec
return print_gem_path(spec) if @options[:path]
+ return print_gem_version(spec) if @options[:version]
print_gem_info(spec)
end
end
@@ -39,13 +40,18 @@ module Bundler
raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(gem_name, Bundler.definition.dependencies)
end
+ def print_gem_version(spec)
+ Bundler.ui.info spec.version.to_s
+ end
+
def print_gem_path(spec)
- if spec.name == "bundler"
+ name = spec.name
+ if name == "bundler"
path = File.expand_path("../../../..", __FILE__)
else
path = spec.full_gem_path
- unless File.directory?(path)
- return Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at: #{path}"
+ if spec.deleted_gem?
+ return Bundler.ui.warn "The gem #{name} has been deleted. It was installed at: #{path}"
end
end
@@ -54,8 +60,9 @@ module Bundler
def print_gem_info(spec)
metadata = spec.metadata
+ name = spec.name
gem_info = String.new
- gem_info << " * #{spec.name} (#{spec.version}#{spec.git_version})\n"
+ gem_info << " * #{name} (#{spec.version}#{spec.git_version})\n"
gem_info << "\tSummary: #{spec.summary}\n" if spec.summary
gem_info << "\tHomepage: #{spec.homepage}\n" if spec.homepage
gem_info << "\tDocumentation: #{metadata["documentation_uri"]}\n" if metadata.key?("documentation_uri")
@@ -67,6 +74,11 @@ module Bundler
gem_info << "\tMailing List: #{metadata["mailing_list_uri"]}\n" if metadata.key?("mailing_list_uri")
gem_info << "\tPath: #{spec.full_gem_path}\n"
gem_info << "\tDefault Gem: yes" if spec.respond_to?(:default_gem?) && spec.default_gem?
+
+ if name != "bundler" && spec.deleted_gem?
+ return Bundler.ui.warn "The gem #{name} has been deleted. Gemspec information is still available though:\n#{gem_info}"
+ end
+
Bundler.ui.info gem_info
end
end
diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb
index c702eb14d1..4c1915fea6 100644
--- a/lib/bundler/cli/install.rb
+++ b/lib/bundler/cli/install.rb
@@ -8,7 +8,7 @@ module Bundler
end
def run
- Bundler.ui.level = "error" if options[:quiet]
+ Bundler.ui.level = "warn" if options[:quiet]
warn_if_root
@@ -33,12 +33,8 @@ module Bundler
options[:local] = true if Bundler.app_cache.exist?
- if Bundler.feature_flag.deployment_means_frozen?
- Bundler.settings.set_command_option :deployment, true
- else
- Bundler.settings.set_command_option :deployment, true if options[:deployment]
- Bundler.settings.set_command_option :frozen, true if options[:frozen]
- end
+ Bundler.settings.set_command_option :deployment, true if options[:deployment]
+ Bundler.settings.set_command_option :frozen, true if options[:frozen]
end
# When install is called with --no-deployment, disable deployment mode
@@ -62,7 +58,10 @@ module Bundler
definition.validate_runtime!
installer = Installer.install(Bundler.root, definition, options)
- Bundler.load.cache if Bundler.app_cache.exist? && !options["no-cache"] && !Bundler.frozen_bundle?
+
+ Bundler.settings.temporary(:cache_all_platforms => options[:local] ? false : Bundler.settings[:cache_all_platforms]) do
+ Bundler.load.cache(nil, options[:local]) if Bundler.app_cache.exist? && !options["no-cache"] && !Bundler.frozen_bundle?
+ end
Bundler.ui.confirm "Bundle complete! #{dependencies_count_for(definition)}, #{gems_installed_for(definition)}."
Bundler::CLI::Common.output_without_groups_message(:install)
@@ -84,28 +83,15 @@ module Bundler
end
Bundler::CLI::Common.output_fund_metadata_summary
- rescue GemNotFound, VersionConflict => e
- if options[:local] && Bundler.app_cache.exist?
- Bundler.ui.warn "Some gems seem to be missing from your #{Bundler.settings.app_cache_path} directory."
- end
-
- unless Bundler.definition.has_rubygems_remotes?
- Bundler.ui.warn <<-WARN, :wrap => true
- Your Gemfile has no gem server sources. If you need gems that are \
- not already on your machine, add a line like this to your Gemfile:
- source 'https://rubygems.org'
- WARN
- end
- raise e
- rescue Gem::InvalidSpecificationException => e
+ rescue Gem::InvalidSpecificationException
Bundler.ui.warn "You have one or more invalid gemspecs that need to be fixed."
- raise e
+ raise
end
private
def warn_if_root
- return if Bundler.settings[:silence_root_warning] || Bundler::WINDOWS || !Process.uid.zero?
+ return if Bundler.settings[:silence_root_warning] || Gem.win_platform? || !Process.uid.zero?
Bundler.ui.warn "Don't run Bundler as root. Bundler can ask for sudo " \
"if it is needed, and installing your bundle as root will break this " \
"application for all non-root users on this machine.", :wrap => true
diff --git a/lib/bundler/cli/issue.rb b/lib/bundler/cli/issue.rb
index f4cd5ac4df..b891ecb1d2 100644
--- a/lib/bundler/cli/issue.rb
+++ b/lib/bundler/cli/issue.rb
@@ -20,9 +20,10 @@ module Bundler
Hopefully the troubleshooting steps above resolved your problem! If things
still aren't working the way you expect them to, please let us know so
- that we can diagnose and help fix the problem you're having. Please
- view the Filing Issues guide for more information:
- https://github.com/rubygems/rubygems/blob/master/bundler/doc/contributing/ISSUES.md
+ that we can diagnose and help fix the problem you're having, by filling
+ in the new issue form located at
+ https://github.com/rubygems/rubygems/issues/new?labels=Bundler&template=bundler-related-issue.md,
+ and copy and pasting the information below.
EOS
diff --git a/lib/bundler/cli/list.rb b/lib/bundler/cli/list.rb
index 66abd32650..f56bf5b86a 100644
--- a/lib/bundler/cli/list.rb
+++ b/lib/bundler/cli/list.rb
@@ -16,7 +16,13 @@ module Bundler
specs = if @only_group.any? || @without_group.any?
filtered_specs_by_groups
else
- Bundler.load.specs
+ begin
+ Bundler.load.specs
+ rescue GemNotFound => e
+ Bundler.ui.error e.message
+ Bundler.ui.warn "Install missing gems with `bundle install`."
+ exit 1
+ end
end.reject {|s| s.name == "bundler" }.sort_by(&:name)
return Bundler.ui.info "No gems in the Gemfile" if specs.empty?
diff --git a/lib/bundler/cli/lock.rb b/lib/bundler/cli/lock.rb
index 7dd078b1ef..7d613a6644 100644
--- a/lib/bundler/cli/lock.rb
+++ b/lib/bundler/cli/lock.rb
@@ -21,9 +21,13 @@ module Bundler
Bundler::Fetcher.disable_endpoint = options["full-index"]
update = options[:update]
+ conservative = options[:conservative]
+
if update.is_a?(Array) # unlocking specific gems
Bundler::CLI::Common.ensure_all_gems_in_lockfile!(update)
- update = { :gems => update, :lock_shared_dependencies => options[:conservative] }
+ update = { :gems => update, :conservative => conservative }
+ elsif update
+ update = { :conservative => conservative } if conservative
end
definition = Bundler.definition(update)
diff --git a/lib/bundler/cli/open.rb b/lib/bundler/cli/open.rb
index df32e2f38b..ea504344f3 100644
--- a/lib/bundler/cli/open.rb
+++ b/lib/bundler/cli/open.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "shellwords"
-
module Bundler
class CLI::Open
attr_reader :options, :name
@@ -19,6 +17,7 @@ module Bundler
else
path = spec.full_gem_path
Dir.chdir(path) do
+ require "shellwords"
command = Shellwords.split(editor) + [path]
Bundler.with_original_env do
system(*command)
diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb
index 6a1789e235..d5183b060b 100644
--- a/lib/bundler/cli/outdated.rb
+++ b/lib/bundler/cli/outdated.rb
@@ -72,7 +72,7 @@ module Bundler
gemfile_specs + dependency_specs
end
- specs.sort_by(&:name).each do |current_spec|
+ specs.sort_by(&:name).uniq(&:name).each do |current_spec|
next unless gems.empty? || gems.include?(current_spec.name)
active_spec = retrieve_active_spec(definition, current_spec)
@@ -146,17 +146,16 @@ module Bundler
end
def retrieve_active_spec(definition, current_spec)
- if strict
- active_spec = definition.find_resolved_spec(current_spec)
- else
- active_specs = definition.find_indexed_specs(current_spec)
- if !current_spec.version.prerelease? && !options[:pre] && active_specs.size > 1
- active_specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? }
- end
- active_spec = active_specs.last
- end
+ active_spec = definition.resolve.find_by_name_and_platform(current_spec.name, current_spec.platform)
+ return unless active_spec
- active_spec
+ return active_spec if strict
+
+ active_specs = active_spec.source.specs.search(current_spec.name).select {|spec| spec.match_platform(current_spec.platform) }.sort_by(&:version)
+ if !current_spec.version.prerelease? && !options[:pre] && active_specs.size > 1
+ active_specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? }
+ end
+ active_specs.last
end
def print_gems(gems_list)
diff --git a/lib/bundler/cli/remove.rb b/lib/bundler/cli/remove.rb
index cd6a2cec28..44a4d891dd 100644
--- a/lib/bundler/cli/remove.rb
+++ b/lib/bundler/cli/remove.rb
@@ -11,8 +11,7 @@ module Bundler
raise InvalidOption, "Please specify gems to remove." if @gems.empty?
Injector.remove(@gems, {})
-
- Installer.install(Bundler.root, Bundler.definition) if @options["install"]
+ Installer.install(Bundler.root, Bundler.definition)
end
end
end
diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb
index 94699484d4..95a8886ea5 100644
--- a/lib/bundler/cli/update.rb
+++ b/lib/bundler/cli/update.rb
@@ -9,7 +9,7 @@ module Bundler
end
def run
- Bundler.ui.level = "error" if options[:quiet]
+ Bundler.ui.level = "warn" if options[:quiet]
Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins?
@@ -27,9 +27,14 @@ module Bundler
raise InvalidOption, "Cannot specify --all along with specific options."
end
+ conservative = options[:conservative]
+
if full_update
- # We're doing a full update
- Bundler.definition(true)
+ if conservative
+ Bundler.definition(:conservative => conservative)
+ else
+ Bundler.definition(true)
+ end
else
unless Bundler.default_lockfile.exist?
raise GemfileLockNotFound, "This Bundle hasn't been installed yet. " \
@@ -43,7 +48,7 @@ module Bundler
end
Bundler.definition(:gems => gems, :sources => sources, :ruby => options[:ruby],
- :lock_shared_dependencies => options[:conservative],
+ :conservative => conservative,
:bundler => options[:bundler])
end
@@ -61,7 +66,7 @@ module Bundler
if locked_gems = Bundler.definition.locked_gems
previous_locked_info = locked_gems.specs.reduce({}) do |h, s|
- h[s.name] = { :spec => s, :version => s.version, :source => s.source.to_s }
+ h[s.name] = { :spec => s, :version => s.version, :source => s.source.identifier }
h
end
end
@@ -90,7 +95,7 @@ module Bundler
end
locked_source = locked_info[:source]
- new_source = new_spec.source.to_s
+ new_source = new_spec.source.identifier
next if locked_source != new_source
new_version = new_spec.version
diff --git a/lib/bundler/compact_index_client.rb b/lib/bundler/compact_index_client.rb
index cf67f0e7a0..d5dbeb3b10 100644
--- a/lib/bundler/compact_index_client.rb
+++ b/lib/bundler/compact_index_client.rb
@@ -5,7 +5,7 @@ require "set"
module Bundler
class CompactIndexClient
- DEBUG_MUTEX = Mutex.new
+ DEBUG_MUTEX = Thread::Mutex.new
def self.debug
return unless ENV["DEBUG_COMPACT_INDEX"]
DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") }
@@ -25,7 +25,7 @@ module Bundler
@endpoints = Set.new
@info_checksums_by_name = {}
@parsed_checksums = false
- @mutex = Mutex.new
+ @mutex = Thread::Mutex.new
end
def execution_mode=(block)
diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb
index 7959e5c089..d9b9cec0d4 100644
--- a/lib/bundler/compact_index_client/updater.rb
+++ b/lib/bundler/compact_index_client/updater.rb
@@ -50,16 +50,20 @@ module Bundler
content = response.body
- SharedHelpers.filesystem_access(local_temp_path) do
+ etag = (response["ETag"] || "").gsub(%r{\AW/}, "")
+ correct_response = SharedHelpers.filesystem_access(local_temp_path) do
if response.is_a?(Net::HTTPPartialContent) && local_temp_path.size.nonzero?
local_temp_path.open("a") {|f| f << slice_body(content, 1..-1) }
+
+ etag_for(local_temp_path) == etag
else
- local_temp_path.open("w") {|f| f << content }
+ local_temp_path.open("wb") {|f| f << content }
+
+ etag.length.zero? || etag_for(local_temp_path) == etag
end
end
- etag = (response["ETag"] || "").gsub(%r{\AW/}, "")
- if etag.length.zero? || etag_for(local_temp_path) == etag
+ if correct_response
SharedHelpers.filesystem_access(local_path) do
FileUtils.mv(local_temp_path, local_path)
end
@@ -72,11 +76,6 @@ module Bundler
update(local_path, remote_path, :retrying)
end
- rescue Errno::EACCES
- raise Bundler::PermissionError,
- "Bundler does not have write access to create a temp directory " \
- "within #{Dir.tmpdir}. Bundler must have write access to your " \
- "systems temp directory to function properly. "
rescue Zlib::GzipFile::Error
raise Bundler::HTTPError
end
@@ -92,11 +91,11 @@ module Bundler
def checksum_for_file(path)
return nil unless path.file?
- # This must use IO.read instead of Digest.file().hexdigest
+ # This must use File.read instead of Digest.file().hexdigest
# because we need to preserve \n line endings on windows when calculating
# the checksum
SharedHelpers.filesystem_access(path, :read) do
- SharedHelpers.digest(:MD5).hexdigest(IO.read(path))
+ SharedHelpers.digest(:MD5).hexdigest(File.read(path))
end
end
end
diff --git a/lib/bundler/current_ruby.rb b/lib/bundler/current_ruby.rb
index c132e8ecc0..f84d68e262 100644
--- a/lib/bundler/current_ruby.rb
+++ b/lib/bundler/current_ruby.rb
@@ -20,6 +20,7 @@ module Bundler
2.5
2.6
2.7
+ 3.0
].freeze
KNOWN_MAJOR_VERSIONS = KNOWN_MINOR_VERSIONS.map {|v| v.split(".", 2).first }.uniq.freeze
@@ -64,19 +65,19 @@ module Bundler
end
def mswin?
- Bundler::WINDOWS
+ Gem.win_platform?
end
def mswin64?
- Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
+ Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
end
def mingw?
- Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
+ Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
end
def x64_mingw?
- Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu == "x64"
+ Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu == "x64"
end
(KNOWN_MINOR_VERSIONS + KNOWN_MAJOR_VERSIONS).each do |version|
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 3c25149d33..584f379256 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -56,10 +56,8 @@ module Bundler
@unlocking_bundler = false
@unlocking = unlock
else
- unlock = unlock.dup
@unlocking_bundler = unlock.delete(:bundler)
- unlock.delete_if {|_k, v| Array(v).empty? }
- @unlocking = !unlock.empty?
+ @unlocking = unlock.any? {|_k, v| !Array(v).empty? }
end
@dependencies = dependencies
@@ -75,7 +73,6 @@ module Bundler
@lockfile_contents = String.new
@locked_bundler_version = nil
@locked_ruby_version = nil
- @locked_specs_incomplete_for_platform = false
@new_platform = nil
if lockfile && File.exist?(lockfile)
@@ -106,7 +103,19 @@ module Bundler
@locked_platforms = []
end
- @unlock[:gems] ||= []
+ locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
+ @multisource_allowed = locked_gem_sources.size == 1 && locked_gem_sources.first.multiple_remotes? && Bundler.frozen_bundle?
+
+ if @multisource_allowed
+ unless sources.aggregate_global_source?
+ msg = "Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure."
+
+ Bundler::SharedHelpers.major_deprecation 2, msg
+ end
+
+ @sources.merged_gem_lockfile_sections!(locked_gem_sources.first)
+ end
+
@unlock[:sources] ||= []
@unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object
@ruby_version.diff(locked_ruby_version_object)
@@ -119,14 +128,18 @@ module Bundler
@path_changes = converge_paths
@source_changes = converge_sources
- unless @unlock[:lock_shared_dependencies]
- eager_unlock = expand_dependencies(@unlock[:gems], true)
- @unlock[:gems] = @locked_specs.for(eager_unlock, [], false, false, false).map(&:name)
+ if @unlock[:conservative]
+ @unlock[:gems] ||= @dependencies.map(&:name)
+ else
+ eager_unlock = expand_dependencies(@unlock[:gems] || [], true)
+ @unlock[:gems] = @locked_specs.for(eager_unlock, false, false).map(&:name)
end
@dependency_changes = converge_dependencies
@local_changes = converge_locals
+ @locked_specs_incomplete_for_platform = !@locked_specs.for(requested_dependencies & expand_dependencies(locked_dependencies), true, true)
+
@requires = compute_requires
end
@@ -145,17 +158,21 @@ module Bundler
end
end
+ def resolve_only_locally!
+ @remote = false
+ sources.local_only!
+ resolve
+ end
+
def resolve_with_cache!
- raise "Specs already loaded" if @specs
sources.cached!
- specs
+ resolve
end
def resolve_remotely!
- return if @specs
@remote = true
sources.remote!
- specs
+ resolve
end
# For given dependency list returns a SpecSet with Gemspec of all the required
@@ -165,25 +182,7 @@ module Bundler
#
# @return [Bundler::SpecSet]
def specs
- @specs ||= begin
- begin
- specs = resolve.materialize(requested_dependencies)
- rescue GemNotFound => e # Handle yanked gem
- gem_name, gem_version = extract_gem_info(e)
- locked_gem = @locked_specs[gem_name].last
- raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote
- raise GemNotFound, "Your bundle is locked to #{locked_gem}, but that version could not " \
- "be found in any of the sources listed in your Gemfile. If you haven't changed sources, " \
- "that means the author of #{locked_gem} has removed it. You'll need to update your bundle " \
- "to a version other than #{locked_gem} that hasn't been removed in order to install."
- end
- unless specs["bundler"].any?
- bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
- specs["bundler"] = bundler
- end
-
- specs
- end
+ @specs ||= materialize(requested_dependencies)
end
def new_specs
@@ -195,9 +194,7 @@ module Bundler
end
def missing_specs
- missing = []
- resolve.materialize(requested_dependencies, missing)
- missing
+ resolve.materialize(requested_dependencies).missing_specs
end
def missing_specs?
@@ -206,7 +203,6 @@ module Bundler
Bundler.ui.debug "The definition is missing #{missing.map(&:full_name)}"
true
rescue BundlerError => e
- @index = nil
@resolve = nil
@specs = nil
@gem_version_promoter = nil
@@ -216,17 +212,11 @@ module Bundler
end
def requested_specs
- @requested_specs ||= begin
- groups = requested_groups
- groups.map!(&:to_sym)
- specs_for(groups)
- end
+ specs_for(requested_groups)
end
def requested_dependencies
- groups = requested_groups
- groups.map!(&:to_sym)
- dependencies_for(groups)
+ dependencies_for(requested_groups)
end
def current_dependencies
@@ -235,15 +225,22 @@ module Bundler
end
end
+ def locked_dependencies
+ @locked_deps.values
+ end
+
def specs_for(groups)
+ return specs if groups.empty?
deps = dependencies_for(groups)
- specs.for(expand_dependencies(deps))
+ materialize(deps)
end
def dependencies_for(groups)
- current_dependencies.reject do |d|
+ groups.map!(&:to_sym)
+ deps = current_dependencies.reject do |d|
(d.groups & groups).empty?
end
+ expand_dependencies(deps)
end
# Resolve all the dependencies specified in Gemfile. It ensures that
@@ -264,59 +261,11 @@ module Bundler
# Run a resolve against the locally available gems
Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}")
expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, @remote)
- Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms)
+ Resolver.resolve(expanded_dependencies, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms)
end
end
end
- def index
- @index ||= Index.build do |idx|
- dependency_names = @dependencies.map(&:name)
-
- sources.all_sources.each do |source|
- source.dependency_names = dependency_names - pinned_spec_names(source)
- idx.add_source source.specs
- dependency_names.concat(source.unmet_deps).uniq!
- end
-
- double_check_for_index(idx, dependency_names)
- end
- end
-
- # Suppose the gem Foo depends on the gem Bar. Foo exists in Source A. Bar has some versions that exist in both
- # sources A and B. At this point, the API request will have found all the versions of Bar in source A,
- # but will not have found any versions of Bar from source B, which is a problem if the requested version
- # of Foo specifically depends on a version of Bar that is only found in source B. This ensures that for
- # each spec we found, we add all possible versions from all sources to the index.
- def double_check_for_index(idx, dependency_names)
- pinned_names = pinned_spec_names
- loop do
- idxcount = idx.size
-
- names = :names # do this so we only have to traverse to get dependency_names from the index once
- unmet_dependency_names = lambda do
- return names unless names == :names
- new_names = sources.all_sources.map(&:dependency_names_to_double_check)
- return names = nil if new_names.compact!
- names = new_names.flatten(1).concat(dependency_names)
- names.uniq!
- names -= pinned_names
- names
- end
-
- sources.all_sources.each do |source|
- source.double_check_for(unmet_dependency_names)
- end
-
- break if idxcount == idx.size
- end
- end
- private :double_check_for_index
-
- def has_rubygems_remotes?
- sources.rubygems_sources.any? {|s| s.remotes.any? }
- end
-
def spec_git_paths
sources.git_sources.map {|s| File.realpath(s.path) if File.exist?(s.path) }.compact
end
@@ -415,44 +364,26 @@ module Bundler
added.concat new_platforms.map {|p| "* platform: #{p}" }
deleted.concat deleted_platforms.map {|p| "* platform: #{p}" }
- gemfile_sources = sources.lock_sources
-
- new_sources = gemfile_sources - @locked_sources
- deleted_sources = @locked_sources - gemfile_sources
-
- new_deps = @dependencies - @locked_deps.values
- deleted_deps = @locked_deps.values - @dependencies
-
- # Check if it is possible that the source is only changed thing
- if (new_deps.empty? && deleted_deps.empty?) && (!new_sources.empty? && !deleted_sources.empty?)
- new_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) }
- deleted_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) }
- end
-
- if @locked_sources != gemfile_sources
- if new_sources.any?
- added.concat new_sources.map {|source| "* source: #{source}" }
- end
-
- if deleted_sources.any?
- deleted.concat deleted_sources.map {|source| "* source: #{source}" }
- end
- end
+ new_deps = @dependencies - locked_dependencies
+ deleted_deps = locked_dependencies - @dependencies
added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
- if deleted_deps.any?
- deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" }
- end
+ deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
both_sources = Hash.new {|h, k| h[k] = [] }
@dependencies.each {|d| both_sources[d.name][0] = d }
- @locked_deps.each {|name, d| both_sources[name][1] = d.source }
+ locked_dependencies.each {|d| both_sources[d.name][1] = d }
+
+ both_sources.each do |name, (dep, lock_dep)|
+ next if dep.nil? || lock_dep.nil?
+
+ gemfile_source = dep.source || sources.default_source
+ lock_source = lock_dep.source || sources.default_source
+ next if lock_source.include?(gemfile_source)
- both_sources.each do |name, (dep, lock_source)|
- next if lock_source.nil? || (dep && lock_source.can_lock?(dep))
- gemfile_source_name = (dep && dep.source) || "no specified source"
- lockfile_source_name = lock_source
- changed << "* #{name} from `#{gemfile_source_name}` to `#{lockfile_source_name}`"
+ gemfile_source_name = dep.source ? gemfile_source.identifier : "no specified source"
+ lockfile_source_name = lock_dep.source ? lock_source.identifier : "no specified source"
+ changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
end
reason = change_reason
@@ -519,14 +450,6 @@ module Bundler
end
end
- def find_resolved_spec(current_spec)
- specs.find_by_name_and_platform(current_spec.name, current_spec.platform)
- end
-
- def find_indexed_specs(current_spec)
- index[current_spec.name].select {|spec| spec.match_platform(current_spec.platform) }.sort_by(&:version)
- end
-
attr_reader :sources
private :sources
@@ -540,6 +463,35 @@ module Bundler
private
+ def materialize(dependencies)
+ specs = resolve.materialize(dependencies)
+ missing_specs = specs.missing_specs
+
+ if missing_specs.any?
+ missing_specs.each do |s|
+ locked_gem = @locked_specs[s.name].last
+ next if locked_gem.nil? || locked_gem.version != s.version || !@remote
+ raise GemNotFound, "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
+ "no longer be found in that source. That means the author of #{locked_gem} has removed it. " \
+ "You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \
+ "removed in order to install."
+ end
+
+ raise GemNotFound, "Could not find #{missing_specs.map(&:full_name).join(", ")} in any of the sources"
+ end
+
+ unless specs["bundler"].any?
+ bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
+ specs["bundler"] = bundler
+ end
+
+ specs
+ end
+
+ def precompute_source_requirements_for_indirect_dependencies?
+ @remote && sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
+ end
+
def current_ruby_platform_locked?
return false unless generic_local_platform == Gem::Platform::RUBY
@@ -592,9 +544,9 @@ module Bundler
def dependencies_for_source_changed?(source, locked_source = source)
deps_for_source = @dependencies.select {|s| s.source == source }
- locked_deps_for_source = @locked_deps.values.select {|dep| dep.source == locked_source }
+ locked_deps_for_source = locked_dependencies.select {|dep| dep.source == locked_source }
- deps_for_source.sort != locked_deps_for_source.sort
+ deps_for_source.uniq.sort != locked_deps_for_source.sort
end
def specs_for_source_changed?(source)
@@ -653,36 +605,11 @@ module Bundler
end
end
- def converge_rubygems_sources
- return false if Bundler.feature_flag.disable_multisource?
-
- changes = false
-
- # Get the RubyGems sources from the Gemfile.lock
- locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
- # Get the RubyGems remotes from the Gemfile
- actual_remotes = sources.rubygems_remotes
-
- # If there is a RubyGems source in both
- if !locked_gem_sources.empty? && !actual_remotes.empty?
- locked_gem_sources.each do |locked_gem|
- # Merge the remotes from the Gemfile into the Gemfile.lock
- changes |= locked_gem.replace_remotes(actual_remotes, Bundler.settings[:allow_deployment_source_credential_changes])
- end
- end
-
- changes
- end
-
def converge_sources
- changes = false
-
- changes |= converge_rubygems_sources
-
# Replace the sources from the Gemfile with the sources from the Gemfile.lock,
# if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
# source in the Gemfile.lock, use the one from the Gemfile.
- changes |= sources.replace_sources!(@locked_sources)
+ changes = sources.replace_sources!(@locked_sources)
sources.all_sources.each do |source|
# If the source is unlockable and the current command allows an unlock of
@@ -700,25 +627,14 @@ module Bundler
end
def converge_dependencies
- frozen = Bundler.frozen_bundle?
- (@dependencies + @locked_deps.values).each do |dep|
- locked_source = @locked_deps[dep.name]
- # This is to make sure that if bundler is installing in deployment mode and
- # after locked_source and sources don't match, we still use locked_source.
- if frozen && !locked_source.nil? &&
- locked_source.respond_to?(:source) && locked_source.source.instance_of?(Source::Path) && locked_source.source.path.exist?
- dep.source = locked_source.source
- elsif dep.source
+ changes = false
+
+ @dependencies.each do |dep|
+ if dep.source
dep.source = sources.get(dep.source)
end
- end
- changes = false
- # We want to know if all match, but don't want to check all entries
- # This means we need to return false if any dependency doesn't match
- # the lock or doesn't exist in the lock.
- @dependencies.each do |dependency|
- unless locked_dep = @locked_deps[dependency.name]
+ unless locked_dep = @locked_deps[dep.name]
changes = true
next
end
@@ -729,11 +645,11 @@ module Bundler
# directive, the lockfile dependencies and resolved dependencies end up
# with a mismatch on #type. Work around that by setting the type on the
# dep from the lockfile.
- locked_dep.instance_variable_set(:@type, dependency.type)
+ locked_dep.instance_variable_set(:@type, dep.type)
# We already know the name matches from the hash lookup
# so we only need to check the requirement now
- changes ||= dependency.requirement != locked_dep.requirement
+ changes ||= dep.requirement != locked_dep.requirement
end
changes
@@ -743,47 +659,37 @@ module Bundler
# commonly happen if the Gemfile has changed since the lockfile was last
# generated
def converge_locked_specs
- deps = []
-
- # Build a list of dependencies that are the same in the Gemfile
- # and Gemfile.lock. If the Gemfile modified a dependency, but
- # the gem in the Gemfile.lock still satisfies it, this is fine
- # too.
- @dependencies.each do |dep|
- locked_dep = @locked_deps[dep.name]
+ resolve = converge_specs(@locked_specs)
- # If the locked_dep doesn't match the dependency we're looking for then we ignore the locked_dep
- locked_dep = nil unless locked_dep == dep
+ diff = nil
- if in_locked_deps?(dep, locked_dep) || satisfies_locked_spec?(dep)
- deps << dep
- elsif dep.source.is_a?(Source::Path) && dep.current_platform? && (!locked_dep || dep.source != locked_dep.source)
- @locked_specs.each do |s|
- @unlock[:gems] << s.name if s.source == dep.source
- end
+ # Now, we unlock any sources that do not have anymore gems pinned to it
+ sources.all_sources.each do |source|
+ next unless source.respond_to?(:unlock!)
- dep.source.unlock! if dep.source.respond_to?(:unlock!)
- dep.source.specs.each {|s| @unlock[:gems] << s.name }
+ unless resolve.any? {|s| s.source == source }
+ diff ||= @locked_specs.to_a - resolve.to_a
+ source.unlock! if diff.any? {|s| s.source == source }
end
end
- unlock_source_unlocks_spec = Bundler.feature_flag.unlock_source_unlocks_spec?
+ resolve
+ end
+ def converge_specs(specs)
+ deps = []
converged = []
- @locked_specs.each do |s|
+ specs.each do |s|
# Replace the locked dependency's source with the equivalent source from the Gemfile
dep = @dependencies.find {|d| s.satisfies?(d) }
- s.source = (dep && dep.source) || sources.get(s.source)
- # Don't add a spec to the list if its source is expired. For example,
- # if you change a Git gem to RubyGems.
- next if s.source.nil?
- next if @unlock[:sources].include?(s.source.name)
+ if dep && (!dep.source || s.source.include?(dep.source))
+ deps << dep
+ end
+
+ s.source = (dep && dep.source) || sources.get(s.source) || sources.default_source unless Bundler.frozen_bundle?
- # XXX This is a backwards-compatibility fix to preserve the ability to
- # unlock a single gem by passing its name via `--source`. See issue #3759
- # TODO: delete in Bundler 2
- next if unlock_source_unlocks_spec && @unlock[:sources].include?(s.name)
+ next if @unlock[:sources].include?(s.source.name)
# If the spec is from a path source and it doesn't exist anymore
# then we unlock it.
@@ -795,8 +701,8 @@ module Bundler
rescue PathError, GitError
# if we won't need the source (according to the lockfile),
# don't error if the path/git source isn't available
- next if @locked_specs.
- for(requested_dependencies, [], false, true, false).
+ next if specs.
+ for(requested_dependencies, false, true).
none? {|locked_spec| locked_spec.source == s.source }
raise
@@ -811,36 +717,15 @@ module Bundler
s.dependencies.replace(new_spec.dependencies)
end
- converged << s
- end
-
- resolve = SpecSet.new(converged)
- @locked_specs_incomplete_for_platform = !resolve.for(expand_dependencies(requested_dependencies & deps), @unlock[:gems], true, true)
- resolve = resolve.for(expand_dependencies(deps, true), @unlock[:gems], false, false, false)
- diff = nil
-
- # Now, we unlock any sources that do not have anymore gems pinned to it
- sources.all_sources.each do |source|
- next unless source.respond_to?(:unlock!)
-
- unless resolve.any? {|s| s.source == source }
- diff ||= @locked_specs.to_a - resolve.to_a
- source.unlock! if diff.any? {|s| s.source == source }
+ if dep.nil? && requested_dependencies.find {|d| s.name == d.name }
+ @unlock[:gems] << s.name
+ else
+ converged << s
end
end
- resolve
- end
-
- def in_locked_deps?(dep, locked_dep)
- # Because the lockfile can't link a dep to a specific remote, we need to
- # treat sources as equivalent anytime the locked dep has all the remotes
- # that the Gemfile dep does.
- locked_dep && locked_dep.source && dep.source && locked_dep.source.include?(dep.source)
- end
-
- def satisfies_locked_spec?(dep)
- @locked_specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
+ resolve = SpecSet.new(converged)
+ SpecSet.new(resolve.for(expand_dependencies(deps, true), false, false).reject{|s| @unlock[:gems].include?(s.name) })
end
def metadata_dependencies
@@ -887,38 +772,22 @@ module Bundler
end
def source_requirements
- # Load all specs from remote sources
- index
-
# Record the specs available in each gem's source, so that those
# specs will be available later when the resolver knows where to
# look for that gemspec (or its dependencies)
- default = sources.default_source
- source_requirements = { :default => default }
- default = nil unless Bundler.feature_flag.disable_multisource?
- dependencies.each do |dep|
- next unless source = dep.source || default
- source_requirements[dep.name] = source
+ source_requirements = if precompute_source_requirements_for_indirect_dependencies?
+ { :default => sources.default_source }.merge(source_map.all_requirements)
+ else
+ { :default => Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
end
metadata_dependencies.each do |dep|
source_requirements[dep.name] = sources.metadata_source
end
- source_requirements[:default_bundler] = source_requirements["bundler"] || source_requirements[:default]
+ source_requirements[:default_bundler] = source_requirements["bundler"] || sources.default_source
source_requirements["bundler"] = sources.metadata_source # needs to come last to override
source_requirements
end
- def pinned_spec_names(skip = nil)
- pinned_names = []
- default = Bundler.feature_flag.disable_multisource? && sources.default_source
- @dependencies.each do |dep|
- next unless dep_source = dep.source || default
- next if dep_source == skip
- pinned_names << dep.name
- end
- pinned_names
- end
-
def requested_groups
groups - Bundler.settings[:without] - @optional_groups + Bundler.settings[:with]
end
@@ -936,12 +805,6 @@ module Bundler
current == proposed
end
- def extract_gem_info(error)
- # This method will extract the error message like "Could not find foo-1.2.3 in any of the sources"
- # to an array. The first element will be the gem name (e.g. foo), the second will be the version number.
- error.message.scan(/Could not find (\w+)-(\d+(?:\.\d+)+)/).flatten
- end
-
def compute_requires
dependencies.reduce({}) do |requires, dep|
next requires unless dep.should_include?
@@ -954,24 +817,16 @@ module Bundler
end
def additional_base_requirements_for_resolve
- return [] unless @locked_gems && Bundler.feature_flag.only_update_to_newer_versions?
- dependencies_by_name = dependencies.inject({}) {|memo, dep| memo.update(dep.name => dep) }
- @locked_gems.specs.reduce({}) do |requirements, locked_spec|
+ return [] unless @locked_gems && unlocking? && !sources.expired_sources?(@locked_gems.sources)
+ converge_specs(@locked_gems.specs).map do |locked_spec|
name = locked_spec.name
- dependency = dependencies_by_name[name]
- next requirements unless dependency
- next requirements if @locked_gems.dependencies[name] != dependency
- next requirements if dependency.source.is_a?(Source::Path)
dep = Gem::Dependency.new(name, ">= #{locked_spec.version}")
- requirements[name] = DepProxy.get_proxy(dep, locked_spec.platform)
- requirements
- end.values
+ DepProxy.get_proxy(dep, locked_spec.platform)
+ end
end
- def equivalent_rubygems_remotes?(source)
- return false unless source.is_a?(Source::Rubygems)
-
- Bundler.settings[:allow_deployment_source_credential_changes] && source.equivalent_remotes?(sources.rubygems_remotes)
+ def source_map
+ @source_map ||= SourceMap.new(sources, dependencies)
end
end
end
diff --git a/lib/bundler/digest.rb b/lib/bundler/digest.rb
new file mode 100644
index 0000000000..759f609416
--- /dev/null
+++ b/lib/bundler/digest.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+# This code was extracted from https://github.com/Solistra/ruby-digest which is under public domain
+module Bundler
+ module Digest
+ # The initial constant values for the 32-bit constant words A, B, C, D, and
+ # E, respectively.
+ SHA1_WORDS = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0].freeze
+
+ # The 8-bit field used for bitwise `AND` masking. Defaults to `0xFFFFFFFF`.
+ SHA1_MASK = 0xFFFFFFFF
+
+ class << self
+ def sha1(string)
+ unless string.is_a?(String)
+ raise TypeError, "can't convert #{string.class.inspect} into String"
+ end
+
+ buffer = string.b
+
+ words = SHA1_WORDS.dup
+ generate_split_buffer(buffer) do |chunk|
+ w = []
+ chunk.each_slice(4) do |a, b, c, d|
+ w << (((a << 8 | b) << 8 | c) << 8 | d)
+ end
+ a, b, c, d, e = *words
+ (16..79).each do |i|
+ w[i] = SHA1_MASK & rotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1)
+ end
+ 0.upto(79) do |i|
+ case i
+ when 0..19
+ f = ((b & c) | (~b & d))
+ k = 0x5A827999
+ when 20..39
+ f = (b ^ c ^ d)
+ k = 0x6ED9EBA1
+ when 40..59
+ f = ((b & c) | (b & d) | (c & d))
+ k = 0x8F1BBCDC
+ when 60..79
+ f = (b ^ c ^ d)
+ k = 0xCA62C1D6
+ end
+ t = SHA1_MASK & (SHA1_MASK & rotate(a, 5) + f + e + k + w[i])
+ a, b, c, d, e = t, a, SHA1_MASK & rotate(b, 30), c, d # rubocop:disable Style/ParallelAssignment
+ end
+ mutated = [a, b, c, d, e]
+ words.map!.with_index {|word, index| SHA1_MASK & (word + mutated[index]) }
+ end
+
+ words.pack("N*").unpack("H*").first
+ end
+
+ private
+
+ def generate_split_buffer(string, &block)
+ size = string.bytesize * 8
+ buffer = string.bytes << 128
+ buffer << 0 while buffer.size % 64 != 56
+ buffer.concat([size].pack("Q>").bytes)
+ buffer.each_slice(64, &block)
+ end
+
+ def rotate(value, spaces)
+ value << spaces | value >> (32 - spaces)
+ end
+ end
+ end
+end
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index 1cc7908b8a..1108fc3b78 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -18,6 +18,8 @@ module Bundler
VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules
platform platforms type source install_if gemfile].freeze
+ GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}.freeze
+
attr_reader :gemspecs
attr_accessor :dependencies
@@ -103,8 +105,8 @@ module Bundler
if current = @dependencies.find {|d| d.name == dep.name }
deleted_dep = @dependencies.delete(current) if current.type == :development
- if current.requirement != dep.requirement
- unless deleted_dep
+ unless deleted_dep
+ if current.requirement != dep.requirement
return if dep.type == :development
update_prompt = ""
@@ -122,17 +124,14 @@ module Bundler
raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
"#{update_prompt}"
+ else
+ Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
+ "You should probably keep only one of them.\n" \
+ "Remove any duplicate entries and specify the gem only once.\n" \
+ "While it's not a problem now, it could cause errors if you change the version of one of them later."
end
- else
- Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
- "You should probably keep only one of them.\n" \
- "Remove any duplicate entries and specify the gem only once.\n" \
- "While it's not a problem now, it could cause errors if you change the version of one of them later."
- end
-
- if current.source != dep.source
- unless deleted_dep
+ if current.source != dep.source
return if dep.type == :development
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
@@ -164,8 +163,7 @@ module Bundler
elsif block_given?
with_source(@sources.add_rubygems_source("remotes" => source), &blk)
else
- check_primary_source_safety(@sources)
- @sources.global_rubygems_source = source
+ @sources.add_global_rubygems_remote(source)
end
end
@@ -183,24 +181,14 @@ module Bundler
end
def path(path, options = {}, &blk)
- unless block_given?
- msg = "You can no longer specify a path source by itself. Instead, \n" \
- "either use the :path option on a gem, or specify the gems that \n" \
- "bundler should find in the path source by passing a block to \n" \
- "the path method, like: \n\n" \
- " path 'dir/containing/rails' do\n" \
- " gem 'rails'\n" \
- " end\n\n"
-
- raise DeprecatedError, msg if Bundler.feature_flag.disable_multisource?
- SharedHelpers.major_deprecation(2, msg.strip)
- end
-
source_options = normalize_hash(options).merge(
"path" => Pathname.new(path),
"root_path" => gemfile_root,
"gemspec" => gemspecs.find {|g| g.name == options["name"] }
)
+
+ source_options["global"] = true unless block_given?
+
source = @sources.add_path_source(source_options)
with_source(source, &blk)
end
@@ -229,6 +217,7 @@ module Bundler
end
def to_definition(lockfile, unlock)
+ check_primary_source_safety
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles)
end
@@ -279,6 +268,11 @@ module Bundler
raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile"
end
+ def check_primary_source_safety
+ check_path_source_safety
+ check_rubygems_source_safety
+ end
+
private
def add_git_sources
@@ -286,8 +280,17 @@ module Bundler
warn_deprecated_git_source(:github, <<-'RUBY'.strip, 'Change any "reponame" :github sources to "username/reponame".')
"https://github.com/#{repo_name}.git"
RUBY
- repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
- "https://github.com/#{repo_name}.git"
+ if repo_name =~ GITHUB_PULL_REQUEST_URL
+ {
+ "git" => "https://github.com/#{$1}.git",
+ "branch" => "refs/pull/#{$2}/head",
+ "ref" => nil,
+ "tag" => nil,
+ }
+ else
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
+ "https://github.com/#{repo_name}.git"
+ end
end
git_source(:gist) do |repo_name|
@@ -373,7 +376,11 @@ repo_name ||= user_name
git_name = (git_names & opts.keys).last
if @git_sources[git_name]
- opts["git"] = @git_sources[git_name].call(opts[git_name])
+ git_opts = @git_sources[git_name].call(opts[git_name])
+ git_opts = { "git" => git_opts } if git_opts.is_a?(String)
+ opts.merge!(git_opts) do |key, _gemfile_value, _git_source_value|
+ raise GemfileError, %(The :#{key} option can't be used with `#{git_name}: #{opts[git_name].inspect}`)
+ end
end
%w[git path].each do |type|
@@ -440,25 +447,46 @@ repo_name ||= user_name
end
end
- def check_primary_source_safety(source_list)
- return if source_list.rubygems_primary_remotes.empty? && source_list.global_rubygems_source.nil?
+ def check_path_source_safety
+ return if @sources.global_path_source.nil?
+
+ msg = "You can no longer specify a path source by itself. Instead, \n" \
+ "either use the :path option on a gem, or specify the gems that \n" \
+ "bundler should find in the path source by passing a block to \n" \
+ "the path method, like: \n\n" \
+ " path 'dir/containing/rails' do\n" \
+ " gem 'rails'\n" \
+ " end\n\n"
+
+ SharedHelpers.major_deprecation(2, msg.strip)
+ end
+
+ def check_rubygems_source_safety
+ if @sources.implicit_global_source?
+ implicit_global_source_warning
+ elsif @sources.aggregate_global_source?
+ multiple_global_source_warning
+ end
+ end
+
+ def implicit_global_source_warning
+ Bundler::SharedHelpers.major_deprecation 2, "This Gemfile does not include an explicit global source. " \
+ "Not using an explicit global source may result in a different lockfile being generated depending on " \
+ "the gems you have installed locally before bundler is run. " \
+ "Instead, define a global source in your Gemfile like this: source \"https://rubygems.org\"."
+ end
- if Bundler.feature_flag.disable_multisource?
+ def multiple_global_source_warning
+ if Bundler.feature_flag.bundler_3_mode?
msg = "This Gemfile contains multiple primary sources. " \
"Each source after the first must include a block to indicate which gems " \
"should come from that source"
- unless Bundler.feature_flag.bundler_2_mode?
- msg += ". To downgrade this error to a warning, run " \
- "`bundle config unset disable_multisource`"
- end
raise GemfileEvalError, msg
else
Bundler::SharedHelpers.major_deprecation 2, "Your Gemfile contains multiple primary sources. " \
"Using `source` more than once without a block is a security risk, and " \
"may result in installing unexpected gems. To resolve this warning, use " \
- "a block to indicate which gems should come from the secondary source. " \
- "To upgrade this warning to an error, run `bundle config set --local " \
- "disable_multisource true`."
+ "a block to indicate which gems should come from the secondary source."
end
end
diff --git a/lib/bundler/environment_preserver.rb b/lib/bundler/environment_preserver.rb
index a77f7e0816..0f08e049d8 100644
--- a/lib/bundler/environment_preserver.rb
+++ b/lib/bundler/environment_preserver.rb
@@ -38,7 +38,10 @@ module Bundler
# Replaces `ENV` with the bundler environment variables backed up
def replace_with_backup
- ENV.replace(backup) unless Gem.win_platform?
+ unless Gem.win_platform?
+ ENV.replace(backup)
+ return
+ end
# Fallback logic for Windows below to workaround
# https://bugs.ruby-lang.org/issues/16798. Can be dropped once all
diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb
index 11763b4e88..9ad7460e58 100644
--- a/lib/bundler/errors.rb
+++ b/lib/bundler/errors.rb
@@ -75,10 +75,26 @@ module Bundler
end
end
+ def permission_type
+ case @permission_type
+ when :create
+ "executable permissions for all parent directories and write permissions for `#{parent_folder}`"
+ when :delete
+ permissions = "executable permissions for all parent directories and write permissions for `#{parent_folder}`"
+ permissions += ", and the same thing for all subdirectories inside #{@path}" if File.directory?(@path)
+ permissions
+ else
+ "#{@permission_type} permissions for that path"
+ end
+ end
+
+ def parent_folder
+ File.dirname(@path)
+ end
+
def message
"There was an error while trying to #{action} `#{@path}`. " \
- "It is likely that you need to grant #{@permission_type} permissions " \
- "for that path."
+ "It is likely that you need to grant #{permission_type}."
end
status_code(23)
@@ -122,7 +138,7 @@ module Bundler
class VirtualProtocolError < BundlerError
def message
- "There was an error relating to virtualization and file access." \
+ "There was an error relating to virtualization and file access. " \
"It is likely that you need to grant access to or mount some file system correctly."
end
diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb
index a1b443b042..e441b941c2 100644
--- a/lib/bundler/feature_flag.rb
+++ b/lib/bundler/feature_flag.rb
@@ -31,17 +31,13 @@ module Bundler
settings_flag(:auto_clean_without_path) { bundler_3_mode? }
settings_flag(:cache_all) { bundler_3_mode? }
settings_flag(:default_install_uses_path) { bundler_3_mode? }
- settings_flag(:deployment_means_frozen) { bundler_3_mode? }
- settings_flag(:disable_multisource) { bundler_3_mode? }
settings_flag(:forget_cli_options) { bundler_3_mode? }
settings_flag(:global_gem_cache) { bundler_3_mode? }
- settings_flag(:only_update_to_newer_versions) { bundler_3_mode? }
settings_flag(:path_relative_to_cwd) { bundler_3_mode? }
settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") }
settings_flag(:print_only_version_number) { bundler_3_mode? }
settings_flag(:setup_makes_kernel_gem_public) { !bundler_3_mode? }
settings_flag(:suppress_install_using_messages) { bundler_3_mode? }
- settings_flag(:unlock_source_unlocks_spec) { !bundler_3_mode? }
settings_flag(:update_requires_all_flag) { bundler_4_mode? }
settings_flag(:use_gem_version_promoter_for_major_updates) { bundler_3_mode? }
diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb
index 0c81c54740..e3253a942f 100644
--- a/lib/bundler/fetcher.rb
+++ b/lib/bundler/fetcher.rb
@@ -28,7 +28,8 @@ module Bundler
" is a chance you are experiencing a man-in-the-middle attack, but" \
" most likely your system doesn't have the CA certificates needed" \
" for verification. For information about OpenSSL certificates, see" \
- " http://bit.ly/ruby-ssl. To connect without using SSL, edit your Gemfile" \
+ " https://railsapps.github.io/openssl-certificate-verify-failed.html." \
+ " To connect without using SSL, edit your Gemfile" \
" sources and change 'https' to 'http'."
end
end
@@ -47,7 +48,8 @@ module Bundler
remote_uri = filter_uri(remote_uri)
super "Authentication is required for #{remote_uri}.\n" \
"Please supply credentials for this source. You can do this by running:\n" \
- " bundle config set --global #{remote_uri} username:password"
+ "`bundle config set --global #{remote_uri} username:password`\n" \
+ "or by storing the credentials in the `#{Settings.key_for(remote_uri)}` environment variable"
end
end
# This error is raised if HTTP authentication is provided, but incorrect.
diff --git a/lib/bundler/fetcher/compact_index.rb b/lib/bundler/fetcher/compact_index.rb
index 0304155bdd..bc69b884ec 100644
--- a/lib/bundler/fetcher/compact_index.rb
+++ b/lib/bundler/fetcher/compact_index.rb
@@ -111,7 +111,7 @@ module Bundler
def bundle_worker(func = nil)
@bundle_worker ||= begin
worker_name = "Compact Index (#{display_uri.host})"
- Bundler::Worker.new(Bundler.current_ruby.rbx? ? 1 : 25, worker_name, func)
+ Bundler::Worker.new(Bundler.settings.processor_count, worker_name, func)
end
@bundle_worker.tap do |worker|
worker.instance_variable_set(:@func, func) if func
diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb
index a2289aaaae..f2aad3a500 100644
--- a/lib/bundler/fetcher/downloader.rb
+++ b/lib/bundler/fetcher/downloader.rb
@@ -14,8 +14,10 @@ module Bundler
def fetch(uri, headers = {}, counter = 0)
raise HTTPError, "Too many redirects" if counter >= redirect_limit
+ filtered_uri = URICredentialsFilter.credential_filtered_uri(uri)
+
response = request(uri, headers)
- Bundler.ui.debug("HTTP #{response.code} #{response.message} #{uri}")
+ Bundler.ui.debug("HTTP #{response.code} #{response.message} #{filtered_uri}")
case response
when Net::HTTPSuccess, Net::HTTPNotModified
@@ -40,7 +42,7 @@ module Bundler
raise BadAuthenticationError, uri.host if uri.userinfo
raise AuthenticationRequiredError, uri.host
when Net::HTTPNotFound
- raise FallbackError, "Net::HTTPNotFound: #{URICredentialsFilter.credential_filtered_uri(uri)}"
+ raise FallbackError, "Net::HTTPNotFound: #{filtered_uri}"
else
raise HTTPError, "#{response.class}#{": #{response.body}" unless response.body.empty?}"
end
@@ -49,7 +51,9 @@ module Bundler
def request(uri, headers)
validate_uri_scheme!(uri)
- Bundler.ui.debug "HTTP GET #{uri}"
+ filtered_uri = URICredentialsFilter.credential_filtered_uri(uri)
+
+ Bundler.ui.debug "HTTP GET #{filtered_uri}"
req = Net::HTTP::Get.new uri.request_uri, headers
if uri.user
user = CGI.unescape(uri.user)
@@ -64,12 +68,11 @@ module Bundler
raise CertificateFailureError.new(uri)
rescue *HTTP_ERRORS => e
Bundler.ui.trace e
- case e.message
- when /host down:/, /getaddrinfo: nodename nor servname provided/
+ if e.is_a?(SocketError) || e.message =~ /host down:/
raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \
"connection and try again."
else
- raise HTTPError, "Network error while fetching #{URICredentialsFilter.credential_filtered_uri(uri)}" \
+ raise HTTPError, "Network error while fetching #{filtered_uri}" \
" (#{e})"
end
end
diff --git a/lib/bundler/fetcher/index.rb b/lib/bundler/fetcher/index.rb
index 08b041897e..0d14c47aa7 100644
--- a/lib/bundler/fetcher/index.rb
+++ b/lib/bundler/fetcher/index.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require_relative "base"
-require "rubygems/remote_fetcher"
module Bundler
class Fetcher
diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb
index 5d0bb905bc..cc615db60c 100644
--- a/lib/bundler/friendly_errors.rb
+++ b/lib/bundler/friendly_errors.rb
@@ -49,8 +49,6 @@ module Bundler
"Alternatively, you can increase the amount of memory the JVM is able to use by running Bundler with jruby -J-Xmx1024m -S bundle (JRuby defaults to 500MB)."
else request_issue_report_for(error)
end
- rescue StandardError
- raise error
end
def exit_status(error)
@@ -65,34 +63,6 @@ module Bundler
def request_issue_report_for(e)
Bundler.ui.error <<-EOS.gsub(/^ {8}/, ""), nil, nil
--- ERROR REPORT TEMPLATE -------------------------------------------------------
- # Error Report
-
- ## Questions
-
- Please fill out answers to these questions, it'll help us figure out
- why things are going wrong.
-
- - **What did you do?**
-
- I ran the command `#{$PROGRAM_NAME} #{ARGV.join(" ")}`
-
- - **What did you expect to happen?**
-
- I expected Bundler to...
-
- - **What happened instead?**
-
- Instead, what happened was...
-
- - **Have you tried any solutions posted on similar issues in our issue tracker, stack overflow, or google?**
-
- I tried...
-
- - **Have you read our issues document, https://github.com/rubygems/rubygems/blob/master/bundler/doc/contributing/ISSUES.md?**
-
- ...
-
- ## Backtrace
```
#{e.class}: #{e.message}
@@ -111,8 +81,7 @@ module Bundler
First, try this link to see if there are any existing issue reports for this error:
#{issues_url(e)}
- If there aren't any reports for this error yet, please create copy and paste the report template above into a new issue. Don't forget to anonymize any private data! The new issue form is located at:
- https://github.com/rubygems/rubygems/issues/new?labels=Bundler
+ If there aren't any reports for this error yet, please fill in the new issue form located at #{new_issue_url}, and copy and paste the report template above in there.
EOS
end
@@ -123,6 +92,10 @@ module Bundler
"https://github.com/rubygems/rubygems/search?q=" \
"#{CGI.escape(message)}&type=Issues"
end
+
+ def new_issue_url
+ "https://github.com/rubygems/rubygems/issues/new?labels=Bundler&template=bundler-related-issue.md"
+ end
end
def self.with_friendly_errors
diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb
index d3e30124f9..034f2e5960 100644
--- a/lib/bundler/gem_helper.rb
+++ b/lib/bundler/gem_helper.rb
@@ -47,6 +47,11 @@ module Bundler
built_gem_path = build_gem
end
+ desc "Generate SHA512 checksum if #{name}-#{version}.gem into the checksums directory."
+ task "build:checksum" => "build" do
+ build_checksum(built_gem_path)
+ end
+
desc "Build and install #{name}-#{version}.gem into system gems."
task "install" => "build" do
install_gem(built_gem_path)
@@ -71,7 +76,7 @@ module Bundler
tag_version { git_push(args[:remote]) } unless already_tagged?
end
- task "release:rubygem_push" do
+ task "release:rubygem_push" => "build" do
rubygem_push(built_gem_path) if gem_push?
end
@@ -93,13 +98,21 @@ module Bundler
built_gem_path ||= build_gem
cmd = [*gem_command, "install", built_gem_path.to_s]
cmd << "--local" if local
- _, status = sh_with_status(cmd)
- unless status.success?
- raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output"
- end
+ sh(cmd)
Bundler.ui.confirm "#{name} (#{version}) installed."
end
+ def build_checksum(built_gem_path = nil)
+ built_gem_path ||= build_gem
+ SharedHelpers.filesystem_access(File.join(base, "checksums")) {|p| FileUtils.mkdir_p(p) }
+ file_name = "#{File.basename(built_gem_path)}.sha512"
+ require "digest/sha2"
+ checksum = ::Digest::SHA512.new.hexdigest(built_gem_path.to_s)
+ target = File.join(base, "checksums", file_name)
+ File.write(target, checksum)
+ Bundler.ui.confirm "#{name} #{version} checksum written to checksums/#{file_name}."
+ end
+
protected
def rubygem_push(path)
@@ -116,8 +129,8 @@ module Bundler
def git_push(remote = nil)
remote ||= default_remote
- perform_git_push "#{remote} refs/heads/#{current_branch}"
- perform_git_push "#{remote} refs/tags/#{version_tag}"
+ sh("git push #{remote} refs/heads/#{current_branch}".shellsplit)
+ sh("git push #{remote} refs/tags/#{version_tag}".shellsplit)
Bundler.ui.confirm "Pushed git commits and release tag."
end
@@ -145,13 +158,6 @@ module Bundler
allowed_push_host || env_rubygems_host || "rubygems.org"
end
- def perform_git_push(options = "")
- cmd = "git push #{options}"
- out, status = sh_with_status(cmd.shellsplit)
- return if status.success?
- raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n"
- end
-
def already_tagged?
return false unless sh(%w[git tag]).split(/\n/).include?(version_tag)
Bundler.ui.confirm "Tag #{version_tag} has already been created."
@@ -202,8 +208,7 @@ module Bundler
def sh(cmd, &block)
out, status = sh_with_status(cmd, &block)
unless status.success?
- cmd = cmd.shelljoin if cmd.respond_to?(:shelljoin)
- raise(out.empty? ? "Running `#{cmd}` failed. Run this command directly for more detailed output." : out)
+ raise("Running `#{cmd.shelljoin}` failed with the following output:\n\n#{out}\n")
end
out
end
diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb
index f945176037..8930fca6d0 100644
--- a/lib/bundler/index.rb
+++ b/lib/bundler/index.rb
@@ -122,10 +122,9 @@ module Bundler
names
end
- # returns a list of the dependencies
def unmet_dependency_names
dependency_names.select do |name|
- name != "bundler" && search(name).empty?
+ search(name).empty?
end
end
@@ -196,11 +195,7 @@ module Bundler
if base # allow all platforms when searching from a lockfile
dependency.matches_spec?(spec)
else
- if Gem::Platform.respond_to? :match_spec?
- dependency.matches_spec?(spec) && Gem::Platform.match_spec?(spec)
- else
- dependency.matches_spec?(spec) && Gem::Platform.match(spec.platform)
- end
+ dependency.matches_spec?(spec) && Gem::Platform.match_spec?(spec)
end
end
diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb
index e9aa13a357..613bda4f84 100644
--- a/lib/bundler/injector.rb
+++ b/lib/bundler/injector.rb
@@ -128,7 +128,7 @@ module Bundler
# evaluates a gemfile to remove the specified gem
# from it.
def remove_deps(gemfile_path)
- initial_gemfile = IO.readlines(gemfile_path)
+ initial_gemfile = File.readlines(gemfile_path)
Bundler.ui.info "Removing gems from #{gemfile_path}"
@@ -181,7 +181,7 @@ module Bundler
patterns = /gem\s+(['"])#{Regexp.union(gems)}\1|gem\s*\((['"])#{Regexp.union(gems)}\2\)/
new_gemfile = []
multiline_removal = false
- IO.readlines(gemfile_path).each do |line|
+ File.readlines(gemfile_path).each do |line|
match_data = line.match(patterns)
if match_data && is_not_within_comment?(line, match_data)
multiline_removal = line.rstrip.end_with?(",")
diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb
index 59211193d4..a718418fce 100644
--- a/lib/bundler/inline.rb
+++ b/lib/bundler/inline.rb
@@ -50,8 +50,9 @@ def gemfile(install = false, options = {}, &gemfile)
Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins?
builder = Bundler::Dsl.new
builder.instance_eval(&gemfile)
+ builder.check_primary_source_safety
- Bundler.settings.temporary(:frozen => false) do
+ Bundler.settings.temporary(:deployment => false, :frozen => false) do
definition = builder.to_definition(nil, true)
def definition.lock(*); end
definition.validate_runtime!
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index 8d55784006..63e02ba496 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require "rubygems/dependency_installer"
require_relative "worker"
require_relative "installer/parallel_installer"
require_relative "installer/standalone"
@@ -89,6 +88,8 @@ module Bundler
end
install(options)
+ Gem::Specification.reset # invalidate gem specification cache so that installed gems are immediately available
+
lock unless Bundler.frozen_bundle?
Standalone.new(options[:standalone], @definition).generate if options[:standalone]
end
@@ -133,7 +134,7 @@ module Bundler
next
end
- mode = Bundler::WINDOWS ? "wb:UTF-8" : "w"
+ mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb"
content = if RUBY_VERSION >= "2.6"
ERB.new(template, :trim_mode => "-").result(binding)
@@ -142,7 +143,7 @@ module Bundler
end
File.write(binstub_path, content, :mode => mode, :perm => 0o777 & ~File.umask)
- if Bundler::WINDOWS || options[:all_platforms]
+ if Gem.win_platform? || options[:all_platforms]
prefix = "@ruby -x \"%~f0\" %*\n@exit /b %ERRORLEVEL%\n\n"
File.write("#{binstub_path}.cmd", prefix + content, :mode => mode)
end
@@ -180,7 +181,7 @@ module Bundler
executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path)
executable_path = executable_path
- mode = Bundler::WINDOWS ? "wb:UTF-8" : "w"
+ mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb"
content = if RUBY_VERSION >= "2.6"
ERB.new(template, :trim_mode => "-").result(binding)
@@ -189,7 +190,7 @@ module Bundler
end
File.write("#{bin_path}/#{executable}", content, :mode => mode, :perm => 0o755)
- if Bundler::WINDOWS || options[:all_platforms]
+ if Gem.win_platform? || options[:all_platforms]
prefix = "@ruby -x \"%~f0\" %*\n@exit /b %ERRORLEVEL%\n\n"
File.write("#{bin_path}/#{executable}.cmd", prefix + content, :mode => mode)
end
@@ -220,14 +221,7 @@ module Bundler
# Parallelization has some issues on Windows, so it's not yet the default
return 1 if Gem.win_platform?
- processor_count
- end
-
- def processor_count
- require "etc"
- Etc.nprocessors
- rescue StandardError
- 1
+ Bundler.settings.processor_count
end
def load_plugins
diff --git a/lib/bundler/installer/gem_installer.rb b/lib/bundler/installer/gem_installer.rb
index 507fd1802c..1df86ccfbc 100644
--- a/lib/bundler/installer/gem_installer.rb
+++ b/lib/bundler/installer/gem_installer.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "shellwords"
-
module Bundler
class GemInstaller
attr_reader :spec, :standalone, :worker, :force, :installer
@@ -31,34 +29,23 @@ module Bundler
def specific_failure_message(e)
message = "#{e.class}: #{e.message}\n"
- message += " " + e.backtrace.join("\n ") + "\n\n" if Bundler.ui.debug?
+ message += " " + e.backtrace.join("\n ") + "\n\n"
message = message.lines.first + Bundler.ui.add_color(message.lines.drop(1).join, :clear)
message + Bundler.ui.add_color(failure_message, :red)
end
def failure_message
- return install_error_message if spec.source.options["git"]
- "#{install_error_message}\n#{gem_install_message}"
+ install_error_message
end
def install_error_message
"An error occurred while installing #{spec.name} (#{spec.version}), and Bundler cannot continue."
end
- def gem_install_message
- source = spec.source
- return unless source.respond_to?(:remotes)
-
- if source.remotes.size == 1
- "Make sure that `gem install #{spec.name} -v '#{spec.version}' --source '#{source.remotes.first}'` succeeds before bundling."
- else
- "Make sure that `gem install #{spec.name} -v '#{spec.version}'` succeeds before bundling."
- end
- end
-
def spec_settings
# Fetch the build settings, if there are any
if settings = Bundler.settings["build.#{spec.name}"]
+ require "shellwords"
Shellwords.shellsplit(settings)
end
end
diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb
index a6d1de29e6..5b6680e5e1 100644
--- a/lib/bundler/installer/parallel_installer.rb
+++ b/lib/bundler/installer/parallel_installer.rb
@@ -6,10 +6,11 @@ require_relative "gem_installer"
module Bundler
class ParallelInstaller
class SpecInstallation
- attr_accessor :spec, :name, :post_install_message, :state, :error
+ attr_accessor :spec, :name, :full_name, :post_install_message, :state, :error
def initialize(spec)
@spec = spec
@name = spec.name
+ @full_name = spec.full_name
@state = :none
@post_install_message = ""
@error = nil
@@ -27,13 +28,8 @@ module Bundler
state == :failed
end
- def installation_attempted?
- installed? || failed?
- end
-
- # Only true when spec in neither installed nor already enqueued
def ready_to_enqueue?
- !enqueued? && !installation_attempted?
+ state == :none
end
def has_post_install_message?
@@ -54,14 +50,11 @@ module Bundler
# Represents only the non-development dependencies, the ones that are
# itself and are in the total list.
def dependencies
- @dependencies ||= begin
- all_dependencies.reject {|dep| ignorable_dependency? dep }
- end
+ @dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
end
def missing_lockfile_dependencies(all_spec_names)
- deps = all_dependencies.reject {|dep| ignorable_dependency? dep }
- deps.reject {|dep| all_spec_names.include? dep.name }
+ dependencies.reject {|dep| all_spec_names.include? dep.name }
end
# Represents all dependencies
@@ -70,7 +63,7 @@ module Bundler
end
def to_s
- "#<#{self.class} #{@spec.full_name} (#{state})>"
+ "#<#{self.class} #{full_name} (#{state})>"
end
end
@@ -93,18 +86,48 @@ module Bundler
def call
check_for_corrupt_lockfile
+ if @rake
+ do_install(@rake, 0)
+ Gem::Specification.reset
+ end
+
if @size > 1
install_with_worker
else
install_serially
end
+ check_for_unmet_dependencies
+
handle_error if failed_specs.any?
@specs
ensure
worker_pool && worker_pool.stop
end
+ def check_for_unmet_dependencies
+ unmet_dependencies = @specs.map do |s|
+ [
+ s,
+ s.dependencies.reject {|dep| @specs.any? {|spec| dep.matches_spec?(spec.spec) } },
+ ]
+ end.reject {|a| a.last.empty? }
+ return if unmet_dependencies.empty?
+
+ warning = []
+ warning << "Your lockfile doesn't include a valid resolution."
+ warning << "You can fix this by regenerating your lockfile or trying to manually editing the bad locked gems to a version that satisfies all dependencies."
+ warning << "The unmet dependencies are:"
+
+ unmet_dependencies.each do |spec, unmet_spec_dependencies|
+ unmet_spec_dependencies.each do |unmet_spec_dependency|
+ warning << "* #{unmet_spec_dependency}, depended upon #{spec.full_name}, unsatisfied by #{@specs.find {|s| s.name == unmet_spec_dependency.name && !unmet_spec_dependency.matches_spec?(s.spec) }.full_name}"
+ end
+ end
+
+ Bundler.ui.warn(warning.join("\n"))
+ end
+
def check_for_corrupt_lockfile
missing_dependencies = @specs.map do |s|
[
@@ -217,8 +240,6 @@ module Bundler
# are installed.
def enqueue_specs
@specs.select(&:ready_to_enqueue?).each do |spec|
- next if @rake && !@rake.installed? && spec.name != @rake.name
-
if spec.dependencies_installed? @specs
spec.state = :enqueued
worker_pool.enq spec
diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb
index 2a3f5cfe35..e8494b4bcd 100644
--- a/lib/bundler/installer/standalone.rb
+++ b/lib/bundler/installer/standalone.rb
@@ -3,7 +3,7 @@
module Bundler
class Standalone
def initialize(groups, definition)
- @specs = groups.empty? ? definition.requested_specs : definition.specs_for(groups.map(&:to_sym))
+ @specs = definition.specs_for(groups)
end
def generate
@@ -12,12 +12,13 @@ module Bundler
end
File.open File.join(bundler_path, "setup.rb"), "w" do |file|
file.puts "require 'rbconfig'"
- file.puts "ruby_engine = RUBY_ENGINE"
- file.puts "ruby_version = RbConfig::CONFIG[\"ruby_version\"]"
- file.puts "path = File.expand_path('..', __FILE__)"
file.puts reverse_rubygems_kernel_mixin
paths.each do |path|
- file.puts %($:.unshift File.expand_path("\#{path}/#{path}"))
+ if Pathname.new(path).absolute?
+ file.puts %($:.unshift "#{path}")
+ else
+ file.puts %($:.unshift File.expand_path("\#{__dir__}/#{path}"))
+ end
end
end
end
@@ -28,14 +29,14 @@ module Bundler
@specs.map do |spec|
next if spec.name == "bundler"
Array(spec.require_paths).map do |path|
- gem_path(path, spec).sub(version_dir, '#{ruby_engine}/#{ruby_version}')
+ gem_path(path, spec).sub(version_dir, '#{RUBY_ENGINE}/#{RbConfig::CONFIG["ruby_version"]}')
# This is a static string intentionally. It's interpolated at a later time.
end
- end.flatten
+ end.flatten.compact
end
def version_dir
- "#{Bundler::RubyVersion.system.engine}/#{RbConfig::CONFIG["ruby_version"]}"
+ "#{RUBY_ENGINE}/#{RbConfig::CONFIG["ruby_version"]}"
end
def bundler_path
@@ -44,7 +45,11 @@ module Bundler
def gem_path(path, spec)
full_path = Pathname.new(path).absolute? ? path : File.join(spec.full_gem_path, path)
- Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s
+ if spec.source.instance_of?(Source::Path)
+ full_path
+ else
+ Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s
+ end
rescue TypeError
error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
raise Gem::InvalidSpecificationException.new(error_message)
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index 04ba2a2364..4eb228f314 100644
--- a/lib/bundler/lazy_specification.rb
+++ b/lib/bundler/lazy_specification.rb
@@ -38,8 +38,24 @@ module Bundler
identifier.hash
end
+ ##
+ # Does this locked specification satisfy +dependency+?
+ #
+ # NOTE: Rubygems default requirement is ">= 0", which doesn't match
+ # prereleases of 0 versions, like "0.0.0.dev" or "0.0.0.SNAPSHOT". However,
+ # bundler users expect those to work. We need to make sure that Gemfile
+ # dependencies without explicit requirements (which use ">= 0" under the
+ # hood by default) are still valid for locked specs using this kind of
+ # versions. The method implements an ad-hoc fix for that. A better solution
+ # might be to change default rubygems requirement of dependencies to be ">=
+ # 0.A" but that's a major refactoring likely to break things. Hopefully we
+ # can attempt it in the future.
+ #
+
def satisfies?(dependency)
- @name == dependency.name && dependency.requirement.satisfied_by?(Gem::Version.new(@version))
+ effective_requirement = dependency.requirement == Gem::Requirement.default ? Gem::Requirement.new(">= 0.A") : dependency.requirement
+
+ @name == dependency.name && effective_requirement.satisfied_by?(Gem::Version.new(@version))
end
def to_lock
@@ -73,7 +89,12 @@ module Bundler
same_platform_candidates = candidates.select do |spec|
MatchPlatform.platforms_match?(spec.platform, platform_object)
end
- search = same_platform_candidates.last || candidates.last
+ installable_candidates = same_platform_candidates.select do |spec|
+ !spec.is_a?(EndpointSpecification) ||
+ (spec.required_ruby_version.satisfied_by?(Gem.ruby_version) &&
+ spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version))
+ end
+ search = installable_candidates.last || same_platform_candidates.last
search.dependencies = dependencies if search && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification))
search
end
diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb
index f836737621..6ff4910a36 100644
--- a/lib/bundler/lockfile_parser.rb
+++ b/lib/bundler/lockfile_parser.rb
@@ -1,16 +1,5 @@
# frozen_string_literal: true
-#--
-# Some versions of the Bundler 1.1 RC series introduced corrupted
-# lockfiles. There were two major problems:
-#
-# * multiple copies of the same GIT section appeared in the lockfile
-# * when this happened, those sections got multiple copies of gems
-# in those sections.
-#
-# As a result, Bundler 1.1 contains code that fixes the earlier
-# corruption. We will remove this fix-up code in Bundler 1.2.
-
module Bundler
class LockfileParser
attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version
@@ -64,8 +53,6 @@ module Bundler
@state = nil
@specs = {}
- @rubygems_aggregate = Source::Rubygems.new
-
if lockfile.match(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
raise LockfileError, "Your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} contains merge conflicts.\n" \
"Run `git checkout HEAD -- #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` first to get a clean lock."
@@ -89,7 +76,6 @@ module Bundler
send("parse_#{@state}", line)
end
end
- @sources << @rubygems_aggregate unless Bundler.feature_flag.disable_multisource?
@specs = @specs.values.sort_by(&:identifier)
warn_for_outdated_bundler_version
rescue ArgumentError => e
@@ -100,6 +86,7 @@ module Bundler
def warn_for_outdated_bundler_version
return unless bundler_version
+ return if bundler_version.segments.last == "dev"
prerelease_text = bundler_version.prerelease? ? " --pre" : ""
current_version = Gem::Version.create(Bundler::VERSION)
return unless current_version < bundler_version
@@ -127,23 +114,11 @@ module Bundler
@sources << @current_source
when GIT
@current_source = TYPES[@type].from_lock(@opts)
- # Strip out duplicate GIT sections
- if @sources.include?(@current_source)
- @current_source = @sources.find {|s| s == @current_source }
- else
- @sources << @current_source
- end
+ @sources << @current_source
when GEM
- if Bundler.feature_flag.disable_multisource?
- @opts["remotes"] = @opts.delete("remote")
- @current_source = TYPES[@type].from_lock(@opts)
- @sources << @current_source
- else
- Array(@opts["remote"]).each do |url|
- @rubygems_aggregate.add_remote(url)
- end
- @current_source = @rubygems_aggregate
- end
+ @opts["remotes"] = Array(@opts.delete("remote")).reverse
+ @current_source = TYPES[@type].from_lock(@opts)
+ @sources << @current_source
when PLUGIN
@current_source = Plugin.source_from_lock(@opts)
@sources << @current_source
@@ -221,10 +196,9 @@ module Bundler
platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
@current_spec = LazySpecification.new(name, version, platform)
@current_spec.source = @current_source
+ @current_source.add_dependency_names(name)
- # Avoid introducing multiple copies of the same spec (caused by
- # duplicate GIT sections)
- @specs[@current_spec.identifier] ||= @current_spec
+ @specs[@current_spec.identifier] = @current_spec
elsif spaces.size == 6
version = version.split(",").map(&:strip) if version
dep = Gem::Dependency.new(name, version)
diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1
index b44295b2d3..a94467e25f 100644
--- a/lib/bundler/man/bundle-add.1
+++ b/lib/bundler/man/bundle-add.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-ADD" "1" "January 2021" "" ""
+.TH "BUNDLE\-ADD" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install
diff --git a/lib/bundler/man/bundle-binstubs.1 b/lib/bundler/man/bundle-binstubs.1
index 99876d023e..6d1b1d4247 100644
--- a/lib/bundler/man/bundle-binstubs.1
+++ b/lib/bundler/man/bundle-binstubs.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-BINSTUBS" "1" "January 2021" "" ""
+.TH "BUNDLE\-BINSTUBS" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-binstubs\fR \- Install the binstubs of the listed gems
diff --git a/lib/bundler/man/bundle-cache.1 b/lib/bundler/man/bundle-cache.1
index 3b3c6f9734..acbdae0df2 100644
--- a/lib/bundler/man/bundle-cache.1
+++ b/lib/bundler/man/bundle-cache.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CACHE" "1" "January 2021" "" ""
+.TH "BUNDLE\-CACHE" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application
diff --git a/lib/bundler/man/bundle-check.1 b/lib/bundler/man/bundle-check.1
index 4436c3e971..e555c9b399 100644
--- a/lib/bundler/man/bundle-check.1
+++ b/lib/bundler/man/bundle-check.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CHECK" "1" "January 2021" "" ""
+.TH "BUNDLE\-CHECK" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems
diff --git a/lib/bundler/man/bundle-clean.1 b/lib/bundler/man/bundle-clean.1
index b7e882ecbf..d403247524 100644
--- a/lib/bundler/man/bundle-clean.1
+++ b/lib/bundler/man/bundle-clean.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CLEAN" "1" "January 2021" "" ""
+.TH "BUNDLE\-CLEAN" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory
diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1
index 7a4a16c43e..976dae3aec 100644
--- a/lib/bundler/man/bundle-config.1
+++ b/lib/bundler/man/bundle-config.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-CONFIG" "1" "January 2021" "" ""
+.TH "BUNDLE\-CONFIG" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-config\fR \- Set bundler configuration options
@@ -56,9 +56,6 @@ Executing \fBbundle config unset \-\-local <name> <value>\fR will delete the con
.P
Executing bundle with the \fBBUNDLE_IGNORE_CONFIG\fR environment variable set will cause it to ignore all configuration\.
.
-.P
-Executing \fBbundle config set \-\-local disable_multisource true\fR upgrades the warning about the Gemfile containing multiple primary sources to an error\. Executing \fBbundle config unset disable_multisource\fR downgrades this error to a warning\.
-.
.SH "REMEMBERING OPTIONS"
Flags passed to \fBbundle install\fR or the Bundler runtime, such as \fB\-\-path foo\fR or \fB\-\-without production\fR, are remembered between commands and saved to your local application\'s configuration (normally, \fB\./\.bundle/config\fR)\.
.
@@ -184,9 +181,6 @@ The following is a list of all configuration keys and their purpose\. You can le
\fBdisable_local_revision_check\fR (\fBBUNDLE_DISABLE_LOCAL_REVISION_CHECK\fR): Allow Bundler to use a local git override without checking if the revision present in the lockfile is present in the repository\.
.
.IP "\(bu" 4
-\fBdisable_multisource\fR (\fBBUNDLE_DISABLE_MULTISOURCE\fR): When set, Gemfiles containing multiple sources will produce errors instead of warnings\. Use \fBbundle config unset disable_multisource\fR to unset\.
-.
-.IP "\(bu" 4
\fBdisable_shared_gems\fR (\fBBUNDLE_DISABLE_SHARED_GEMS\fR): Stop Bundler from accessing gems installed to RubyGems\' normal location\.
.
.IP "\(bu" 4
@@ -199,6 +193,9 @@ The following is a list of all configuration keys and their purpose\. You can le
\fBfrozen\fR (\fBBUNDLE_FROZEN\fR): Disallow changes to the \fBGemfile\fR\. When the \fBGemfile\fR is changed and the lockfile has not been updated, running Bundler commands will be blocked\. Defaults to \fBtrue\fR when \fB\-\-deployment\fR is used\.
.
.IP "\(bu" 4
+\fBgem\.github_username\fR (\fBBUNDLE_GEM__GITHUB_USERNAME\fR): Sets a GitHub username or organization to be used in \fBREADME\fR file when you create a new gem via \fBbundle gem\fR command\. It can be overridden by passing an explicit \fB\-\-github\-username\fR flag to \fBbundle gem\fR\.
+.
+.IP "\(bu" 4
\fBgem\.push_key\fR (\fBBUNDLE_GEM__PUSH_KEY\fR): Sets the \fB\-\-key\fR parameter for \fBgem push\fR when using the \fBrake release\fR command with a private gemstash server\.
.
.IP "\(bu" 4
@@ -211,10 +208,10 @@ The following is a list of all configuration keys and their purpose\. You can le
\fBignore_messages\fR (\fBBUNDLE_IGNORE_MESSAGES\fR): When set, no post install messages will be printed\. To silence a single gem, use dot notation like \fBignore_messages\.httparty true\fR\.
.
.IP "\(bu" 4
-\fBinit_gems_rb\fR (\fBBUNDLE_INIT_GEMS_RB\fR) Generate a \fBgems\.rb\fR instead of a \fBGemfile\fR when running \fBbundle init\fR\.
+\fBinit_gems_rb\fR (\fBBUNDLE_INIT_GEMS_RB\fR): Generate a \fBgems\.rb\fR instead of a \fBGemfile\fR when running \fBbundle init\fR\.
.
.IP "\(bu" 4
-\fBjobs\fR (\fBBUNDLE_JOBS\fR): The number of gems Bundler can install in parallel\. Defaults to 1\.
+\fBjobs\fR (\fBBUNDLE_JOBS\fR): The number of gems Bundler can install in parallel\. Defaults to 1 on Windows, and to the the number of processors on other platforms\.
.
.IP "\(bu" 4
\fBno_install\fR (\fBBUNDLE_NO_INSTALL\fR): Whether \fBbundle package\fR should skip installing gems\.
@@ -223,9 +220,6 @@ The following is a list of all configuration keys and their purpose\. You can le
\fBno_prune\fR (\fBBUNDLE_NO_PRUNE\fR): Whether Bundler should leave outdated gems unpruned when caching\.
.
.IP "\(bu" 4
-\fBonly_update_to_newer_versions\fR (\fBBUNDLE_ONLY_UPDATE_TO_NEWER_VERSIONS\fR): During \fBbundle update\fR, only resolve to newer versions of the gems in the lockfile\.
-.
-.IP "\(bu" 4
\fBpath\fR (\fBBUNDLE_PATH\fR): The location on disk where all gems in your bundle will be located regardless of \fB$GEM_HOME\fR or \fB$GEM_PATH\fR values\. Bundle gems not found in this location will be installed by \fBbundle install\fR\. Defaults to \fBGem\.dir\fR\. When \-\-deployment is used, defaults to vendor/bundle\.
.
.IP "\(bu" 4
@@ -241,7 +235,7 @@ The following is a list of all configuration keys and their purpose\. You can le
\fBprefer_patch\fR (BUNDLE_PREFER_PATCH): Prefer updating only to next patch version during updates\. Makes \fBbundle update\fR calls equivalent to \fBbundler update \-\-patch\fR\.
.
.IP "\(bu" 4
-\fBprint_only_version_number\fR (\fBBUNDLE_PRINT_ONLY_VERSION_NUMBER\fR) Print only version number from \fBbundler \-\-version\fR\.
+\fBprint_only_version_number\fR (\fBBUNDLE_PRINT_ONLY_VERSION_NUMBER\fR): Print only version number from \fBbundler \-\-version\fR\.
.
.IP "\(bu" 4
\fBredirect\fR (\fBBUNDLE_REDIRECT\fR): The number of redirects allowed for network requests\. Defaults to \fB5\fR\.
@@ -280,10 +274,7 @@ The following is a list of all configuration keys and their purpose\. You can le
\fBtimeout\fR (\fBBUNDLE_TIMEOUT\fR): The seconds allowed before timing out for network requests\. Defaults to \fB10\fR\.
.
.IP "\(bu" 4
-\fBunlock_source_unlocks_spec\fR (\fBBUNDLE_UNLOCK_SOURCE_UNLOCKS_SPEC\fR): Whether running \fBbundle update \-\-source NAME\fR unlocks a gem with the given name\. Defaults to \fBtrue\fR\.
-.
-.IP "\(bu" 4
-\fBupdate_requires_all_flag\fR (\fBBUNDLE_UPDATE_REQUIRES_ALL_FLAG\fR) Require passing \fB\-\-all\fR to \fBbundle update\fR when everything should be updated, and disallow passing no options to \fBbundle update\fR\.
+\fBupdate_requires_all_flag\fR (\fBBUNDLE_UPDATE_REQUIRES_ALL_FLAG\fR): Require passing \fB\-\-all\fR to \fBbundle update\fR when everything should be updated, and disallow passing no options to \fBbundle update\fR\.
.
.IP "\(bu" 4
\fBuser_agent\fR (\fBBUNDLE_USER_AGENT\fR): The custom user agent fragment Bundler includes in API requests\.
@@ -470,6 +461,23 @@ export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x\-oauth\-basic
.
.IP "" 0
.
+.P
+Note that any configured credentials will be redacted by informative commands such as \fBbundle config list\fR or \fBbundle config get\fR, unless you use the \fB\-\-parseable\fR flag\. This is to avoid unintentionally leaking credentials when copy\-pasting bundler output\.
+.
+.P
+Also note that to guarantee a sane mapping between valid environment variable names and valid host names, bundler makes the following transformations:
+.
+.IP "\(bu" 4
+Any \fB\-\fR characters in a host name are mapped to a triple dash (\fB___\fR) in the corresponding environment variable\.
+.
+.IP "\(bu" 4
+Any \fB\.\fR characters in a host name are mapped to a double dash (\fB__\fR) in the corresponding environment variable\.
+.
+.IP "" 0
+.
+.P
+This means that if you have a gem server named \fBmy\.gem\-host\.com\fR, you\'ll need to use the \fBBUNDLE_MY__GEM___HOST__COM\fR variable to configure credentials for it through ENV\.
+.
.SH "CONFIGURE BUNDLER DIRECTORIES"
Bundler\'s home, config, cache and plugin directories are able to be configured through environment variables\. The default location for Bundler\'s home directory is \fB~/\.bundle\fR, which all directories inherit from by default\. The following outlines the available environment variables and their default values
.
diff --git a/lib/bundler/man/bundle-config.1.ronn b/lib/bundler/man/bundle-config.1.ronn
index b3c4e59a78..f412ee3aec 100644
--- a/lib/bundler/man/bundle-config.1.ronn
+++ b/lib/bundler/man/bundle-config.1.ronn
@@ -47,10 +47,6 @@ configuration only from the local application.
Executing bundle with the `BUNDLE_IGNORE_CONFIG` environment variable set will
cause it to ignore all configuration.
-Executing `bundle config set --local disable_multisource true` upgrades the warning about
-the Gemfile containing multiple primary sources to an error. Executing `bundle
-config unset disable_multisource` downgrades this error to a warning.
-
## REMEMBERING OPTIONS
Flags passed to `bundle install` or the Bundler runtime, such as `--path foo` or
@@ -178,10 +174,6 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
* `disable_local_revision_check` (`BUNDLE_DISABLE_LOCAL_REVISION_CHECK`):
Allow Bundler to use a local git override without checking if the revision
present in the lockfile is present in the repository.
-* `disable_multisource` (`BUNDLE_DISABLE_MULTISOURCE`):
- When set, Gemfiles containing multiple sources will produce errors
- instead of warnings.
- Use `bundle config unset disable_multisource` to unset.
* `disable_shared_gems` (`BUNDLE_DISABLE_SHARED_GEMS`):
Stop Bundler from accessing gems installed to RubyGems' normal location.
* `disable_version_check` (`BUNDLE_DISABLE_VERSION_CHECK`):
@@ -194,6 +186,10 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
Disallow changes to the `Gemfile`. When the `Gemfile` is changed and the
lockfile has not been updated, running Bundler commands will be blocked.
Defaults to `true` when `--deployment` is used.
+* `gem.github_username` (`BUNDLE_GEM__GITHUB_USERNAME`):
+ Sets a GitHub username or organization to be used in `README` file when you
+ create a new gem via `bundle gem` command. It can be overridden by passing an
+ explicit `--github-username` flag to `bundle gem`.
* `gem.push_key` (`BUNDLE_GEM__PUSH_KEY`):
Sets the `--key` parameter for `gem push` when using the `rake release`
command with a private gemstash server.
@@ -206,20 +202,18 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
* `global_gem_cache` (`BUNDLE_GLOBAL_GEM_CACHE`):
Whether Bundler should cache all gems globally, rather than locally to the
installing Ruby installation.
-* `ignore_messages` (`BUNDLE_IGNORE_MESSAGES`): When set, no post install
- messages will be printed. To silence a single gem, use dot notation like
- `ignore_messages.httparty true`.
-* `init_gems_rb` (`BUNDLE_INIT_GEMS_RB`)
+* `ignore_messages` (`BUNDLE_IGNORE_MESSAGES`):
+ When set, no post install messages will be printed. To silence a single gem,
+ use dot notation like `ignore_messages.httparty true`.
+* `init_gems_rb` (`BUNDLE_INIT_GEMS_RB`):
Generate a `gems.rb` instead of a `Gemfile` when running `bundle init`.
* `jobs` (`BUNDLE_JOBS`):
- The number of gems Bundler can install in parallel. Defaults to 1.
+ The number of gems Bundler can install in parallel. Defaults to 1 on Windows,
+ and to the the number of processors on other platforms.
* `no_install` (`BUNDLE_NO_INSTALL`):
Whether `bundle package` should skip installing gems.
* `no_prune` (`BUNDLE_NO_PRUNE`):
Whether Bundler should leave outdated gems unpruned when caching.
-* `only_update_to_newer_versions` (`BUNDLE_ONLY_UPDATE_TO_NEWER_VERSIONS`):
- During `bundle update`, only resolve to newer versions of the gems in the
- lockfile.
* `path` (`BUNDLE_PATH`):
The location on disk where all gems in your bundle will be located regardless
of `$GEM_HOME` or `$GEM_PATH` values. Bundle gems not found in this location
@@ -233,7 +227,7 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
Enable Bundler's experimental plugin system.
* `prefer_patch` (BUNDLE_PREFER_PATCH):
Prefer updating only to next patch version during updates. Makes `bundle update` calls equivalent to `bundler update --patch`.
-* `print_only_version_number` (`BUNDLE_PRINT_ONLY_VERSION_NUMBER`)
+* `print_only_version_number` (`BUNDLE_PRINT_ONLY_VERSION_NUMBER`):
Print only version number from `bundler --version`.
* `redirect` (`BUNDLE_REDIRECT`):
The number of redirects allowed for network requests. Defaults to `5`.
@@ -266,10 +260,7 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
The location where RubyGems installs binstubs. Defaults to `Gem.bindir`.
* `timeout` (`BUNDLE_TIMEOUT`):
The seconds allowed before timing out for network requests. Defaults to `10`.
-* `unlock_source_unlocks_spec` (`BUNDLE_UNLOCK_SOURCE_UNLOCKS_SPEC`):
- Whether running `bundle update --source NAME` unlocks a gem with the given
- name. Defaults to `true`.
-* `update_requires_all_flag` (`BUNDLE_UPDATE_REQUIRES_ALL_FLAG`)
+* `update_requires_all_flag` (`BUNDLE_UPDATE_REQUIRES_ALL_FLAG`):
Require passing `--all` to `bundle update` when everything should be updated,
and disallow passing no options to `bundle update`.
* `user_agent` (`BUNDLE_USER_AGENT`):
@@ -374,6 +365,23 @@ where you can use personal OAuth tokens:
export BUNDLE_GITHUB__COM=abcd0123generatedtoken:x-oauth-basic
+Note that any configured credentials will be redacted by informative commands
+such as `bundle config list` or `bundle config get`, unless you use the
+`--parseable` flag. This is to avoid unintentionally leaking credentials when
+copy-pasting bundler output.
+
+Also note that to guarantee a sane mapping between valid environment variable
+names and valid host names, bundler makes the following transformations:
+
+* Any `-` characters in a host name are mapped to a triple dash (`___`) in the
+ corresponding environment variable.
+
+* Any `.` characters in a host name are mapped to a double dash (`__`) in the
+ corresponding environment variable.
+
+This means that if you have a gem server named `my.gem-host.com`, you'll need to
+use the `BUNDLE_MY__GEM___HOST__COM` variable to configure credentials for it
+through ENV.
## CONFIGURE BUNDLER DIRECTORIES
diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1
index 2923517608..87a7fe5f2f 100644
--- a/lib/bundler/man/bundle-doctor.1
+++ b/lib/bundler/man/bundle-doctor.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-DOCTOR" "1" "January 2021" "" ""
+.TH "BUNDLE\-DOCTOR" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-doctor\fR \- Checks the bundle for common problems
diff --git a/lib/bundler/man/bundle-exec.1 b/lib/bundler/man/bundle-exec.1
index aa0c509278..69adfa7c92 100644
--- a/lib/bundler/man/bundle-exec.1
+++ b/lib/bundler/man/bundle-exec.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-EXEC" "1" "January 2021" "" ""
+.TH "BUNDLE\-EXEC" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-exec\fR \- Execute a command in the context of the bundle
diff --git a/lib/bundler/man/bundle-gem.1 b/lib/bundler/man/bundle-gem.1
index c1abf90f5c..fae5c34e7e 100644
--- a/lib/bundler/man/bundle-gem.1
+++ b/lib/bundler/man/bundle-gem.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-GEM" "1" "January 2021" "" ""
+.TH "BUNDLE\-GEM" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem
@@ -90,6 +90,19 @@ When Bundler is configured to not generate CI files, an interactive prompt will
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
.TP
+\fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR
+Specify the linter and code formatter that Bundler should add to the project\'s development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
+.
+.IP
+When Bundler is configured to add a linter, this defaults to Bundler\'s global config setting \fBgem\.linter\fR\.
+.
+.IP
+When Bundler is configured not to add a linter, an interactive prompt will be displayed and the answer will be used for the current rubygem project\.
+.
+.IP
+When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
+.
+.TP
\fB\-e\fR, \fB\-\-edit[=EDITOR]\fR
Open the resulting GEM_NAME\.gemspec in EDITOR, or the default editor if not specified\. The default is \fB$BUNDLER_EDITOR\fR, \fB$VISUAL\fR, or \fB$EDITOR\fR\.
.
diff --git a/lib/bundler/man/bundle-gem.1.ronn b/lib/bundler/man/bundle-gem.1.ronn
index a997cb907a..61c741fb24 100644
--- a/lib/bundler/man/bundle-gem.1.ronn
+++ b/lib/bundler/man/bundle-gem.1.ronn
@@ -92,6 +92,22 @@ configuration file using the following names:
the answer will be saved in Bundler's global config for future `bundle gem`
use.
+* `--linter`, `--linter=rubocop`, `--linter=standard`:
+ Specify the linter and code formatter that Bundler should add to the
+ project's development dependencies. Acceptable values are `rubocop` and
+ `standard`. A configuration file will be generated in the project directory.
+ Given no option is specified:
+
+ When Bundler is configured to add a linter, this defaults to Bundler's
+ global config setting `gem.linter`.
+
+ When Bundler is configured not to add a linter, an interactive prompt
+ will be displayed and the answer will be used for the current rubygem project.
+
+ When Bundler is unconfigured, an interactive prompt will be displayed and
+ the answer will be saved in Bundler's global config for future `bundle gem`
+ use.
+
* `-e`, `--edit[=EDITOR]`:
Open the resulting GEM_NAME.gemspec in EDITOR, or the default editor if not
specified. The default is `$BUNDLER_EDITOR`, `$VISUAL`, or `$EDITOR`.
diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1
index c715af4063..9e1400ec56 100644
--- a/lib/bundler/man/bundle-info.1
+++ b/lib/bundler/man/bundle-info.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INFO" "1" "January 2021" "" ""
+.TH "BUNDLE\-INFO" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-info\fR \- Show information for the given gem in your bundle
diff --git a/lib/bundler/man/bundle-init.1 b/lib/bundler/man/bundle-init.1
index 6c8f441bdd..612d16031c 100644
--- a/lib/bundler/man/bundle-init.1
+++ b/lib/bundler/man/bundle-init.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INIT" "1" "January 2021" "" ""
+.TH "BUNDLE\-INIT" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-init\fR \- Generates a Gemfile into the current working directory
diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1
index 2342b44716..ded4d6d64b 100644
--- a/lib/bundler/man/bundle-inject.1
+++ b/lib/bundler/man/bundle-inject.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INJECT" "1" "January 2021" "" ""
+.TH "BUNDLE\-INJECT" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile
diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1
index c223185b6f..6ccf100b4e 100644
--- a/lib/bundler/man/bundle-install.1
+++ b/lib/bundler/man/bundle-install.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-INSTALL" "1" "January 2021" "" ""
+.TH "BUNDLE\-INSTALL" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-install\fR \- Install the dependencies specified in your Gemfile
diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1
index 929b0f79f8..a697173af9 100644
--- a/lib/bundler/man/bundle-list.1
+++ b/lib/bundler/man/bundle-list.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-LIST" "1" "January 2021" "" ""
+.TH "BUNDLE\-LIST" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-list\fR \- List all the gems in the bundle
diff --git a/lib/bundler/man/bundle-lock.1 b/lib/bundler/man/bundle-lock.1
index bcf588a3cf..ef515b0337 100644
--- a/lib/bundler/man/bundle-lock.1
+++ b/lib/bundler/man/bundle-lock.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-LOCK" "1" "January 2021" "" ""
+.TH "BUNDLE\-LOCK" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-lock\fR \- Creates / Updates a lockfile without installing
diff --git a/lib/bundler/man/bundle-open.1 b/lib/bundler/man/bundle-open.1
index 27308ff624..dd28566bdb 100644
--- a/lib/bundler/man/bundle-open.1
+++ b/lib/bundler/man/bundle-open.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-OPEN" "1" "January 2021" "" ""
+.TH "BUNDLE\-OPEN" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-open\fR \- Opens the source directory for a gem in your bundle
diff --git a/lib/bundler/man/bundle-outdated.1 b/lib/bundler/man/bundle-outdated.1
index 40b23568d5..b9d50a1c71 100644
--- a/lib/bundler/man/bundle-outdated.1
+++ b/lib/bundler/man/bundle-outdated.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-OUTDATED" "1" "January 2021" "" ""
+.TH "BUNDLE\-OUTDATED" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-outdated\fR \- List installed gems with newer versions available
diff --git a/lib/bundler/man/bundle-platform.1 b/lib/bundler/man/bundle-platform.1
index d0ec5c643c..b1c859f64b 100644
--- a/lib/bundler/man/bundle-platform.1
+++ b/lib/bundler/man/bundle-platform.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-PLATFORM" "1" "January 2021" "" ""
+.TH "BUNDLE\-PLATFORM" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-platform\fR \- Displays platform compatibility information
diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1
index 9fd42d04a4..6e4a028666 100644
--- a/lib/bundler/man/bundle-pristine.1
+++ b/lib/bundler/man/bundle-pristine.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-PRISTINE" "1" "January 2021" "" ""
+.TH "BUNDLE\-PRISTINE" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-pristine\fR \- Restores installed gems to their pristine condition
diff --git a/lib/bundler/man/bundle-remove.1 b/lib/bundler/man/bundle-remove.1
index 366e5f4be2..0b4edd1414 100644
--- a/lib/bundler/man/bundle-remove.1
+++ b/lib/bundler/man/bundle-remove.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-REMOVE" "1" "January 2021" "" ""
+.TH "BUNDLE\-REMOVE" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-remove\fR \- Removes gems from the Gemfile
diff --git a/lib/bundler/man/bundle-show.1 b/lib/bundler/man/bundle-show.1
index e2de241438..375699ddf0 100644
--- a/lib/bundler/man/bundle-show.1
+++ b/lib/bundler/man/bundle-show.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-SHOW" "1" "January 2021" "" ""
+.TH "BUNDLE\-SHOW" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem
diff --git a/lib/bundler/man/bundle-update.1 b/lib/bundler/man/bundle-update.1
index a78633bbf8..c08bc66ef0 100644
--- a/lib/bundler/man/bundle-update.1
+++ b/lib/bundler/man/bundle-update.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-UPDATE" "1" "January 2021" "" ""
+.TH "BUNDLE\-UPDATE" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-update\fR \- Update your gems to the latest available versions
@@ -79,7 +79,7 @@ Do not allow any gem to be updated past latest \fB\-\-patch\fR | \fB\-\-minor\fR
.
.TP
\fB\-\-conservative\fR
-Use bundle install conservative update behavior and do not allow shared dependencies to be updated\.
+Use bundle install conservative update behavior and do not allow indirect dependencies to be updated\.
.
.SH "UPDATING ALL GEMS"
If you run \fBbundle update \-\-all\fR, bundler will ignore any previously installed gems and resolve all dependencies again based on the latest versions of all gems available in the sources\.
@@ -208,13 +208,13 @@ In this case, the two gems have their own set of dependencies, but they share \f
In short, by default, when you update a gem using \fBbundle update\fR, bundler will update all dependencies of that gem, including those that are also dependencies of another gem\.
.
.P
-To prevent updating shared dependencies, prior to version 1\.14 the only option was the \fBCONSERVATIVE UPDATING\fR behavior in bundle install(1) \fIbundle\-install\.1\.html\fR:
+To prevent updating indirect dependencies, prior to version 1\.14 the only option was the \fBCONSERVATIVE UPDATING\fR behavior in bundle install(1) \fIbundle\-install\.1\.html\fR:
.
.P
In this scenario, updating the \fBthin\fR version manually in the Gemfile(5), and then running bundle install(1) \fIbundle\-install\.1\.html\fR will only update \fBdaemons\fR and \fBeventmachine\fR, but not \fBrack\fR\. For more information, see the \fBCONSERVATIVE UPDATING\fR section of bundle install(1) \fIbundle\-install\.1\.html\fR\.
.
.P
-Starting with 1\.14, specifying the \fB\-\-conservative\fR option will also prevent shared dependencies from being updated\.
+Starting with 1\.14, specifying the \fB\-\-conservative\fR option will also prevent indirect dependencies from being updated\.
.
.SH "PATCH LEVEL OPTIONS"
Version 1\.14 introduced 4 patch\-level options that will influence how gem versions are resolved\. One of the following options can be used: \fB\-\-patch\fR, \fB\-\-minor\fR or \fB\-\-major\fR\. \fB\-\-strict\fR can be added to further influence resolution\.
diff --git a/lib/bundler/man/bundle-update.1.ronn b/lib/bundler/man/bundle-update.1.ronn
index 397fecadcb..3a16f29149 100644
--- a/lib/bundler/man/bundle-update.1.ronn
+++ b/lib/bundler/man/bundle-update.1.ronn
@@ -80,7 +80,7 @@ gem.
Do not allow any gem to be updated past latest `--patch` | `--minor` | `--major`.
* `--conservative`:
- Use bundle install conservative update behavior and do not allow shared dependencies to be updated.
+ Use bundle install conservative update behavior and do not allow indirect dependencies to be updated.
## UPDATING ALL GEMS
@@ -195,7 +195,7 @@ In short, by default, when you update a gem using `bundle update`, bundler will
update all dependencies of that gem, including those that are also dependencies
of another gem.
-To prevent updating shared dependencies, prior to version 1.14 the only option
+To prevent updating indirect dependencies, prior to version 1.14 the only option
was the `CONSERVATIVE UPDATING` behavior in [bundle install(1)](bundle-install.1.html):
In this scenario, updating the `thin` version manually in the Gemfile(5),
@@ -203,7 +203,7 @@ and then running [bundle install(1)](bundle-install.1.html) will only update `da
but not `rack`. For more information, see the `CONSERVATIVE UPDATING` section
of [bundle install(1)](bundle-install.1.html).
-Starting with 1.14, specifying the `--conservative` option will also prevent shared
+Starting with 1.14, specifying the `--conservative` option will also prevent indirect
dependencies from being updated.
## PATCH LEVEL OPTIONS
diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1
index fead09e6ad..f792aa6346 100644
--- a/lib/bundler/man/bundle-viz.1
+++ b/lib/bundler/man/bundle-viz.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE\-VIZ" "1" "January 2021" "" ""
+.TH "BUNDLE\-VIZ" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile
diff --git a/lib/bundler/man/bundle.1 b/lib/bundler/man/bundle.1
index 50aa6e17c7..b1458bf57b 100644
--- a/lib/bundler/man/bundle.1
+++ b/lib/bundler/man/bundle.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "BUNDLE" "1" "January 2021" "" ""
+.TH "BUNDLE" "1" "December 2021" "" ""
.
.SH "NAME"
\fBbundle\fR \- Ruby Dependency Management
diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5
index 3942806f75..2e423522b7 100644
--- a/lib/bundler/man/gemfile.5
+++ b/lib/bundler/man/gemfile.5
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "GEMFILE" "5" "January 2021" "" ""
+.TH "GEMFILE" "5" "December 2021" "" ""
.
.SH "NAME"
\fBGemfile\fR \- A format for describing gem dependencies for Ruby programs
@@ -506,6 +506,32 @@ gem "rails", :git => "git://github\.com/rails/rails\.git"
.P
Since the \fBgithub\fR method is a specialization of \fBgit_source\fR, it accepts a \fB:branch\fR named argument\.
.
+.P
+You can also directly pass a pull request URL:
+.
+.IP "" 4
+.
+.nf
+
+gem "rails", :github => "https://github\.com/rails/rails/pull/43753"
+.
+.fi
+.
+.IP "" 0
+.
+.P
+Which is equivalent to:
+.
+.IP "" 4
+.
+.nf
+
+gem "rails", :github => "rails/rails", branch: "refs/pull/43753/head"
+.
+.fi
+.
+.IP "" 0
+.
.SS "GIST"
If the git repository you want to use is hosted as a Github Gist and is public, you can use the :gist shorthand to specify the gist identifier (without the trailing "\.git")\.
.
diff --git a/lib/bundler/man/gemfile.5.ronn b/lib/bundler/man/gemfile.5.ronn
index 994f0d66bd..20ce3242ae 100644
--- a/lib/bundler/man/gemfile.5.ronn
+++ b/lib/bundler/man/gemfile.5.ronn
@@ -372,6 +372,14 @@ Are both equivalent to
Since the `github` method is a specialization of `git_source`, it accepts a `:branch` named argument.
+You can also directly pass a pull request URL:
+
+ gem "rails", :github => "https://github.com/rails/rails/pull/43753"
+
+Which is equivalent to:
+
+ gem "rails", :github => "rails/rails", branch: "refs/pull/43753/head"
+
### GIST
If the git repository you want to use is hosted as a Github Gist and is public, you can use
diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb
index da3f468da5..158c69e1a1 100644
--- a/lib/bundler/plugin.rb
+++ b/lib/bundler/plugin.rb
@@ -13,6 +13,7 @@ module Bundler
class MalformattedPlugin < PluginError; end
class UndefinedCommandError < PluginError; end
class UnknownSourceError < PluginError; end
+ class PluginInstallError < PluginError; end
PLUGIN_FILE_NAME = "plugins.rb".freeze
@@ -38,12 +39,11 @@ module Bundler
specs = Installer.new.install(names, options)
save_plugins names, specs
- rescue PluginError => e
+ rescue PluginError
specs_to_delete = specs.select {|k, _v| names.include?(k) && !index.commands.values.include?(k) }
specs_to_delete.each_value {|spec| Bundler.rm_rf(spec.full_gem_path) }
- names_list = names.map {|name| "`#{name}`" }.join(", ")
- Bundler.ui.error "Failed to install the following plugins: #{names_list}. The underlying error was: #{e.message}.\n #{e.backtrace.join("\n ")}"
+ raise
end
# Uninstalls plugins by the given names
@@ -105,6 +105,7 @@ module Bundler
else
builder.eval_gemfile(gemfile)
end
+ builder.check_primary_source_safety
definition = builder.to_definition(nil, true)
return if definition.dependencies.empty?
@@ -163,7 +164,7 @@ module Bundler
end
# To be called from Cli class to pass the command and argument to
- # approriate plugin class
+ # appropriate plugin class
def exec_command(command, args)
raise UndefinedCommandError, "Command `#{command}` not found" unless command? command
@@ -182,7 +183,7 @@ module Bundler
!index.source_plugin(name.to_s).nil?
end
- # @return [Class] that handles the source. The calss includes API::Source
+ # @return [Class] that handles the source. The class includes API::Source
def source(name)
raise UnknownSourceError, "Source #{name} not found" unless source? name
@@ -244,10 +245,11 @@ module Bundler
# @param [Array<String>] names of inferred source plugins that can be ignored
def save_plugins(plugins, specs, optional_plugins = [])
plugins.each do |name|
+ next if index.installed?(name)
+
spec = specs[name]
- validate_plugin! Pathname.new(spec.full_gem_path)
- installed = register_plugin(name, spec, optional_plugins.include?(name))
- Bundler.ui.info "Installed plugin #{name}" if installed
+
+ save_plugin(name, spec, optional_plugins.include?(name))
end
end
@@ -262,6 +264,22 @@ module Bundler
raise MalformattedPlugin, "#{PLUGIN_FILE_NAME} was not found in the plugin." unless plugin_file.file?
end
+ # Validates and registers a plugin.
+ #
+ # @param [String] name the name of the plugin
+ # @param [Specification] spec of installed plugin
+ # @param [Boolean] optional_plugin, removed if there is conflict with any
+ # other plugin (used for default source plugins)
+ #
+ # @raise [PluginInstallError] if validation or registration raises any error
+ def save_plugin(name, spec, optional_plugin = false)
+ validate_plugin! Pathname.new(spec.full_gem_path)
+ installed = register_plugin(name, spec, optional_plugin)
+ Bundler.ui.info "Installed plugin #{name}" if installed
+ rescue PluginError => e
+ raise PluginInstallError, "Failed to install plugin `#{spec.name}`, due to #{e.class} (#{e.message})"
+ end
+
# Runs the plugins.rb file in an isolated namespace, records the plugin
# actions it registers for and then passes the data to index to be stored.
#
@@ -308,6 +326,8 @@ module Bundler
#
# @param [String] name of the plugin
def load_plugin(name)
+ return unless name && !name.empty?
+
# Need to ensure before this that plugin root where the rest of gems
# are installed to be on load path to support plugin deps. Currently not
# done to avoid conflicts
diff --git a/lib/bundler/plugin/api/source.rb b/lib/bundler/plugin/api/source.rb
index e1f0826874..32b1d0ee38 100644
--- a/lib/bundler/plugin/api/source.rb
+++ b/lib/bundler/plugin/api/source.rb
@@ -140,6 +140,13 @@ module Bundler
end
end
+ # Set internal representation to fetch the gems/specs locally.
+ #
+ # When this is called, the source should try to fetch the specs and
+ # install from the local system.
+ def local!
+ end
+
# Set internal representation to fetch the gems/specs from remote.
#
# When this is called, the source should try to fetch the specs and
@@ -237,6 +244,20 @@ module Bundler
specs.unmet_dependency_names
end
+ # Used by definition.
+ #
+ # Note: Do not override if you don't know what you are doing.
+ def spec_names
+ specs.spec_names
+ end
+
+ # Used by definition.
+ #
+ # Note: Do not override if you don't know what you are doing.
+ def add_dependency_names(names)
+ @dependencies |= Array(names)
+ end
+
# Note: Do not override if you don't know what you are doing.
def can_lock?(spec)
spec.source == self
@@ -262,6 +283,7 @@ module Bundler
def to_s
"plugin source for #{@type} with uri #{@uri}"
end
+ alias_method :identifier, :to_s
# Note: Do not override if you don't know what you are doing.
def include?(other)
diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb
index 819bc60588..29d33be718 100644
--- a/lib/bundler/plugin/index.rb
+++ b/lib/bundler/plugin/index.rb
@@ -74,7 +74,10 @@ module Bundler
def unregister_plugin(name)
@commands.delete_if {|_, v| v == name }
@sources.delete_if {|_, v| v == name }
- @hooks.each {|_, plugin_names| plugin_names.delete(name) }
+ @hooks.each do |hook, names|
+ names.delete(name)
+ @hooks.delete(hook) if names.empty?
+ end
@plugin_paths.delete(name)
@load_paths.delete(name)
save_index
diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb
index 26cec4f18c..d7411fff45 100644
--- a/lib/bundler/plugin/installer.rb
+++ b/lib/bundler/plugin/installer.rb
@@ -16,15 +16,13 @@ module Bundler
version = options[:version] || [">= 0"]
- Bundler.settings.temporary(:disable_multisource => false) do
- if options[:git]
- install_git(names, version, options)
- elsif options[:local_git]
- install_local_git(names, version, options)
- else
- sources = options[:source] || Bundler.rubygems.sources
- install_rubygems(names, version, sources)
- end
+ if options[:git]
+ install_git(names, version, options)
+ elsif options[:local_git]
+ install_local_git(names, version, options)
+ else
+ sources = options[:source] || Bundler.rubygems.sources
+ install_rubygems(names, version, sources)
end
end
@@ -79,10 +77,12 @@ module Bundler
source_list = SourceList.new
source_list.add_git_source(git_source_options) if git_source_options
- source_list.add_rubygems_source("remotes" => rubygems_source) if rubygems_source
+ Array(rubygems_source).each {|remote| source_list.add_global_rubygems_remote(remote) } if rubygems_source
deps = names.map {|name| Dependency.new name, version }
+ Bundler.configure_gem_home_and_path(Plugin.root)
+
definition = Definition.new(nil, deps, source_list, true)
install_definition(definition)
end
diff --git a/lib/bundler/plugin/source_list.rb b/lib/bundler/plugin/source_list.rb
index b90a331d28..547661cf2f 100644
--- a/lib/bundler/plugin/source_list.rb
+++ b/lib/bundler/plugin/source_list.rb
@@ -17,6 +17,10 @@ module Bundler
path_sources + git_sources + rubygems_sources + [metadata_source]
end
+ def default_source
+ git_sources.first || global_rubygems_source
+ end
+
private
def rubygems_aggregate_class
diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb
index 0f17ba3afb..5eb17a3921 100644
--- a/lib/bundler/resolver.rb
+++ b/lib/bundler/resolver.rb
@@ -17,15 +17,14 @@ module Bundler
# ==== Returns
# <GemBundle>,nil:: If the list of dependencies can be resolved, a
# collection of gemspecs is returned. Otherwise, nil is returned.
- def self.resolve(requirements, index, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil)
+ def self.resolve(requirements, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil)
base = SpecSet.new(base) unless base.is_a?(SpecSet)
- resolver = new(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms)
+ resolver = new(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms)
result = resolver.start(requirements)
- SpecSet.new(result)
+ SpecSet.new(SpecSet.new(result).for(requirements.reject{|dep| dep.name.end_with?("\0") }))
end
- def initialize(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms)
- @index = index
+ def initialize(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms)
@source_requirements = source_requirements
@base = base
@resolver = Molinillo::Resolver.new(self, self)
@@ -36,14 +35,10 @@ module Bundler
@base_dg.add_vertex(ls.name, DepProxy.get_proxy(dep, ls.platform), true)
end
additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) }
- @platforms = platforms
+ @platforms = platforms.reject {|p| p != Gem::Platform::RUBY && (platforms - [p]).any? {|pl| generic(pl) == p } }
@resolving_only_for_ruby = platforms == [Gem::Platform::RUBY]
@gem_version_promoter = gem_version_promoter
@use_gvp = Bundler.feature_flag.use_gem_version_promoter_for_major_updates? || !@gem_version_promoter.major?
- @lockfile_uses_separate_rubygems_sources = Bundler.feature_flag.disable_multisource?
-
- @variant_specific_names = []
- @generic_names = []
end
def start(requirements)
@@ -53,7 +48,6 @@ module Bundler
verify_gemfile_dependencies_are_found!(requirements)
dg = @resolver.resolve(requirements, @base_dg)
dg.
- tap {|resolved| validate_resolved_specs!(resolved) }.
map(&:payload).
reject {|sg| sg.name.end_with?("\0") }.
map(&:to_specs).
@@ -107,26 +101,15 @@ module Bundler
include Molinillo::SpecificationProvider
def dependencies_for(specification)
- all_dependencies = specification.dependencies_for_activated_platforms
-
- if @variant_specific_names.include?(specification.name)
- @variant_specific_names |= all_dependencies.map(&:name) - @generic_names
- else
- generic_names, variant_specific_names = specification.partitioned_dependency_names_for_activated_platforms
- @variant_specific_names |= variant_specific_names - @generic_names
- @generic_names |= generic_names
- end
-
- all_dependencies
+ specification.dependencies_for_activated_platforms
end
def search_for(dependency_proxy)
platform = dependency_proxy.__platform
dependency = dependency_proxy.dep
name = dependency.name
- search_result = @search_for[dependency_proxy] ||= begin
- index = index_for(dependency)
- results = index.search(dependency, @base[name])
+ @search_for[dependency_proxy] ||= begin
+ results = results_for(dependency, @base[name])
if vertex = @base_dg.vertex_named(name)
locked_requirement = vertex.payload.requirement
@@ -177,39 +160,18 @@ module Bundler
@gem_version_promoter.sort_versions(dependency, spec_groups)
end
end
+ end
- unless search_result.empty?
- specific_dependency = @variant_specific_names.include?(name)
- return search_result unless specific_dependency
-
- search_result.each do |sg|
- if @generic_names.include?(name)
- @variant_specific_names -= [name]
- sg.activate_all_platforms!
- else
- sg.activate_platform!(platform)
- end
- end
- end
+ def index_for(dependency)
+ source_for(dependency.name).specs
+ end
- search_result
+ def source_for(name)
+ @source_requirements[name] || @source_requirements[:default]
end
- def index_for(dependency)
- source = @source_requirements[dependency.name]
- if source
- source.specs
- elsif @lockfile_uses_separate_rubygems_sources
- Index.build do |idx|
- if dependency.all_sources
- dependency.all_sources.each {|s| idx.add_source(s.specs) if s }
- else
- idx.add_source @source_requirements[:default].specs
- end
- end
- else
- @index
- end
+ def results_for(dependency, base)
+ index_for(dependency).search(dependency, base)
end
def name_for(dependency)
@@ -236,19 +198,8 @@ module Bundler
dependencies.map(&:dep) == other_dependencies.map(&:dep)
end
- def relevant_sources_for_vertex(vertex)
- if vertex.root?
- [@source_requirements[vertex.name]]
- elsif @lockfile_uses_separate_rubygems_sources
- vertex.recursive_predecessors.map do |v|
- @source_requirements[v.name]
- end << @source_requirements[:default]
- end
- end
-
def sort_dependencies(dependencies, activated, conflicts)
dependencies.sort_by do |dependency|
- dependency.all_sources = relevant_sources_for_vertex(activated.vertex_named(dependency.name))
name = name_for(dependency)
vertex = activated.vertex_named(name)
[
@@ -302,12 +253,6 @@ module Bundler
next if name == "bundler"
next unless search_for(requirement).empty?
- cache_message = begin
- " or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist?
- rescue GemfileNotFound
- nil
- end
-
if (base = @base[name]) && !base.empty?
version = base.first.version
message = "You have requested:\n" \
@@ -316,18 +261,17 @@ module Bundler
"Try running `bundle update #{name}`\n\n" \
"If you are updating multiple gems in your Gemfile at once,\n" \
"try passing them all to `bundle update`"
- elsif source = @source_requirements[name]
- specs = source.specs[name]
+ else
+ source = source_for(name)
+ specs = source.specs.search(name)
versions_with_platforms = specs.map {|s| [s.version, s.platform] }
+ cache_message = begin
+ " or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist?
+ rescue GemfileNotFound
+ nil
+ end
message = String.new("Could not find gem '#{SharedHelpers.pretty_dependency(requirement)}' in #{source}#{cache_message}.\n")
- message << if versions_with_platforms.any?
- "The source contains the following versions of '#{name}': #{formatted_versions_with_platforms(versions_with_platforms)}"
- else
- "The source does not contain any versions of '#{name}'"
- end
- else
- message = "Could not find gem '#{requirement}' in any of the gem sources " \
- "listed in your Gemfile#{cache_message}."
+ message << "The source contains the following versions of '#{name}': #{formatted_versions_with_platforms(versions_with_platforms)}" if versions_with_platforms.any?
end
raise GemNotFound, message
end
@@ -392,7 +336,7 @@ module Bundler
if other_bundler_required
o << "\n\n"
- candidate_specs = @source_requirements[:default_bundler].specs.search(conflict_dependency)
+ candidate_specs = source_for(:default_bundler).specs.search(conflict_dependency)
if candidate_specs.any?
target_version = candidate_specs.last.version
new_command = [File.basename($PROGRAM_NAME), "_#{target_version}_", *ARGV].join(" ")
@@ -409,17 +353,7 @@ module Bundler
elsif !conflict.existing
o << "\n"
- relevant_sources = if conflict.requirement.source
- [conflict.requirement.source]
- elsif conflict.requirement.all_sources
- conflict.requirement.all_sources
- elsif @lockfile_uses_separate_rubygems_sources
- # every conflict should have an explicit group of sources when we
- # enforce strict pinning
- raise "no source set for #{conflict}"
- else
- []
- end.compact.map(&:to_s).uniq.sort
+ relevant_source = conflict.requirement.source || source_for(name)
metadata_requirement = name.end_with?("\0")
@@ -432,12 +366,10 @@ module Bundler
end
o << " "
- o << if relevant_sources.empty?
- "in any of the sources.\n"
- elsif metadata_requirement
- "is not available in #{relevant_sources.join(" or ")}"
+ o << if metadata_requirement
+ "is not available in #{relevant_source}"
else
- "in any of the relevant sources:\n #{relevant_sources * "\n "}\n"
+ "in #{relevant_source}.\n"
end
end
end,
@@ -451,29 +383,5 @@ module Bundler
end
)
end
-
- def validate_resolved_specs!(resolved_specs)
- resolved_specs.each do |v|
- name = v.name
- next unless sources = relevant_sources_for_vertex(v)
- sources.compact!
- if default_index = sources.index(@source_requirements[:default])
- sources.delete_at(default_index)
- end
- sources.reject! {|s| s.specs[name].empty? }
- sources.uniq!
- next if sources.size <= 1
-
- multisource_disabled = Bundler.feature_flag.disable_multisource?
-
- msg = ["The gem '#{name}' was found in multiple relevant sources."]
- msg.concat sources.map {|s| " * #{s}" }.sort
- msg << "You #{multisource_disabled ? :must : :should} add this gem to the source block for the source you wish it to be installed from."
- msg = msg.join("\n")
-
- raise SecurityError, msg if multisource_disabled
- Bundler.ui.warn "Warning: #{msg}"
- end
- end
end
end
diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb
index 73ffec5838..8f4fd18c46 100644
--- a/lib/bundler/resolver/spec_group.rb
+++ b/lib/bundler/resolver/spec_group.rb
@@ -21,14 +21,10 @@ module Bundler
@version = exemplary_spec.version
@source = exemplary_spec.source
- @all_platforms = relevant_platforms
@activated_platforms = relevant_platforms
@dependencies = Hash.new do |dependencies, platforms|
dependencies[platforms] = dependencies_for(platforms)
end
- @partitioned_dependency_names = Hash.new do |partitioned_dependency_names, platforms|
- partitioned_dependency_names[platforms] = partitioned_dependency_names_for(platforms)
- end
@specs = specs
end
@@ -45,14 +41,6 @@ module Bundler
end.flatten.compact.uniq
end
- def activate_platform!(platform)
- self.activated_platforms = [platform]
- end
-
- def activate_all_platforms!
- self.activated_platforms = @all_platforms
- end
-
def to_s
activated_platforms_string = sorted_activated_platforms.join(", ")
"#{name} (#{version}) (#{activated_platforms_string})"
@@ -62,10 +50,6 @@ module Bundler
@dependencies[activated_platforms]
end
- def partitioned_dependency_names_for_activated_platforms
- @partitioned_dependency_names[activated_platforms]
- end
-
def ==(other)
return unless other.is_a?(SpecGroup)
name == other.name &&
@@ -100,14 +84,6 @@ module Bundler
end.flatten
end
- def partitioned_dependency_names_for(platforms)
- return @dependencies[platforms].map(&:name), [] if platforms.size == 1
-
- @dependencies[platforms].partition do |dep_proxy|
- @dependencies[platforms].count {|dp| dp.dep == dep_proxy.dep } == platforms.size
- end.map {|deps| deps.map(&:name) }
- end
-
def __dependencies(platform)
dependencies = []
@specs[platform].first.dependencies.each do |dep|
diff --git a/lib/bundler/retry.rb b/lib/bundler/retry.rb
index e95f15f7fb..2415ade200 100644
--- a/lib/bundler/retry.rb
+++ b/lib/bundler/retry.rb
@@ -49,7 +49,7 @@ module Bundler
raise e
end
return true unless name
- Bundler.ui.info "" unless Bundler.ui.debug? # Add new line incase dots preceded this
+ Bundler.ui.info "" unless Bundler.ui.debug? # Add new line in case dots preceded this
Bundler.ui.warn "Retrying #{name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}", Bundler.ui.debug?
end
diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb
index 2bd2dcb451..a3efff1e86 100644
--- a/lib/bundler/rubygems_ext.rb
+++ b/lib/bundler/rubygems_ext.rb
@@ -85,6 +85,10 @@ module Gem
dependencies - development_dependencies
end
+ def deleted_gem?
+ !default_gem? && !File.directory?(full_gem_path)
+ end
+
private
def dependencies_to_gemfile(dependencies, group = nil)
@@ -105,7 +109,7 @@ module Gem
end
class Dependency
- attr_accessor :source, :groups, :all_sources
+ attr_accessor :source, :groups
alias_method :eql?, :==
@@ -116,7 +120,7 @@ module Gem
end
def to_yaml_properties
- instance_variables.reject {|p| ["@source", "@groups", "@all_sources"].include?(p.to_s) }
+ instance_variables.reject {|p| ["@source", "@groups"].include?(p.to_s) }
end
def to_lock
@@ -174,20 +178,36 @@ module Gem
end
end
+ require "rubygems/platform"
+
class Platform
JAVA = Gem::Platform.new("java") unless defined?(JAVA)
MSWIN = Gem::Platform.new("mswin32") unless defined?(MSWIN)
MSWIN64 = Gem::Platform.new("mswin64") unless defined?(MSWIN64)
MINGW = Gem::Platform.new("x86-mingw32") unless defined?(MINGW)
X64_MINGW = Gem::Platform.new("x64-mingw32") unless defined?(X64_MINGW)
+ end
- undef_method :hash if method_defined? :hash
- def hash
- @cpu.hash ^ @os.hash ^ @version.hash
- end
+ Platform.singleton_class.module_eval do
+ unless Platform.singleton_methods.include?(:match_spec?)
+ def match_spec?(spec)
+ match_gem?(spec.platform, spec.name)
+ end
- undef_method :eql? if method_defined? :eql?
- alias_method :eql?, :==
+ def match_gem?(platform, gem_name)
+ match_platforms?(platform, Gem.platforms)
+ end
+
+ private
+
+ def match_platforms?(platform, platforms)
+ platforms.any? do |local_platform|
+ platform.nil? ||
+ local_platform == platform ||
+ (local_platform != Gem::Platform::RUBY && local_platform =~ platform)
+ end
+ end
+ end
end
require "rubygems/util"
diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb
index cd5eb152f9..bb9f1cb3f5 100644
--- a/lib/bundler/rubygems_gem_installer.rb
+++ b/lib/bundler/rubygems_gem_installer.rb
@@ -8,13 +8,68 @@ module Bundler
# Bundler needs to install gems regardless of binstub overwriting
end
+ def install
+ pre_install_checks
+
+ run_pre_install_hooks
+
+ spec.loaded_from = spec_file
+
+ # Completely remove any previous gem files
+ strict_rm_rf gem_dir
+ strict_rm_rf spec.extension_dir
+
+ SharedHelpers.filesystem_access(gem_dir, :create) do
+ FileUtils.mkdir_p gem_dir, :mode => 0o755
+ end
+
+ extract_files
+
+ build_extensions
+ write_build_info_file
+ run_post_build_hooks
+
+ generate_bin
+ generate_plugins
+
+ write_spec
+
+ SharedHelpers.filesystem_access("#{gem_home}/cache", :write) do
+ write_cache_file
+ end
+
+ say spec.post_install_message unless spec.post_install_message.nil?
+
+ run_post_install_hooks
+
+ spec
+ end
+
+ def generate_plugins
+ return unless Gem::Installer.instance_methods(false).include?(:generate_plugins)
+
+ latest = Gem::Specification.stubs_for(spec.name).first
+ return if latest && latest.version > spec.version
+
+ ensure_writable_dir @plugins_dir
+
+ if spec.plugins.empty?
+ remove_plugins_for(spec, @plugins_dir)
+ else
+ regenerate_plugins_for(spec, @plugins_dir)
+ end
+ end
+
def pre_install_checks
super && validate_bundler_checksum(options[:bundler_expected_checksum])
end
def build_extensions
extension_cache_path = options[:bundler_extension_cache_path]
- return super unless extension_cache_path && extension_dir = spec.extension_dir
+ unless extension_cache_path && extension_dir = spec.extension_dir
+ require "shellwords" # compensate missing require in rubygems before version 3.2.25
+ return super
+ end
extension_dir = Pathname.new(extension_dir)
build_complete = SharedHelpers.filesystem_access(extension_cache_path.join("gem.build_complete"), :read, &:file?)
@@ -24,6 +79,7 @@ module Bundler
FileUtils.cp_r extension_cache_path, spec.extension_dir
end
else
+ require "shellwords" # compensate missing require in rubygems before version 3.2.25
super
if extension_dir.directory? # not made for gems without extensions
SharedHelpers.filesystem_access(extension_cache_path.parent, &:mkpath)
@@ -36,6 +92,17 @@ module Bundler
private
+ def strict_rm_rf(dir)
+ # FileUtils.rm_rf should probably rise in case of permission issues like
+ # `rm -rf` does. However, it fails to delete the folder silently due to
+ # https://github.com/ruby/fileutils/issues/57. It should probably be fixed
+ # inside `fileutils` but for now I`m checking whether the folder was
+ # removed after it completes, and raising otherwise.
+ FileUtils.rm_rf dir
+
+ raise PermissionError.new(dir, :delete) if File.directory?(dir)
+ end
+
def validate_bundler_checksum(checksum)
return true if Bundler.settings[:disable_checksum_validation]
return true unless checksum
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb
index d060e21f50..f6d59baf15 100644
--- a/lib/bundler/rubygems_integration.rb
+++ b/lib/bundler/rubygems_integration.rb
@@ -34,10 +34,12 @@ module Bundler
end
def build_args
+ require "rubygems/command"
Gem::Command.build_args
end
def build_args=(args)
+ require "rubygems/command"
Gem::Command.build_args = args
end
@@ -84,16 +86,12 @@ module Bundler
def spec_missing_extensions?(spec, default = true)
return spec.missing_extensions? if spec.respond_to?(:missing_extensions?)
- return false if spec_default_gem?(spec)
+ return false if spec.default_gem?
return false if spec.extensions.empty?
default
end
- def spec_default_gem?(spec)
- spec.respond_to?(:default_gem?) && spec.default_gem?
- end
-
def spec_matches_for_glob(spec, glob)
return spec.matches_for_glob(glob) if spec.respond_to?(:matches_for_glob)
@@ -502,14 +500,15 @@ module Bundler
end
def fetch_specs(remote, name)
+ require "rubygems/remote_fetcher"
path = remote.uri.to_s + "#{name}.#{Gem.marshal_version}.gz"
fetcher = gem_remote_fetcher
fetcher.headers = { "X-Gemfile-Source" => remote.original_uri.to_s } if remote.original_uri
string = fetcher.fetch_path(path)
Bundler.load_marshal(string)
- rescue Gem::RemoteFetcher::FetchError => e
+ rescue Gem::RemoteFetcher::FetchError
# it's okay for prerelease to fail
- raise e unless name == "prerelease_specs"
+ raise unless name == "prerelease_specs"
end
def fetch_all_remote_specs(remote)
@@ -519,20 +518,41 @@ module Bundler
specs.concat(pres)
end
- def download_gem(spec, uri, path)
+ def download_gem(spec, uri, cache_dir)
+ require "rubygems/remote_fetcher"
uri = Bundler.settings.mirror_for(uri)
fetcher = gem_remote_fetcher
fetcher.headers = { "X-Gemfile-Source" => spec.remote.original_uri.to_s } if spec.remote.original_uri
Bundler::Retry.new("download gem from #{uri}").attempts do
- fetcher.download(spec, uri, path)
+ gem_file_name = spec.file_name
+ local_gem_path = File.join cache_dir, gem_file_name
+ return if File.exist? local_gem_path
+
+ begin
+ remote_gem_path = uri + "gems/#{gem_file_name}"
+ remote_gem_path = remote_gem_path.to_s if provides?("< 3.2.0.rc.1")
+
+ SharedHelpers.filesystem_access(local_gem_path) do
+ fetcher.cache_update_path remote_gem_path, local_gem_path
+ end
+ rescue Gem::RemoteFetcher::FetchError
+ raise if spec.original_platform == spec.platform
+
+ original_gem_file_name = "#{spec.original_name}.gem"
+ raise if gem_file_name == original_gem_file_name
+
+ gem_file_name = original_gem_file_name
+ retry
+ end
end
+ rescue Gem::RemoteFetcher::FetchError => e
+ raise Bundler::HTTPError, "Could not download gem from #{uri} due to underlying error <#{e.message}>"
end
def gem_remote_fetcher
- require "resolv"
+ require "rubygems/remote_fetcher"
proxy = configuration[:http_proxy]
- dns = Resolv::DNS.new
- Gem::RemoteFetcher.new(proxy, dns)
+ Gem::RemoteFetcher.new(proxy)
end
def gem_from_path(path, policy = nil)
diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb
index e259b590bf..c7276b0e25 100644
--- a/lib/bundler/runtime.rb
+++ b/lib/bundler/runtime.rb
@@ -12,22 +12,16 @@ module Bundler
def setup(*groups)
@definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle?
- groups.map!(&:to_sym)
-
# Has to happen first
clean_load_path
- specs = groups.any? ? @definition.specs_for(groups) : requested_specs
+ specs = @definition.specs_for(groups)
SharedHelpers.set_bundle_environment
Bundler.rubygems.replace_entrypoints(specs)
# Activate the specs
load_paths = specs.map do |spec|
- unless spec.loaded_from
- raise GemNotFound, "#{spec.full_name} is missing. Run `bundle install` to get it."
- end
-
check_for_activated_spec!(spec)
Bundler.rubygems.mark_loaded(spec)
@@ -106,7 +100,7 @@ module Bundler
alias_method :gems, :specs
- def cache(custom_path = nil)
+ def cache(custom_path = nil, local = false)
cache_path = Bundler.app_cache(custom_path)
SharedHelpers.filesystem_access(cache_path) do |p|
FileUtils.mkdir_p(p)
@@ -114,7 +108,20 @@ module Bundler
Bundler.ui.info "Updating files in #{Bundler.settings.app_cache_path}"
- specs_to_cache = Bundler.settings[:cache_all_platforms] ? @definition.resolve.materialized_for_all_platforms : specs
+ specs_to_cache = if Bundler.settings[:cache_all_platforms]
+ @definition.resolve.materialized_for_all_platforms
+ else
+ begin
+ specs
+ rescue GemNotFound
+ if local
+ Bundler.ui.warn "Some gems seem to be missing from your #{Bundler.settings.app_cache_path} directory."
+ end
+
+ raise
+ end
+ end
+
specs_to_cache.each do |spec|
next if spec.name == "bundler"
next if spec.source.is_a?(Source::Gemspec)
@@ -258,7 +265,7 @@ module Bundler
return if manuals.empty?
Bundler::SharedHelpers.set_env "MANPATH", manuals.concat(
- ENV["MANPATH"].to_s.split(File::PATH_SEPARATOR)
+ ENV["MANPATH"] ? ENV["MANPATH"].to_s.split(File::PATH_SEPARATOR) : [""]
).uniq.join(File::PATH_SEPARATOR)
end
@@ -284,7 +291,7 @@ module Bundler
return unless activated_spec = Bundler.rubygems.loaded_specs(spec.name)
return if activated_spec.version == spec.version
- suggestion = if Bundler.rubygems.spec_default_gem?(activated_spec)
+ suggestion = if activated_spec.default_gem?
"Since #{spec.name} is a default gem, you can either remove your dependency on it" \
" or try updating to a newer version of bundler that supports #{spec.name} as a default gem."
else
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
index 749e4eb60e..1ced590b6f 100644
--- a/lib/bundler/settings.rb
+++ b/lib/bundler/settings.rb
@@ -13,27 +13,28 @@ module Bundler
auto_install
cache_all
cache_all_platforms
+ clean
default_install_uses_path
deployment
- deployment_means_frozen
disable_checksum_validation
disable_exec_load
disable_local_branch_check
disable_local_revision_check
- disable_multisource
disable_shared_gems
disable_version_check
force_ruby_platform
forget_cli_options
frozen
+ gem.changelog
gem.coc
gem.mit
+ git.allow_insecure
global_gem_cache
ignore_messages
init_gems_rb
+ inline
no_install
no_prune
- only_update_to_newer_versions
path_relative_to_cwd
path.system
plugins
@@ -43,7 +44,6 @@ module Bundler
silence_deprecations
silence_root_warning
suppress_install_using_messages
- unlock_source_unlocks_spec
update_requires_all_flag
use_gem_version_promoter_for_major_updates
].freeze
@@ -61,6 +61,22 @@ module Bundler
without
].freeze
+ STRING_KEYS = %w[
+ bin
+ cache_path
+ console
+ gem.ci
+ gem.github_username
+ gem.linter
+ gem.rubocop
+ gem.test
+ gemfile
+ path
+ shebang
+ system_bindir
+ trust-policy
+ ].freeze
+
DEFAULT_CONFIG = {
"BUNDLE_SILENCE_DEPRECATIONS" => false,
"BUNDLE_DISABLE_VERSION_CHECK" => true,
@@ -126,8 +142,8 @@ module Bundler
keys = @temporary.keys | @global_config.keys | @local_config.keys | @env_config.keys
keys.map do |key|
- key.sub(/^BUNDLE_/, "").gsub(/__/, ".").downcase
- end
+ key.sub(/^BUNDLE_/, "").gsub(/___/, "-").gsub(/__/, ".").downcase
+ end.sort
end
def local_overrides
@@ -173,25 +189,32 @@ module Bundler
locations = []
if value = @temporary[key]
- locations << "Set for the current command: #{converted_value(value, exposed_key).inspect}"
+ locations << "Set for the current command: #{printable_value(value, exposed_key).inspect}"
end
if value = @local_config[key]
- locations << "Set for your local app (#{local_config_file}): #{converted_value(value, exposed_key).inspect}"
+ locations << "Set for your local app (#{local_config_file}): #{printable_value(value, exposed_key).inspect}"
end
if value = @env_config[key]
- locations << "Set via #{key}: #{converted_value(value, exposed_key).inspect}"
+ locations << "Set via #{key}: #{printable_value(value, exposed_key).inspect}"
end
if value = @global_config[key]
- locations << "Set for the current user (#{global_config_file}): #{converted_value(value, exposed_key).inspect}"
+ locations << "Set for the current user (#{global_config_file}): #{printable_value(value, exposed_key).inspect}"
end
return ["You have not configured a value for `#{exposed_key}`"] if locations.empty?
locations
end
+ def processor_count
+ require "etc"
+ Etc.nprocessors
+ rescue StandardError
+ 1
+ end
+
# for legacy reasons, in Bundler 2, we do not respect :disable_shared_gems
def path
configs.each do |_level, settings|
@@ -277,9 +300,7 @@ module Bundler
end
def key_for(key)
- key = Settings.normalize_uri(key).to_s if key.is_a?(String) && /https?:/ =~ key
- key = key.to_s.gsub(".", "__").upcase
- "BUNDLE_#{key}"
+ self.class.key_for(key)
end
private
@@ -314,6 +335,10 @@ module Bundler
BOOL_KEYS.include?(name.to_s) || BOOL_KEYS.include?(parent_setting_for(name.to_s))
end
+ def is_string(name)
+ STRING_KEYS.include?(name.to_s) || name.to_s.start_with?("local.") || name.to_s.start_with?("mirror.") || name.to_s.start_with?("build.")
+ end
+
def to_bool(value)
case value
when nil, /\A(false|f|no|n|0|)\z/i, false
@@ -331,6 +356,14 @@ module Bundler
ARRAY_KEYS.include?(key.to_s)
end
+ def is_credential(key)
+ key == "gem.push_key"
+ end
+
+ def is_userinfo(value)
+ value.include?(":")
+ end
+
def to_array(value)
return [] unless value
value.split(":").map(&:to_sym)
@@ -377,15 +410,38 @@ module Bundler
end
end
+ def printable_value(value, key)
+ converted = converted_value(value, key)
+ return converted unless converted.is_a?(String)
+
+ if is_string(key)
+ converted
+ elsif is_credential(key)
+ "[REDACTED]"
+ elsif is_userinfo(converted)
+ username, pass = converted.split(":", 2)
+
+ if pass == "x-oauth-basic"
+ username = "[REDACTED]"
+ else
+ pass = "[REDACTED]"
+ end
+
+ [username, pass].join(":")
+ else
+ converted
+ end
+ end
+
def global_config_file
if ENV["BUNDLE_CONFIG"] && !ENV["BUNDLE_CONFIG"].empty?
Pathname.new(ENV["BUNDLE_CONFIG"])
- else
- begin
- Bundler.user_bundle_path("config")
- rescue PermissionError, GenericSystemCallError
- nil
- end
+ elsif ENV["BUNDLE_USER_CONFIG"] && !ENV["BUNDLE_USER_CONFIG"].empty?
+ Pathname.new(ENV["BUNDLE_USER_CONFIG"])
+ elsif ENV["BUNDLE_USER_HOME"] && !ENV["BUNDLE_USER_HOME"].empty?
+ Pathname.new(ENV["BUNDLE_USER_HOME"]).join("config")
+ elsif Bundler.rubygems.user_home && !Bundler.rubygems.user_home.empty?
+ Pathname.new(Bundler.rubygems.user_home).join(".bundle/config")
end
end
@@ -399,7 +455,20 @@ module Bundler
valid_file = file.exist? && !file.size.zero?
return {} unless valid_file
require_relative "yaml_serializer"
- YAMLSerializer.load file.read
+ YAMLSerializer.load(file.read).inject({}) do |config, (k, v)|
+ new_k = k
+
+ if k.include?("-")
+ Bundler.ui.warn "Your #{file} config includes `#{k}`, which contains the dash character (`-`).\n" \
+ "This is deprecated, because configuration through `ENV` should be possible, but `ENV` keys cannot include dashes.\n" \
+ "Please edit #{file} and replace any dashes in configuration keys with a triple underscore (`___`)."
+
+ new_k = k.gsub("-", "___")
+ end
+
+ config[new_k] = v
+ config
+ end
end
end
@@ -416,6 +485,12 @@ module Bundler
\z
/ix.freeze
+ def self.key_for(key)
+ key = normalize_uri(key).to_s if key.is_a?(String) && /https?:/ =~ key
+ key = key.to_s.gsub(".", "__").gsub("-", "___").upcase
+ "BUNDLE_#{key}"
+ end
+
# TODO: duplicates Rubygems#normalize_uri
# TODO: is this the correct place to validate mirror URIs?
def self.normalize_uri(uri)
diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb
index 27911dc1ad..32e9b2d7c0 100644
--- a/lib/bundler/setup.rb
+++ b/lib/bundler/setup.rb
@@ -9,10 +9,10 @@ if Bundler::SharedHelpers.in_bundle?
begin
Bundler.ui.silence { Bundler.setup }
rescue Bundler::BundlerError => e
- Bundler.ui.warn "\e[31m#{e.message}\e[0m"
+ Bundler.ui.error e.message
Bundler.ui.warn e.backtrace.join("\n") if ENV["DEBUG"]
if e.is_a?(Bundler::GemNotFound)
- Bundler.ui.warn "\e[33mRun `bundle install` to install missing gems.\e[0m"
+ Bundler.ui.warn "Run `bundle install` to install missing gems."
end
exit e.status_code
end
diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb
index 09b79acbf9..df1c136c56 100644
--- a/lib/bundler/shared_helpers.rb
+++ b/lib/bundler/shared_helpers.rb
@@ -152,13 +152,6 @@ module Bundler
Bundler.ui.warn message
end
- def trap(signal, override = false, &block)
- prior = Signal.trap(signal) do
- block.call
- prior.call unless override
- end
- end
-
def ensure_same_dependencies(spec, old_deps, new_deps)
new_deps = new_deps.reject {|d| d.type == :development }
old_deps = old_deps.reject {|d| d.type == :development }
@@ -327,12 +320,11 @@ module Bundler
end
def clean_load_path
- bundler_lib = bundler_ruby_lib
-
loaded_gem_paths = Bundler.rubygems.loaded_gem_paths
$LOAD_PATH.reject! do |p|
- next if resolve_path(p).start_with?(bundler_lib)
+ resolved_path = resolve_path(p)
+ next if $LOADED_FEATURES.any? {|lf| lf.start_with?(resolved_path) }
loaded_gem_paths.delete(p)
end
$LOAD_PATH.uniq!
diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb
index be00143f5a..2a2b332cff 100644
--- a/lib/bundler/source.rb
+++ b/lib/bundler/source.rb
@@ -7,6 +7,7 @@ module Bundler
autoload :Metadata, File.expand_path("source/metadata", __dir__)
autoload :Path, File.expand_path("source/path", __dir__)
autoload :Rubygems, File.expand_path("source/rubygems", __dir__)
+ autoload :RubygemsAggregate, File.expand_path("source/rubygems_aggregate", __dir__)
attr_accessor :dependency_names
@@ -33,6 +34,18 @@ module Bundler
spec.source == self
end
+ def local!; end
+
+ def local_only!; end
+
+ def cached!; end
+
+ def remote!; end
+
+ def add_dependency_names(names)
+ @dependency_names = Array(dependency_names) | Array(names)
+ end
+
# it's possible that gems from one source depend on gems from some
# other source, so now we download gemspecs and iterate over those
# dependencies, looking for gems we don't have info on yet.
@@ -42,6 +55,10 @@ module Bundler
specs.dependency_names
end
+ def spec_names
+ specs.spec_names
+ end
+
def include?(other)
other == self
end
@@ -50,6 +67,10 @@ module Bundler
"#<#{self.class}:0x#{object_id} #{self}>"
end
+ def identifier
+ to_s
+ end
+
def path?
instance_of?(Bundler::Source::Path)
end
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index fb13ca0578..a41a2f23e9 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -42,7 +42,7 @@ module Bundler
%w[ref branch tag submodules].each do |opt|
out << " #{opt}: #{options[opt]}\n" if options[opt]
end
- out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
+ out << " glob: #{@glob}\n" unless default_glob?
out << " specs:\n"
end
@@ -75,12 +75,20 @@ module Bundler
git_proxy.branch
end
- rev = " (at #{at}@#{shortref_for_display(revision)})"
+ rev = "at #{at}@#{shortref_for_display(revision)}"
rescue GitError
""
end
- "#{@safe_uri}#{rev}"
+ specifiers = [rev, glob_for_display].compact
+ suffix =
+ if specifiers.any?
+ " (#{specifiers.join(", ")})"
+ else
+ ""
+ end
+
+ "#{@safe_uri}#{suffix}"
end
def name
@@ -282,6 +290,14 @@ module Bundler
ref[0..11]
end
+ def glob_for_display
+ default_glob? ? nil : "glob: #{@glob}"
+ end
+
+ def default_glob?
+ @glob == DEFAULT_GLOB
+ end
+
def uri_hash
if uri =~ %r{^\w+://(\w+@)?}
# Downcase the domain component of the URI
@@ -291,7 +307,9 @@ module Bundler
# If there is no URI scheme, assume it is an ssh/git URI
input = uri
end
- SharedHelpers.digest(:SHA1).hexdigest(input)
+ # We use SHA1 here for historical reason and to preserve backward compatibility.
+ # But a transition to a simpler mangling algorithm would be welcome.
+ Bundler::Digest.sha1(input)
end
def cached_revision
diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb
index ae21770306..745a7fe118 100644
--- a/lib/bundler/source/git/git_proxy.rb
+++ b/lib/bundler/source/git/git_proxy.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "shellwords"
-
module Bundler
class Source
class Git
@@ -58,7 +56,6 @@ module Bundler
@ref = ref
@revision = revision
@git = git
- raise GitNotInstalledError.new if allow? && !Bundler.git_present?
end
def revision
@@ -98,12 +95,12 @@ module Bundler
SharedHelpers.filesystem_access(path.dirname) do |p|
FileUtils.mkdir_p(p)
end
- git_retry "clone", configured_uri, path.to_s, "--bare", "--no-hardlinks", "--quiet"
+ git_retry "clone", "--bare", "--no-hardlinks", "--quiet", "--", configured_uri, path.to_s
return unless extra_ref
end
with_path do
- git_retry(*["fetch", "--force", "--quiet", "--tags", configured_uri, "refs/heads/*:refs/heads/*", extra_ref].compact, :dir => path)
+ git_retry(*["fetch", "--force", "--quiet", "--tags", "--", configured_uri, "refs/heads/*:refs/heads/*", extra_ref].compact, :dir => path)
end
end
@@ -210,7 +207,11 @@ module Bundler
end
def allow?
- @git ? @git.allow_git_ops? : true
+ allowed = @git ? @git.allow_git_ops? : true
+
+ raise GitNotInstalledError.new if allowed && !Bundler.git_present?
+
+ allowed
end
def with_path(&blk)
@@ -224,6 +225,7 @@ module Bundler
end
def check_allowed(command)
+ require "shellwords"
command_with_no_credentials = URICredentialsFilter.credential_filtered_string("git #{command.shelljoin}", uri)
raise GitNotAllowedError.new(command_with_no_credentials) unless allow?
command_with_no_credentials
diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb
index 0867879861..50b65ce0ea 100644
--- a/lib/bundler/source/metadata.rb
+++ b/lib/bundler/source/metadata.rb
@@ -33,10 +33,6 @@ module Bundler
end
end
- def cached!; end
-
- def remote!; end
-
def options
{}
end
diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb
index 2c2e9023b3..01f89b204d 100644
--- a/lib/bundler/source/path.rb
+++ b/lib/bundler/source/path.rb
@@ -82,7 +82,9 @@ module Bundler
end
def install(spec, options = {})
- print_using_message "Using #{version_message(spec)} from #{self}"
+ using_message = "Using #{version_message(spec)} from #{self}"
+ using_message += " and installing its executables" unless spec.executables.empty?
+ print_using_message using_message
generate_bin(spec, :disable_extensions => true)
nil # no post-install message
end
diff --git a/lib/bundler/source/path/installer.rb b/lib/bundler/source/path/installer.rb
index 72bfbb4836..a70973bde7 100644
--- a/lib/bundler/source/path/installer.rb
+++ b/lib/bundler/source/path/installer.rb
@@ -35,7 +35,7 @@ module Bundler
run_hooks(:post_build)
end
- generate_bin unless spec.executables.nil? || spec.executables.empty?
+ generate_bin unless spec.executables.empty?
run_hooks(:post_install)
ensure
diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb
index 5b89b1645d..8bc3aa17e9 100644
--- a/lib/bundler/source/rubygems.rb
+++ b/lib/bundler/source/rubygems.rb
@@ -20,18 +20,38 @@ module Bundler
@dependency_names = []
@allow_remote = false
@allow_cached = false
+ @allow_local = options["allow_local"] || false
@caches = [cache_path, *Bundler.rubygems.gem_cache]
- Array(options["remotes"] || []).reverse_each {|r| add_remote(r) }
+ Array(options["remotes"]).reverse_each {|r| add_remote(r) }
+ end
+
+ def local_only!
+ @specs = nil
+ @allow_local = true
+ @allow_cached = false
+ @allow_remote = false
+ end
+
+ def local!
+ return if @allow_local
+
+ @specs = nil
+ @allow_local = true
end
def remote!
+ return if @allow_remote
+
@specs = nil
@allow_remote = true
end
def cached!
+ return if @allow_cached
+
@specs = nil
+ @allow_local = true
@allow_cached = true
end
@@ -49,9 +69,17 @@ module Bundler
o.is_a?(Rubygems) && (o.credless_remotes - credless_remotes).empty?
end
+ def multiple_remotes?
+ @remotes.size > 1
+ end
+
+ def no_remotes?
+ @remotes.size == 0
+ end
+
def can_lock?(spec)
- return super if Bundler.feature_flag.disable_multisource?
- spec.source.is_a?(Rubygems)
+ return super unless multiple_remotes?
+ include?(spec.source)
end
def options
@@ -73,12 +101,27 @@ module Bundler
def to_s
if remotes.empty?
"locally installed gems"
- else
- remote_names = remotes.map(&:to_s).join(", ")
+ elsif @allow_remote && @allow_cached && @allow_local
+ "rubygems repository #{remote_names}, cached gems or installed locally"
+ elsif @allow_remote && @allow_local
"rubygems repository #{remote_names} or installed locally"
+ elsif @allow_remote
+ "rubygems repository #{remote_names}"
+ elsif @allow_cached && @allow_local
+ "cached gems or installed locally"
+ else
+ "locally installed gems"
end
end
- alias_method :name, :to_s
+
+ def identifier
+ if remotes.empty?
+ "locally installed gems"
+ else
+ "rubygems repository #{remote_names}"
+ end
+ end
+ alias_method :name, :identifier
def specs
@specs ||= begin
@@ -87,7 +130,7 @@ module Bundler
# small_idx.use large_idx.
idx = @allow_remote ? remote_specs.dup : Index.new
idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
- idx.use(installed_specs, :override_dupes)
+ idx.use(installed_specs, :override_dupes) if @allow_local
idx
end
end
@@ -96,7 +139,7 @@ module Bundler
force = opts[:force]
ensure_builtin_gems_cached = opts[:ensure_builtin_gems_cached]
- if ensure_builtin_gems_cached && builtin_gem?(spec)
+ if ensure_builtin_gems_cached && spec.default_gem?
if !cached_path(spec)
cached_built_in_gem(spec) unless spec.remote
force = true
@@ -105,7 +148,7 @@ module Bundler
end
end
- if (installed?(spec) || Plugin.installed?(spec.name)) && !force
+ if installed?(spec) && !force
print_using_message "Using #{version_message(spec)}"
return nil # no post-install message
end
@@ -123,7 +166,7 @@ module Bundler
begin
s = Bundler.rubygems.spec_from_gem(path, Bundler.settings["trust-policy"])
spec.__swap__(s)
- rescue StandardError
+ rescue Gem::Package::FormatError
Bundler.rm_rf(path)
raise
end
@@ -135,6 +178,7 @@ module Bundler
Bundler.ui.confirm message
path = cached_gem(spec)
+ raise GemNotFound, "Could not find #{spec.file_name} for installation" unless path
if requires_sudo?
install_path = Bundler.tmp(spec.full_name)
bin_path = install_path.join("bin")
@@ -194,12 +238,8 @@ module Bundler
end
def cache(spec, custom_path = nil)
- if builtin_gem?(spec)
- cached_path = cached_built_in_gem(spec)
- else
- cached_path = cached_gem(spec)
- end
- raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
+ cached_path = cached_gem(spec)
+ raise GemNotFound, "Missing gem file '#{spec.file_name}'." unless cached_path
return if File.dirname(cached_path) == Bundler.app_cache.to_s
Bundler.ui.info " * #{File.basename(cached_path)}"
FileUtils.cp(cached_path, Bundler.app_cache(custom_path))
@@ -226,25 +266,16 @@ module Bundler
@remotes.unshift(uri) unless @remotes.include?(uri)
end
- def equivalent_remotes?(other_remotes)
- other_remotes.map(&method(:remove_auth)) == @remotes.map(&method(:remove_auth))
- end
-
- def replace_remotes(other_remotes, allow_equivalent = false)
- return false if other_remotes == @remotes
-
- equivalent = allow_equivalent && equivalent_remotes?(other_remotes)
-
- @remotes = []
- other_remotes.reverse_each do |r|
- add_remote r.to_s
+ def spec_names
+ if @allow_remote && dependency_api_available?
+ remote_specs.spec_names
+ else
+ []
end
-
- !equivalent
end
def unmet_deps
- if @allow_remote && api_fetchers.any?
+ if @allow_remote && dependency_api_available?
remote_specs.unmet_dependency_names
else
[]
@@ -260,7 +291,7 @@ module Bundler
def double_check_for(unmet_dependency_names)
return unless @allow_remote
- return unless api_fetchers.any?
+ return unless dependency_api_available?
unmet_dependency_names = unmet_dependency_names.call
unless unmet_dependency_names.nil?
@@ -282,21 +313,32 @@ module Bundler
remote_specs.each do |spec|
case spec
when EndpointSpecification, Gem::Specification, StubSpecification, LazySpecification
- names.concat(spec.runtime_dependencies)
+ names.concat(spec.runtime_dependencies.map(&:name))
when RemoteSpecification # from the full index
return nil
else
raise "unhandled spec type (#{spec.inspect})"
end
end
- names.map!(&:name) if names
names
end
+ def dependency_api_available?
+ api_fetchers.any?
+ end
+
protected
+ def remote_names
+ remotes.map(&:to_s).join(", ")
+ end
+
def credless_remotes
- remotes.map(&method(:suppress_configured_credentials))
+ if Bundler.settings[:allow_deployment_source_credential_changes]
+ remotes.map(&method(:remove_auth))
+ else
+ remotes.map(&method(:suppress_configured_credentials))
+ end
end
def remotes_for_spec(spec)
@@ -311,14 +353,17 @@ module Bundler
end
def cached_gem(spec)
- cached_gem = cached_path(spec)
- unless cached_gem
- raise Bundler::GemNotFound, "Could not find #{spec.file_name} for installation"
+ if spec.default_gem?
+ cached_built_in_gem(spec)
+ else
+ cached_path(spec)
end
- cached_gem
end
def cached_path(spec)
+ global_cache_path = download_cache_path(spec)
+ @caches << global_cache_path if global_cache_path
+
possibilities = @caches.map {|p| "#{p}/#{spec.file_name}" }
possibilities.find {|p| File.exist?(p) }
end
@@ -365,16 +410,12 @@ module Bundler
def cached_specs
@cached_specs ||= begin
- idx = installed_specs.dup
+ idx = @allow_local ? installed_specs.dup : Index.new
Dir["#{cache_path}/*.gem"].each do |gemfile|
next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
s ||= Bundler.rubygems.spec_from_gem(gemfile)
s.source = self
- if Bundler.rubygems.spec_missing_extensions?(s, false)
- Bundler.ui.debug "Source #{self} is ignoring #{s} because it is missing extensions"
- next
- end
idx << s
end
@@ -407,11 +448,11 @@ module Bundler
def fetch_names(fetchers, dependency_names, index, override_dupes)
fetchers.each do |f|
if dependency_names
- Bundler.ui.info "Fetching gem metadata from #{f.uri}", Bundler.ui.debug?
+ Bundler.ui.info "Fetching gem metadata from #{URICredentialsFilter.credential_filtered_uri(f.uri)}", Bundler.ui.debug?
index.use f.specs_with_retry(dependency_names, self), override_dupes
Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
else
- Bundler.ui.info "Fetching source index from #{f.uri}"
+ Bundler.ui.info "Fetching source index from #{URICredentialsFilter.credential_filtered_uri(f.uri)}"
index.use f.specs_with_retry(nil, self), override_dupes
end
end
@@ -422,19 +463,26 @@ module Bundler
spec.fetch_platform
- download_path = requires_sudo? ? Bundler.tmp(spec.full_name) : rubygems_dir
- gem_path = "#{rubygems_dir}/cache/#{spec.full_name}.gem"
+ cache_path = download_cache_path(spec) || default_cache_path_for(rubygems_dir)
+ gem_path = "#{cache_path}/#{spec.file_name}"
- SharedHelpers.filesystem_access("#{download_path}/cache") do |p|
+ if requires_sudo?
+ download_path = Bundler.tmp(spec.full_name)
+ download_cache_path = default_cache_path_for(download_path)
+ else
+ download_cache_path = cache_path
+ end
+
+ SharedHelpers.filesystem_access(download_cache_path) do |p|
FileUtils.mkdir_p(p)
end
- download_gem(spec, download_path)
+ download_gem(spec, download_cache_path)
if requires_sudo?
- SharedHelpers.filesystem_access("#{rubygems_dir}/cache") do |p|
+ SharedHelpers.filesystem_access(cache_path) do |p|
Bundler.mkdir_p(p)
end
- Bundler.sudo "mv #{download_path}/cache/#{spec.full_name}.gem #{gem_path}"
+ Bundler.sudo "mv #{download_cache_path}/#{spec.file_name} #{gem_path}"
end
gem_path
@@ -442,16 +490,8 @@ module Bundler
Bundler.rm_rf(download_path) if requires_sudo?
end
- def builtin_gem?(spec)
- # Ruby 2.1, where all included gems have this summary
- return true if spec.summary =~ /is bundled with Ruby/
-
- # Ruby 2.0, where gemspecs are stored in specifications/default/
- spec.loaded_from && spec.loaded_from.include?("specifications/default/")
- end
-
def installed?(spec)
- installed_specs[spec].any?
+ installed_specs[spec].any? && !spec.deleted_gem?
end
def requires_sudo?
@@ -462,6 +502,10 @@ module Bundler
Bundler.rubygems.gem_dir
end
+ def default_cache_path_for(dir)
+ "#{dir}/cache"
+ end
+
def cache_path
Bundler.app_cache
end
@@ -474,52 +518,13 @@ module Bundler
# @param [Specification] spec
# the spec we want to download or retrieve from the cache.
#
- # @param [String] download_path
+ # @param [String] download_cache_path
# the local directory the .gem will end up in.
#
- def download_gem(spec, download_path)
- local_path = File.join(download_path, "cache/#{spec.full_name}.gem")
-
- if (cache_path = download_cache_path(spec)) && cache_path.file?
- SharedHelpers.filesystem_access(local_path) do
- FileUtils.cp(cache_path, local_path)
- end
- else
- uri = spec.remote.uri
- Bundler.ui.confirm("Fetching #{version_message(spec)}")
- rubygems_local_path = Bundler.rubygems.download_gem(spec, uri, download_path)
-
- # older rubygems return varying file:// variants depending on version
- rubygems_local_path = rubygems_local_path.gsub(/\Afile:/, "") unless Bundler.rubygems.provides?(">= 3.2.0.rc.2")
- rubygems_local_path = rubygems_local_path.gsub(%r{\A//}, "") if Bundler.rubygems.provides?("< 3.1.0")
-
- if rubygems_local_path != local_path
- SharedHelpers.filesystem_access(local_path) do
- FileUtils.mv(rubygems_local_path, local_path)
- end
- end
- cache_globally(spec, local_path)
- end
- end
-
- # Checks if the requested spec exists in the global cache. If it does
- # not, we create the relevant global cache subdirectory if it does not
- # exist and copy the spec from the local cache to the global cache.
- #
- # @param [Specification] spec
- # the spec we want to copy to the global cache.
- #
- # @param [String] local_cache_path
- # the local directory from which we want to copy the .gem.
- #
- def cache_globally(spec, local_cache_path)
- return unless cache_path = download_cache_path(spec)
- return if cache_path.exist?
-
- SharedHelpers.filesystem_access(cache_path.dirname, &:mkpath)
- SharedHelpers.filesystem_access(cache_path) do
- FileUtils.cp(local_cache_path, cache_path)
- end
+ def download_gem(spec, download_cache_path)
+ uri = spec.remote.uri
+ Bundler.ui.confirm("Fetching #{version_message(spec)}")
+ Bundler.rubygems.download_gem(spec, uri, download_cache_path)
end
# Returns the global cache path of the calling Rubygems::Source object.
@@ -538,7 +543,7 @@ module Bundler
return unless remote = spec.remote
return unless cache_slug = remote.cache_slug
- Bundler.user_cache.join("gems", cache_slug, spec.file_name)
+ Bundler.user_cache.join("gems", cache_slug)
end
def extension_cache_slug(spec)
diff --git a/lib/bundler/source/rubygems_aggregate.rb b/lib/bundler/source/rubygems_aggregate.rb
new file mode 100644
index 0000000000..99ef81ad54
--- /dev/null
+++ b/lib/bundler/source/rubygems_aggregate.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module Bundler
+ class Source
+ class RubygemsAggregate
+ attr_reader :source_map, :sources
+
+ def initialize(sources, source_map)
+ @sources = sources
+ @source_map = source_map
+
+ @index = build_index
+ end
+
+ def specs
+ @index
+ end
+
+ def identifier
+ to_s
+ end
+
+ def to_s
+ "any of the sources"
+ end
+
+ private
+
+ def build_index
+ Index.build do |idx|
+ dependency_names = source_map.pinned_spec_names
+
+ sources.all_sources.each do |source|
+ source.dependency_names = dependency_names - source_map.pinned_spec_names(source)
+ idx.add_source source.specs
+ dependency_names.concat(source.unmet_deps).uniq!
+ end
+
+ double_check_for_index(idx, dependency_names)
+ end
+ end
+
+ # Suppose the gem Foo depends on the gem Bar. Foo exists in Source A. Bar has some versions that exist in both
+ # sources A and B. At this point, the API request will have found all the versions of Bar in source A,
+ # but will not have found any versions of Bar from source B, which is a problem if the requested version
+ # of Foo specifically depends on a version of Bar that is only found in source B. This ensures that for
+ # each spec we found, we add all possible versions from all sources to the index.
+ def double_check_for_index(idx, dependency_names)
+ pinned_names = source_map.pinned_spec_names
+
+ names = :names # do this so we only have to traverse to get dependency_names from the index once
+ unmet_dependency_names = lambda do
+ return names unless names == :names
+ new_names = sources.all_sources.map(&:dependency_names_to_double_check)
+ return names = nil if new_names.compact!
+ names = new_names.flatten(1).concat(dependency_names)
+ names.uniq!
+ names -= pinned_names
+ names
+ end
+
+ sources.all_sources.each do |source|
+ source.double_check_for(unmet_dependency_names)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb
index 436891256d..a4773397c7 100644
--- a/lib/bundler/source_list.rb
+++ b/lib/bundler/source_list.rb
@@ -5,24 +5,49 @@ module Bundler
attr_reader :path_sources,
:git_sources,
:plugin_sources,
- :global_rubygems_source,
+ :global_path_source,
:metadata_source
+ def global_rubygems_source
+ @global_rubygems_source ||= rubygems_aggregate_class.new("allow_local" => true)
+ end
+
def initialize
@path_sources = []
@git_sources = []
@plugin_sources = []
@global_rubygems_source = nil
- @rubygems_aggregate = rubygems_aggregate_class.new
+ @global_path_source = nil
@rubygems_sources = []
@metadata_source = Source::Metadata.new
+
+ @merged_gem_lockfile_sections = false
+ end
+
+ def merged_gem_lockfile_sections?
+ @merged_gem_lockfile_sections
+ end
+
+ def merged_gem_lockfile_sections!(replacement_source)
+ @merged_gem_lockfile_sections = true
+ @global_rubygems_source = replacement_source
+ end
+
+ def aggregate_global_source?
+ global_rubygems_source.multiple_remotes?
+ end
+
+ def implicit_global_source?
+ global_rubygems_source.no_remotes?
end
def add_path_source(options = {})
if options["gemspec"]
add_source_to_list Source::Gemspec.new(options), path_sources
else
- add_source_to_list Source::Path.new(options), path_sources
+ path_source = add_source_to_list Source::Path.new(options), path_sources
+ @global_path_source ||= path_source if options["global"]
+ path_source
end
end
@@ -33,32 +58,31 @@ module Bundler
end
def add_rubygems_source(options = {})
- add_source_to_list Source::Rubygems.new(options), @rubygems_sources
+ new_source = Source::Rubygems.new(options)
+ return @global_rubygems_source if @global_rubygems_source == new_source
+
+ add_source_to_list new_source, @rubygems_sources
end
def add_plugin_source(source, options = {})
add_source_to_list Plugin.source(source).new(options), @plugin_sources
end
- def global_rubygems_source=(uri)
- if Bundler.feature_flag.disable_multisource?
- @global_rubygems_source ||= rubygems_aggregate_class.new("remotes" => uri)
- end
- add_rubygems_remote(uri)
- end
-
- def add_rubygems_remote(uri)
- return if Bundler.feature_flag.disable_multisource?
- @rubygems_aggregate.add_remote(uri)
- @rubygems_aggregate
+ def add_global_rubygems_remote(uri)
+ global_rubygems_source.add_remote(uri)
+ global_rubygems_source
end
def default_source
- global_rubygems_source || @rubygems_aggregate
+ global_path_source || global_rubygems_source
end
def rubygems_sources
- @rubygems_sources + [default_source]
+ non_global_rubygems_sources + [global_rubygems_source]
+ end
+
+ def non_global_rubygems_sources
+ @rubygems_sources
end
def rubygems_remotes
@@ -69,37 +93,51 @@ module Bundler
path_sources + git_sources + plugin_sources + rubygems_sources + [metadata_source]
end
+ def non_default_explicit_sources
+ all_sources - [default_source, metadata_source]
+ end
+
def get(source)
- source_list_for(source).find {|s| equal_source?(source, s) || equivalent_source?(source, s) }
+ source_list_for(source).find {|s| equivalent_source?(source, s) }
end
def lock_sources
- lock_sources = (path_sources + git_sources + plugin_sources).sort_by(&:to_s)
- if Bundler.feature_flag.disable_multisource?
- lock_sources + rubygems_sources.sort_by(&:to_s)
+ lock_other_sources + lock_rubygems_sources
+ end
+
+ def lock_other_sources
+ (path_sources + git_sources + plugin_sources).sort_by(&:identifier)
+ end
+
+ def lock_rubygems_sources
+ if merged_gem_lockfile_sections?
+ [combine_rubygems_sources]
else
- lock_sources << combine_rubygems_sources
+ rubygems_sources.sort_by(&:identifier)
end
end
# Returns true if there are changes
def replace_sources!(replacement_sources)
- return true if replacement_sources.empty?
+ return false if replacement_sources.empty?
- [path_sources, git_sources, plugin_sources].each do |source_list|
- source_list.map! do |source|
- replacement_sources.find {|s| s == source } || source
- end
- end
+ @rubygems_sources, @path_sources, @git_sources, @plugin_sources = map_sources(replacement_sources)
+ @global_rubygems_source = global_replacement_source(replacement_sources)
+
+ different_sources?(lock_sources, replacement_sources)
+ end
- replacement_rubygems = !Bundler.feature_flag.disable_multisource? &&
- replacement_sources.detect {|s| s.is_a?(Source::Rubygems) }
- @rubygems_aggregate = replacement_rubygems if replacement_rubygems
+ # Returns true if there are changes
+ def expired_sources?(replacement_sources)
+ return false if replacement_sources.empty?
+
+ lock_sources = dup_with_replaced_sources(replacement_sources).lock_sources
- return true if !equal_sources?(lock_sources, replacement_sources) && !equivalent_sources?(lock_sources, replacement_sources)
- return true if replacement_rubygems && rubygems_remotes.sort_by(&:to_s) != replacement_rubygems.remotes.sort_by(&:to_s)
+ different_sources?(lock_sources, replacement_sources)
+ end
- false
+ def local_only!
+ all_sources.each(&:local_only!)
end
def cached!
@@ -110,11 +148,33 @@ module Bundler
all_sources.each(&:remote!)
end
- def rubygems_primary_remotes
- @rubygems_aggregate.remotes
+ private
+
+ def dup_with_replaced_sources(replacement_sources)
+ new_source_list = dup
+ new_source_list.replace_sources!(replacement_sources)
+ new_source_list
end
- private
+ def map_sources(replacement_sources)
+ [@rubygems_sources, @path_sources, @git_sources, @plugin_sources].map do |sources|
+ sources.map do |source|
+ replacement_sources.find {|s| s == source } || source
+ end
+ end
+ end
+
+ def global_replacement_source(replacement_sources)
+ replacement_source = replacement_sources.find {|s| s == global_rubygems_source }
+ return global_rubygems_source unless replacement_source
+
+ replacement_source.local!
+ replacement_source
+ end
+
+ def different_sources?(lock_sources, replacement_sources)
+ !equivalent_sources?(lock_sources, replacement_sources)
+ end
def rubygems_aggregate_class
Source::Rubygems
@@ -150,32 +210,12 @@ module Bundler
end
end
- def equal_sources?(lock_sources, replacement_sources)
- lock_sources.sort_by(&:to_s) == replacement_sources.sort_by(&:to_s)
- end
-
- def equal_source?(source, other_source)
- source == other_source
- end
-
- def equivalent_source?(source, other_source)
- return false unless Bundler.settings[:allow_deployment_source_credential_changes] && source.is_a?(Source::Rubygems)
-
- equivalent_rubygems_sources?([source], [other_source])
- end
-
def equivalent_sources?(lock_sources, replacement_sources)
- return false unless Bundler.settings[:allow_deployment_source_credential_changes]
-
- lock_rubygems_sources, lock_other_sources = lock_sources.partition {|s| s.is_a?(Source::Rubygems) }
- replacement_rubygems_sources, replacement_other_sources = replacement_sources.partition {|s| s.is_a?(Source::Rubygems) }
-
- equivalent_rubygems_sources?(lock_rubygems_sources, replacement_rubygems_sources) && equal_sources?(lock_other_sources, replacement_other_sources)
+ lock_sources.sort_by(&:identifier) == replacement_sources.sort_by(&:identifier)
end
- def equivalent_rubygems_sources?(lock_sources, replacement_sources)
- actual_remotes = replacement_sources.map(&:remotes).flatten.uniq
- lock_sources.all? {|s| s.equivalent_remotes?(actual_remotes) }
+ def equivalent_source?(source, other_source)
+ source == other_source
end
end
end
diff --git a/lib/bundler/source_map.rb b/lib/bundler/source_map.rb
new file mode 100644
index 0000000000..a554f26f76
--- /dev/null
+++ b/lib/bundler/source_map.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module Bundler
+ class SourceMap
+ attr_reader :sources, :dependencies
+
+ def initialize(sources, dependencies)
+ @sources = sources
+ @dependencies = dependencies
+ end
+
+ def pinned_spec_names(skip = nil)
+ direct_requirements.reject {|_, source| source == skip }.keys
+ end
+
+ def all_requirements
+ requirements = direct_requirements.dup
+
+ unmet_deps = sources.non_default_explicit_sources.map do |source|
+ (source.spec_names - pinned_spec_names).each do |indirect_dependency_name|
+ previous_source = requirements[indirect_dependency_name]
+ if previous_source.nil?
+ requirements[indirect_dependency_name] = source
+ else
+ no_ambiguous_sources = Bundler.feature_flag.bundler_3_mode?
+
+ msg = ["The gem '#{indirect_dependency_name}' was found in multiple relevant sources."]
+ msg.concat [previous_source, source].map {|s| " * #{s}" }.sort
+ msg << "You #{no_ambiguous_sources ? :must : :should} add this gem to the source block for the source you wish it to be installed from."
+ msg = msg.join("\n")
+
+ raise SecurityError, msg if no_ambiguous_sources
+ Bundler.ui.warn "Warning: #{msg}"
+ end
+ end
+
+ source.unmet_deps
+ end
+
+ sources.default_source.add_dependency_names(unmet_deps.flatten - requirements.keys)
+
+ requirements
+ end
+
+ def direct_requirements
+ @direct_requirements ||= begin
+ requirements = {}
+ default = sources.default_source
+ dependencies.each do |dep|
+ dep_source = dep.source || default
+ dep_source.add_dependency_names(dep.name)
+ requirements[dep.name] = dep_source
+ end
+ requirements
+ end
+ end
+ end
+end
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index 399c91fea5..a19d18388a 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "tsort"
+require_relative "vendored_tsort"
module Bundler
class SpecSet
@@ -11,21 +11,20 @@ module Bundler
@specs = specs
end
- def for(dependencies, skip = [], check = false, match_current_platform = false, raise_on_missing = true)
+ def for(dependencies, check = false, match_current_platform = false)
handled = []
deps = dependencies.dup
specs = []
- skip += ["bundler"]
loop do
break unless dep = deps.shift
- next if handled.include?(dep) || skip.include?(dep.name)
+ next if handled.any?{|d| d.name == dep.name && (match_current_platform || d.__platform == dep.__platform) } || dep.name == "bundler"
handled << dep
specs_for_dep = spec_for_dependency(dep, match_current_platform)
if specs_for_dep.any?
- specs += specs_for_dep
+ match_current_platform ? specs += specs_for_dep : specs |= specs_for_dep
specs_for_dep.first.dependencies.each do |d|
next if d.type == :development
@@ -34,11 +33,6 @@ module Bundler
end
elsif check
return false
- elsif raise_on_missing
- others = lookup[dep.name] if match_current_platform
- message = "Unable to find a spec satisfying #{dep} in the set. Perhaps the lockfile is corrupted?"
- message += " Found #{others.join(", ")} that did not match the current platform." if others && !others.empty?
- raise GemNotFound, message
end
end
@@ -46,11 +40,7 @@ module Bundler
specs << spec
end
- check ? true : SpecSet.new(specs)
- end
-
- def valid_for?(deps)
- self.for(deps, [], true)
+ check ? true : specs
end
def [](key)
@@ -76,32 +66,24 @@ module Bundler
lookup.dup
end
- def materialize(deps, missing_specs = nil)
- materialized = self.for(deps, [], false, true, !missing_specs).to_a
- deps = materialized.map(&:name).uniq
+ def materialize(deps)
+ materialized = self.for(deps, false, true)
+
materialized.map! do |s|
next s unless s.is_a?(LazySpecification)
- s.source.dependency_names = deps if s.source.respond_to?(:dependency_names=)
- spec = s.__materialize__
- unless spec
- unless missing_specs
- raise GemNotFound, "Could not find #{s.full_name} in any of the sources"
- end
- missing_specs << s
- end
- spec
+ s.source.local!
+ s.__materialize__ || s
end
- SpecSet.new(missing_specs ? materialized.compact : materialized)
+ SpecSet.new(materialized)
end
# Materialize for all the specs in the spec set, regardless of what platform they're for
# This is in contrast to how for does platform filtering (and specifically different from how `materialize` calls `for` only for the current platform)
# @return [Array<Gem::Specification>]
def materialized_for_all_platforms
- names = @specs.map(&:name).uniq
@specs.map do |s|
next s unless s.is_a?(LazySpecification)
- s.source.dependency_names = names if s.source.respond_to?(:dependency_names=)
+ s.source.local!
s.source.remote!
spec = s.__materialize__
raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec
@@ -109,6 +91,10 @@ module Bundler
end
end
+ def missing_specs
+ @specs.select {|s| s.is_a?(LazySpecification) }
+ end
+
def merge(set)
arr = sorted.dup
set.each do |set_spec|
@@ -186,7 +172,7 @@ module Bundler
def spec_for_dependency(dep, match_current_platform)
specs_for_platforms = lookup[dep.name]
if match_current_platform
- GemHelpers.select_best_platform_match(specs_for_platforms, Bundler.local_platform)
+ GemHelpers.select_best_platform_match(specs_for_platforms.select{|s| Gem::Platform.match_spec?(s) }, Bundler.local_platform)
else
GemHelpers.select_best_platform_match(specs_for_platforms, dep.__platform)
end
diff --git a/lib/bundler/stub_specification.rb b/lib/bundler/stub_specification.rb
index 2456d268da..fa071901e5 100644
--- a/lib/bundler/stub_specification.rb
+++ b/lib/bundler/stub_specification.rb
@@ -26,11 +26,19 @@ module Bundler
# @!group Stub Delegates
+ def manually_installed?
+ # This is for manually installed gems which are gems that were fixed in place after a
+ # failed installation. Once the issue was resolved, the user then manually created
+ # the gem specification using the instructions provided by `gem help install`
+ installed_by_version == Gem::Version.new(0)
+ end
+
# This is defined directly to avoid having to loading the full spec
def missing_extensions?
return false if default_gem?
return false if extensions.empty?
return false if File.exist? gem_build_complete_path
+ return false if manually_installed?
true
end
diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler
index 69f26bb9c0..8009412ea2 100644
--- a/lib/bundler/templates/Executable.bundler
+++ b/lib/bundler/templates/Executable.bundler
@@ -60,16 +60,16 @@ m = Module.new do
Regexp.last_match(1)
end
- def bundler_version
- @bundler_version ||=
+ def bundler_requirement
+ @bundler_requirement ||=
env_var_version || cli_arg_version ||
- lockfile_version
+ bundler_requirement_for(lockfile_version)
end
- def bundler_requirement
- return "#{Gem::Requirement.default}.a" unless bundler_version
+ def bundler_requirement_for(version)
+ return "#{Gem::Requirement.default}.a" unless version
- bundler_gem_version = Gem::Version.new(bundler_version)
+ bundler_gem_version = Gem::Version.new(version)
requirement = bundler_gem_version.approximate_recommendation
diff --git a/lib/bundler/templates/Gemfile b/lib/bundler/templates/Gemfile
index 1afd2cce67..d41f2719b4 100644
--- a/lib/bundler/templates/Gemfile
+++ b/lib/bundler/templates/Gemfile
@@ -2,6 +2,6 @@
source "https://rubygems.org"
-git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
+git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
# gem "rails"
diff --git a/lib/bundler/templates/gems.rb b/lib/bundler/templates/gems.rb
index 547cd6e8d9..56a62a7a82 100644
--- a/lib/bundler/templates/gems.rb
+++ b/lib/bundler/templates/gems.rb
@@ -3,6 +3,6 @@
# A sample gems.rb
source "https://rubygems.org"
-git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
+git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
# gem "rails"
diff --git a/lib/bundler/templates/newgem/Gemfile.tt b/lib/bundler/templates/newgem/Gemfile.tt
index b09ccfff15..de82a63c5f 100644
--- a/lib/bundler/templates/newgem/Gemfile.tt
+++ b/lib/bundler/templates/newgem/Gemfile.tt
@@ -14,7 +14,10 @@ gem "rake-compiler"
gem "<%= config[:test] %>", "~> <%= config[:test_framework_version] %>"
<%- end -%>
-<%- if config[:rubocop] -%>
+<%- if config[:linter] == "rubocop" -%>
-gem "rubocop", "~> <%= config[:rubocop_version] %>"
+gem "rubocop", "~> <%= config[:linter_version] %>"
+<%- elsif config[:linter] == "standard" -%>
+
+gem "standard", "~> <%= config[:linter_version] %>"
<%- end -%>
diff --git a/lib/bundler/templates/newgem/README.md.tt b/lib/bundler/templates/newgem/README.md.tt
index 315bc745a3..8fd87abe9a 100644
--- a/lib/bundler/templates/newgem/README.md.tt
+++ b/lib/bundler/templates/newgem/README.md.tt
@@ -29,19 +29,21 @@ TODO: Write usage instructions here
After checking out the repo, run `bin/setup` to install dependencies.<% if config[:test] %> Then, run `rake <%= config[:test].sub('mini', '').sub('rspec', 'spec') %>` to run the tests.<% end %> You can also run `bin/console` for an interactive prompt that will allow you to experiment.<% if config[:bin] %> Run `bundle exec <%= config[:name] %>` to use the gem in this directory, ignoring other installed copies of this gem.<% end %>
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
+<% if config[:git] -%>
## Contributing
-Bug reports and pull requests are welcome on GitHub at https://github.com/<%= config[:github_username] %>/<%= config[:name] %>.<% if config[:coc] %> This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/master/CODE_OF_CONDUCT.md).<% end %>
+Bug reports and pull requests are welcome on GitHub at https://github.com/<%= config[:github_username] %>/<%= config[:name] %>.<% if config[:coc] %> This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/<%= config[:git_default_branch] %>/CODE_OF_CONDUCT.md).<% end %>
+<% end -%>
<% if config[:mit] -%>
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
<% end -%>
-<% if config[:coc] -%>
+<% if config[:git] && config[:coc] -%>
## Code of Conduct
-Everyone interacting in the <%= config[:constant_name] %> project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/master/CODE_OF_CONDUCT.md).
+Everyone interacting in the <%= config[:constant_name] %> project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/<%= config[:git_default_branch] %>/CODE_OF_CONDUCT.md).
<% end -%>
diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt
index 5393eb4e22..00b91c8464 100644
--- a/lib/bundler/templates/newgem/Rakefile.tt
+++ b/lib/bundler/templates/newgem/Rakefile.tt
@@ -18,12 +18,16 @@ require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec)
<% end -%>
-<% if config[:rubocop] -%>
+<% if config[:linter] == "rubocop" -%>
<% default_task_names << :rubocop -%>
require "rubocop/rake_task"
RuboCop::RakeTask.new
+<% elsif config[:linter] == "standard" -%>
+<% default_task_names << :standard -%>
+require "standard/rake"
+
<% end -%>
<% if config[:ext] -%>
<% default_task_names.unshift(:clobber, :compile) -%>
diff --git a/lib/bundler/templates/newgem/github/workflows/main.yml.tt b/lib/bundler/templates/newgem/github/workflows/main.yml.tt
index 807c8dd79a..6570d177af 100644
--- a/lib/bundler/templates/newgem/github/workflows/main.yml.tt
+++ b/lib/bundler/templates/newgem/github/workflows/main.yml.tt
@@ -1,18 +1,27 @@
name: Ruby
-on: [push,pull_request]
+on:
+ push:
+ branches:
+ - <%= config[:git_default_branch] %>
+
+ pull_request:
jobs:
build:
runs-on: ubuntu-latest
+ name: Ruby ${{ matrix.ruby }}
+ strategy:
+ matrix:
+ ruby:
+ - '<%= RUBY_VERSION %>'
+
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
- ruby-version: <%= RUBY_VERSION %>
+ ruby-version: ${{ matrix.ruby }}
+ bundler-cache: true
- name: Run the default task
- run: |
- gem install bundler -v <%= Bundler::VERSION %>
- bundle install
- bundle exec rake
+ run: bundle exec rake
diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt
index 632b24dda9..82281ab674 100644
--- a/lib/bundler/templates/newgem/newgem.gemspec.tt
+++ b/lib/bundler/templates/newgem/newgem.gemspec.tt
@@ -3,20 +3,20 @@
require_relative "lib/<%=config[:namespaced_path]%>/version"
Gem::Specification.new do |spec|
- spec.name = <%= config[:name].inspect %>
- spec.version = <%= config[:constant_name] %>::VERSION
- spec.authors = [<%= config[:author].inspect %>]
- spec.email = [<%= config[:email].inspect %>]
-
- spec.summary = "TODO: Write a short summary, because RubyGems requires one."
- spec.description = "TODO: Write a longer description or delete this line."
- spec.homepage = "TODO: Put your gem's website or public repo URL here."
+ spec.name = <%= config[:name].inspect %>
+ spec.version = <%= config[:constant_name] %>::VERSION
+ spec.authors = [<%= config[:author].inspect %>]
+ spec.email = [<%= config[:email].inspect %>]
+
+ spec.summary = "TODO: Write a short summary, because RubyGems requires one."
+ spec.description = "TODO: Write a longer description or delete this line."
+ spec.homepage = "TODO: Put your gem's website or public repo URL here."
<%- if config[:mit] -%>
- spec.license = "MIT"
+ spec.license = "MIT"
<%- end -%>
- spec.required_ruby_version = Gem::Requirement.new(">= <%= config[:required_ruby_version] %>")
+ spec.required_ruby_version = ">= <%= config[:required_ruby_version] %>"
- spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
+ spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
@@ -25,13 +25,15 @@ Gem::Specification.new do |spec|
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(File.expand_path(__dir__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
+ `git ls-files -z`.split("\x0").reject do |f|
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
+ end
end
- spec.bindir = "exe"
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
<%- if config[:ext] -%>
- spec.extensions = ["ext/<%= config[:underscored_name] %>/extconf.rb"]
+ spec.extensions = ["ext/<%= config[:underscored_name] %>/extconf.rb"]
<%- end -%>
# Uncomment to register a new dependency of your gem
diff --git a/lib/bundler/templates/newgem/sig/newgem.rbs.tt b/lib/bundler/templates/newgem/sig/newgem.rbs.tt
new file mode 100644
index 0000000000..eb7b380bbb
--- /dev/null
+++ b/lib/bundler/templates/newgem/sig/newgem.rbs.tt
@@ -0,0 +1,8 @@
+<%- config[:constant_array].each_with_index do |c, i| -%>
+<%= " " * i %>module <%= c %>
+<%- end -%>
+<%= " " * config[:constant_array].size %>VERSION: String
+<%= " " * config[:constant_array].size %># See the writing guide of rbs: https://github.com/ruby/rbs#guides
+<%- (config[:constant_array].size-1).downto(0) do |i| -%>
+<%= " " * i %>end
+<%- end -%>
diff --git a/lib/bundler/templates/newgem/standard.yml.tt b/lib/bundler/templates/newgem/standard.yml.tt
new file mode 100644
index 0000000000..9e88fbbe8b
--- /dev/null
+++ b/lib/bundler/templates/newgem/standard.yml.tt
@@ -0,0 +1,2 @@
+# For available configuration options, see:
+# https://github.com/testdouble/standard
diff --git a/lib/bundler/vendor/.document b/lib/bundler/vendor/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/bundler/vendor/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/bundler/vendor/connection_pool/LICENSE b/lib/bundler/vendor/connection_pool/LICENSE
new file mode 100644
index 0000000000..7673cbfb7a
--- /dev/null
+++ b/lib/bundler/vendor/connection_pool/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2011 Mike Perham
+
+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/lib/bundler/vendor/connection_pool/lib/connection_pool.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
index fbcd26c765..984c1c3dcb 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb
@@ -1,14 +1,18 @@
-require_relative 'connection_pool/version'
-require_relative 'connection_pool/timed_stack'
+require "timeout"
+require_relative "connection_pool/version"
+class Bundler::ConnectionPool
+ class Error < ::RuntimeError; end
+ class PoolShuttingDownError < ::Bundler::ConnectionPool::Error; end
+ class TimeoutError < ::Timeout::Error; end
+end
-# Generic connection pool class for e.g. sharing a limited number of network connections
-# among many threads. Note: Connections are lazily created.
+# Generic connection pool class for sharing a limited number of objects or network connections
+# among many threads. Note: pool elements are lazily created.
#
# Example usage with block (faster):
#
# @pool = Bundler::ConnectionPool.new { Redis.new }
-#
# @pool.with do |redis|
# redis.lpop('my-list') if redis.llen('my-list') > 0
# end
@@ -34,29 +38,23 @@ require_relative 'connection_pool/timed_stack'
class Bundler::ConnectionPool
DEFAULTS = {size: 5, timeout: 5}
- class Error < RuntimeError
- end
-
def self.wrap(options, &block)
Wrapper.new(options, &block)
end
def initialize(options = {}, &block)
- raise ArgumentError, 'Connection pool requires a block' unless block
+ raise ArgumentError, "Connection pool requires a block" unless block
options = DEFAULTS.merge(options)
- @size = options.fetch(:size)
+ @size = Integer(options.fetch(:size))
@timeout = options.fetch(:timeout)
@available = TimedStack.new(@size, &block)
- @key = :"current-#{@available.object_id}"
- @key_count = :"current-#{@available.object_id}-count"
+ @key = :"pool-#{@available.object_id}"
+ @key_count = :"pool-#{@available.object_id}-count"
end
-if Thread.respond_to?(:handle_interrupt)
-
- # MRI
def with(options = {})
Thread.handle_interrupt(Exception => :never) do
conn = checkout(options)
@@ -69,28 +67,15 @@ if Thread.respond_to?(:handle_interrupt)
end
end
end
-
-else
-
- # jruby 1.7.x
- def with(options = {})
- conn = checkout(options)
- begin
- yield conn
- ensure
- checkin
- end
- end
-
-end
+ alias then with
def checkout(options = {})
if ::Thread.current[@key]
- ::Thread.current[@key_count]+= 1
+ ::Thread.current[@key_count] += 1
::Thread.current[@key]
else
- ::Thread.current[@key_count]= 1
- ::Thread.current[@key]= @available.pop(options[:timeout] || @timeout)
+ ::Thread.current[@key_count] = 1
+ ::Thread.current[@key] = @available.pop(options[:timeout] || @timeout)
end
end
@@ -98,64 +83,44 @@ end
if ::Thread.current[@key]
if ::Thread.current[@key_count] == 1
@available.push(::Thread.current[@key])
- ::Thread.current[@key]= nil
+ ::Thread.current[@key] = nil
+ ::Thread.current[@key_count] = nil
else
- ::Thread.current[@key_count]-= 1
+ ::Thread.current[@key_count] -= 1
end
else
- raise Bundler::ConnectionPool::Error, 'no connections are checked out'
+ raise Bundler::ConnectionPool::Error, "no connections are checked out"
end
nil
end
+ ##
+ # Shuts down the Bundler::ConnectionPool by passing each connection to +block+ and
+ # then removing it from the pool. Attempting to checkout a connection after
+ # shutdown will raise +Bundler::ConnectionPool::PoolShuttingDownError+.
+
def shutdown(&block)
@available.shutdown(&block)
end
- # Size of this connection pool
- def size
- @size
+ ##
+ # Reloads the Bundler::ConnectionPool by passing each connection to +block+ and then
+ # removing it the pool. Subsequent checkouts will create new connections as
+ # needed.
+
+ def reload(&block)
+ @available.shutdown(reload: true, &block)
end
+ # Size of this connection pool
+ attr_reader :size
+
# Number of pool entries available for checkout at this instant.
def available
@available.length
end
-
- private
-
- class Wrapper < ::BasicObject
- METHODS = [:with, :pool_shutdown]
-
- def initialize(options = {}, &block)
- @pool = options.fetch(:pool) { ::Bundler::ConnectionPool.new(options, &block) }
- end
-
- def with(&block)
- @pool.with(&block)
- end
-
- def pool_shutdown(&block)
- @pool.shutdown(&block)
- end
-
- def pool_size
- @pool.size
- end
-
- def pool_available
- @pool.available
- end
-
- def respond_to?(id, *args)
- METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
- end
-
- def method_missing(name, *args, &block)
- with do |connection|
- connection.send(name, *args, &block)
- end
- end
- end
end
+
+require_relative "connection_pool/timed_stack"
+require_relative "connection_pool/wrapper"
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb
deleted file mode 100644
index 5a9c4a27bb..0000000000
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# Global monotonic clock from Concurrent Ruby 1.0.
-# Copyright (c) Jerry D'Antonio -- released under the MIT license.
-# Slightly modified; used with permission.
-# https://github.com/ruby-concurrency/concurrent-ruby
-
-require 'thread'
-
-class Bundler::ConnectionPool
-
- class_definition = Class.new do
-
- if defined?(Process::CLOCK_MONOTONIC)
-
- # @!visibility private
- def get_time
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
- end
-
- elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
-
- # @!visibility private
- def get_time
- java.lang.System.nanoTime() / 1_000_000_000.0
- end
-
- else
-
- # @!visibility private
- def initialize
- @mutex = Mutex.new
- @last_time = Time.now.to_f
- end
-
- # @!visibility private
- def get_time
- @mutex.synchronize do
- now = Time.now.to_f
- if @last_time < now
- @last_time = now
- else # clock has moved back in time
- @last_time += 0.000_001
- end
- end
- end
- end
- end
-
- ##
- # Clock that cannot be set and represents monotonic time since
- # some unspecified starting point.
- #
- # @!visibility private
- GLOBAL_MONOTONIC_CLOCK = class_definition.new
- private_constant :GLOBAL_MONOTONIC_CLOCK
-
- class << self
- ##
- # Returns the current time a tracked by the application monotonic clock.
- #
- # @return [Float] The current monotonic time when `since` not given else
- # the elapsed monotonic time between `since` and the current time
- def monotonic_time
- GLOBAL_MONOTONIC_CLOCK.get_time
- end
- end
-end
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb
index f3fe1e04ad..a7b1cf06a8 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb
@@ -1,13 +1,3 @@
-require 'thread'
-require 'timeout'
-require_relative 'monotonic_time'
-
-##
-# Raised when you attempt to retrieve a connection from a pool that has been
-# shut down.
-
-class Bundler::ConnectionPool::PoolShuttingDownError < RuntimeError; end
-
##
# The TimedStack manages a pool of homogeneous connections (or any resource
# you wish to manage). Connections are created lazily up to a given maximum
@@ -25,7 +15,7 @@ class Bundler::ConnectionPool::PoolShuttingDownError < RuntimeError; end
#
# conn = ts.pop
# ts.pop timeout: 5
-# #=> raises Timeout::Error after 5 seconds
+# #=> raises Bundler::ConnectionPool::TimeoutError after 5 seconds
class Bundler::ConnectionPool::TimedStack
attr_reader :max
@@ -39,8 +29,8 @@ class Bundler::ConnectionPool::TimedStack
@created = 0
@que = []
@max = size
- @mutex = Mutex.new
- @resource = ConditionVariable.new
+ @mutex = Thread::Mutex.new
+ @resource = Thread::ConditionVariable.new
@shutdown_block = nil
end
@@ -59,12 +49,12 @@ class Bundler::ConnectionPool::TimedStack
@resource.broadcast
end
end
- alias_method :<<, :push
+ alias << push
##
# Retrieves a connection from the stack. If a connection is available it is
# immediately returned. If no connection is available within the given
- # timeout a Timeout::Error is raised.
+ # timeout a Bundler::ConnectionPool::TimeoutError is raised.
#
# +:timeout+ is the only checked entry in +options+ and is preferred over
# the +timeout+ argument (which will be removed in a future release). Other
@@ -74,7 +64,7 @@ class Bundler::ConnectionPool::TimedStack
options, timeout = timeout, 0.5 if Hash === timeout
timeout = options.fetch :timeout, timeout
- deadline = Bundler::ConnectionPool.monotonic_time + timeout
+ deadline = current_time + timeout
@mutex.synchronize do
loop do
raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block
@@ -83,18 +73,20 @@ class Bundler::ConnectionPool::TimedStack
connection = try_create(options)
return connection if connection
- to_wait = deadline - Bundler::ConnectionPool.monotonic_time
- raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0
+ to_wait = deadline - current_time
+ raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec" if to_wait <= 0
@resource.wait(@mutex, to_wait)
end
end
end
##
- # Shuts down the TimedStack which prevents connections from being checked
- # out. The +block+ is called once for each connection on the stack.
+ # Shuts down the TimedStack by passing each connection to +block+ and then
+ # removing it from the pool. Attempting to checkout a connection after
+ # shutdown will raise +Bundler::ConnectionPool::PoolShuttingDownError+ unless
+ # +:reload+ is +true+.
- def shutdown(&block)
+ def shutdown(reload: false, &block)
raise ArgumentError, "shutdown must receive a block" unless block_given?
@mutex.synchronize do
@@ -102,6 +94,7 @@ class Bundler::ConnectionPool::TimedStack
@resource.broadcast
shutdown_connections
+ @shutdown_block = nil if reload
end
end
@@ -121,6 +114,10 @@ class Bundler::ConnectionPool::TimedStack
private
+ def current_time
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end
+
##
# This is an extension point for TimedStack and is called with a mutex.
#
@@ -149,6 +146,7 @@ class Bundler::ConnectionPool::TimedStack
conn = fetch_connection(options)
@shutdown_block.call(conn)
end
+ @created = 0
end
##
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb
index b149c0e242..56ebf69902 100644
--- a/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb
@@ -1,3 +1,3 @@
class Bundler::ConnectionPool
- VERSION = "2.2.2"
+ VERSION = "2.3.0"
end
diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb
new file mode 100644
index 0000000000..880170c06b
--- /dev/null
+++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb
@@ -0,0 +1,57 @@
+class Bundler::ConnectionPool
+ class Wrapper < ::BasicObject
+ METHODS = [:with, :pool_shutdown, :wrapped_pool]
+
+ def initialize(options = {}, &block)
+ @pool = options.fetch(:pool) { ::Bundler::ConnectionPool.new(options, &block) }
+ end
+
+ def wrapped_pool
+ @pool
+ end
+
+ def with(&block)
+ @pool.with(&block)
+ end
+
+ def pool_shutdown(&block)
+ @pool.shutdown(&block)
+ end
+
+ def pool_size
+ @pool.size
+ end
+
+ def pool_available
+ @pool.available
+ end
+
+ def respond_to?(id, *args)
+ METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
+ end
+
+ # rubocop:disable Style/MethodMissingSuper
+ # rubocop:disable Style/MissingRespondToMissing
+ if ::RUBY_VERSION >= "3.0.0"
+ def method_missing(name, *args, **kwargs, &block)
+ with do |connection|
+ connection.send(name, *args, **kwargs, &block)
+ end
+ end
+ elsif ::RUBY_VERSION >= "2.7.0"
+ ruby2_keywords def method_missing(name, *args, &block)
+ with do |connection|
+ connection.send(name, *args, &block)
+ end
+ end
+ else
+ def method_missing(name, *args, &block)
+ with do |connection|
+ connection.send(name, *args, &block)
+ end
+ end
+ end
+ # rubocop:enable Style/MethodMissingSuper
+ # rubocop:enable Style/MissingRespondToMissing
+ end
+end
diff --git a/lib/bundler/vendor/fileutils/LICENSE.txt b/lib/bundler/vendor/fileutils/LICENSE.txt
new file mode 100644
index 0000000000..a009caefea
--- /dev/null
+++ b/lib/bundler/vendor/fileutils/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/lib/bundler/vendor/molinillo/LICENSE b/lib/bundler/vendor/molinillo/LICENSE
new file mode 100644
index 0000000000..01feffa088
--- /dev/null
+++ b/lib/bundler/vendor/molinillo/LICENSE
@@ -0,0 +1,9 @@
+This project is licensed under the MIT license.
+
+Copyright (c) 2014 Samuel E. Giddins segiddins@segiddins.me
+
+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/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
index 936399ed2f..10a25d2f08 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'tsort'
+require_relative '../../../../vendored_tsort'
require_relative 'dependency_graph/log'
require_relative 'dependency_graph/vertex'
@@ -17,7 +17,7 @@ module Bundler::Molinillo
vertices.values.each { |v| yield v }
end
- include TSort
+ include Bundler::TSort
# @!visibility private
alias tsort_each_node each
diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb
index edf2366b7b..eeae79af3c 100644
--- a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb
+++ b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Bundler::Molinillo
- # Provides information about specifcations and dependencies to the resolver,
+ # Provides information about specifications and dependencies to the resolver,
# allowing the {Resolver} class to remain generic while still providing power
# and flexibility.
#
diff --git a/lib/bundler/vendor/net-http-persistent/README.rdoc b/lib/bundler/vendor/net-http-persistent/README.rdoc
new file mode 100644
index 0000000000..4f95ad33ba
--- /dev/null
+++ b/lib/bundler/vendor/net-http-persistent/README.rdoc
@@ -0,0 +1,82 @@
+= net-http-persistent
+
+home :: https://github.com/drbrain/net-http-persistent
+rdoc :: http://docs.seattlerb.org/net-http-persistent
+
+== DESCRIPTION:
+
+Manages persistent connections using Net::HTTP including a thread pool for
+connecting to multiple hosts.
+
+Using persistent HTTP connections can dramatically increase the speed of HTTP.
+Creating a new HTTP connection for every request involves an extra TCP
+round-trip and causes TCP congestion avoidance negotiation to start over.
+
+Net::HTTP supports persistent connections with some API methods but does not
+make setting up a single persistent connection or managing multiple
+connections easy. Net::HTTP::Persistent wraps Net::HTTP and allows you to
+focus on how to make HTTP requests.
+
+== FEATURES/PROBLEMS:
+
+* Supports TLS with secure defaults
+* Thread-safe
+* Pure ruby
+
+== SYNOPSIS
+
+The following example will make two requests to the same server. The
+connection is kept alive between requests:
+
+ require 'net/http/persistent'
+
+ uri = URI 'http://example.com/awesome/web/service'
+
+ http = Net::HTTP::Persistent.new name: 'my_app_name'
+
+ # perform a GET
+ response = http.request uri
+
+ # create a POST
+ post_uri = uri + 'create'
+ post = Net::HTTP::Post.new post_uri.path
+ post.set_form_data 'some' => 'cool data'
+
+ # perform the POST, the URI is always required
+ response = http.request post_uri, post
+
+ # if you are done making http requests, or won't make requests for several
+ # minutes
+ http.shutdown
+
+Please see the documentation on Net::HTTP::Persistent for more information,
+including SSL connection verification, header handling and tunable options.
+
+== INSTALL:
+
+ gem install net-http-persistent
+
+== LICENSE:
+
+(The MIT License)
+
+Copyright (c) Eric Hodel, Aaron Patterson
+
+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/lib/bundler/vendor/thor/LICENSE.md b/lib/bundler/vendor/thor/LICENSE.md
new file mode 100644
index 0000000000..ef80540b2a
--- /dev/null
+++ b/lib/bundler/vendor/thor/LICENSE.md
@@ -0,0 +1,20 @@
+Copyright (c) 2008 Yehuda Katz, Eric Hodel, et al.
+
+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/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
index 62c82b3dba..90a8d2e847 100644
--- a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
+++ b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb
@@ -252,7 +252,7 @@ class Bundler::Thor
# flag<Regexp|String>:: the regexp or string to be replaced
# replacement<String>:: the replacement, can be also given as a block
# config<Hash>:: give :verbose => false to not log the status, and
- # :force => true, to force the replacement regardles of runner behavior.
+ # :force => true, to force the replacement regardless of runner behavior.
#
# ==== Example
#
diff --git a/lib/bundler/vendor/tmpdir/lib/tmpdir.rb b/lib/bundler/vendor/tmpdir/lib/tmpdir.rb
index a00496687c..70d43e0c6b 100644
--- a/lib/bundler/vendor/tmpdir/lib/tmpdir.rb
+++ b/lib/bundler/vendor/tmpdir/lib/tmpdir.rb
@@ -115,7 +115,7 @@ class Bundler::Dir < Dir
Bundler::Dir.tmpdir
end
- UNUSABLE_CHARS = [File::SEPARATOR, File::ALT_SEPARATOR, File::PATH_SEPARATOR, ":"].uniq.join("").freeze
+ UNUSABLE_CHARS = "^,-.0-9A-Z_a-z~"
class << (RANDOM = Random.new)
MAX = 36**6 # < 0x100000000
diff --git a/lib/bundler/vendor/tsort/LICENSE.txt b/lib/bundler/vendor/tsort/LICENSE.txt
new file mode 100644
index 0000000000..a009caefea
--- /dev/null
+++ b/lib/bundler/vendor/tsort/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/lib/bundler/vendor/tsort/lib/tsort.rb b/lib/bundler/vendor/tsort/lib/tsort.rb
new file mode 100644
index 0000000000..8454583295
--- /dev/null
+++ b/lib/bundler/vendor/tsort/lib/tsort.rb
@@ -0,0 +1,453 @@
+# frozen_string_literal: true
+
+#--
+# tsort.rb - provides a module for topological sorting and strongly connected components.
+#++
+#
+
+#
+# TSort implements topological sorting using Tarjan's algorithm for
+# strongly connected components.
+#
+# TSort is designed to be able to be used with any object which can be
+# interpreted as a directed graph.
+#
+# TSort requires two methods to interpret an object as a graph,
+# tsort_each_node and tsort_each_child.
+#
+# * tsort_each_node is used to iterate for all nodes over a graph.
+# * tsort_each_child is used to iterate for child nodes of a given node.
+#
+# The equality of nodes are defined by eql? and hash since
+# TSort uses Hash internally.
+#
+# == A Simple Example
+#
+# The following example demonstrates how to mix the TSort module into an
+# existing class (in this case, Hash). Here, we're treating each key in
+# the hash as a node in the graph, and so we simply alias the required
+# #tsort_each_node method to Hash's #each_key method. For each key in the
+# hash, the associated value is an array of the node's child nodes. This
+# choice in turn leads to our implementation of the required #tsort_each_child
+# method, which fetches the array of child nodes and then iterates over that
+# array using the user-supplied block.
+#
+# require 'tsort'
+#
+# class Hash
+# include TSort
+# alias tsort_each_node each_key
+# def tsort_each_child(node, &block)
+# fetch(node).each(&block)
+# end
+# end
+#
+# {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
+# #=> [3, 2, 1, 4]
+#
+# {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
+# #=> [[4], [2, 3], [1]]
+#
+# == A More Realistic Example
+#
+# A very simple `make' like tool can be implemented as follows:
+#
+# require 'tsort'
+#
+# class Make
+# def initialize
+# @dep = {}
+# @dep.default = []
+# end
+#
+# def rule(outputs, inputs=[], &block)
+# triple = [outputs, inputs, block]
+# outputs.each {|f| @dep[f] = [triple]}
+# @dep[triple] = inputs
+# end
+#
+# def build(target)
+# each_strongly_connected_component_from(target) {|ns|
+# if ns.length != 1
+# fs = ns.delete_if {|n| Array === n}
+# raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
+# end
+# n = ns.first
+# if Array === n
+# outputs, inputs, block = n
+# inputs_time = inputs.map {|f| File.mtime f}.max
+# begin
+# outputs_time = outputs.map {|f| File.mtime f}.min
+# rescue Errno::ENOENT
+# outputs_time = nil
+# end
+# if outputs_time == nil ||
+# inputs_time != nil && outputs_time <= inputs_time
+# sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i
+# block.call
+# end
+# end
+# }
+# end
+#
+# def tsort_each_child(node, &block)
+# @dep[node].each(&block)
+# end
+# include TSort
+# end
+#
+# def command(arg)
+# print arg, "\n"
+# system arg
+# end
+#
+# m = Make.new
+# m.rule(%w[t1]) { command 'date > t1' }
+# m.rule(%w[t2]) { command 'date > t2' }
+# m.rule(%w[t3]) { command 'date > t3' }
+# m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' }
+# m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' }
+# m.build('t5')
+#
+# == Bugs
+#
+# * 'tsort.rb' is wrong name because this library uses
+# Tarjan's algorithm for strongly connected components.
+# Although 'strongly_connected_components.rb' is correct but too long.
+#
+# == References
+#
+# R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
+# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
+#
+module Bundler
+ module TSort
+ class Cyclic < StandardError
+ end
+
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # If there is a cycle, TSort::Cyclic is raised.
+ #
+ # class G
+ # include TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # p graph.tsort #=> [4, 2, 3, 1]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # p graph.tsort # raises TSort::Cyclic
+ #
+ def tsort
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ TSort.tsort(each_node, each_child)
+ end
+
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # If there is a cycle, TSort::Cyclic is raised.
+ #
+ # 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) }
+ # p TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p TSort.tsort(each_node, each_child) # raises TSort::Cyclic
+ #
+ def TSort.tsort(each_node, each_child)
+ TSort.tsort_each(each_node, each_child).to_a
+ end
+
+ # The iterator version of the #tsort method.
+ # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #tsort_each returns +nil+.
+ # If there is a cycle, TSort::Cyclic is raised.
+ #
+ # class G
+ # include TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.tsort_each {|n| p n }
+ # #=> 4
+ # # 2
+ # # 3
+ # # 1
+ #
+ def tsort_each(&block) # :yields: node
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ TSort.tsort_each(each_node, each_child, &block)
+ end
+
+ # The iterator version of the TSort.tsort method.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # 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) }
+ # TSort.tsort_each(each_node, each_child) {|n| p n }
+ # #=> 4
+ # # 2
+ # # 3
+ # # 1
+ #
+ def TSort.tsort_each(each_node, each_child) # :yields: node
+ return to_enum(__method__, each_node, each_child) unless block_given?
+
+ TSort.each_strongly_connected_component(each_node, each_child) {|component|
+ if component.size == 1
+ yield component.first
+ else
+ raise Cyclic.new("topological sort failed: #{component.inspect}")
+ end
+ }
+ end
+
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ # class G
+ # include TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
+ #
+ def strongly_connected_components
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ TSort.strongly_connected_components(each_node, each_child)
+ end
+
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # 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) }
+ # p TSort.strongly_connected_components(each_node, each_child)
+ # #=> [[4], [2], [3], [1]]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p TSort.strongly_connected_components(each_node, each_child)
+ # #=> [[4], [2, 3], [1]]
+ #
+ def TSort.strongly_connected_components(each_node, each_child)
+ TSort.each_strongly_connected_component(each_node, each_child).to_a
+ end
+
+ # The iterator version of the #strongly_connected_components method.
+ # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
+ # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #each_strongly_connected_component returns +nil+.
+ #
+ # class G
+ # include TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.each_strongly_connected_component {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ # # [3]
+ # # [1]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # graph.each_strongly_connected_component {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def each_strongly_connected_component(&block) # :yields: nodes
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ TSort.each_strongly_connected_component(each_node, each_child, &block)
+ end
+
+ # The iterator version of the TSort.strongly_connected_components method.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # 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) }
+ # TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ # # [3]
+ # # [1]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def TSort.each_strongly_connected_component(each_node, each_child) # :yields: nodes
+ return to_enum(__method__, each_node, each_child) unless block_given?
+
+ id_map = {}
+ stack = []
+ each_node.call {|node|
+ unless id_map.include? node
+ TSort.each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
+ yield c
+ }
+ end
+ }
+ nil
+ end
+
+ # Iterates over strongly connected component in the subgraph reachable from
+ # _node_.
+ #
+ # Return value is unspecified.
+ #
+ # #each_strongly_connected_component_from doesn't call #tsort_each_node.
+ #
+ # class G
+ # include TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.each_strongly_connected_component_from(2) {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # graph.each_strongly_connected_component_from(2) {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ #
+ def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
+ TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
+ end
+
+ # Iterates over strongly connected components in a graph.
+ # The graph is represented by _node_ and _each_child_.
+ #
+ # _node_ is the first node.
+ # _each_child_ should have +call+ method which takes a node argument
+ # and yields for each child node.
+ #
+ # Return value is unspecified.
+ #
+ # #TSort.each_strongly_connected_component_from is a class method and
+ # it doesn't need a class to represent a graph which includes TSort.
+ #
+ # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_child = lambda {|n, &b| graph[n].each(&b) }
+ # TSort.each_strongly_connected_component_from(1, each_child) {|scc|
+ # p scc
+ # }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def TSort.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
+ return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
+
+ minimum_id = node_id = id_map[node] = id_map.size
+ stack_length = stack.length
+ stack << node
+
+ each_child.call(node) {|child|
+ if id_map.include? child
+ child_id = id_map[child]
+ minimum_id = child_id if child_id && child_id < minimum_id
+ else
+ sub_minimum_id =
+ TSort.each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
+ yield c
+ }
+ minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
+ end
+ }
+
+ if node_id == minimum_id
+ component = stack.slice!(stack_length .. -1)
+ component.each {|n| id_map[n] = nil}
+ yield component
+ end
+
+ minimum_id
+ end
+
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_node is used to iterate for all nodes over a graph.
+ #
+ def tsort_each_node # :yields: node
+ raise NotImplementedError.new
+ end
+
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_child is used to iterate for child nodes of _node_.
+ #
+ def tsort_each_child(node) # :yields: child
+ raise NotImplementedError.new
+ end
+ end
+end
diff --git a/lib/bundler/vendor/uri/LICENSE.txt b/lib/bundler/vendor/uri/LICENSE.txt
new file mode 100644
index 0000000000..a009caefea
--- /dev/null
+++ b/lib/bundler/vendor/uri/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
index a0d62ede64..7634e16958 100644
--- a/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
+++ b/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb
@@ -504,8 +504,8 @@ module Bundler::URI
ret = {}
# for Bundler::URI::split
- ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
- ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
# for Bundler::URI::extract
ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
diff --git a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
index 07ef4391c0..3d0c7b91f0 100644
--- a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
+++ b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb
@@ -3,8 +3,8 @@ module Bundler::URI
class RFC3986_Parser # :nodoc:
# Bundler::URI defined in RFC3986
# this regexp is modified not to host is not empty string
- RFC3986_URI = /\A(?<Bundler::URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
- RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
+ RFC3986_URI = /\A(?<Bundler::URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
+ RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
attr_reader :regexp
def initialize
@@ -106,7 +106,7 @@ module Bundler::URI
QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
OPAQUE: /\A(?:[^\/].*)?\z/,
- PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/,
+ PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
}
end
diff --git a/lib/bundler/vendor/uri/lib/uri/version.rb b/lib/bundler/vendor/uri/lib/uri/version.rb
index 56177ef194..02079b26d4 100644
--- a/lib/bundler/vendor/uri/lib/uri/version.rb
+++ b/lib/bundler/vendor/uri/lib/uri/version.rb
@@ -1,6 +1,6 @@
module Bundler::URI
# :stopdoc:
- VERSION_CODE = '001000'.freeze
+ VERSION_CODE = '00100003'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
# :startdoc:
end
diff --git a/lib/bundler/vendored_tsort.rb b/lib/bundler/vendored_tsort.rb
new file mode 100644
index 0000000000..38aed0b5de
--- /dev/null
+++ b/lib/bundler/vendored_tsort.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+module Bundler; end
+require_relative "vendor/tsort/lib/tsort"
diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb
index f9569c8f78..863eeaae5f 100644
--- a/lib/bundler/version.rb
+++ b/lib/bundler/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
module Bundler
- VERSION = "2.2.9".freeze
+ VERSION = "2.2.33".freeze
def self.bundler_major_version
@bundler_major_version ||= VERSION.split(".").first.to_i
diff --git a/lib/bundler/worker.rb b/lib/bundler/worker.rb
index 10139ed25b..5e4ee21c51 100644
--- a/lib/bundler/worker.rb
+++ b/lib/bundler/worker.rb
@@ -21,12 +21,12 @@ module Bundler
# @param func [Proc] job to run in inside the worker pool
def initialize(size, name, func)
@name = name
- @request_queue = Queue.new
- @response_queue = Queue.new
+ @request_queue = Thread::Queue.new
+ @response_queue = Thread::Queue.new
@func = func
@size = size
@threads = nil
- SharedHelpers.trap("INT") { abort_threads }
+ @previous_interrupt_handler = nil
end
# Enqueue a request to be executed in the worker pool
@@ -68,13 +68,16 @@ module Bundler
# so as worker threads after retrieving it, shut themselves down
def stop_threads
return unless @threads
+
@threads.each { @request_queue.enq POISON }
@threads.each(&:join)
+
+ remove_interrupt_handler
+
@threads = nil
end
def abort_threads
- return unless @threads
Bundler.ui.debug("\n#{caller.join("\n")}")
@threads.each(&:exit)
exit 1
@@ -94,11 +97,23 @@ module Bundler
end
end.compact
+ add_interrupt_handler unless @threads.empty?
+
return if creation_errors.empty?
message = "Failed to create threads for the #{name} worker: #{creation_errors.map(&:to_s).uniq.join(", ")}"
raise ThreadCreationError, message if @threads.empty?
Bundler.ui.info message
end
+
+ def add_interrupt_handler
+ @previous_interrupt_handler = trap("INT") { abort_threads }
+ end
+
+ def remove_interrupt_handler
+ return unless @previous_interrupt_handler
+
+ trap "INT", @previous_interrupt_handler
+ end
end
end
diff --git a/lib/cgi.rb b/lib/cgi.rb
index 3b53d27a2e..6feeaf4624 100644
--- a/lib/cgi.rb
+++ b/lib/cgi.rb
@@ -288,7 +288,7 @@
#
class CGI
- VERSION = "0.2.0"
+ VERSION = "0.2.2"
end
require 'cgi/core'
diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb
index ae9ab58ede..1a9c1a82c1 100644
--- a/lib/cgi/cookie.rb
+++ b/lib/cgi/cookie.rb
@@ -40,6 +40,10 @@ class CGI
class Cookie < Array
@@accept_charset="UTF-8" unless defined?(@@accept_charset)
+ TOKEN_RE = %r"\A[[!-~]&&[^()<>@,;:\\\"/?=\[\]{}]]+\z"
+ PATH_VALUE_RE = %r"\A[[ -~]&&[^;]]*\z"
+ DOMAIN_VALUE_RE = %r"\A(?<label>(?!-)[-A-Za-z0-9]+(?<!-))(?:\.\g<label>)*\z"
+
# Create a new CGI::Cookie object.
#
# :call-seq:
@@ -72,8 +76,8 @@ class CGI
@domain = nil
@expires = nil
if name.kind_of?(String)
- @name = name
- @path = (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
+ self.name = name
+ self.path = (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
@secure = false
@httponly = false
return super(value)
@@ -84,11 +88,11 @@ class CGI
raise ArgumentError, "`name' required"
end
- @name = options["name"]
+ self.name = options["name"]
value = Array(options["value"])
# simple support for IE
- @path = options["path"] || (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
- @domain = options["domain"]
+ self.path = options["path"] || (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
+ self.domain = options["domain"]
@expires = options["expires"]
@secure = options["secure"] == true
@httponly = options["httponly"] == true
@@ -97,11 +101,35 @@ class CGI
end
# Name of this cookie, as a +String+
- attr_accessor :name
+ attr_reader :name
+ # Set name of this cookie
+ def name=(str)
+ if str and !TOKEN_RE.match?(str)
+ raise ArgumentError, "invalid name: #{str.dump}"
+ end
+ @name = str
+ end
+
# Path for which this cookie applies, as a +String+
- attr_accessor :path
+ attr_reader :path
+ # Set path for which this cookie applies
+ def path=(str)
+ if str and !PATH_VALUE_RE.match?(str)
+ raise ArgumentError, "invalid path: #{str.dump}"
+ end
+ @path = str
+ end
+
# Domain for which this cookie applies, as a +String+
- attr_accessor :domain
+ attr_reader :domain
+ # Set domain for which this cookie applies
+ def domain=(str)
+ if str and ((str = str.b).bytesize > 255 or !DOMAIN_VALUE_RE.match?(str))
+ raise ArgumentError, "invalid domain: #{str.dump}"
+ end
+ @domain = str
+ end
+
# Time at which this cookie expires, as a +Time+
attr_accessor :expires
# True if this cookie is secure; false otherwise
@@ -159,7 +187,6 @@ class CGI
raw_cookie.split(/;\s?/).each do |pairs|
name, values = pairs.split('=',2)
next unless name and values
- name = CGI.unescape(name)
values ||= ""
values = values.split('&').collect{|v| CGI.unescape(v,@@accept_charset) }
if cookies.has_key?(name)
diff --git a/lib/cgi/core.rb b/lib/cgi/core.rb
index bec76e0749..62e606837a 100644
--- a/lib/cgi/core.rb
+++ b/lib/cgi/core.rb
@@ -188,17 +188,28 @@ class CGI
# Using #header with the HTML5 tag maker will create a <header> element.
alias :header :http_header
+ def _no_crlf_check(str)
+ if str
+ str = str.to_s
+ raise "A HTTP status or header field must not include CR and LF" if str =~ /[\r\n]/
+ str
+ else
+ nil
+ end
+ end
+ private :_no_crlf_check
+
def _header_for_string(content_type) #:nodoc:
buf = ''.dup
if nph?()
- buf << "#{$CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'} 200 OK#{EOL}"
+ buf << "#{_no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'} 200 OK#{EOL}"
buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
- buf << "Server: #{$CGI_ENV['SERVER_SOFTWARE']}#{EOL}"
+ buf << "Server: #{_no_crlf_check($CGI_ENV['SERVER_SOFTWARE'])}#{EOL}"
buf << "Connection: close#{EOL}"
end
- buf << "Content-Type: #{content_type}#{EOL}"
+ buf << "Content-Type: #{_no_crlf_check(content_type)}#{EOL}"
if @output_cookies
- @output_cookies.each {|cookie| buf << "Set-Cookie: #{cookie}#{EOL}" }
+ @output_cookies.each {|cookie| buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}" }
end
return buf
end # _header_for_string
@@ -213,9 +224,9 @@ class CGI
## NPH
options.delete('nph') if defined?(MOD_RUBY)
if options.delete('nph') || nph?()
- protocol = $CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'
+ protocol = _no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'
status = options.delete('status')
- status = HTTP_STATUS[status] || status || '200 OK'
+ status = HTTP_STATUS[status] || _no_crlf_check(status) || '200 OK'
buf << "#{protocol} #{status}#{EOL}"
buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
options['server'] ||= $CGI_ENV['SERVER_SOFTWARE'] || ''
@@ -223,38 +234,38 @@ class CGI
end
## common headers
status = options.delete('status')
- buf << "Status: #{HTTP_STATUS[status] || status}#{EOL}" if status
+ buf << "Status: #{HTTP_STATUS[status] || _no_crlf_check(status)}#{EOL}" if status
server = options.delete('server')
- buf << "Server: #{server}#{EOL}" if server
+ buf << "Server: #{_no_crlf_check(server)}#{EOL}" if server
connection = options.delete('connection')
- buf << "Connection: #{connection}#{EOL}" if connection
+ buf << "Connection: #{_no_crlf_check(connection)}#{EOL}" if connection
type = options.delete('type')
- buf << "Content-Type: #{type}#{EOL}" #if type
+ buf << "Content-Type: #{_no_crlf_check(type)}#{EOL}" #if type
length = options.delete('length')
- buf << "Content-Length: #{length}#{EOL}" if length
+ buf << "Content-Length: #{_no_crlf_check(length)}#{EOL}" if length
language = options.delete('language')
- buf << "Content-Language: #{language}#{EOL}" if language
+ buf << "Content-Language: #{_no_crlf_check(language)}#{EOL}" if language
expires = options.delete('expires')
buf << "Expires: #{CGI.rfc1123_date(expires)}#{EOL}" if expires
## cookie
if cookie = options.delete('cookie')
case cookie
when String, Cookie
- buf << "Set-Cookie: #{cookie}#{EOL}"
+ buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}"
when Array
arr = cookie
- arr.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ arr.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
when Hash
hash = cookie
- hash.each_value {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ hash.each_value {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
end
end
if @output_cookies
- @output_cookies.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ @output_cookies.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
end
## other headers
options.each do |key, value|
- buf << "#{key}: #{value}#{EOL}"
+ buf << "#{_no_crlf_check(key)}: #{_no_crlf_check(value)}#{EOL}"
end
return buf
end # _header_for_hash
diff --git a/lib/debug.gemspec b/lib/debug.gemspec
index 71f93f7409..0c414d4827 100644
--- a/lib/debug.gemspec
+++ b/lib/debug.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "debug"
- spec.version = "0.1.0"
+ spec.version = "0.2.1"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
@@ -17,6 +17,6 @@ Gem::Specification.new do |spec|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
end
spec.bindir = "exe"
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.executables = []
spec.require_paths = ["lib"]
end
diff --git a/lib/debug.rb b/lib/debug.rb
index bf53eb80a4..bf63ccf34d 100644
--- a/lib/debug.rb
+++ b/lib/debug.rb
@@ -3,7 +3,10 @@
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
# Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
-require 'continuation'
+if $SAFE > 0
+ STDERR.print "-r debug.rb is not available in safe mode\n"
+ exit 1
+end
require 'tracer'
require 'pp'
@@ -178,6 +181,9 @@ SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ # :nodoc:
class DEBUGGER__
MUTEX = Thread::Mutex.new # :nodoc:
+ CONTINUATIONS_SUPPORTED = RUBY_ENGINE == 'ruby'
+
+ require 'continuation' if CONTINUATIONS_SUPPORTED
class Context # :nodoc:
DEBUG_LAST_CMD = []
@@ -375,8 +381,10 @@ class DEBUGGER__
def debug_command(file, line, id, binding)
MUTEX.lock
- unless defined?($debugger_restart) and $debugger_restart
- callcc{|c| $debugger_restart = c}
+ if CONTINUATIONS_SUPPORTED
+ unless defined?($debugger_restart) and $debugger_restart
+ callcc{|c| $debugger_restart = c}
+ end
end
set_last_thread(Thread.current)
frame_pos = 0
@@ -648,7 +656,11 @@ class DEBUGGER__
stdout.printf "%s\n", debug_eval($', binding).inspect
when /^\s*r(?:estart)?$/
- $debugger_restart.call
+ if CONTINUATIONS_SUPPORTED
+ $debugger_restart.call
+ else
+ stdout.print "Restart requires continuations.\n"
+ end
when /^\s*h(?:elp)?$/
debug_print_help()
@@ -1097,9 +1109,11 @@ EOHELP
stdout.printf "Debug.rb\n"
stdout.printf "Emacs support available.\n\n"
- RubyVM::InstructionSequence.compile_option = {
- trace_instruction: true
- }
+ if defined?(RubyVM::InstructionSequence)
+ RubyVM::InstructionSequence.compile_option = {
+ trace_instruction: true
+ }
+ end
set_trace_func proc { |event, file, line, id, binding, klass, *rest|
DEBUGGER__.context.trace_func event, file, line, id, binding, klass
}
diff --git a/lib/drb/version.rb b/lib/drb/version.rb
index ffba81d48e..e1f33cc65e 100644
--- a/lib/drb/version.rb
+++ b/lib/drb/version.rb
@@ -1,3 +1,3 @@
module DRb
- VERSION = "2.0.4"
+ VERSION = "2.0.5"
end
diff --git a/lib/irb.rb b/lib/irb.rb
index 7f99974f28..93c4d25c92 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -60,7 +60,11 @@ require_relative "irb/easter-egg"
# -E enc Same as `ruby -E`
# -w Same as `ruby -w`
# -W[level=2] Same as `ruby -W`
-# --inspect Use `inspect' for output (default except for bc mode)
+# --context-mode n Set n[0-4] to method to create Binding Object,
+# when new workspace was created
+# --echo Show result(default)
+# --noecho Don't show result
+# --inspect Use `inspect' for output
# --noinspect Don't use inspect for output
# --multiline Use multiline editor module
# --nomultiline Don't use multiline editor module
@@ -68,19 +72,24 @@ require_relative "irb/easter-egg"
# --nosingleline Don't use singleline editor module
# --colorize Use colorization
# --nocolorize Don't use colorization
-# --prompt prompt-mode
-# --prompt-mode prompt-mode
+# --prompt prompt-mode/--prompt-mode prompt-mode
# Switch prompt mode. Pre-defined prompt modes are
# `default', `simple', `xmp' and `inf-ruby'
# --inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs.
# Suppresses --multiline and --singleline.
-# --simple-prompt Simple prompt mode
+# --sample-book-mode/--simple-prompt
+# Simple prompt mode
# --noprompt No prompt mode
+# --single-irb Share self with sub-irb.
# --tracer Display trace for each execution of commands.
# --back-trace-limit n
# Display backtrace top n and tail n. The default
# value is 16.
-# -v, --version Print the version of irb
+# --verbose Show details
+# --noverbose Don't show details
+# -v, --version Print the version of irb
+# -h, --help Print help
+# -- Separate options of irb from the list of command-line args
#
# == Configuration
#
@@ -463,7 +472,7 @@ module IRB
conf[:IRB_RC].call(context) if conf[:IRB_RC]
conf[:MAIN_CONTEXT] = context
- trap("SIGINT") do
+ prev_trap = trap("SIGINT") do
signal_handle
end
@@ -472,6 +481,7 @@ module IRB
eval_input
end
ensure
+ trap("SIGINT", prev_trap)
conf[:AT_EXIT].each{|hook| hook.call}
end
end
diff --git a/lib/irb/cmd/ls.rb b/lib/irb/cmd/ls.rb
new file mode 100644
index 0000000000..f163f4f9e6
--- /dev/null
+++ b/lib/irb/cmd/ls.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require "reline"
+require_relative "nop"
+require_relative "../color"
+
+# :stopdoc:
+module IRB
+ module ExtendCommand
+ class Ls < Nop
+ def execute(*arg, grep: nil)
+ o = Output.new(grep: grep)
+
+ obj = arg.empty? ? irb_context.workspace.main : arg.first
+ locals = arg.empty? ? irb_context.workspace.binding.local_variables : []
+ klass = (obj.class == Class || obj.class == Module ? obj : obj.class)
+
+ o.dump("constants", obj.constants) if obj.respond_to?(:constants)
+ o.dump("#{klass}.methods", obj.singleton_methods(false))
+ o.dump("#{klass}#methods", klass.public_instance_methods(false))
+ o.dump("instance variables", obj.instance_variables)
+ o.dump("class variables", klass.class_variables)
+ o.dump("locals", locals)
+ end
+
+ class Output
+ MARGIN = " "
+
+ def initialize(grep: nil)
+ @grep = grep
+ @line_width = screen_width - MARGIN.length # right padding
+ end
+
+ def dump(name, strs)
+ strs = strs.grep(@grep) if @grep
+ strs = strs.sort
+ return if strs.empty?
+
+ # Attempt a single line
+ print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
+ if fits_on_line?(strs, cols: strs.size, offset: "#{name}: ".length)
+ puts strs.join(MARGIN)
+ return
+ end
+ puts
+
+ # Dump with the largest # of columns that fits on a line
+ cols = strs.size
+ until fits_on_line?(strs, cols: cols, offset: MARGIN.length) || cols == 1
+ cols -= 1
+ end
+ widths = col_widths(strs, cols: cols)
+ strs.each_slice(cols) do |ss|
+ puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
+ end
+ end
+
+ private
+
+ def fits_on_line?(strs, cols:, offset: 0)
+ width = col_widths(strs, cols: cols).sum + MARGIN.length * (cols - 1)
+ width <= @line_width - offset
+ end
+
+ def col_widths(strs, cols:)
+ cols.times.map do |col|
+ (col...strs.size).step(cols).map do |i|
+ strs[i].length
+ end.max
+ end
+ end
+
+ def screen_width
+ Reline.get_screen_size.last
+ rescue Errno::EINVAL # in `winsize': Invalid argument - <STDIN>
+ 80
+ end
+ end
+ private_constant :Output
+ end
+ end
+end
+# :startdoc:
diff --git a/lib/irb/cmd/nop.rb b/lib/irb/cmd/nop.rb
index fa3c011b5f..d6f7a611a6 100644
--- a/lib/irb/cmd/nop.rb
+++ b/lib/irb/cmd/nop.rb
@@ -14,10 +14,16 @@ module IRB
module ExtendCommand
class Nop
-
- def self.execute(conf, *opts, &block)
- command = new(conf)
- command.execute(*opts, &block)
+ if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.7.0"
+ def self.execute(conf, *opts, **kwargs, &block)
+ command = new(conf)
+ command.execute(*opts, **kwargs, &block)
+ end
+ else
+ def self.execute(conf, *opts, &block)
+ command = new(conf)
+ command.execute(*opts, &block)
+ end
end
def initialize(conf)
diff --git a/lib/irb/cmd/show_source.rb b/lib/irb/cmd/show_source.rb
new file mode 100644
index 0000000000..0bd40b7d4e
--- /dev/null
+++ b/lib/irb/cmd/show_source.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require_relative "nop"
+require_relative "../color"
+require_relative "../ruby-lex"
+
+# :stopdoc:
+module IRB
+ module ExtendCommand
+ class ShowSource < Nop
+ def execute(str = nil)
+ unless str.is_a?(String)
+ puts "Error: Expected a string but got #{str.inspect}"
+ return
+ end
+ source = find_source(str)
+ if source && File.exist?(source.file)
+ show_source(source)
+ else
+ puts "Error: Couldn't locate a definition for #{str}"
+ end
+ nil
+ end
+
+ private
+
+ # @param [IRB::ExtendCommand::ShowSource::Source] source
+ def show_source(source)
+ puts
+ puts "#{bold("From")}: #{source.file}:#{source.first_line}"
+ puts
+ code = IRB::Color.colorize_code(File.read(source.file))
+ puts code.lines[(source.first_line - 1)...source.last_line].join
+ puts
+ end
+
+ def find_source(str)
+ case str
+ when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
+ eval(str, irb_context.workspace.binding) # trigger autoload
+ base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
+ file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
+ when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
+ owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
+ method = Regexp.last_match[:method]
+ if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
+ file, line = owner.instance_method(method).source_location
+ end
+ when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
+ receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
+ method = Regexp.last_match[:method]
+ file, line = receiver.method(method).source_location if receiver.respond_to?(method)
+ end
+ if file && line
+ Source.new(file: file, first_line: line, last_line: find_end(file, line))
+ end
+ end
+
+ def find_end(file, first_line)
+ return first_line unless File.exist?(file)
+ lex = RubyLex.new
+ code = +""
+ File.read(file).lines[(first_line - 1)..-1].each_with_index do |line, i|
+ _ltype, _indent, continue, code_block_open = lex.check_state(code << line)
+ if !continue && !code_block_open
+ return first_line + i
+ end
+ end
+ first_line
+ end
+
+ def bold(str)
+ Color.colorize(str, [:BOLD])
+ end
+
+ Source = Struct.new(
+ :file, # @param [String] - file name
+ :first_line, # @param [String] - first line
+ :last_line, # @param [String] - last line
+ keyword_init: true,
+ )
+ private_constant :Source
+ end
+ end
+end
+# :startdoc:
diff --git a/lib/irb/cmd/whereami.rb b/lib/irb/cmd/whereami.rb
new file mode 100644
index 0000000000..b3def11b93
--- /dev/null
+++ b/lib/irb/cmd/whereami.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require_relative "nop"
+
+# :stopdoc:
+module IRB
+ module ExtendCommand
+ class Whereami < Nop
+ def execute(*)
+ code = irb_context.workspace.code_around_binding
+ if code
+ puts code
+ else
+ puts "The current context doesn't have code."
+ end
+ end
+ end
+ end
+end
+# :startdoc:
diff --git a/lib/irb/color.rb b/lib/irb/color.rb
index a054bb20f8..cfbb3cc668 100644
--- a/lib/irb/color.rb
+++ b/lib/irb/color.rb
@@ -64,6 +64,7 @@ module IRB # :nodoc:
on_alias_error: [[RED, REVERSE], ALL],
on_class_name_error:[[RED, REVERSE], ALL],
on_param_error: [[RED, REVERSE], ALL],
+ on___end__: [[GREEN], ALL],
}
rescue NameError
# Give up highlighting Ripper-incompatible older Ruby
@@ -120,6 +121,7 @@ module IRB # :nodoc:
symbol_state = SymbolState.new
colored = +''
length = 0
+ end_seen = false
scan(code, allow_last_error: !complete) do |token, str, expr|
# IRB::ColorPrinter skips colorizing fragments with any invalid token
@@ -138,10 +140,11 @@ module IRB # :nodoc:
end
end
length += str.bytesize
+ end_seen = true if token == :on___end__
end
# give up colorizing incomplete Ripper tokens
- if length != code.bytesize
+ unless end_seen or length == code.bytesize
return Reline::Unicode.escape_for_print(code)
end
diff --git a/lib/irb/color_printer.rb b/lib/irb/color_printer.rb
index 92afea51cd..30c6825750 100644
--- a/lib/irb/color_printer.rb
+++ b/lib/irb/color_printer.rb
@@ -21,6 +21,15 @@ module IRB
end
end
+ def pp(obj)
+ if obj.is_a?(String)
+ # Avoid calling Ruby 2.4+ String#pretty_print that splits a string by "\n"
+ text(obj.inspect)
+ else
+ super
+ end
+ end
+
def text(str, width = nil)
unless str.is_a?(String)
str = str.inspect
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb
index 22a1ad1d3d..d1bb82122e 100644
--- a/lib/irb/completion.rb
+++ b/lib/irb/completion.rb
@@ -7,7 +7,7 @@
# From Original Idea of shugo@ruby-lang.org
#
-autoload :RDoc, "rdoc"
+require_relative 'ruby-lex'
module IRB
module InputCompletor # :nodoc:
@@ -38,8 +38,69 @@ module IRB
BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
- CompletionProc = proc { |input|
- retrieve_completion_data(input).compact.map{ |i| i.encode(Encoding.default_external) }
+ def self.retrieve_files_to_require_from_load_path
+ @@files_from_load_path ||= $LOAD_PATH.flat_map { |path|
+ begin
+ Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: path)
+ rescue Errno::ENOENT
+ []
+ end
+ }.uniq.map { |path|
+ path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
+ }
+ end
+
+ def self.retrieve_files_to_require_relative_from_current_dir
+ @@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
+ path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
+ }
+ end
+
+ CompletionRequireProc = lambda { |target, preposing = nil, postposing = nil|
+ if target =~ /\A(['"])([^'"]+)\Z/
+ quote = $1
+ actual_target = $2
+ else
+ return nil # It's not String literal
+ end
+ tokens = RubyLex.ripper_lex_without_warning(preposing.gsub(/\s*\z/, ''))
+ tok = nil
+ tokens.reverse_each do |t|
+ unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event)
+ tok = t
+ break
+ end
+ end
+ result = []
+ if tok && tok.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
+ case tok.tok
+ when 'require'
+ result = retrieve_files_to_require_from_load_path.select { |path|
+ path.start_with?(actual_target)
+ }.map { |path|
+ quote + path
+ }
+ when 'require_relative'
+ result = retrieve_files_to_require_relative_from_current_dir.select { |path|
+ path.start_with?(actual_target)
+ }.map { |path|
+ quote + path
+ }
+ end
+ end
+ result
+ }
+
+ CompletionProc = lambda { |target, preposing = nil, postposing = nil|
+ if preposing && postposing
+ result = CompletionRequireProc.(target, preposing, postposing)
+ unless result
+ result = retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
+ end
+ result
+ else
+ retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
+ end
}
def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
@@ -266,13 +327,22 @@ module IRB
end
PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) {
+ begin
+ require 'rdoc'
+ rescue LoadError
+ return
+ end
+
RDocRIDriver ||= RDoc::RI::Driver.new
+
if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
IRB.__send__(:easter_egg)
return
end
+
namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true)
return unless namespace
+
if namespace.is_a?(Array)
out = RDoc::Markup::Document.new
namespace.each do |m|
diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb
index ac358c8ccb..7acaebe36a 100644
--- a/lib/irb/ext/save-history.rb
+++ b/lib/irb/ext/save-history.rb
@@ -81,6 +81,8 @@ module IRB
end
}
end
+ @loaded_history_lines = history.size
+ @loaded_history_mtime = File.mtime(history_file)
end
end
@@ -105,12 +107,20 @@ module IRB
raise
end
- open(history_file, "w:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
+ if File.exist?(history_file) && @loaded_history_mtime &&
+ File.mtime(history_file) != @loaded_history_mtime
+ history = history[@loaded_history_lines..-1]
+ append_history = true
+ end
+
+ open(history_file, "#{append_history ? 'a' : 'w'}:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
hist = history.map{ |l| l.split("\n").join("\\\n") }
- begin
- hist = hist.last(num) if hist.size > num and num > 0
- rescue RangeError # bignum too big to convert into `long'
- # Do nothing because the bignum should be treated as inifinity
+ unless append_history
+ begin
+ hist = hist.last(num) if hist.size > num and num > 0
+ rescue RangeError # bignum too big to convert into `long'
+ # Do nothing because the bignum should be treated as inifinity
+ end
end
f.puts(hist)
end
diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb
index 347323247e..339e9e6084 100644
--- a/lib/irb/extend-command.rb
+++ b/lib/irb/extend-command.rb
@@ -126,7 +126,23 @@ module IRB # :nodoc:
],
[
- :measure, :Measure, "irb/cmd/measure"
+ :irb_ls, :Ls, "irb/cmd/ls",
+ [:ls, NO_OVERRIDE],
+ ],
+
+ [
+ :irb_measure, :Measure, "irb/cmd/measure",
+ [:measure, NO_OVERRIDE],
+ ],
+
+ [
+ :irb_show_source, :ShowSource, "irb/cmd/show_source",
+ [:show_source, NO_OVERRIDE],
+ ],
+
+ [
+ :irb_whereami, :Whereami, "irb/cmd/whereami",
+ [:whereami, NO_OVERRIDE],
],
]
@@ -168,12 +184,13 @@ module IRB # :nodoc:
end
if load_file
+ kwargs = ", **kwargs" if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.7.0"
line = __LINE__; eval %[
- def #{cmd_name}(*opts, &b)
+ def #{cmd_name}(*opts#{kwargs}, &b)
require "#{load_file}"
arity = ExtendCommand::#{cmd_class}.instance_method(:execute).arity
args = (1..(arity < 0 ? ~arity : arity)).map {|i| "arg" + i.to_s }
- args << "*opts" if arity < 0
+ args << "*opts#{kwargs}" if arity < 0
args << "&block"
args = args.join(", ")
line = __LINE__; eval %[
@@ -184,7 +201,7 @@ module IRB # :nodoc:
end
end
], nil, __FILE__, line
- __send__ :#{cmd_name}_, *opts, &b
+ __send__ :#{cmd_name}_, *opts#{kwargs}, &b
end
], nil, __FILE__, line
else
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index e223672985..1854567a2d 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -280,6 +280,7 @@ module IRB
Reline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
end
Reline.completion_append_character = nil
+ Reline.completer_quote_characters = ''
Reline.completion_proc = IRB::InputCompletor::CompletionProc
Reline.output_modifier_proc =
if IRB.conf[:USE_COLORIZE]
diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec
index 9842b4bce1..38fee9d9fb 100644
--- a/lib/irb/irb.gemspec
+++ b/lib/irb/irb.gemspec
@@ -28,53 +28,8 @@ Gem::Specification.new do |spec|
"doc/irb/irb.rd.ja",
"exe/irb",
"irb.gemspec",
- "lib/irb.rb",
- "lib/irb/cmd/chws.rb",
- "lib/irb/cmd/fork.rb",
- "lib/irb/cmd/help.rb",
- "lib/irb/cmd/info.rb",
- "lib/irb/cmd/load.rb",
- "lib/irb/cmd/measure.rb",
- "lib/irb/cmd/nop.rb",
- "lib/irb/cmd/pushws.rb",
- "lib/irb/cmd/subirb.rb",
- "lib/irb/color.rb",
- "lib/irb/color_printer.rb",
- "lib/irb/completion.rb",
- "lib/irb/context.rb",
- "lib/irb/easter-egg.rb",
- "lib/irb/ext/change-ws.rb",
- "lib/irb/ext/history.rb",
- "lib/irb/ext/loader.rb",
- "lib/irb/ext/multi-irb.rb",
- "lib/irb/ext/save-history.rb",
- "lib/irb/ext/tracer.rb",
- "lib/irb/ext/use-loader.rb",
- "lib/irb/ext/workspaces.rb",
- "lib/irb/extend-command.rb",
- "lib/irb/frame.rb",
- "lib/irb/help.rb",
- "lib/irb/init.rb",
- "lib/irb/input-method.rb",
- "lib/irb/inspector.rb",
- "lib/irb/lc/error.rb",
- "lib/irb/lc/help-message",
- "lib/irb/lc/ja/encoding_aliases.rb",
- "lib/irb/lc/ja/error.rb",
- "lib/irb/lc/ja/help-message",
- "lib/irb/locale.rb",
- "lib/irb/magic-file.rb",
- "lib/irb/notifier.rb",
- "lib/irb/output-method.rb",
- "lib/irb/ruby-lex.rb",
- "lib/irb/ruby_logo.aa",
- "lib/irb/src_encoding.rb",
- "lib/irb/version.rb",
- "lib/irb/workspace.rb",
- "lib/irb/ws-for-case-2.rb",
- "lib/irb/xmp.rb",
"man/irb.1",
- ]
+ ] + Dir.glob("lib/**/*")
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message
index a80facc9c5..9c3ea859ad 100644
--- a/lib/irb/lc/help-message
+++ b/lib/irb/lc/help-message
@@ -10,7 +10,7 @@
#
#
Usage: irb.rb [options] [programfile] [arguments]
- -f Suppress read of ~/.irbrc
+ -f Suppress read of ~/.irbrc
-d Set $DEBUG to true (same as `ruby -d')
-r load-module Same as `ruby -r'
-I path Specify $LOAD_PATH directory
@@ -18,7 +18,7 @@ Usage: irb.rb [options] [programfile] [arguments]
-E enc Same as `ruby -E`
-w Same as `ruby -w`
-W[level=2] Same as `ruby -W`
- --context-mode n Set n[0-3] to method to create Binding Object,
+ --context-mode n Set n[0-4] to method to create Binding Object,
when new workspace was created
--echo Show result(default)
--noecho Don't show result
@@ -31,8 +31,8 @@ Usage: irb.rb [options] [programfile] [arguments]
--colorize Use colorization
--nocolorize Don't use colorization
--prompt prompt-mode/--prompt-mode prompt-mode
- Switch prompt mode. Pre-defined prompt modes are
- `default', `simple', `xmp' and `inf-ruby'
+ Switch prompt mode. Pre-defined prompt modes are
+ `default', `simple', `xmp' and `inf-ruby'
--inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs.
Suppresses --multiline and --singleline.
--sample-book-mode/--simple-prompt
@@ -41,8 +41,8 @@ Usage: irb.rb [options] [programfile] [arguments]
--single-irb Share self with sub-irb.
--tracer Display trace for each execution of commands.
--back-trace-limit n
- Display backtrace top n and tail n. The default
- value is 16.
+ Display backtrace top n and tail n. The default
+ value is 16.
--verbose Show details
--noverbose Don't show details
-v, --version Print the version of irb
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index ce94797dad..82df06da2b 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -47,12 +47,26 @@ class RubyLex
@io = io
if @io.respond_to?(:check_termination)
@io.check_termination do |code|
- code.gsub!(/\s*\z/, '').concat("\n")
- ltype, indent, continue, code_block_open = check_state(code)
- if ltype or indent > 0 or continue or code_block_open
- false
+ if Reline::IOGate.in_pasting?
+ lex = RubyLex.new
+ rest = lex.check_termination_in_prev_line(code)
+ if rest
+ Reline.delete_text
+ rest.bytes.reverse_each do |c|
+ Reline.ungetc(c)
+ end
+ true
+ else
+ false
+ end
else
- true
+ code.gsub!(/\s*\z/, '').concat("\n")
+ ltype, indent, continue, code_block_open = check_state(code)
+ if ltype or indent > 0 or continue or code_block_open
+ false
+ else
+ true
+ end
end
end
end
@@ -60,7 +74,7 @@ class RubyLex
@io.dynamic_prompt do |lines|
lines << '' if lines.empty?
result = []
- tokens = ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join)
+ tokens = self.class.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join)
code = String.new
partial_tokens = []
unprocessed_tokens = []
@@ -115,10 +129,10 @@ class RubyLex
:on_param_error
]
- def ripper_lex_without_warning(code)
+ def self.ripper_lex_without_warning(code)
verbose, $VERBOSE = $VERBOSE, nil
tokens = nil
- self.class.compile_with_errors_suppressed(code) do |inner_code, line_no|
+ compile_with_errors_suppressed(code) do |inner_code, line_no|
lexer = Ripper::Lexer.new(inner_code, '-', line_no)
if lexer.respond_to?(:scan) # Ruby 2.7+
tokens = []
@@ -168,7 +182,7 @@ class RubyLex
if @io.respond_to?(:auto_indent) and context.auto_indent_mode
@io.auto_indent do |lines, line_index, byte_pointer, is_newline|
if is_newline
- @tokens = ripper_lex_without_warning(lines[0..line_index].join("\n"))
+ @tokens = self.class.ripper_lex_without_warning(lines[0..line_index].join("\n"))
prev_spaces = find_prev_spaces(line_index)
depth_difference = check_newline_depth_difference
depth_difference = 0 if depth_difference < 0
@@ -177,7 +191,7 @@ class RubyLex
code = line_index.zero? ? '' : lines[0..(line_index - 1)].map{ |l| l + "\n" }.join
last_line = lines[line_index]&.byteslice(0, byte_pointer)
code += last_line if last_line
- @tokens = ripper_lex_without_warning(code)
+ @tokens = self.class.ripper_lex_without_warning(code)
corresponding_token_depth = check_corresponding_token_depth
if corresponding_token_depth
corresponding_token_depth
@@ -190,7 +204,7 @@ class RubyLex
end
def check_state(code, tokens = nil)
- tokens = ripper_lex_without_warning(code) unless tokens
+ tokens = self.class.ripper_lex_without_warning(code) unless tokens
ltype = process_literal_type(tokens)
indent = process_nesting_level(tokens)
continue = process_continue(tokens)
@@ -256,7 +270,7 @@ class RubyLex
end
code = @line + (line.nil? ? '' : line)
code.gsub!(/\s*\z/, '').concat("\n")
- @tokens = ripper_lex_without_warning(code)
+ @tokens = self.class.ripper_lex_without_warning(code)
@continue = process_continue
@code_block_open = check_code_block(code)
@indent = process_nesting_level
@@ -277,8 +291,9 @@ class RubyLex
return true
elsif tokens.size >= 1 and tokens[-1][1] == :on_heredoc_end # "EOH\n"
return false
- elsif tokens.size >= 2 and defined?(Ripper::EXPR_BEG) and tokens[-2][3].anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME)
+ elsif tokens.size >= 2 and defined?(Ripper::EXPR_BEG) and tokens[-2][3].anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME) and tokens[-2][2] !~ /\A\.\.\.?\z/
# end of literal except for regexp
+ # endless range at end of line is not a continue
return true
end
false
@@ -738,5 +753,50 @@ class RubyLex
nil
end
end
+
+ def check_termination_in_prev_line(code)
+ tokens = self.class.ripper_lex_without_warning(code)
+ past_first_newline = false
+ index = tokens.rindex do |t|
+ # traverse first token before last line
+ if past_first_newline
+ if t.tok.include?("\n")
+ true
+ end
+ elsif t.tok.include?("\n")
+ past_first_newline = true
+ false
+ else
+ false
+ end
+ end
+ if index
+ first_token = nil
+ last_line_tokens = tokens[(index + 1)..(tokens.size - 1)]
+ last_line_tokens.each do |t|
+ unless [:on_sp, :on_ignored_sp, :on_comment].include?(t.event)
+ first_token = t
+ break
+ end
+ end
+ if first_token.nil?
+ return false
+ elsif first_token && first_token.state == Ripper::EXPR_DOT
+ return false
+ else
+ tokens_without_last_line = tokens[0..index]
+ ltype = process_literal_type(tokens_without_last_line)
+ indent = process_nesting_level(tokens_without_last_line)
+ continue = process_continue(tokens_without_last_line)
+ code_block_open = check_code_block(tokens_without_last_line.map(&:tok).join(''), tokens_without_last_line)
+ if ltype or indent > 0 or continue or code_block_open
+ return false
+ else
+ return last_line_tokens.map(&:tok).join('')
+ end
+ end
+ end
+ false
+ end
end
# :startdoc:
diff --git a/lib/irb/version.rb b/lib/irb/version.rb
index 0a4a1bb922..0014bdda74 100644
--- a/lib/irb/version.rb
+++ b/lib/irb/version.rb
@@ -11,7 +11,7 @@
#
module IRB # :nodoc:
- VERSION = "1.3.4"
+ VERSION = "1.3.5"
@RELEASE_VERSION = VERSION
- @LAST_UPDATE_DATE = "2021-02-25"
+ @LAST_UPDATE_DATE = "2021-04-03"
end
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
index 672c5448fa..66f2960cb8 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -207,8 +207,8 @@ module MakeMakefile
['RUBYCOMMONDIR', '$(vendordir)$(target_prefix)'],
['RUBYLIBDIR', '$(vendorlibdir)$(target_prefix)'],
['RUBYARCHDIR', '$(vendorarchdir)$(target_prefix)'],
- ['HDRDIR', '$(rubyhdrdir)/ruby$(target_prefix)'],
- ['ARCHHDRDIR', '$(rubyhdrdir)/$(arch)/ruby$(target_prefix)'],
+ ['HDRDIR', '$(vendorhdrdir)$(target_prefix)'],
+ ['ARCHHDRDIR', '$(vendorarchhdrdir)$(target_prefix)'],
]
else
dirs = [
@@ -216,8 +216,8 @@ module MakeMakefile
['RUBYCOMMONDIR', '$(sitedir)$(target_prefix)'],
['RUBYLIBDIR', '$(sitelibdir)$(target_prefix)'],
['RUBYARCHDIR', '$(sitearchdir)$(target_prefix)'],
- ['HDRDIR', '$(rubyhdrdir)/ruby$(target_prefix)'],
- ['ARCHHDRDIR', '$(rubyhdrdir)/$(arch)/ruby$(target_prefix)'],
+ ['HDRDIR', '$(sitehdrdir)$(target_prefix)'],
+ ['ARCHHDRDIR', '$(sitearchhdrdir)$(target_prefix)'],
]
end
dirs << ['target_prefix', (target_prefix ? "/#{target_prefix}" : "")]
@@ -1942,7 +1942,7 @@ NULLCMD = #{CONFIG['NULLCMD']}
srcdir = #{srcdir.gsub(/\$\((srcdir)\)|\$\{(srcdir)\}/) {mkintpath(CONFIG[$1||$2]).unspace}}
topdir = #{mkintpath(topdir = $extmk ? CONFIG["topdir"] : $topdir).unspace}
hdrdir = #{(hdrdir = CONFIG["hdrdir"]) == topdir ? "$(topdir)" : mkintpath(hdrdir).unspace}
-arch_hdrdir = #{$arch_hdrdir.quote}
+arch_hdrdir = #{mkintpath($arch_hdrdir).unspace}
PATH_SEPARATOR = #{CONFIG['PATH_SEPARATOR']}
VPATH = #{vpath.join(CONFIG['PATH_SEPARATOR'])}
}
@@ -2034,6 +2034,11 @@ sitearch = #{CONFIG['sitearch']}
ruby_version = #{RbConfig::CONFIG['ruby_version']}
ruby = #{$ruby.sub(%r[\A#{Regexp.quote(RbConfig::CONFIG['bindir'])}(?=/|\z)]) {'$(bindir)'}}
RUBY = $(ruby#{sep})
+BUILTRUBY = #{if defined?($builtruby) && $builtruby
+ $builtruby
+ else
+ File.join('$(bindir)', CONFIG["RUBY_INSTALL_NAME"] + CONFIG['EXEEXT'])
+ end}
ruby_headers = #{headers.join(' ')}
RM = #{config_string('RM', &possible_command) || '$(RUBY) -run -e rm -- -f'}
@@ -2552,6 +2557,7 @@ site-install-rb: install-rb
$INCFLAGS << " -I$(hdrdir)/ruby/backward" unless $extmk
$INCFLAGS << " -I$(hdrdir) -I$(srcdir)"
$DLDFLAGS = with_config("dldflags", arg_config("DLDFLAGS", config["DLDFLAGS"])).dup
+ config_string("ADDITIONAL_DLDFLAGS") {|flags| $DLDFLAGS << " " << flags} unless $extmk
$LIBEXT = config['LIBEXT'].dup
$OBJEXT = config["OBJEXT"].dup
$EXEEXT = config["EXEEXT"].dup
diff --git a/lib/net/ftp.rb b/lib/net/ftp.rb
index 88e8655c1c..2161d30d7c 100644
--- a/lib/net/ftp.rb
+++ b/lib/net/ftp.rb
@@ -85,7 +85,7 @@ module Net
end
# :stopdoc:
- VERSION = "0.1.1"
+ VERSION = "0.1.2"
FTP_PORT = 21
CRLF = "\r\n"
DEFAULT_BLOCKSIZE = BufferedIO::BUFSIZE
@@ -98,6 +98,10 @@ module Net
# When +true+, the connection is in passive mode. Default: +true+.
attr_accessor :passive
+ # When +true+, use the IP address in PASV responses. Otherwise, it uses
+ # the same IP address for the control connection. Default: +false+.
+ attr_accessor :use_pasv_ip
+
# When +true+, all traffic to and from the server is written
# to +$stdout+. Default: +false+.
attr_accessor :debug_mode
@@ -206,6 +210,9 @@ module Net
# handshake.
# See Net::FTP#ssl_handshake_timeout for
# details. Default: +nil+.
+ # use_pasv_ip:: When +true+, use the IP address in PASV responses.
+ # Otherwise, it uses the same IP address for the control
+ # connection. Default: +false+.
# debug_mode:: When +true+, all traffic to and from the server is
# written to +$stdout+. Default: +false+.
#
@@ -266,6 +273,7 @@ module Net
@open_timeout = options[:open_timeout]
@ssl_handshake_timeout = options[:ssl_handshake_timeout]
@read_timeout = options[:read_timeout] || 60
+ @use_pasv_ip = options[:use_pasv_ip] || false
if host
connect(host, options[:port] || FTP_PORT)
if options[:username]
@@ -330,14 +338,19 @@ module Net
# SOCKS_SERVER, then a SOCKSSocket is returned, else a Socket is
# returned.
def open_socket(host, port) # :nodoc:
- return Timeout.timeout(@open_timeout, OpenTimeout) {
- if defined? SOCKSSocket and ENV["SOCKS_SERVER"]
- @passive = true
+ if defined? SOCKSSocket and ENV["SOCKS_SERVER"]
+ @passive = true
+ Timeout.timeout(@open_timeout, OpenTimeout) do
SOCKSSocket.open(host, port)
- else
- Socket.tcp(host, port)
end
- }
+ else
+ begin
+ Socket.tcp host, port, nil, nil, connect_timeout: @open_timeout
+ rescue Errno::ETIMEDOUT #raise Net:OpenTimeout instead for compatibility with previous versions
+ raise Net::OpenTimeout, "Timeout to open TCP connection to "\
+ "#{host}:#{port} (exceeds #{@open_timeout} seconds)"
+ end
+ end
end
private :open_socket
@@ -542,18 +555,22 @@ module Net
def transfercmd(cmd, rest_offset = nil) # :nodoc:
if @passive
host, port = makepasv
- conn = open_socket(host, port)
- if @resume and rest_offset
- resp = sendcmd("REST " + rest_offset.to_s)
- if !resp.start_with?("3")
+ begin
+ conn = open_socket(host, port)
+ if @resume and rest_offset
+ resp = sendcmd("REST " + rest_offset.to_s)
+ if !resp.start_with?("3")
+ raise FTPReplyError, resp
+ end
+ end
+ resp = sendcmd(cmd)
+ # skip 2XX for some ftp servers
+ resp = getresp if resp.start_with?("2")
+ if !resp.start_with?("1")
raise FTPReplyError, resp
end
- end
- resp = sendcmd(cmd)
- # skip 2XX for some ftp servers
- resp = getresp if resp.start_with?("2")
- if !resp.start_with?("1")
- raise FTPReplyError, resp
+ ensure
+ conn.close if conn && $!
end
else
sock = makeport
@@ -1045,10 +1062,11 @@ module Net
TIME_PARSER = ->(value, local = false) {
unless /\A(?<year>\d{4})(?<month>\d{2})(?<day>\d{2})
(?<hour>\d{2})(?<min>\d{2})(?<sec>\d{2})
- (?:\.(?<fractions>\d+))?/x =~ value
+ (?:\.(?<fractions>\d{1,17}))?/x =~ value
+ value = value[0, 97] + "..." if value.size > 100
raise FTPProtoError, "invalid time-val: #{value}"
end
- usec = fractions.to_i * 10 ** (6 - fractions.to_s.size)
+ usec = ".#{fractions}".to_r * 1_000_000 if fractions
Time.public_send(local ? :local : :utc, year, month, day, hour, min, sec, usec)
}
FACT_PARSERS = Hash.new(CASE_DEPENDENT_PARSER)
@@ -1356,7 +1374,7 @@ module Net
end
#
- # Returns +true+ iff the connection is closed.
+ # Returns +true+ if and only if the connection is closed.
#
def closed?
@sock == nil or @sock.closed?
@@ -1371,7 +1389,12 @@ module Net
raise FTPReplyError, resp
end
if m = /\((?<host>\d+(?:,\d+){3}),(?<port>\d+,\d+)\)/.match(resp)
- return parse_pasv_ipv4_host(m["host"]), parse_pasv_port(m["port"])
+ if @use_pasv_ip
+ host = parse_pasv_ipv4_host(m["host"])
+ else
+ host = @bare_sock.remote_address.ip_address
+ end
+ return host, parse_pasv_port(m["port"])
else
raise FTPProtoError, resp
end
diff --git a/lib/net/http/header.rb b/lib/net/http/header.rb
index a8901e79cb..495425d148 100644
--- a/lib/net/http/header.rb
+++ b/lib/net/http/header.rb
@@ -9,6 +9,8 @@
# convenient formats.
#
module Net::HTTPHeader
+ MAX_KEY_LENGTH = 1024
+ MAX_FIELD_LENGTH = 65536
def initialize_http_header(initheader)
@header = {}
@@ -19,6 +21,12 @@ module Net::HTTPHeader
warn "net/http: nil HTTP header: #{key}", uplevel: 3 if $VERBOSE
else
value = value.strip # raise error for invalid byte sequences
+ if key.to_s.bytesize > MAX_KEY_LENGTH
+ raise ArgumentError, "too long (#{key.bytesize} bytes) header: #{key[0, 30].inspect}..."
+ end
+ if value.to_s.bytesize > MAX_FIELD_LENGTH
+ raise ArgumentError, "header #{key} has too long field vallue: #{value.bytesize}"
+ end
if value.count("\r\n") > 0
raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF"
end
diff --git a/lib/net/imap.rb b/lib/net/imap.rb
index 505b4c8950..d45304f289 100644
--- a/lib/net/imap.rb
+++ b/lib/net/imap.rb
@@ -1218,12 +1218,14 @@ module Net
end
resp = @tagged_responses.delete(tag)
case resp.name
+ when /\A(?:OK)\z/ni
+ return resp
when /\A(?:NO)\z/ni
raise NoResponseError, resp
when /\A(?:BAD)\z/ni
raise BadResponseError, resp
else
- return resp
+ raise UnknownResponseError, resp
end
end
@@ -3719,6 +3721,10 @@ module Net
class ByeResponseError < ResponseError
end
+ # Error raised upon an unknown response from the server.
+ class UnknownResponseError < ResponseError
+ end
+
RESPONSE_ERRORS = Hash.new(ResponseError)
RESPONSE_ERRORS["NO"] = NoResponseError
RESPONSE_ERRORS["BAD"] = BadResponseError
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb
index cba92613c7..edf2ed5a00 100644
--- a/lib/net/protocol.rb
+++ b/lib/net/protocol.rb
@@ -26,7 +26,7 @@ require 'io/wait'
module Net # :nodoc:
class Protocol #:nodoc: internal use only
- VERSION = "0.1.0"
+ VERSION = "0.1.1"
private
def Protocol.protocol_param(name, val)
diff --git a/lib/optparse.rb b/lib/optparse.rb
index bc0e821460..598ebd12bd 100644
--- a/lib/optparse.rb
+++ b/lib/optparse.rb
@@ -72,10 +72,10 @@
# require 'optparse'
#
# options = {}
-# OptionParser.new do |opts|
-# opts.banner = "Usage: example.rb [options]"
+# OptionParser.new do |parser|
+# parser.banner = "Usage: example.rb [options]"
#
-# opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
+# parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
# options[:verbose] = v
# end
# end.parse!
@@ -96,15 +96,15 @@
# def self.parse(options)
# args = Options.new("world")
#
-# opt_parser = OptionParser.new do |opts|
-# opts.banner = "Usage: example.rb [options]"
+# opt_parser = OptionParser.new do |parser|
+# parser.banner = "Usage: example.rb [options]"
#
-# opts.on("-nNAME", "--name=NAME", "Name to say hello to") do |n|
+# parser.on("-nNAME", "--name=NAME", "Name to say hello to") do |n|
# args.name = n
# end
#
-# opts.on("-h", "--help", "Prints this help") do
-# puts opts
+# parser.on("-h", "--help", "Prints this help") do
+# puts parser
# exit
# end
# end
@@ -241,10 +241,10 @@
# require 'optparse'
#
# params = {}
-# OptionParser.new do |opts|
-# opts.on('-a')
-# opts.on('-b NUM', Integer)
-# opts.on('-v', '--verbose')
+# OptionParser.new do |parser|
+# parser.on('-a')
+# parser.on('-b NUM', Integer)
+# parser.on('-v', '--verbose')
# end.parse!(into: params)
#
# p params
@@ -419,7 +419,7 @@
# have any questions, file a ticket at http://bugs.ruby-lang.org.
#
class OptionParser
- OptionParser::Version = "0.1.0"
+ OptionParser::Version = "0.1.1"
# :stopdoc:
NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
@@ -1091,6 +1091,7 @@ XXX
@summary_width = width
@summary_indent = indent
@default_argv = ARGV
+ @require_exact = false
add_officious
yield self if block_given?
end
@@ -1164,6 +1165,10 @@ XXX
# Strings to be parsed in default.
attr_accessor :default_argv
+ # Whether to require that options match exactly (disallows providing
+ # abbreviated long option as short option).
+ attr_accessor :require_exact
+
#
# Heading banner preceding summary.
#
@@ -1305,13 +1310,16 @@ XXX
private :notwice
SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a} # :nodoc:
+
+ # :call-seq:
+ # make_switch(params, block = nil)
#
# Creates an OptionParser::Switch from the parameters. The parsed argument
# value is passed to the given block, where it can be processed.
#
# See at the beginning of OptionParser for some full examples.
#
- # +opts+ can include the following elements:
+ # +params+ can include the following elements:
#
# [Argument style:]
# One of the following:
@@ -1498,11 +1506,16 @@ XXX
nolong
end
+ # :call-seq:
+ # define(*params, &block)
+ #
def define(*opts, &block)
top.append(*(sw = make_switch(opts, block)))
sw[0]
end
+ # :call-seq:
+ # on(*params, &block)
#
# Add option switch and handler. See #make_switch for an explanation of
# parameters.
@@ -1513,11 +1526,16 @@ XXX
end
alias def_option define
+ # :call-seq:
+ # define_head(*params, &block)
+ #
def define_head(*opts, &block)
top.prepend(*(sw = make_switch(opts, block)))
sw[0]
end
+ # :call-seq:
+ # on_head(*params, &block)
#
# Add option switch like with #on, but at head of summary.
#
@@ -1527,12 +1545,18 @@ XXX
end
alias def_head_option define_head
+ # :call-seq:
+ # define_tail(*params, &block)
+ #
def define_tail(*opts, &block)
base.append(*(sw = make_switch(opts, block)))
sw[0]
end
#
+ # :call-seq:
+ # on_tail(*params, &block)
+ #
# Add option switch like with #on, but at tail of summary.
#
def on_tail(*opts, &block)
@@ -1583,6 +1607,9 @@ XXX
opt.tr!('_', '-')
begin
sw, = complete(:long, opt, true)
+ if require_exact && !sw.long.include?(arg)
+ raise InvalidOption, arg
+ end
rescue ParseError
raise $!.set_option(arg, true)
end
@@ -1607,6 +1634,7 @@ XXX
val = arg.delete_prefix('-')
has_arg = true
rescue InvalidOption
+ raise if require_exact
# if no short options match, try completion with long
# options.
sw, = complete(:long, opt)
@@ -1618,7 +1646,12 @@ XXX
end
begin
opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
+ rescue ParseError
+ raise $!.set_option(arg, arg.length > 2)
+ else
raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
+ end
+ begin
argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-')
val = cb.call(val) if cb
setter.call(sw.switch_name, val) if setter
diff --git a/lib/optparse/kwargs.rb b/lib/optparse/kwargs.rb
index ed58cc142b..5a2def4747 100644
--- a/lib/optparse/kwargs.rb
+++ b/lib/optparse/kwargs.rb
@@ -2,6 +2,9 @@
require 'optparse'
class OptionParser
+ # :call-seq:
+ # define_by_keywords(options, method, **params)
+ #
def define_by_keywords(options, meth, **opts)
meth.parameters.each do |type, name|
case type
diff --git a/lib/optparse/optparse.gemspec b/lib/optparse/optparse.gemspec
index afb90420f0..831c787ac7 100644
--- a/lib/optparse/optparse.gemspec
+++ b/lib/optparse/optparse.gemspec
@@ -23,7 +23,9 @@ Gem::Specification.new do |spec|
spec.metadata["source_code_uri"] = spec.homepage
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
+ `git ls-files -z`.split("\x0").reject { |f|
+ f.match(%r{\A(?:(?:test|spec|features)/|Gemfile|\.(?:editor|git))})
+ }
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
diff --git a/lib/pp.gemspec b/lib/pp.gemspec
index f3f3527086..b81df28bee 100644
--- a/lib/pp.gemspec
+++ b/lib/pp.gemspec
@@ -1,21 +1,24 @@
Gem::Specification.new do |spec|
spec.name = "pp"
- spec.version = "0.1.0"
+ spec.version = "0.2.1"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
spec.summary = %q{Provides a PrettyPrinter for Ruby objects}
spec.description = %q{Provides a PrettyPrinter for Ruby objects}
spec.homepage = "https://github.com/ruby/pp"
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
spec.licenses = ["Ruby", "BSD-2-Clause"]
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
+
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
- end
+ spec.files = %w[
+ LICENSE.txt
+ lib/pp.rb
+ pp.gemspec
+ ]
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
diff --git a/lib/pp.rb b/lib/pp.rb
index ce282bee00..72480e5304 100644
--- a/lib/pp.rb
+++ b/lib/pp.rb
@@ -237,7 +237,7 @@ class PP < PrettyPrint
else
sep.call
end
- yield(*v, **{})
+ RUBY_VERSION >= "3.0" ? yield(*v, **{}) : yield(*v)
}
end
diff --git a/lib/prettyprint.gemspec b/lib/prettyprint.gemspec
index 169267fb16..eae2227d60 100644
--- a/lib/prettyprint.gemspec
+++ b/lib/prettyprint.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "prettyprint"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
diff --git a/lib/racc/info.rb b/lib/racc/info.rb
index 6934f902e8..f599b13cbb 100644
--- a/lib/racc/info.rb
+++ b/lib/racc/info.rb
@@ -11,7 +11,7 @@
#++
module Racc
- VERSION = '1.5.1'
+ VERSION = '1.5.2'
Version = VERSION
Copyright = 'Copyright (c) 1999-2006 Minero Aoki'
end
diff --git a/lib/racc/racc.gemspec b/lib/racc/racc.gemspec
index 1e375fe26b..5c345891ad 100644
--- a/lib/racc/racc.gemspec
+++ b/lib/racc/racc.gemspec
@@ -1,8 +1,14 @@
# -*- encoding: utf-8 -*-
+begin
+ require_relative "lib/racc/info"
+rescue LoadError # Fallback to load version file in ruby core repository
+ require_relative "info"
+end
+
Gem::Specification.new do |s|
s.name = "racc"
- s.version = "1.5.1"
+ s.version = Racc::VERSION
s.summary = "Racc is a LALR(1) parser generator"
s.description = <<DESC
Racc is a LALR(1) parser generator.
diff --git a/lib/rdoc/generator/template/darkfish/css/rdoc.css b/lib/rdoc/generator/template/darkfish/css/rdoc.css
index 35aff77fcb..ebe2e93af6 100644
--- a/lib/rdoc/generator/template/darkfish/css/rdoc.css
+++ b/lib/rdoc/generator/template/darkfish/css/rdoc.css
@@ -79,6 +79,25 @@ pre {
border-radius: 0.2em;
}
+table {
+ margin: 0;
+ border-spacing: 0;
+ border-collapse: collapse;
+}
+
+table tr th, table tr td {
+ padding: 0.2em 0.4em;
+ border: 1px solid #ccc;
+}
+
+table tr th {
+ background-color: #eceaed;
+}
+
+table tr:nth-child(even) td {
+ background-color: #f5f4f6;
+}
+
/* @group Generic Classes */
.initially-hidden {
@@ -166,6 +185,7 @@ nav {
width: 260px;
font-family: Helvetica, sans-serif;
font-size: 14px;
+ border-right: 1px solid #ccc;
}
main {
diff --git a/lib/rdoc/markdown.rb b/lib/rdoc/markdown.rb
index 903e744105..3442f76b1b 100644
--- a/lib/rdoc/markdown.rb
+++ b/lib/rdoc/markdown.rb
@@ -344,9 +344,8 @@ class RDoc::Markdown
end
def scan(reg)
- if m = reg.match(@string[@pos..-1])
- width = m.end(0)
- @pos += width
+ if m = reg.match(@string, @pos)
+ @pos = m.end(0)
return true
end
@@ -897,7 +896,7 @@ class RDoc::Markdown
return _tmp
end
- # Block = @BlankLine* (BlockQuote | Verbatim | CodeFence | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain)
+ # Block = @BlankLine* (BlockQuote | Verbatim | CodeFence | Table | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain)
def _Block
_save = self.pos
@@ -923,6 +922,9 @@ class RDoc::Markdown
_tmp = apply(:_CodeFence)
break if _tmp
self.pos = _save2
+ _tmp = apply(:_Table)
+ break if _tmp
+ self.pos = _save2
_tmp = apply(:_Note)
break if _tmp
self.pos = _save2
@@ -1057,7 +1059,7 @@ class RDoc::Markdown
self.pos = _save3
break
end
- _tmp = scan(/\A(?-mix:#*)/)
+ _tmp = scan(/\G(?-mix:#*)/)
unless _tmp
self.pos = _save3
break
@@ -1097,7 +1099,7 @@ class RDoc::Markdown
_save = self.pos
while true # sequence
_text_start = self.pos
- _tmp = scan(/\A(?-mix:\#{1,6})/)
+ _tmp = scan(/\G(?-mix:\#{1,6})/)
if _tmp
text = get_text(_text_start)
end
@@ -1162,7 +1164,7 @@ class RDoc::Markdown
self.pos = _save3
break
end
- _tmp = scan(/\A(?-mix:#*)/)
+ _tmp = scan(/\G(?-mix:#*)/)
unless _tmp
self.pos = _save3
break
@@ -1222,7 +1224,7 @@ class RDoc::Markdown
_save = self.pos
while true # sequence
- _tmp = scan(/\A(?-mix:={1,})/)
+ _tmp = scan(/\G(?-mix:={1,})/)
unless _tmp
self.pos = _save
break
@@ -1243,7 +1245,7 @@ class RDoc::Markdown
_save = self.pos
while true # sequence
- _tmp = scan(/\A(?-mix:-{1,})/)
+ _tmp = scan(/\G(?-mix:-{1,})/)
unless _tmp
self.pos = _save
break
@@ -2127,7 +2129,7 @@ class RDoc::Markdown
self.pos = _save
break
end
- _tmp = scan(/\A(?-mix:[+*-])/)
+ _tmp = scan(/\G(?-mix:[+*-])/)
unless _tmp
self.pos = _save
break
@@ -9320,7 +9322,7 @@ class RDoc::Markdown
return _tmp
end
- # Inlines = (!@Endline Inline:i { i } | @Endline:c &Inline { c })+:chunks @Endline? { chunks }
+ # Inlines = (!@Endline Inline:i { i } | @Endline:c !(&{ github? } Ticks3 /[^`\n]*$/) &Inline { c })+:chunks @Endline? { chunks }
def _Inlines
_save = self.pos
@@ -9367,12 +9369,41 @@ class RDoc::Markdown
break
end
_save6 = self.pos
- _tmp = apply(:_Inline)
+
+ _save7 = self.pos
+ while true # sequence
+ _save8 = self.pos
+ _tmp = begin; github? ; end
+ self.pos = _save8
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _tmp = apply(:_Ticks3)
+ unless _tmp
+ self.pos = _save7
+ break
+ end
+ _tmp = scan(/\G(?-mix:[^`\n]*$)/)
+ unless _tmp
+ self.pos = _save7
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
self.pos = _save6
unless _tmp
self.pos = _save5
break
end
+ _save9 = self.pos
+ _tmp = apply(:_Inline)
+ self.pos = _save9
+ unless _tmp
+ self.pos = _save5
+ break
+ end
@result = begin; c ; end
_tmp = true
unless _tmp
@@ -9390,61 +9421,90 @@ class RDoc::Markdown
_ary << @result
while true
- _save7 = self.pos
+ _save10 = self.pos
while true # choice
- _save8 = self.pos
+ _save11 = self.pos
while true # sequence
- _save9 = self.pos
+ _save12 = self.pos
_tmp = _Endline()
_tmp = _tmp ? nil : true
- self.pos = _save9
+ self.pos = _save12
unless _tmp
- self.pos = _save8
+ self.pos = _save11
break
end
_tmp = apply(:_Inline)
i = @result
unless _tmp
- self.pos = _save8
+ self.pos = _save11
break
end
@result = begin; i ; end
_tmp = true
unless _tmp
- self.pos = _save8
+ self.pos = _save11
end
break
end # end sequence
break if _tmp
- self.pos = _save7
+ self.pos = _save10
- _save10 = self.pos
+ _save13 = self.pos
while true # sequence
_tmp = _Endline()
c = @result
unless _tmp
- self.pos = _save10
+ self.pos = _save13
break
end
- _save11 = self.pos
+ _save14 = self.pos
+
+ _save15 = self.pos
+ while true # sequence
+ _save16 = self.pos
+ _tmp = begin; github? ; end
+ self.pos = _save16
+ unless _tmp
+ self.pos = _save15
+ break
+ end
+ _tmp = apply(:_Ticks3)
+ unless _tmp
+ self.pos = _save15
+ break
+ end
+ _tmp = scan(/\G(?-mix:[^`\n]*$)/)
+ unless _tmp
+ self.pos = _save15
+ end
+ break
+ end # end sequence
+
+ _tmp = _tmp ? nil : true
+ self.pos = _save14
+ unless _tmp
+ self.pos = _save13
+ break
+ end
+ _save17 = self.pos
_tmp = apply(:_Inline)
- self.pos = _save11
+ self.pos = _save17
unless _tmp
- self.pos = _save10
+ self.pos = _save13
break
end
@result = begin; c ; end
_tmp = true
unless _tmp
- self.pos = _save10
+ self.pos = _save13
end
break
end # end sequence
break if _tmp
- self.pos = _save7
+ self.pos = _save10
break
end # end choice
@@ -9461,11 +9521,11 @@ class RDoc::Markdown
self.pos = _save
break
end
- _save12 = self.pos
+ _save18 = self.pos
_tmp = _Endline()
unless _tmp
_tmp = true
- self.pos = _save12
+ self.pos = _save18
end
unless _tmp
self.pos = _save
@@ -9664,7 +9724,7 @@ class RDoc::Markdown
_save3 = self.pos
while true # sequence
- _tmp = scan(/\A(?-mix:_+)/)
+ _tmp = scan(/\G(?-mix:_+)/)
unless _tmp
self.pos = _save3
break
@@ -9694,7 +9754,7 @@ class RDoc::Markdown
_save6 = self.pos
while true # sequence
- _tmp = scan(/\A(?-mix:_+)/)
+ _tmp = scan(/\G(?-mix:_+)/)
unless _tmp
self.pos = _save6
break
@@ -9757,7 +9817,7 @@ class RDoc::Markdown
break
end
_text_start = self.pos
- _tmp = scan(/\A(?-mix:[:\\`|*_{}\[\]()#+.!><-])/)
+ _tmp = scan(/\G(?-mix:[:\\`|*_{}\[\]()#+.!><-])/)
if _tmp
text = get_text(_text_start)
end
@@ -9883,7 +9943,7 @@ class RDoc::Markdown
self.pos = _save5
break
end
- _tmp = scan(/\A(?-mix:={1,}|-{1,})/)
+ _tmp = scan(/\G(?-mix:={1,}|-{1,})/)
unless _tmp
self.pos = _save5
break
@@ -10035,7 +10095,7 @@ class RDoc::Markdown
_save1 = self.pos
while true # sequence
_text_start = self.pos
- _tmp = scan(/\A(?-mix:\*{4,})/)
+ _tmp = scan(/\G(?-mix:\*{4,})/)
if _tmp
text = get_text(_text_start)
end
@@ -10065,7 +10125,7 @@ class RDoc::Markdown
self.pos = _save3
break
end
- _tmp = scan(/\A(?-mix:\*+)/)
+ _tmp = scan(/\G(?-mix:\*+)/)
unless _tmp
self.pos = _save3
break
@@ -10112,7 +10172,7 @@ class RDoc::Markdown
_save1 = self.pos
while true # sequence
_text_start = self.pos
- _tmp = scan(/\A(?-mix:_{4,})/)
+ _tmp = scan(/\G(?-mix:_{4,})/)
if _tmp
text = get_text(_text_start)
end
@@ -10142,7 +10202,7 @@ class RDoc::Markdown
self.pos = _save3
break
end
- _tmp = scan(/\A(?-mix:_+)/)
+ _tmp = scan(/\G(?-mix:_+)/)
unless _tmp
self.pos = _save3
break
@@ -11503,7 +11563,7 @@ class RDoc::Markdown
_save1 = self.pos
while true # sequence
- _tmp = scan(/\A(?-mix:[A-Za-z]+)/)
+ _tmp = scan(/\G(?-mix:[A-Za-z]+)/)
unless _tmp
self.pos = _save1
break
@@ -11628,7 +11688,7 @@ class RDoc::Markdown
_save2 = self.pos
while true # sequence
- _tmp = scan(/\A(?i-mx:[\w+.\/!%~$-]+)/)
+ _tmp = scan(/\G(?i-mx:[\w+.\/!%~$-]+)/)
unless _tmp
self.pos = _save2
break
@@ -12492,7 +12552,7 @@ class RDoc::Markdown
self.pos = _save10
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save10
end
@@ -12629,7 +12689,7 @@ class RDoc::Markdown
self.pos = _save24
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save24
end
@@ -12806,7 +12866,7 @@ class RDoc::Markdown
self.pos = _save40
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save40
end
@@ -12943,7 +13003,7 @@ class RDoc::Markdown
self.pos = _save54
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save54
end
@@ -13120,7 +13180,7 @@ class RDoc::Markdown
self.pos = _save70
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save70
end
@@ -13257,7 +13317,7 @@ class RDoc::Markdown
self.pos = _save84
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save84
end
@@ -13434,7 +13494,7 @@ class RDoc::Markdown
self.pos = _save100
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save100
end
@@ -13571,7 +13631,7 @@ class RDoc::Markdown
self.pos = _save114
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save114
end
@@ -13748,7 +13808,7 @@ class RDoc::Markdown
self.pos = _save130
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save130
end
@@ -13885,7 +13945,7 @@ class RDoc::Markdown
self.pos = _save144
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save144
end
@@ -14537,7 +14597,7 @@ class RDoc::Markdown
_save = self.pos
while true # choice
- _tmp = scan(/\A(?-mix:[~*_`&\[\]()<!#\\'"])/)
+ _tmp = scan(/\G(?-mix:[~*_`&\[\]()<!#\\'"])/)
break if _tmp
self.pos = _save
_tmp = _ExtendedSpecialChar()
@@ -14642,13 +14702,13 @@ class RDoc::Markdown
_save = self.pos
while true # sequence
- _tmp = scan(/\A(?i-mx:&#x)/)
+ _tmp = scan(/\G(?i-mx:&#x)/)
unless _tmp
self.pos = _save
break
end
_text_start = self.pos
- _tmp = scan(/\A(?-mix:[0-9a-fA-F]+)/)
+ _tmp = scan(/\G(?-mix:[0-9a-fA-F]+)/)
if _tmp
text = get_text(_text_start)
end
@@ -14684,7 +14744,7 @@ class RDoc::Markdown
break
end
_text_start = self.pos
- _tmp = scan(/\A(?-mix:[0-9]+)/)
+ _tmp = scan(/\G(?-mix:[0-9]+)/)
if _tmp
text = get_text(_text_start)
end
@@ -14720,7 +14780,7 @@ class RDoc::Markdown
break
end
_text_start = self.pos
- _tmp = scan(/\A(?-mix:[A-Za-z0-9]+)/)
+ _tmp = scan(/\G(?-mix:[A-Za-z0-9]+)/)
if _tmp
text = get_text(_text_start)
end
@@ -14752,14 +14812,14 @@ class RDoc::Markdown
# NonindentSpace = / {0,3}/
def _NonindentSpace
- _tmp = scan(/\A(?-mix: {0,3})/)
+ _tmp = scan(/\G(?-mix: {0,3})/)
set_failed_rule :_NonindentSpace unless _tmp
return _tmp
end
# Indent = /\t| /
def _Indent
- _tmp = scan(/\A(?-mix:\t| )/)
+ _tmp = scan(/\G(?-mix:\t| )/)
set_failed_rule :_Indent unless _tmp
return _tmp
end
@@ -15701,7 +15761,7 @@ class RDoc::Markdown
self.pos = _save11
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save11
end
@@ -15782,7 +15842,7 @@ class RDoc::Markdown
self.pos = _save19
break
end
- _tmp = scan(/\A(?-mix:`+)/)
+ _tmp = scan(/\G(?-mix:`+)/)
unless _tmp
self.pos = _save19
end
@@ -15847,6 +15907,338 @@ class RDoc::Markdown
return _tmp
end
+ # Table = &{ github? } TableRow:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }
+ def _Table
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _tmp = begin; github? ; end
+ self.pos = _save1
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_TableRow)
+ header = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = apply(:_TableLine)
+ line = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _save2 = self.pos
+ _ary = []
+ _tmp = apply(:_TableRow)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_TableRow)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save2
+ end
+ body = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; table = RDoc::Markup::Table.new(header, line, body) ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_Table unless _tmp
+ return _tmp
+ end
+
+ # TableRow = TableItem+:row "|" @Newline { row }
+ def _TableRow
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _ary = []
+ _tmp = apply(:_TableItem)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_TableItem)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save1
+ end
+ row = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("|")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; row ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TableRow unless _tmp
+ return _tmp
+ end
+
+ # TableItem = "|" < (!"|" !@Newline .)+ > { text.strip }
+ def _TableItem
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("|")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+ _save1 = self.pos
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+ _tmp = match_string("|")
+ _tmp = _tmp ? nil : true
+ self.pos = _save3
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _save4 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save4
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ if _tmp
+ while true
+
+ _save5 = self.pos
+ while true # sequence
+ _save6 = self.pos
+ _tmp = match_string("|")
+ _tmp = _tmp ? nil : true
+ self.pos = _save6
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _save7 = self.pos
+ _tmp = _Newline()
+ _tmp = _tmp ? nil : true
+ self.pos = _save7
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ _tmp = get_byte
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save1
+ end
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text.strip ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TableItem unless _tmp
+ return _tmp
+ end
+
+ # TableLine = TableColumn+:line "|" @Newline { line }
+ def _TableLine
+
+ _save = self.pos
+ while true # sequence
+ _save1 = self.pos
+ _ary = []
+ _tmp = apply(:_TableColumn)
+ if _tmp
+ _ary << @result
+ while true
+ _tmp = apply(:_TableColumn)
+ _ary << @result if _tmp
+ break unless _tmp
+ end
+ _tmp = true
+ @result = _ary
+ else
+ self.pos = _save1
+ end
+ line = @result
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = match_string("|")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _tmp = _Newline()
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; line ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TableLine unless _tmp
+ return _tmp
+ end
+
+ # TableColumn = "|" < ("-"+ ":"? | ":" "-"*) > { text.start_with?(":") ? :left : text.end_with?(":") ? :right : nil }
+ def _TableColumn
+
+ _save = self.pos
+ while true # sequence
+ _tmp = match_string("|")
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ _text_start = self.pos
+
+ _save1 = self.pos
+ while true # choice
+
+ _save2 = self.pos
+ while true # sequence
+ _save3 = self.pos
+ _tmp = match_string("-")
+ if _tmp
+ while true
+ _tmp = match_string("-")
+ break unless _tmp
+ end
+ _tmp = true
+ else
+ self.pos = _save3
+ end
+ unless _tmp
+ self.pos = _save2
+ break
+ end
+ _save4 = self.pos
+ _tmp = match_string(":")
+ unless _tmp
+ _tmp = true
+ self.pos = _save4
+ end
+ unless _tmp
+ self.pos = _save2
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+
+ _save5 = self.pos
+ while true # sequence
+ _tmp = match_string(":")
+ unless _tmp
+ self.pos = _save5
+ break
+ end
+ while true
+ _tmp = match_string("-")
+ break unless _tmp
+ end
+ _tmp = true
+ unless _tmp
+ self.pos = _save5
+ end
+ break
+ end # end sequence
+
+ break if _tmp
+ self.pos = _save1
+ break
+ end # end choice
+
+ if _tmp
+ text = get_text(_text_start)
+ end
+ unless _tmp
+ self.pos = _save
+ break
+ end
+ @result = begin; text.start_with?(":") ? :left :
+ text.end_with?(":") ? :right : nil
+ ; end
+ _tmp = true
+ unless _tmp
+ self.pos = _save
+ end
+ break
+ end # end sequence
+
+ set_failed_rule :_TableColumn unless _tmp
+ return _tmp
+ end
+
# DefinitionList = &{ definition_lists? } DefinitionListItem+:list { RDoc::Markup::List.new :NOTE, *list.flatten }
def _DefinitionList
@@ -16046,7 +16438,7 @@ class RDoc::Markdown
Rules = {}
Rules[:_root] = rule_info("root", "Doc")
Rules[:_Doc] = rule_info("Doc", "BOM? Block*:a { RDoc::Markup::Document.new(*a.compact) }")
- Rules[:_Block] = rule_info("Block", "@BlankLine* (BlockQuote | Verbatim | CodeFence | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain)")
+ Rules[:_Block] = rule_info("Block", "@BlankLine* (BlockQuote | Verbatim | CodeFence | Table | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain)")
Rules[:_Para] = rule_info("Para", "@NonindentSpace Inlines:a @BlankLine+ { paragraph a }")
Rules[:_Plain] = rule_info("Plain", "Inlines:a { paragraph a }")
Rules[:_AtxInline] = rule_info("AtxInline", "!@Newline !(@Sp /\#*/ @Sp @Newline) Inline")
@@ -16190,7 +16582,7 @@ class RDoc::Markdown
Rules[:_StyleClose] = rule_info("StyleClose", "\"<\" Spnl \"/\" (\"style\" | \"STYLE\") Spnl \">\"")
Rules[:_InStyleTags] = rule_info("InStyleTags", "StyleOpen (!StyleClose .)* StyleClose")
Rules[:_StyleBlock] = rule_info("StyleBlock", "< InStyleTags > @BlankLine* { if css? then RDoc::Markup::Raw.new text end }")
- Rules[:_Inlines] = rule_info("Inlines", "(!@Endline Inline:i { i } | @Endline:c &Inline { c })+:chunks @Endline? { chunks }")
+ Rules[:_Inlines] = rule_info("Inlines", "(!@Endline Inline:i { i } | @Endline:c !(&{ github? } Ticks3 /[^`\\n]*$/) &Inline { c })+:chunks @Endline? { chunks }")
Rules[:_Inline] = rule_info("Inline", "(Str | @Endline | UlOrStarLine | @Space | Strong | Emph | Strike | Image | Link | NoteReference | InlineNote | Code | RawHtml | Entity | EscapedChar | Symbol)")
Rules[:_Space] = rule_info("Space", "@Spacechar+ { \" \" }")
Rules[:_Str] = rule_info("Str", "@StartList:a < @NormalChar+ > { a = text } (StrChunk:c { a << c })* { a }")
@@ -16279,6 +16671,11 @@ class RDoc::Markdown
Rules[:_Notes] = rule_info("Notes", "(Note | SkipBlock)*")
Rules[:_RawNoteBlock] = rule_info("RawNoteBlock", "@StartList:a (!@BlankLine OptionallyIndentedLine:l { a << l })+ < @BlankLine* > { a << text } { a }")
Rules[:_CodeFence] = rule_info("CodeFence", "&{ github? } Ticks3 (@Sp StrChunk:format)? Spnl < ((!\"`\" Nonspacechar)+ | !Ticks3 /`+/ | Spacechar | @Newline)+ > Ticks3 @Sp @Newline* { verbatim = RDoc::Markup::Verbatim.new text verbatim.format = format.intern if format.instance_of?(String) verbatim }")
+ Rules[:_Table] = rule_info("Table", "&{ github? } TableRow:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }")
+ Rules[:_TableRow] = rule_info("TableRow", "TableItem+:row \"|\" @Newline { row }")
+ Rules[:_TableItem] = rule_info("TableItem", "\"|\" < (!\"|\" !@Newline .)+ > { text.strip }")
+ Rules[:_TableLine] = rule_info("TableLine", "TableColumn+:line \"|\" @Newline { line }")
+ Rules[:_TableColumn] = rule_info("TableColumn", "\"|\" < (\"-\"+ \":\"? | \":\" \"-\"*) > { text.start_with?(\":\") ? :left : text.end_with?(\":\") ? :right : nil }")
Rules[:_DefinitionList] = rule_info("DefinitionList", "&{ definition_lists? } DefinitionListItem+:list { RDoc::Markup::List.new :NOTE, *list.flatten }")
Rules[:_DefinitionListItem] = rule_info("DefinitionListItem", "DefinitionListLabel+:label DefinitionListDefinition+:defns { list_items = [] list_items << RDoc::Markup::ListItem.new(label, defns.shift) list_items.concat defns.map { |defn| RDoc::Markup::ListItem.new nil, defn } unless list_items.empty? list_items }")
Rules[:_DefinitionListLabel] = rule_info("DefinitionListLabel", "StrChunk:label @Sp @Newline { label }")
diff --git a/lib/rdoc/markdown/literals.rb b/lib/rdoc/markdown/literals.rb
index 31cd237f12..943c2d268a 100644
--- a/lib/rdoc/markdown/literals.rb
+++ b/lib/rdoc/markdown/literals.rb
@@ -174,9 +174,8 @@ class RDoc::Markdown::Literals
end
def scan(reg)
- if m = reg.match(@string[@pos..-1])
- width = m.end(0)
- @pos += width
+ if m = reg.match(@string, @pos)
+ @pos = m.end(0)
return true
end
@@ -366,14 +365,14 @@ class RDoc::Markdown::Literals
# Alphanumeric = /\p{Word}/
def _Alphanumeric
- _tmp = scan(/\A(?-mix:\p{Word})/)
+ _tmp = scan(/\G(?-mix:\p{Word})/)
set_failed_rule :_Alphanumeric unless _tmp
return _tmp
end
# AlphanumericAscii = /[A-Za-z0-9]/
def _AlphanumericAscii
- _tmp = scan(/\A(?-mix:[A-Za-z0-9])/)
+ _tmp = scan(/\G(?-mix:[A-Za-z0-9])/)
set_failed_rule :_AlphanumericAscii unless _tmp
return _tmp
end
@@ -387,21 +386,21 @@ class RDoc::Markdown::Literals
# Newline = /\n|\r\n?|\p{Zl}|\p{Zp}/
def _Newline
- _tmp = scan(/\A(?-mix:\n|\r\n?|\p{Zl}|\p{Zp})/)
+ _tmp = scan(/\G(?-mix:\n|\r\n?|\p{Zl}|\p{Zp})/)
set_failed_rule :_Newline unless _tmp
return _tmp
end
# NonAlphanumeric = /\p{^Word}/
def _NonAlphanumeric
- _tmp = scan(/\A(?-mix:\p{^Word})/)
+ _tmp = scan(/\G(?-mix:\p{^Word})/)
set_failed_rule :_NonAlphanumeric unless _tmp
return _tmp
end
# Spacechar = /\t|\p{Zs}/
def _Spacechar
- _tmp = scan(/\A(?-mix:\t|\p{Zs})/)
+ _tmp = scan(/\G(?-mix:\t|\p{Zs})/)
set_failed_rule :_Spacechar unless _tmp
return _tmp
end
diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb
index fd59fca314..92aed757cf 100644
--- a/lib/rdoc/markup.rb
+++ b/lib/rdoc/markup.rb
@@ -843,6 +843,7 @@ https://github.com/ruby/rdoc/issues
autoload :List, 'rdoc/markup/list'
autoload :ListItem, 'rdoc/markup/list_item'
autoload :Paragraph, 'rdoc/markup/paragraph'
+ autoload :Table, 'rdoc/markup/table'
autoload :Raw, 'rdoc/markup/raw'
autoload :Rule, 'rdoc/markup/rule'
autoload :Verbatim, 'rdoc/markup/verbatim'
diff --git a/lib/rdoc/markup/attr_span.rb b/lib/rdoc/markup/attr_span.rb
index 63aace60d2..20ef11cd6d 100644
--- a/lib/rdoc/markup/attr_span.rb
+++ b/lib/rdoc/markup/attr_span.rb
@@ -7,16 +7,22 @@ class RDoc::Markup::AttrSpan
##
# Creates a new AttrSpan for +length+ characters
- def initialize(length)
+ def initialize(length, exclusive)
@attrs = Array.new(length, 0)
+ @exclusive = exclusive
end
##
# Toggles +bits+ from +start+ to +length+
def set_attrs(start, length, bits)
+ updated = false
for i in start ... (start+length)
- @attrs[i] |= bits
+ if (@exclusive & @attrs[i]) == 0 || (@exclusive & bits) != 0
+ @attrs[i] |= bits
+ updated = true
+ end
end
+ updated
end
##
diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb
index f052bc8b01..50764510f3 100644
--- a/lib/rdoc/markup/attribute_manager.rb
+++ b/lib/rdoc/markup/attribute_manager.rb
@@ -59,6 +59,10 @@ class RDoc::Markup::AttributeManager
attr_reader :regexp_handlings
##
+ # A bits of exclusive maps
+ attr_reader :exclusive_bitmap
+
+ ##
# Creates a new attribute manager that understands bold, emphasized and
# teletype text.
@@ -68,17 +72,18 @@ class RDoc::Markup::AttributeManager
@protectable = %w[<]
@regexp_handlings = []
@word_pair_map = {}
+ @exclusive_bitmap = 0
@attributes = RDoc::Markup::Attributes.new
- add_word_pair "*", "*", :BOLD
- add_word_pair "_", "_", :EM
- add_word_pair "+", "+", :TT
+ add_word_pair "*", "*", :BOLD, true
+ add_word_pair "_", "_", :EM, true
+ add_word_pair "+", "+", :TT, true
- add_html "em", :EM
- add_html "i", :EM
- add_html "b", :BOLD
- add_html "tt", :TT
- add_html "code", :TT
+ add_html "em", :EM, true
+ add_html "i", :EM, true
+ add_html "b", :BOLD, true
+ add_html "tt", :TT, true
+ add_html "code", :TT, true
end
##
@@ -122,29 +127,67 @@ class RDoc::Markup::AttributeManager
res
end
+ def exclusive?(attr)
+ (attr & @exclusive_bitmap) != 0
+ end
+
+ NON_PRINTING_START = "\1" # :nodoc:
+ NON_PRINTING_END = "\2" # :nodoc:
+
##
# Map attributes like <b>text</b>to the sequence
# \001\002<char>\001\003<char>, where <char> is a per-attribute specific
# character
- def convert_attrs(str, attrs)
+ def convert_attrs(str, attrs, exclusive = false)
+ convert_attrs_matching_word_pairs(str, attrs, exclusive)
+ convert_attrs_word_pair_map(str, attrs, exclusive)
+ end
+
+ def convert_attrs_matching_word_pairs(str, attrs, exclusive)
# first do matching ones
- tags = @matching_word_pairs.keys.join("")
+ tags = @matching_word_pairs.select { |start, bitmap|
+ if exclusive && exclusive?(bitmap)
+ true
+ elsif !exclusive && !exclusive?(bitmap)
+ true
+ else
+ false
+ end
+ }.keys
+ return if tags.empty?
+ all_tags = @matching_word_pairs.keys
- re = /(^|\W)([#{tags}])([#\\]?[\w:.\/-]+?\S?)\2(\W|$)/
+ re = /(^|\W|[#{all_tags.join("")}])([#{tags.join("")}])(\2*[#\\]?[\w:.\/\[\]-]+?\S?)\2(?!\2)([#{all_tags.join("")}]|\W|$)/
- 1 while str.gsub!(re) do
+ 1 while str.gsub!(re) { |orig|
attr = @matching_word_pairs[$2]
- attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
- $1 + NULL * $2.length + $3 + NULL * $2.length + $4
- end
+ attr_updated = attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
+ if attr_updated
+ $1 + NULL * $2.length + $3 + NULL * $2.length + $4
+ else
+ $1 + NON_PRINTING_START + $2 + NON_PRINTING_END + $3 + NON_PRINTING_START + $2 + NON_PRINTING_END + $4
+ end
+ }
+ str.delete!(NON_PRINTING_START + NON_PRINTING_END)
+ end
+ def convert_attrs_word_pair_map(str, attrs, exclusive)
# then non-matching
unless @word_pair_map.empty? then
@word_pair_map.each do |regexp, attr|
- str.gsub!(regexp) {
- attrs.set_attrs($`.length + $1.length, $2.length, attr)
- NULL * $1.length + $2 + NULL * $3.length
+ if !exclusive
+ next if exclusive?(attr)
+ else
+ next if !exclusive?(attr)
+ end
+ 1 while str.gsub!(regexp) { |orig|
+ updated = attrs.set_attrs($`.length + $1.length, $2.length, attr)
+ if updated
+ NULL * $1.length + $2 + NULL * $3.length
+ else
+ orig
+ end
}
end
end
@@ -153,10 +196,18 @@ class RDoc::Markup::AttributeManager
##
# Converts HTML tags to RDoc attributes
- def convert_html(str, attrs)
- tags = @html_tags.keys.join '|'
+ def convert_html(str, attrs, exclusive = false)
+ tags = @html_tags.select { |start, bitmap|
+ if exclusive && exclusive?(bitmap)
+ true
+ elsif !exclusive && !exclusive?(bitmap)
+ true
+ else
+ false
+ end
+ }.keys.join '|'
- 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) {
+ 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) { |orig|
attr = @html_tags[$1.downcase]
html_length = $1.length + 2
seq = NULL * html_length
@@ -168,8 +219,13 @@ class RDoc::Markup::AttributeManager
##
# Converts regexp handling sequences to RDoc attributes
- def convert_regexp_handlings str, attrs
+ def convert_regexp_handlings str, attrs, exclusive = false
@regexp_handlings.each do |regexp, attribute|
+ if exclusive
+ next if !exclusive?(attribute)
+ else
+ next if exclusive?(attribute)
+ end
str.scan(regexp) do
capture = $~.size == 1 ? 0 : 1
@@ -205,7 +261,7 @@ class RDoc::Markup::AttributeManager
#
# am.add_word_pair '*', '*', :BOLD
- def add_word_pair(start, stop, name)
+ def add_word_pair(start, stop, name, exclusive = false)
raise ArgumentError, "Word flags may not start with '<'" if
start[0,1] == '<'
@@ -220,6 +276,8 @@ class RDoc::Markup::AttributeManager
@protectable << start[0,1]
@protectable.uniq!
+
+ @exclusive_bitmap |= bitmap if exclusive
end
##
@@ -228,8 +286,10 @@ class RDoc::Markup::AttributeManager
#
# am.add_html 'em', :EM
- def add_html(tag, name)
- @html_tags[tag.downcase] = @attributes.bitmap_for name
+ def add_html(tag, name, exclusive = false)
+ bitmap = @attributes.bitmap_for name
+ @html_tags[tag.downcase] = bitmap
+ @exclusive_bitmap |= bitmap if exclusive
end
##
@@ -238,8 +298,10 @@ class RDoc::Markup::AttributeManager
#
# @am.add_regexp_handling(/((https?:)\S+\w)/, :HYPERLINK)
- def add_regexp_handling pattern, name
- @regexp_handlings << [pattern, @attributes.bitmap_for(name)]
+ def add_regexp_handling pattern, name, exclusive = false
+ bitmap = @attributes.bitmap_for(name)
+ @regexp_handlings << [pattern, bitmap]
+ @exclusive_bitmap |= bitmap if exclusive
end
##
@@ -250,8 +312,11 @@ class RDoc::Markup::AttributeManager
mask_protected_sequences
- @attrs = RDoc::Markup::AttrSpan.new @str.length
+ @attrs = RDoc::Markup::AttrSpan.new @str.length, @exclusive_bitmap
+ convert_attrs @str, @attrs, true
+ convert_html @str, @attrs, true
+ convert_regexp_handlings @str, @attrs, true
convert_attrs @str, @attrs
convert_html @str, @attrs
convert_regexp_handlings @str, @attrs
diff --git a/lib/rdoc/markup/table.rb b/lib/rdoc/markup/table.rb
new file mode 100644
index 0000000000..7bcb10aff3
--- /dev/null
+++ b/lib/rdoc/markup/table.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+##
+# A section of table
+
+class RDoc::Markup::Table
+ attr_accessor :header, :align, :body
+
+ def initialize header, align, body
+ @header, @align, @body = header, align, body
+ end
+
+ def == other
+ self.class == other.class and
+ @header == other.header and
+ @align == other.align and
+ @body == other.body
+ end
+
+ def accept visitor
+ visitor.accept_table @header, @body, @align
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Table: ', ']' do
+ q.group 2, '[Head: ', ']' do
+ q.seplist @header.zip(@align) do |text, align|
+ q.pp text
+ if align
+ q.text ":"
+ q.breakable
+ q.text align.to_s
+ end
+ end
+ end
+ q.breakable
+ q.group 2, '[Body: ', ']' do
+ q.seplist @body do |body|
+ q.group 2, '[', ']' do
+ q.seplist body do |text|
+ q.pp text
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
index 3b1b0e9d40..8ae4dd4720 100644
--- a/lib/rdoc/markup/to_html.rb
+++ b/lib/rdoc/markup/to_html.rb
@@ -314,6 +314,29 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
@res << raw.parts.join("\n")
end
+ ##
+ # Adds +table+ to the output
+
+ def accept_table header, body, aligns
+ @res << "\n<table role=\"table\">\n<thead>\n<tr>\n"
+ header.zip(aligns) do |text, align|
+ @res << '<th'
+ @res << ' align="' << align << '"' if align
+ @res << '>' << CGI.escapeHTML(text) << "</th>\n"
+ end
+ @res << "</tr>\n</thead>\n<tbody>\n"
+ body.each do |row|
+ @res << "<tr>\n"
+ row.zip(aligns) do |text, align|
+ @res << '<td'
+ @res << ' align="' << align << '"' if align
+ @res << '>' << CGI.escapeHTML(text) << "</td>\n"
+ end
+ @res << "</tr>\n"
+ end
+ @res << "</tbody>\n</table>\n"
+ end
+
# :section: Utilities
##
@@ -334,6 +357,10 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
"<img src=\"#{url}\" />"
else
+ if scheme != 'link' and /\.(?:rb|rdoc|md)\z/i =~ url
+ url = url.sub(%r%\A([./]*)(.*)\z%) { "#$1#{$2.tr('.', '_')}.html" }
+ end
+
text = text.sub %r%^#{scheme}:/*%i, ''
text = text.sub %r%^[*\^](\d+)$%, '\1'
diff --git a/lib/rdoc/markup/to_joined_paragraph.rb b/lib/rdoc/markup/to_joined_paragraph.rb
index 795f3f62ee..46e07c94ad 100644
--- a/lib/rdoc/markup/to_joined_paragraph.rb
+++ b/lib/rdoc/markup/to_joined_paragraph.rb
@@ -41,6 +41,7 @@ class RDoc::Markup::ToJoinedParagraph < RDoc::Markup::Formatter
alias accept_raw ignore
alias accept_rule ignore
alias accept_verbatim ignore
+ alias accept_table ignore
end
diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb
index 81b16c4973..3cdf4fd08b 100644
--- a/lib/rdoc/markup/to_rdoc.rb
+++ b/lib/rdoc/markup/to_rdoc.rb
@@ -238,6 +238,34 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
end
##
+ # Adds +table+ to the output
+
+ def accept_table header, body, aligns
+ widths = header.zip(body) do |h, b|
+ [h.size, b.size].max
+ end
+ aligns = aligns.map do |a|
+ case a
+ when nil
+ :center
+ when :left
+ :ljust
+ when :right
+ :rjust
+ end
+ end
+ @res << header.zip(widths, aligns) do |h, w, a|
+ h.__send__(a, w)
+ end.join("|").rstrip << "\n"
+ @res << widths.map {|w| "-" * w }.join("|") << "\n"
+ body.each do |row|
+ @res << row.zip(widths, aligns) do |t, w, a|
+ t.__send__(a, w)
+ end.join("|").rstrip << "\n"
+ end
+ end
+
+ ##
# Applies attribute-specific markup to +text+ using RDoc::AttributeManager
def attributes text
diff --git a/lib/rdoc/markup/to_table_of_contents.rb b/lib/rdoc/markup/to_table_of_contents.rb
index f68b90bcf6..eb8e8faa16 100644
--- a/lib/rdoc/markup/to_table_of_contents.rb
+++ b/lib/rdoc/markup/to_table_of_contents.rb
@@ -82,6 +82,7 @@ class RDoc::Markup::ToTableOfContents < RDoc::Markup::Formatter
alias accept_list_item_end ignore
alias accept_list_end_bullet ignore
alias accept_list_start ignore
+ alias accept_table ignore
# :startdoc:
end
diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb
index 13c1abae0a..13b7ba5c6c 100644
--- a/lib/rdoc/options.rb
+++ b/lib/rdoc/options.rb
@@ -338,8 +338,9 @@ class RDoc::Options
attr_reader :visibility
- def initialize # :nodoc:
+ def initialize loaded_options = nil # :nodoc:
init_ivars
+ override loaded_options if loaded_options
end
def init_ivars # :nodoc:
@@ -417,6 +418,37 @@ class RDoc::Options
init_with map
end
+ def override map # :nodoc:
+ if map.has_key?('encoding')
+ encoding = map['encoding']
+ @encoding = encoding ? Encoding.find(encoding) : encoding
+ end
+
+ @charset = map['charset'] if map.has_key?('charset')
+ @exclude = map['exclude'] if map.has_key?('exclude')
+ @generator_name = map['generator_name'] if map.has_key?('generator_name')
+ @hyperlink_all = map['hyperlink_all'] if map.has_key?('hyperlink_all')
+ @line_numbers = map['line_numbers'] if map.has_key?('line_numbers')
+ @locale_name = map['locale_name'] if map.has_key?('locale_name')
+ @locale_dir = map['locale_dir'] if map.has_key?('locale_dir')
+ @main_page = map['main_page'] if map.has_key?('main_page')
+ @markup = map['markup'] if map.has_key?('markup')
+ @op_dir = map['op_dir'] if map.has_key?('op_dir')
+ @show_hash = map['show_hash'] if map.has_key?('show_hash')
+ @tab_width = map['tab_width'] if map.has_key?('tab_width')
+ @template_dir = map['template_dir'] if map.has_key?('template_dir')
+ @title = map['title'] if map.has_key?('title')
+ @visibility = map['visibility'] if map.has_key?('visibility')
+ @webcvs = map['webcvs'] if map.has_key?('webcvs')
+
+ if map.has_key?('rdoc_include')
+ @rdoc_include = sanitize_path map['rdoc_include']
+ end
+ if map.has_key?('static_path')
+ @static_path = sanitize_path map['static_path']
+ end
+ end
+
def == other # :nodoc:
self.class === other and
@encoding == other.encoding and
diff --git a/lib/rdoc/parser/changelog.rb b/lib/rdoc/parser/changelog.rb
index 167892f543..9245d49376 100644
--- a/lib/rdoc/parser/changelog.rb
+++ b/lib/rdoc/parser/changelog.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-require 'time'
##
# A ChangeLog file parser.
@@ -106,15 +105,33 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
entries.group_by do |title, _|
begin
time = @time_cache[title]
- (time || Time.parse(title)).strftime '%Y-%m-%d'
+ (time || parse_date(title)).strftime '%Y-%m-%d'
rescue NoMethodError, ArgumentError
time, = title.split ' ', 2
- Time.parse(time).strftime '%Y-%m-%d'
+ parse_date(time).strftime '%Y-%m-%d'
end
end
end
##
+ # Parse date in ISO-8601, RFC-2822, or default of Git
+
+ def parse_date(date)
+ case date
+ when /\A\s*(\d+)-(\d+)-(\d+)(?:[ T](\d+):(\d+):(\d+) *([-+]\d\d):?(\d\d))?\b/
+ Time.new($1, $2, $3, $4, $5, $6, ("#{$7}:#{$8}" if $7))
+ when /\A\s*\w{3}, +(\d+) (\w{3}) (\d+) (\d+):(\d+):(\d+) *(?:([-+]\d\d):?(\d\d))\b/
+ Time.new($3, $2, $1, $4, $5, $6, ("#{$7}:#{$8}" if $7))
+ when /\A\s*\w{3} (\w{3}) +(\d+) (\d+) (\d+):(\d+):(\d+) *(?:([-+]\d\d):?(\d\d))\b/
+ Time.new($3, $1, $2, $4, $5, $6, ("#{$7}:#{$8}" if $7))
+ when /\A\s*\w{3} (\w{3}) +(\d+) (\d+):(\d+):(\d+) (\d+)\b/
+ Time.new($6, $1, $2, $3, $4, $5)
+ else
+ raise ArgumentError, "bad date: #{date}"
+ end
+ end
+
+ ##
# Parses the entries in the ChangeLog.
#
# Returns an Array of each ChangeLog entry in order of parsing.
@@ -131,6 +148,13 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
def parse_entries
@time_cache ||= {}
+
+ if /\A((?:.*\n){,3})commit\s/ =~ @content
+ class << self; prepend Git; end
+ parse_info($1)
+ return parse_entries
+ end
+
entries = []
entry_name = nil
entry_body = []
@@ -145,19 +169,10 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
entry_name = $&
begin
- time = Time.parse entry_name
+ time = parse_date entry_name
@time_cache[entry_name] = time
- # HACK Ruby 1.8 does not raise ArgumentError for Time.parse "Other"
- entry_name = nil unless entry_name =~ /#{time.year}/
- rescue NoMethodError
- # HACK Ruby 2.1.2 and earlier raises NoMethodError if time part is absent
- entry_name.split ' ', 2
rescue ArgumentError
- if /out of range/ =~ $!.message
- Time.parse(entry_name.split(' ', 2)[0]) rescue entry_name = nil
- else
- entry_name = nil
- end
+ entry_name = nil
end
entry_body = []
@@ -190,6 +205,7 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
def scan
@time_cache = {}
+
entries = parse_entries
grouped_entries = group_entries entries
@@ -200,5 +216,120 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
@top_level
end
+ module Git
+ def parse_info(info)
+ /^\s*base-url\s*=\s*(.*\S)/ =~ info
+ @base_url = $1
+ end
+
+ def parse_entries
+ entries = []
+
+ @content.scan(/^commit\s+(\h{20})\h*\n((?:.+\n)*)\n((?: {4}.*\n+)*)/) do
+ entry_name, header, entry_body = $1, $2, $3.gsub(/^ {4}/, '')
+ # header = header.scan(/^ *(\S+?): +(.*)/).to_h
+ # date = header["CommitDate"] || header["Date"]
+ date = header[/^ *(?:Author)?Date: +(.*)/, 1]
+ author = header[/^ *Author: +(.*)/, 1]
+ begin
+ time = parse_date(header[/^ *CommitDate: +(.*)/, 1] || date)
+ @time_cache[entry_name] = time
+ author.sub!(/\s*<(.*)>/, '')
+ email = $1
+ entries << [entry_name, [author, email, date, entry_body]]
+ rescue ArgumentError
+ end
+ end
+
+ entries
+ end
+
+ def create_entries entries
+ # git log entries have no strictly itemized style like the old
+ # style, just assume Markdown.
+ entries.map do |commit, entry|
+ LogEntry.new(@base_url, commit, *entry)
+ end
+ end
+
+ LogEntry = Struct.new(:base, :commit, :author, :email, :date, :contents) do
+ HEADING_LEVEL = 3
+
+ def initialize(base, commit, author, email, date, contents)
+ case contents
+ when String
+ contents = RDoc::Markdown.parse(contents).parts.each do |body|
+ case body
+ when RDoc::Markup::Heading
+ body.level += HEADING_LEVEL + 1
+ end
+ end
+ case first = contents[0]
+ when RDoc::Markup::Paragraph
+ contents[0] = RDoc::Markup::Heading.new(HEADING_LEVEL + 1, first.text)
+ end
+ end
+ super
+ end
+
+ def level
+ HEADING_LEVEL
+ end
+
+ def aref
+ "label-#{commit}"
+ end
+
+ def label context = nil
+ aref
+ end
+
+ def text
+ case base
+ when nil
+ "#{date}"
+ when /%s/
+ "{#{date}}[#{base % commit}]"
+ else
+ "{#{date}}[#{base}#{commit}]"
+ end + " {#{author}}[mailto:#{email}]"
+ end
+
+ def accept visitor
+ visitor.accept_heading self
+ begin
+ if visitor.respond_to?(:code_object=)
+ code_object = visitor.code_object
+ visitor.code_object = self
+ end
+ contents.each do |body|
+ body.accept visitor
+ end
+ ensure
+ if visitor.respond_to?(:code_object)
+ visitor.code_object = code_object
+ end
+ end
+ end
+
+ def pretty_print q # :nodoc:
+ q.group(2, '[log_entry: ', ']') do
+ q.text commit
+ q.text ','
+ q.breakable
+ q.group(2, '[date: ', ']') { q.text date }
+ q.text ','
+ q.breakable
+ q.group(2, '[author: ', ']') { q.text author }
+ q.text ','
+ q.breakable
+ q.group(2, '[email: ', ']') { q.text email }
+ q.text ','
+ q.breakable
+ q.pp contents
+ end
+ end
+ end
+ end
end
diff --git a/lib/rdoc/rd/block_parser.rb b/lib/rdoc/rd/block_parser.rb
index cf30043593..462ba869a2 100644
--- a/lib/rdoc/rd/block_parser.rb
+++ b/lib/rdoc/rd/block_parser.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
#
# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.1
+# This file is automatically generated by Racc 1.5.2
# from Racc grammar file "".
#
diff --git a/lib/rdoc/rd/inline_parser.rb b/lib/rdoc/rd/inline_parser.rb
index 007327bf0e..8f4c2c31ef 100644
--- a/lib/rdoc/rd/inline_parser.rb
+++ b/lib/rdoc/rd/inline_parser.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
#
# DO NOT MODIFY!!!!
-# This file is automatically generated by Racc 1.5.1
+# This file is automatically generated by Racc 1.5.2
# from Racc grammar file "".
#
diff --git a/lib/rdoc/rdoc.gemspec b/lib/rdoc/rdoc.gemspec
index fd222d47e0..7725e40b22 100644
--- a/lib/rdoc/rdoc.gemspec
+++ b/lib/rdoc/rdoc.gemspec
@@ -50,7 +50,6 @@ RDoc includes the +rdoc+ and +ri+ tools for generating and displaying documentat
"bin/setup",
"exe/rdoc",
"exe/ri",
- "man/ri.1",
"lib/rdoc.rb",
"lib/rdoc/alias.rb",
"lib/rdoc/anon_class.rb",
@@ -167,6 +166,7 @@ RDoc includes the +rdoc+ and +ri+ tools for generating and displaying documentat
"lib/rdoc/markup/raw.rb",
"lib/rdoc/markup/regexp_handling.rb",
"lib/rdoc/markup/rule.rb",
+ "lib/rdoc/markup/table.rb",
"lib/rdoc/markup/to_ansi.rb",
"lib/rdoc/markup/to_bs.rb",
"lib/rdoc/markup/to_html.rb",
@@ -222,6 +222,7 @@ RDoc includes the +rdoc+ and +ri+ tools for generating and displaying documentat
"lib/rdoc/tom_doc.rb",
"lib/rdoc/top_level.rb",
"lib/rdoc/version.rb",
+ "man/ri.1",
"rdoc.gemspec",
]
# files from .gitignore
@@ -243,4 +244,6 @@ RDoc includes the +rdoc+ and +ri+ tools for generating and displaying documentat
s.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
s.required_rubygems_version = Gem::Requirement.new(">= 2.2")
+
+ s.add_development_dependency("gettext")
end
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index 93e764c462..6c69553588 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -162,12 +162,20 @@ class RDoc::RDoc
RDoc.load_yaml
begin
- options = YAML.load_file '.rdoc_options'
+ options = YAML.safe_load_file '.rdoc_options', permitted_classes: [RDoc::Options, Symbol]
rescue Psych::SyntaxError
+ raise RDoc::Error, "#{options_file} is not a valid rdoc options file"
end
+ return RDoc::Options.new unless options # Allow empty file.
+
raise RDoc::Error, "#{options_file} is not a valid rdoc options file" unless
- RDoc::Options === options
+ RDoc::Options === options or Hash === options
+
+ if Hash === options
+ # Override the default values with the contents of YAML file.
+ options = RDoc::Options.new options
+ end
options
end
@@ -436,7 +444,7 @@ The internal error was:
files.reject do |file, *|
file =~ /\.(?:class|eps|erb|scpt\.txt|svg|ttf|yml)$/i or
(file =~ /tags$/i and
- open(file, 'rb') { |io|
+ File.open(file, 'rb') { |io|
io.read(100) =~ /\A(\f\n[^,]+,\d+$|!_TAG_)/
})
end
diff --git a/lib/rdoc/store.rb b/lib/rdoc/store.rb
index 5ba671ca1b..c793e49ed8 100644
--- a/lib/rdoc/store.rb
+++ b/lib/rdoc/store.rb
@@ -556,9 +556,7 @@ class RDoc::Store
def load_cache
#orig_enc = @encoding
- File.open cache_path, 'rb' do |io|
- @cache = Marshal.load io.read
- end
+ @cache = marshal_load(cache_path)
load_enc = @cache[:encoding]
@@ -615,9 +613,7 @@ class RDoc::Store
def load_class_data klass_name
file = class_file klass_name
- File.open file, 'rb' do |io|
- Marshal.load io.read
- end
+ marshal_load(file)
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, klass_name)
error.set_backtrace e.backtrace
@@ -630,14 +626,10 @@ class RDoc::Store
def load_method klass_name, method_name
file = method_file klass_name, method_name
- File.open file, 'rb' do |io|
- obj = Marshal.load io.read
- obj.store = self
- obj.parent =
- find_class_or_module(klass_name) || load_class(klass_name) unless
- obj.parent
- obj
- end
+ obj = marshal_load(file)
+ obj.store = self
+ obj.parent ||= find_class_or_module(klass_name) || load_class(klass_name)
+ obj
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, klass_name + method_name)
error.set_backtrace e.backtrace
@@ -650,11 +642,9 @@ class RDoc::Store
def load_page page_name
file = page_file page_name
- File.open file, 'rb' do |io|
- obj = Marshal.load io.read
- obj.store = self
- obj
- end
+ obj = marshal_load(file)
+ obj.store = self
+ obj
rescue Errno::ENOENT => e
error = MissingFileError.new(self, file, page_name)
error.set_backtrace e.backtrace
@@ -976,4 +966,21 @@ class RDoc::Store
@unique_modules
end
+ private
+ def marshal_load(file)
+ File.open(file, 'rb') {|io| Marshal.load(io, MarshalFilter)}
+ end
+
+ MarshalFilter = proc do |obj|
+ case obj
+ when true, false, nil, Array, Class, Encoding, Hash, Integer, String, Symbol, RDoc::Text
+ else
+ unless obj.class.name.start_with?("RDoc::")
+ raise TypeError, "not permitted class: #{obj.class.name}"
+ end
+ end
+ obj
+ end
+ private_constant :MarshalFilter
+
end
diff --git a/lib/rdoc/version.rb b/lib/rdoc/version.rb
index bfb442c5c9..8d6ae1012c 100644
--- a/lib/rdoc/version.rb
+++ b/lib/rdoc/version.rb
@@ -3,6 +3,6 @@ module RDoc
##
# RDoc version you are using
- VERSION = '6.3.0'
+ VERSION = '6.3.4.1'
end
diff --git a/lib/reline.rb b/lib/reline.rb
index 81ea9f9b58..a7bd4d9280 100644
--- a/lib/reline.rb
+++ b/lib/reline.rb
@@ -446,6 +446,10 @@ module Reline
}
end
+ def self.ungetc(c)
+ Reline::IOGate.ungetc(c)
+ end
+
def self.line_editor
core.line_editor
end
diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
index 12a2bde234..7d71e62d63 100644
--- a/lib/reline/line_editor.rb
+++ b/lib/reline/line_editor.rb
@@ -813,6 +813,7 @@ class Reline::LineEditor
end
move_cursor_up(back)
move_cursor_down(@first_line_started_from + @started_from)
+ @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
end
@@ -1158,8 +1159,25 @@ class Reline::LineEditor
def call_completion_proc
result = retrieve_completion_block(true)
- slice = result[1]
- result = @completion_proc.(slice) if @completion_proc and slice
+ preposing, target, postposing = result
+ if @completion_proc and target
+ argnum = @completion_proc.parameters.inject(0) { |result, item|
+ case item.first
+ when :req, :opt
+ result + 1
+ when :rest
+ break 3
+ end
+ }
+ case argnum
+ when 1
+ result = @completion_proc.(target)
+ when 2
+ result = @completion_proc.(target, preposing)
+ when 3..Float::INFINITY
+ result = @completion_proc.(target, preposing, postposing)
+ end
+ end
Reline.core.instance_variable_set(:@completion_quote_character, nil)
result
end
@@ -1207,8 +1225,16 @@ class Reline::LineEditor
end
def retrieve_completion_block(set_completion_quote_character = false)
- word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
- quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
+ if Reline.completer_word_break_characters.empty?
+ word_break_regexp = nil
+ else
+ word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
+ end
+ if Reline.completer_quote_characters.empty?
+ quote_characters_regexp = nil
+ else
+ quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
+ end
before = @line.byteslice(0, @byte_pointer)
rest = nil
break_pointer = nil
@@ -1229,14 +1255,14 @@ class Reline::LineEditor
elsif quote and slice.start_with?(escaped_quote)
# skip
i += 2
- elsif slice =~ quote_characters_regexp # find new "
+ elsif quote_characters_regexp and slice =~ quote_characters_regexp # find new "
rest = $'
quote = $&
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
escaped_quote = /\\#{Regexp.escape(quote)}/
i += 1
break_pointer = i - 1
- elsif not quote and slice =~ word_break_regexp
+ elsif word_break_regexp and not quote and slice =~ word_break_regexp
rest = $'
i += 1
before = @line.byteslice(i, @byte_pointer - i)
@@ -1264,6 +1290,19 @@ class Reline::LineEditor
end
target = before
end
+ if @is_multiline
+ if @previous_line_index
+ lines = whole_lines(index: @previous_line_index, line: @line)
+ else
+ lines = whole_lines
+ end
+ if @line_index > 0
+ preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
+ end
+ if (lines.size - 1) > @line_index
+ postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
+ end
+ end
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
end
@@ -1291,10 +1330,32 @@ class Reline::LineEditor
def delete_text(start = nil, length = nil)
if start.nil? and length.nil?
- @line&.clear
- @byte_pointer = 0
- @cursor = 0
- @cursor_max = 0
+ if @is_multiline
+ if @buffer_of_lines.size == 1
+ @line&.clear
+ @byte_pointer = 0
+ @cursor = 0
+ @cursor_max = 0
+ elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
+ @buffer_of_lines.pop
+ @line_index -= 1
+ @line = @buffer_of_lines[@line_index]
+ @byte_pointer = 0
+ @cursor = 0
+ @cursor_max = calculate_width(@line)
+ elsif @line_index < (@buffer_of_lines.size - 1)
+ @buffer_of_lines.delete_at(@line_index)
+ @line = @buffer_of_lines[@line_index]
+ @byte_pointer = 0
+ @cursor = 0
+ @cursor_max = calculate_width(@line)
+ end
+ else
+ @line&.clear
+ @byte_pointer = 0
+ @cursor = 0
+ @cursor_max = 0
+ end
elsif not start.nil? and not length.nil?
if @line
before = @line.byteslice(0, start)
diff --git a/lib/reline/version.rb b/lib/reline/version.rb
index 11e8145c7f..44db465a2f 100644
--- a/lib/reline/version.rb
+++ b/lib/reline/version.rb
@@ -1,3 +1,3 @@
module Reline
- VERSION = '0.2.4'
+ VERSION = '0.2.5'
end
diff --git a/lib/resolv.gemspec b/lib/resolv.gemspec
index ccfe4c9202..c6a0609b51 100644
--- a/lib/resolv.gemspec
+++ b/lib/resolv.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "resolv"
- spec.version = "0.2.0"
+ spec.version = "0.2.1"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
@@ -17,6 +17,6 @@ Gem::Specification.new do |spec|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
end
spec.bindir = "exe"
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.executables = []
spec.require_paths = ["lib"]
end
diff --git a/lib/resolv.rb b/lib/resolv.rb
index 3ca0f01cfe..b69c7045ca 100644
--- a/lib/resolv.rb
+++ b/lib/resolv.rb
@@ -696,17 +696,17 @@ class Resolv
rescue DecodeError
next # broken DNS message ignored
end
- if s = sender_for(from, msg)
+ if sender == sender_for(from, msg)
break
else
# unexpected DNS message ignored
end
end
- return msg, s.data
+ return msg, sender.data
end
def sender_for(addr, msg)
- @senders.delete([addr,msg.id])
+ @senders[[addr,msg.id]]
end
def close
diff --git a/lib/rinda/rinda.gemspec b/lib/rinda/rinda.gemspec
index 1cc5a453d8..0c13e3c2df 100644
--- a/lib/rinda/rinda.gemspec
+++ b/lib/rinda/rinda.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "rinda"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Masatoshi SEKI"]
spec.email = ["seki@ruby-lang.org"]
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 41b1353e1f..6a7e56fa3c 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -8,15 +8,15 @@
require 'rbconfig'
module Gem
- VERSION = "3.2.9".freeze
+ VERSION = "3.2.33".freeze
end
# Must be first since it unloads the prelude from 1.9.2
-require 'rubygems/compatibility'
+require_relative 'rubygems/compatibility'
-require 'rubygems/defaults'
-require 'rubygems/deprecate'
-require 'rubygems/errors'
+require_relative 'rubygems/defaults'
+require_relative 'rubygems/deprecate'
+require_relative 'rubygems/errors'
##
# RubyGems is the Ruby standard for publishing and managing third party
@@ -178,7 +178,7 @@ module Gem
@configuration = nil
@gemdeps = nil
@loaded_specs = {}
- LOADED_SPECS_MUTEX = Mutex.new
+ LOADED_SPECS_MUTEX = Thread::Mutex.new
@path_to_default_spec_map = {}
@platforms = []
@ruby = nil
@@ -249,9 +249,6 @@ module Gem
# you to specify specific gem versions.
def self.bin_path(name, exec_name = nil, *requirements)
- # TODO: fails test_self_bin_path_bin_file_gone_in_latest
- # Gem::Specification.find_by_name(name, *requirements).bin_file exec_name
-
requirements = Gem::Requirement.default if
requirements.empty?
@@ -562,7 +559,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# => [#<Gem::Specification:0x1013b4528 @name="minitest", ...>]
def self.install(name, version = Gem::Requirement.default, *options)
- require "rubygems/dependency_installer"
+ require_relative "rubygems/dependency_installer"
inst = Gem::DependencyInstaller.new(*options)
inst.install name, version
inst.installed_gems
@@ -626,24 +623,14 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Try requiring the gem version *or* stdlib version of psych.
require 'psych'
rescue ::LoadError
- # If we can't load psych, thats fine, go on.
+ # If we can't load psych, that's fine, go on.
else
- # If 'yaml' has already been required, then we have to
- # be sure to switch it over to the newly loaded psych.
- if defined?(YAML::ENGINE) && YAML::ENGINE.yamler != "psych"
- YAML::ENGINE.yamler = "psych"
- end
-
- require 'rubygems/psych_additions'
- require 'rubygems/psych_tree'
+ require_relative 'rubygems/psych_additions'
+ require_relative 'rubygems/psych_tree'
end
require 'yaml'
- require 'rubygems/safe_yaml'
-
- # Now that we're sure some kind of yaml library is loaded, pull
- # in our hack to deal with Syck's DefaultKey ugliness.
- require 'rubygems/syck_hack'
+ require_relative 'rubygems/safe_yaml'
@yaml_loaded = true
end
@@ -813,18 +800,18 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# Safely write a file in binary mode on all platforms.
def self.write_binary(path, data)
- open(path, 'wb') do |io|
- begin
- io.flock(File::LOCK_EX)
- rescue *WRITE_BINARY_ERRORS
- end
+ File.open(path, File::RDWR | File::CREAT | File::BINARY | File::LOCK_EX) do |io|
+ io.write data
+ end
+ rescue *WRITE_BINARY_ERRORS
+ File.open(path, 'wb') do |io|
io.write data
end
rescue Errno::ENOLCK # NFS
if Thread.main != Thread.current
raise
else
- open(path, 'wb') do |io|
+ File.open(path, 'wb') do |io|
io.write data
end
end
@@ -1003,7 +990,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Lazily loads DefaultUserInteraction and returns the default UI.
def self.ui
- require 'rubygems/user_interaction'
+ require_relative 'rubygems/user_interaction'
Gem::DefaultUserInteraction.ui
end
@@ -1063,7 +1050,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Find rubygems plugin files in the standard location and load them
def self.load_plugins
- load_plugin_files Gem::Util.glob_files_in_dir("*#{Gem.plugin_suffix_pattern}", plugindir)
+ Gem.path.each do |gem_path|
+ load_plugin_files Gem::Util.glob_files_in_dir("*#{Gem.plugin_suffix_pattern}", plugindir(gem_path))
+ end
end
##
@@ -1121,27 +1110,22 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
end
ENV["BUNDLE_GEMFILE"] ||= File.expand_path(path)
- require 'rubygems/user_interaction'
- Gem::DefaultUserInteraction.use_ui(ui) do
- require "bundler"
- begin
- Bundler.ui.silence do
- @gemdeps = Bundler.setup
+ require_relative 'rubygems/user_interaction'
+ require "bundler"
+ begin
+ Gem::DefaultUserInteraction.use_ui(ui) do
+ begin
+ Bundler.ui.silence do
+ @gemdeps = Bundler.setup
+ end
+ ensure
+ Gem::DefaultUserInteraction.ui.close
end
- ensure
- Gem::DefaultUserInteraction.ui.close
end
- @gemdeps.requested_specs.map(&:to_spec).sort_by(&:name)
- end
-
- rescue => e
- case e
- when Gem::LoadError, Gem::UnsatisfiableDependencyError, (defined?(Bundler::GemNotFound) ? Bundler::GemNotFound : Gem::LoadError)
+ rescue Bundler::BundlerError => e
warn e.message
- warn "You may need to `gem install -g` to install missing gems"
+ warn "You may need to `bundle install` to install missing gems"
warn ""
- else
- raise
end
end
@@ -1338,19 +1322,11 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
autoload :Version, File.expand_path('rubygems/version', __dir__)
end
-require 'rubygems/exceptions'
+require_relative 'rubygems/exceptions'
# REFACTOR: This should be pulled out into some kind of hacks file.
begin
##
- # Defaults the operating system (or packager) wants to provide for RubyGems.
-
- require 'rubygems/defaults/operating_system'
-rescue LoadError
-end
-
-begin
- ##
# Defaults the Ruby implementation wants to provide for RubyGems
require "rubygems/defaults/#{RUBY_ENGINE}"
@@ -1361,8 +1337,22 @@ end
# Loads the default specs.
Gem::Specification.load_defaults
-require 'rubygems/core_ext/kernel_gem'
-require 'rubygems/core_ext/kernel_require'
-require 'rubygems/core_ext/kernel_warn'
+require_relative 'rubygems/core_ext/kernel_gem'
+require_relative 'rubygems/core_ext/kernel_require'
+require_relative 'rubygems/core_ext/kernel_warn'
-Gem.use_gemdeps
+begin
+ ##
+ # Defaults the operating system (or packager) wants to provide for RubyGems.
+
+ require 'rubygems/defaults/operating_system'
+rescue LoadError
+ # Ignored
+rescue StandardError => e
+ msg = "#{e.message}\n" \
+ "Loading the rubygems/defaults/operating_system.rb file caused an error. " \
+ "This file is owned by your OS, not by rubygems upstream. " \
+ "Please find out which OS package this file belongs to and follow the guidelines from your OS to report " \
+ "the problem and ask for help."
+ raise e.class, msg
+end
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb
index bf55ce3205..abdaa8e7c6 100644
--- a/lib/rubygems/command.rb
+++ b/lib/rubygems/command.rb
@@ -5,7 +5,7 @@
# See LICENSE.txt for permissions.
#++
-require 'optparse'
+require_relative 'optparse'
require_relative 'requirement'
require_relative 'user_interaction'
@@ -19,7 +19,7 @@ require_relative 'user_interaction'
class Gem::Command
include Gem::UserInteraction
- OptionParser.accept Symbol do |value|
+ Gem::OptionParser.accept Symbol do |value|
value.to_sym
end
@@ -344,7 +344,7 @@ class Gem::Command
##
# Add a command-line option and handler to the command.
#
- # See OptionParser#make_switch for an explanation of +opts+.
+ # See Gem::OptionParser#make_switch for an explanation of +opts+.
#
# +handler+ will be called with two values, the value of the argument and
# the options hash.
@@ -355,6 +355,8 @@ class Gem::Command
def add_option(*opts, &handler) # :yields: value, options
group_name = Symbol === opts.first ? opts.shift : :options
+ raise "Do not pass an empty string in opts" if opts.include?("")
+
@option_groups[group_name] << [opts, handler]
end
@@ -538,7 +540,7 @@ class Gem::Command
# command.
def create_option_parser
- @parser = OptionParser.new
+ @parser = Gem::OptionParser.new
add_parser_options
@@ -634,6 +636,7 @@ RubyGems is a package manager for Ruby.
gem install rake
gem list --local
gem build package.gemspec
+ gem push package-0.0.1.gem
gem help install
Further help:
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index 97e52544ca..cb07757700 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -5,9 +5,9 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems/command'
-require 'rubygems/user_interaction'
-require 'rubygems/text'
+require_relative 'command'
+require_relative 'user_interaction'
+require_relative 'text'
##
# The command manager registers and installs all the individual sub-commands
@@ -73,7 +73,9 @@ class Gem::CommandManager
].freeze
ALIAS_COMMANDS = {
- 'i' => 'install',
+ 'i' => 'install',
+ 'login' => 'signin',
+ 'logout' => 'signout',
}.freeze
##
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
index fff5f7c76f..6d1a057dfa 100644
--- a/lib/rubygems/commands/build_command.rb
+++ b/lib/rubygems/commands/build_command.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/package'
-require 'rubygems/version_option'
+require_relative '../command'
+require_relative '../package'
+require_relative '../version_option'
class Gem::Commands::BuildCommand < Gem::Command
include Gem::VersionOption
@@ -23,7 +23,7 @@ class Gem::Commands::BuildCommand < Gem::Command
options[:output] = value
end
- add_option '-C PATH', '', 'Run as if gem build was started in <PATH> instead of the current working directory.' do |value, options|
+ add_option '-C PATH', 'Run as if gem build was started in <PATH> instead of the current working directory.' do |value, options|
options[:build_path] = value
end
end
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index 998df0621b..b59564d575 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -1,43 +1,15 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/security'
+require_relative '../command'
+require_relative '../security'
class Gem::Commands::CertCommand < Gem::Command
def initialize
super 'cert', 'Manage RubyGems certificates and signing settings',
:add => [], :remove => [], :list => [], :build => [], :sign => []
- OptionParser.accept OpenSSL::X509::Certificate do |certificate_file|
- begin
- certificate = OpenSSL::X509::Certificate.new File.read certificate_file
- rescue Errno::ENOENT
- raise OptionParser::InvalidArgument, "#{certificate_file}: does not exist"
- rescue OpenSSL::X509::CertificateError
- raise OptionParser::InvalidArgument,
- "#{certificate_file}: invalid X509 certificate"
- end
- [certificate, certificate_file]
- end
-
- OptionParser.accept OpenSSL::PKey::RSA do |key_file|
- begin
- passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
- key = OpenSSL::PKey::RSA.new File.read(key_file), passphrase
- rescue Errno::ENOENT
- raise OptionParser::InvalidArgument, "#{key_file}: does not exist"
- rescue OpenSSL::PKey::RSAError
- raise OptionParser::InvalidArgument, "#{key_file}: invalid RSA key"
- end
-
- raise OptionParser::InvalidArgument,
- "#{key_file}: private key not found" unless key.private?
-
- key
- end
-
- add_option('-a', '--add CERT', OpenSSL::X509::Certificate,
- 'Add a trusted certificate.') do |(cert, _), options|
- options[:add] << cert
+ add_option('-a', '--add CERT',
+ 'Add a trusted certificate.') do |cert_file, options|
+ options[:add] << open_cert(cert_file)
end
add_option('-l', '--list [FILTER]',
@@ -60,21 +32,26 @@ class Gem::Commands::CertCommand < Gem::Command
options[:build] << email_address
end
- add_option('-C', '--certificate CERT', OpenSSL::X509::Certificate,
- 'Signing certificate for --sign') do |(cert, cert_file), options|
- options[:issuer_cert] = cert
+ add_option('-C', '--certificate CERT',
+ 'Signing certificate for --sign') do |cert_file, options|
+ options[:issuer_cert] = open_cert(cert_file)
options[:issuer_cert_file] = cert_file
end
- add_option('-K', '--private-key KEY', OpenSSL::PKey::RSA,
- 'Key for --sign or --build') do |key, options|
- options[:key] = key
+ add_option('-K', '--private-key KEY',
+ 'Key for --sign or --build') do |key_file, options|
+ options[:key] = open_private_key(key_file)
+ end
+
+ add_option('-A', '--key-algorithm ALGORITHM',
+ 'Select which key algorithm to use for --build') do |algorithm, options|
+ options[:key_algorithm] = algorithm
end
add_option('-s', '--sign CERT',
'Signs CERT with the key from -K',
'and the certificate from -C') do |cert_file, options|
- raise OptionParser::InvalidArgument, "#{cert_file}: does not exist" unless
+ raise Gem::OptionParser::InvalidArgument, "#{cert_file}: does not exist" unless
File.file? cert_file
options[:sign] << cert_file
@@ -97,7 +74,39 @@ class Gem::Commands::CertCommand < Gem::Command
say "Added '#{certificate.subject}'"
end
+ def check_openssl
+ return if Gem::HAVE_OPENSSL
+
+ alert_error "OpenSSL library is required for the cert command"
+ terminate_interaction 1
+ end
+
+ def open_cert(certificate_file)
+ check_openssl
+ OpenSSL::X509::Certificate.new File.read certificate_file
+ rescue Errno::ENOENT
+ raise Gem::OptionParser::InvalidArgument, "#{certificate_file}: does not exist"
+ rescue OpenSSL::X509::CertificateError
+ raise Gem::OptionParser::InvalidArgument,
+ "#{certificate_file}: invalid X509 certificate"
+ end
+
+ def open_private_key(key_file)
+ check_openssl
+ passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
+ key = OpenSSL::PKey.read File.read(key_file), passphrase
+ raise Gem::OptionParser::InvalidArgument,
+ "#{key_file}: private key not found" unless key.private?
+ key
+ rescue Errno::ENOENT
+ raise Gem::OptionParser::InvalidArgument, "#{key_file}: does not exist"
+ rescue OpenSSL::PKey::PKeyError, ArgumentError
+ raise Gem::OptionParser::InvalidArgument, "#{key_file}: invalid RSA, DSA, or EC key"
+ end
+
def execute
+ check_openssl
+
options[:add].each do |certificate|
add_certificate certificate
end
@@ -166,7 +175,8 @@ class Gem::Commands::CertCommand < Gem::Command
raise Gem::CommandLineError,
"Passphrase and passphrase confirmation don't match" unless passphrase == passphrase_confirmation
- key = Gem::Security.create_key
+ algorithm = options[:key_algorithm] || Gem::Security::DEFAULT_KEY_ALGORITHM
+ key = Gem::Security.create_key(algorithm)
key_path = Gem::Security.write key, "gem-private_key.pem", 0600, passphrase
return key, key_path
@@ -251,13 +261,14 @@ For further reading on signing gems see `ri Gem::Security`.
key_file = File.join Gem.default_key_path
key = File.read key_file
passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
- options[:key] = OpenSSL::PKey::RSA.new key, passphrase
+ options[:key] = OpenSSL::PKey.read key, passphrase
+
rescue Errno::ENOENT
alert_error \
"--private-key not specified and ~/.gem/gem-private_key.pem does not exist"
terminate_interaction 1
- rescue OpenSSL::PKey::RSAError
+ rescue OpenSSL::PKey::PKeyError
alert_error \
"--private-key not specified and ~/.gem/gem-private_key.pem is not valid"
@@ -311,4 +322,4 @@ For further reading on signing gems see `ri Gem::Security`.
# It's simple, but is all we need
email =~ /\A.+@.+\z/
end
-end if Gem::HAVE_OPENSSL
+end
diff --git a/lib/rubygems/commands/check_command.rb b/lib/rubygems/commands/check_command.rb
index 8b8eda53cf..3b6b97ae3b 100644
--- a/lib/rubygems/commands/check_command.rb
+++ b/lib/rubygems/commands/check_command.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/version_option'
-require 'rubygems/validator'
-require 'rubygems/doctor'
+require_relative '../command'
+require_relative '../version_option'
+require_relative '../validator'
+require_relative '../doctor'
class Gem::Commands::CheckCommand < Gem::Command
include Gem::VersionOption
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index 662badce33..c965085880 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/dependency_list'
-require 'rubygems/uninstaller'
+require_relative '../command'
+require_relative '../dependency_list'
+require_relative '../uninstaller'
class Gem::Commands::CleanupCommand < Gem::Command
def initialize
diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb
index f17aed64db..716022c458 100644
--- a/lib/rubygems/commands/contents_command.rb
+++ b/lib/rubygems/commands/contents_command.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/version_option'
+require_relative '../command'
+require_relative '../version_option'
class Gem::Commands::ContentsCommand < Gem::Command
include Gem::VersionOption
diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb
index e472d8fa8d..7d217076a5 100644
--- a/lib/rubygems/commands/dependency_command.rb
+++ b/lib/rubygems/commands/dependency_command.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/local_remote_options'
-require 'rubygems/version_option'
+require_relative '../command'
+require_relative '../local_remote_options'
+require_relative '../version_option'
class Gem::Commands::DependencyCommand < Gem::Command
include Gem::LocalRemoteOptions
diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb
index 37429fb836..b6eeb620bd 100644
--- a/lib/rubygems/commands/environment_command.rb
+++ b/lib/rubygems/commands/environment_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/command'
+require_relative '../command'
class Gem::Commands::EnvironmentCommand < Gem::Command
def initialize
diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb
index 6a1b346dd3..373851643d 100644
--- a/lib/rubygems/commands/fetch_command.rb
+++ b/lib/rubygems/commands/fetch_command.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/local_remote_options'
-require 'rubygems/version_option'
+require_relative '../command'
+require_relative '../local_remote_options'
+require_relative '../version_option'
class Gem::Commands::FetchCommand < Gem::Command
include Gem::LocalRemoteOptions
@@ -60,7 +60,7 @@ then repackaging it.
specs_and_sources = filtered unless filtered.empty?
end
- spec, source = specs_and_sources.max_by {|s,| s.version }
+ spec, source = specs_and_sources.max_by {|s,| s }
if spec.nil?
show_lookup_failure gem_name, version, errors, options[:domain]
diff --git a/lib/rubygems/commands/generate_index_command.rb b/lib/rubygems/commands/generate_index_command.rb
index 93e25ef5e4..87200dab91 100644
--- a/lib/rubygems/commands/generate_index_command.rb
+++ b/lib/rubygems/commands/generate_index_command.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/indexer'
+require_relative '../command'
+require_relative '../indexer'
##
# Generates a index files for use as a gem server.
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
index 4e8d7600fb..7f3383c9f3 100644
--- a/lib/rubygems/commands/help_command.rb
+++ b/lib/rubygems/commands/help_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/command'
+require_relative '../command'
class Gem::Commands::HelpCommand < Gem::Command
# :stopdoc:
diff --git a/lib/rubygems/commands/info_command.rb b/lib/rubygems/commands/info_command.rb
index 9ca6ae364f..3f2dd4ae0b 100644
--- a/lib/rubygems/commands/info_command.rb
+++ b/lib/rubygems/commands/info_command.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/query_utils'
+require_relative '../command'
+require_relative '../query_utils'
class Gem::Commands::InfoCommand < Gem::Command
include Gem::QueryUtils
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index 70825b88fd..7af5060129 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/install_update_options'
-require 'rubygems/dependency_installer'
-require 'rubygems/local_remote_options'
-require 'rubygems/validator'
-require 'rubygems/version_option'
+require_relative '../command'
+require_relative '../install_update_options'
+require_relative '../dependency_installer'
+require_relative '../local_remote_options'
+require_relative '../validator'
+require_relative '../version_option'
##
# Gem installer command line tool
@@ -127,7 +127,7 @@ You can use `i` command instead of `install`.
end
def usage # :nodoc:
- "#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags"
+ "#{program_name} [options] GEMNAME [GEMNAME ...] -- --build-flags"
end
def check_install_dir # :nodoc:
@@ -169,7 +169,7 @@ You can use `i` command instead of `install`.
end
def install_from_gemdeps # :nodoc:
- require 'rubygems/request_set'
+ require_relative '../request_set'
rs = Gem::RequestSet.new
specs = rs.install_from_gemdeps options do |req, inst|
@@ -244,11 +244,11 @@ You can use `i` command instead of `install`.
def load_hooks # :nodoc:
if options[:install_as_default]
- require 'rubygems/install_default_message'
+ require_relative '../install_default_message'
else
- require 'rubygems/install_message'
+ require_relative '../install_message'
end
- require 'rubygems/rdoc'
+ require_relative '../rdoc'
end
def show_install_errors(errors) # :nodoc:
@@ -257,7 +257,8 @@ You can use `i` command instead of `install`.
errors.each do |x|
return unless Gem::SourceFetchProblem === x
- msg = "Unable to pull data from '#{x.source.uri}': #{x.error.message}"
+ require_relative "../uri"
+ msg = "Unable to pull data from '#{Gem::Uri.new(x.source.uri).redacted}': #{x.error.message}"
alert_warning msg
end
diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb
index 5c99d3d73d..dea11111c9 100644
--- a/lib/rubygems/commands/list_command.rb
+++ b/lib/rubygems/commands/list_command.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/query_utils'
+require_relative '../command'
+require_relative '../query_utils'
##
# Searches for gems starting with the supplied argument.
diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb
index f1dc1ac586..cb6229a2cb 100644
--- a/lib/rubygems/commands/lock_command.rb
+++ b/lib/rubygems/commands/lock_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/command'
+require_relative '../command'
class Gem::Commands::LockCommand < Gem::Command
def initialize
diff --git a/lib/rubygems/commands/mirror_command.rb b/lib/rubygems/commands/mirror_command.rb
index 86671a93b2..7daa47e2f0 100644
--- a/lib/rubygems/commands/mirror_command.rb
+++ b/lib/rubygems/commands/mirror_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/command'
+require_relative '../command'
unless defined? Gem::Commands::MirrorCommand
class Gem::Commands::MirrorCommand < Gem::Command
diff --git a/lib/rubygems/commands/open_command.rb b/lib/rubygems/commands/open_command.rb
index 1e40758ec5..1e616fd68f 100644
--- a/lib/rubygems/commands/open_command.rb
+++ b/lib/rubygems/commands/open_command.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/version_option'
+require_relative '../command'
+require_relative '../version_option'
class Gem::Commands::OpenCommand < Gem::Command
include Gem::VersionOption
@@ -36,7 +36,7 @@ class Gem::Commands::OpenCommand < Gem::Command
end
def usage # :nodoc:
- "#{program_name} GEMNAME [-e COMMAND]"
+ "#{program_name} [-e COMMAND] GEMNAME"
end
def get_env_editor
diff --git a/lib/rubygems/commands/outdated_command.rb b/lib/rubygems/commands/outdated_command.rb
index 3579bfc3ba..162d338320 100644
--- a/lib/rubygems/commands/outdated_command.rb
+++ b/lib/rubygems/commands/outdated_command.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/local_remote_options'
-require 'rubygems/spec_fetcher'
-require 'rubygems/version_option'
+require_relative '../command'
+require_relative '../local_remote_options'
+require_relative '../spec_fetcher'
+require_relative '../version_option'
class Gem::Commands::OutdatedCommand < Gem::Command
include Gem::LocalRemoteOptions
diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb
index dd49027469..0a5665228f 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/local_remote_options'
-require 'rubygems/gemcutter_utilities'
-require 'rubygems/text'
+require_relative '../command'
+require_relative '../local_remote_options'
+require_relative '../gemcutter_utilities'
+require_relative '../text'
class Gem::Commands::OwnerCommand < Gem::Command
include Gem::Text
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index 143105981e..13979b0a59 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/package'
-require 'rubygems/installer'
-require 'rubygems/version_option'
+require_relative '../command'
+require_relative '../package'
+require_relative '../installer'
+require_relative '../version_option'
class Gem::Commands::PristineCommand < Gem::Command
include Gem::VersionOption
@@ -50,6 +50,11 @@ class Gem::Commands::PristineCommand < Gem::Command
options[:env_shebang] = value
end
+ add_option('-i', '--install-dir DIR',
+ 'Gem repository to get binstubs and plugins installed') do |value, options|
+ options[:install_dir] = File.expand_path(value)
+ end
+
add_option('-n', '--bindir DIR',
'Directory where executables are',
'located') do |value, options|
@@ -138,7 +143,7 @@ extensions will be restored.
gem = spec.cache_file
unless File.exist? gem or options[:only_executables] or options[:only_plugins]
- require 'rubygems/remote_fetcher'
+ require_relative '../remote_fetcher'
say "Cached gem for #{spec.full_name} not found, attempting to fetch..."
@@ -163,11 +168,12 @@ extensions will be restored.
end
bin_dir = options[:bin_dir] if options[:bin_dir]
+ install_dir = options[:install_dir] if options[:install_dir]
installer_options = {
:wrappers => true,
:force => true,
- :install_dir => spec.base_dir,
+ :install_dir => install_dir || spec.base_dir,
:env_shebang => env_shebang,
:build_args => spec.build_args,
:bin_dir => bin_dir,
@@ -177,7 +183,7 @@ extensions will be restored.
installer = Gem::Installer.for_spec(spec, installer_options)
installer.generate_bin
elsif options[:only_plugins]
- installer = Gem::Installer.for_spec(spec)
+ installer = Gem::Installer.for_spec(spec, installer_options)
installer.generate_plugins
else
installer = Gem::Installer.at(gem, installer_options)
diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb
index 1a9a1932f8..1864b4b095 100644
--- a/lib/rubygems/commands/push_command.rb
+++ b/lib/rubygems/commands/push_command.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/local_remote_options'
-require 'rubygems/gemcutter_utilities'
-require 'rubygems/package'
+require_relative '../command'
+require_relative '../local_remote_options'
+require_relative '../gemcutter_utilities'
+require_relative '../package'
class Gem::Commands::PushCommand < Gem::Command
include Gem::LocalRemoteOptions
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index 789afd6509..5896bec44e 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/query_utils'
-require 'rubygems/deprecate'
+require_relative '../command'
+require_relative '../query_utils'
+require_relative '../deprecate'
class Gem::Commands::QueryCommand < Gem::Command
extend Gem::Deprecate
diff --git a/lib/rubygems/commands/rdoc_command.rb b/lib/rubygems/commands/rdoc_command.rb
index e8c9e84b29..305c80ccfe 100644
--- a/lib/rubygems/commands/rdoc_command.rb
+++ b/lib/rubygems/commands/rdoc_command.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/version_option'
-require 'rubygems/rdoc'
+require_relative '../command'
+require_relative '../version_option'
+require_relative '../rdoc'
require 'fileutils'
class Gem::Commands::RdocCommand < Gem::Command
diff --git a/lib/rubygems/commands/search_command.rb b/lib/rubygems/commands/search_command.rb
index aeb2119235..488d777939 100644
--- a/lib/rubygems/commands/search_command.rb
+++ b/lib/rubygems/commands/search_command.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/query_utils'
+require_relative '../command'
+require_relative '../query_utils'
class Gem::Commands::SearchCommand < Gem::Command
include Gem::QueryUtils
diff --git a/lib/rubygems/commands/server_command.rb b/lib/rubygems/commands/server_command.rb
index 594cf77f66..79c682e8f4 100644
--- a/lib/rubygems/commands/server_command.rb
+++ b/lib/rubygems/commands/server_command.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/server'
-require 'rubygems/deprecate'
+require_relative '../command'
+require_relative '../server'
+require_relative '../deprecate'
class Gem::Commands::ServerCommand < Gem::Command
extend Gem::Deprecate
@@ -11,10 +11,10 @@ class Gem::Commands::ServerCommand < Gem::Command
super 'server', 'Documentation and gem repository HTTP server',
:port => 8808, :gemdir => [], :daemon => false
- OptionParser.accept :Port do |port|
+ Gem::OptionParser.accept :Port do |port|
if port =~ /\A\d+\z/
port = Integer port
- raise OptionParser::InvalidArgument, "#{port}: not a port number" if
+ raise Gem::OptionParser::InvalidArgument, "#{port}: not a port number" if
port > 65535
port
@@ -22,7 +22,7 @@ class Gem::Commands::ServerCommand < Gem::Command
begin
Socket.getservbyname port
rescue SocketError
- raise OptionParser::InvalidArgument, "#{port}: no such named service"
+ raise Gem::OptionParser::InvalidArgument, "#{port}: no such named service"
end
end
end
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index 47e215c149..894f94b484 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/command'
+require_relative '../command'
##
# Installs RubyGems itself. This command is ordinarily only available from a
@@ -149,13 +149,6 @@ By default, this RubyGems will install gem as:
def execute
@verbose = Gem.configuration.really_verbose
- install_destdir = options[:destdir]
-
- unless install_destdir.empty?
- ENV['GEM_HOME'] ||= File.join(install_destdir,
- Gem.default_dir.gsub(/^[a-zA-Z]:/, ''))
- end
-
check_ruby_version
require 'fileutils'
@@ -166,8 +159,8 @@ By default, this RubyGems will install gem as:
end
extend MakeDirs
- lib_dir, bin_dir = make_destination_dirs install_destdir
- man_dir = generate_default_man_dir install_destdir
+ lib_dir, bin_dir = make_destination_dirs
+ man_dir = generate_default_man_dir
install_lib lib_dir
@@ -189,8 +182,8 @@ By default, this RubyGems will install gem as:
say "RubyGems #{Gem::VERSION} installed"
- regenerate_binstubs if options[:regenerate_binstubs]
- regenerate_plugins if options[:regenerate_plugins]
+ regenerate_binstubs(bin_dir) if options[:regenerate_binstubs]
+ regenerate_plugins(bin_dir) if options[:regenerate_plugins]
uninstall_old_gemcutter
@@ -258,35 +251,32 @@ By default, this RubyGems will install gem as:
say "Installing #{tool} executable" if @verbose
Dir.chdir path do
- bin_files = Dir['*']
-
- bin_files -= %w[update_rubygems]
+ bin_file = "gem"
- bin_files.each do |bin_file|
- dest_file = target_bin_path(bin_dir, bin_file)
- bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"
+ dest_file = target_bin_path(bin_dir, bin_file)
+ bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"
- begin
- bin = File.readlines bin_file
- bin[0] = shebang
+ begin
+ bin = File.readlines bin_file
+ bin[0] = shebang
- File.open bin_tmp_file, 'w' do |fp|
- fp.puts bin.join
- end
-
- install bin_tmp_file, dest_file, :mode => prog_mode
- bin_file_names << dest_file
- ensure
- rm bin_tmp_file
+ File.open bin_tmp_file, 'w' do |fp|
+ fp.puts bin.join
end
- next unless Gem.win_platform?
+ install bin_tmp_file, dest_file, :mode => prog_mode
+ bin_file_names << dest_file
+ ensure
+ rm bin_tmp_file
+ end
+
+ next unless Gem.win_platform?
- begin
- bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat"
+ begin
+ bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat"
- File.open bin_cmd_file, 'w' do |file|
- file.puts <<-TEXT
+ File.open bin_cmd_file, 'w' do |file|
+ file.puts <<-TEXT
@ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT
@"#{File.basename(Gem.ruby).chomp('"')}" "#{dest_file}" %1 %2 %3 %4 %5 %6 %7 %8 %9
@@ -294,12 +284,11 @@ By default, this RubyGems will install gem as:
:WinNT
@"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %*
TEXT
- end
-
- install bin_cmd_file, "#{dest_file}.bat", :mode => prog_mode
- ensure
- rm bin_cmd_file
end
+
+ install bin_cmd_file, "#{dest_file}.bat", :mode => prog_mode
+ ensure
+ rm bin_cmd_file
end
end
end
@@ -348,7 +337,7 @@ By default, this RubyGems will install gem as:
rm_rf dir
end
- require 'rubygems/rdoc'
+ require_relative '../rdoc'
fake_spec = Gem::Specification.new 'rubygems', Gem::VERSION
def fake_spec.full_gem_path
@@ -371,8 +360,7 @@ By default, this RubyGems will install gem as:
end
def install_default_bundler_gem(bin_dir)
- specs_dir = Gem.default_specifications_dir
- specs_dir = File.join(options[:destdir], specs_dir) unless Gem.win_platform?
+ specs_dir = File.join(default_dir, "specifications", "default")
mkdir_p specs_dir, :mode => 0755
bundler_spec = Dir.chdir("bundler") { Gem::Specification.load("bundler.gemspec") }
@@ -387,8 +375,20 @@ By default, this RubyGems will install gem as:
bundler_spec = Gem::Specification.load(default_spec_path)
+ # The base_dir value for a specification is inferred by walking up from the
+ # folder where the spec was `loaded_from`. In the case of default gems, we
+ # walk up two levels, because they live at `specifications/default/`, whereas
+ # in the case of regular gems we walk up just one level because they live at
+ # `specifications/`. However, in this case, the gem we are installing is
+ # misdetected as a regular gem, when it's a default gem in reality. This is
+ # because when there's a `:destdir`, the `loaded_from` path has changed and
+ # doesn't match `Gem.default_specifications_dir` which is the criteria to
+ # tag a gem as a default gem. So, in that case, write the correct
+ # `@base_dir` directly.
+ bundler_spec.instance_variable_set(:@base_dir, File.dirname(File.dirname(specs_dir)))
+
# Remove gemspec that was same version of vendored bundler.
- normal_gemspec = File.join(Gem.default_dir, "specifications", "bundler-#{bundler_spec.version}.gemspec")
+ normal_gemspec = File.join(default_dir, "specifications", "bundler-#{bundler_spec.version}.gemspec")
if File.file? normal_gemspec
File.delete normal_gemspec
end
@@ -401,19 +401,26 @@ By default, this RubyGems will install gem as:
end
bundler_bin_dir = bundler_spec.bin_dir
- bundler_bin_dir = File.join(options[:destdir], bundler_bin_dir) unless Gem.win_platform?
mkdir_p bundler_bin_dir, :mode => 0755
bundler_spec.executables.each do |e|
cp File.join("bundler", bundler_spec.bindir, e), File.join(bundler_bin_dir, e)
end
- require 'rubygems/installer'
+ require_relative '../installer'
Dir.chdir("bundler") do
built_gem = Gem::Package.build(bundler_spec)
begin
- installer = Gem::Installer.at(built_gem, env_shebang: options[:env_shebang], format_executable: options[:format_executable], force: options[:force], install_as_default: true, bin_dir: bin_dir, wrappers: true)
- installer.install
+ Gem::Installer.at(
+ built_gem,
+ env_shebang: options[:env_shebang],
+ format_executable: options[:format_executable],
+ force: options[:force],
+ install_as_default: true,
+ bin_dir: bin_dir,
+ install_dir: default_dir,
+ wrappers: true
+ ).install
ensure
FileUtils.rm_f built_gem
end
@@ -424,11 +431,11 @@ By default, this RubyGems will install gem as:
say "Bundler #{bundler_spec.version} installed"
end
- def make_destination_dirs(install_destdir)
+ def make_destination_dirs
lib_dir, bin_dir = Gem.default_rubygems_dirs
unless lib_dir
- lib_dir, bin_dir = generate_default_dirs(install_destdir)
+ lib_dir, bin_dir = generate_default_dirs
end
mkdir_p lib_dir, :mode => 0755
@@ -437,7 +444,7 @@ By default, this RubyGems will install gem as:
return lib_dir, bin_dir
end
- def generate_default_man_dir(install_destdir)
+ def generate_default_man_dir
prefix = options[:prefix]
if prefix.empty?
@@ -447,14 +454,10 @@ By default, this RubyGems will install gem as:
man_dir = File.join prefix, 'man'
end
- unless install_destdir.empty?
- man_dir = File.join install_destdir, man_dir.gsub(/^[a-zA-Z]:/, '')
- end
-
- man_dir
+ prepend_destdir_if_present(man_dir)
end
- def generate_default_dirs(install_destdir)
+ def generate_default_dirs
prefix = options[:prefix]
site_or_vendor = options[:site_or_vendor]
@@ -478,12 +481,7 @@ By default, this RubyGems will install gem as:
end
end
- unless install_destdir.empty?
- lib_dir = File.join install_destdir, lib_dir.gsub(/^[a-zA-Z]:/, '')
- bin_dir = File.join install_destdir, bin_dir.gsub(/^[a-zA-Z]:/, '')
- end
-
- [lib_dir, bin_dir]
+ [prepend_destdir_if_present(lib_dir), prepend_destdir_if_present(bin_dir)]
end
def files_in(dir)
@@ -596,7 +594,7 @@ abort "#{deprecation_message}"
end
def uninstall_old_gemcutter
- require 'rubygems/uninstaller'
+ require_relative '../uninstaller'
ui = Gem::Uninstaller.new('gemcutter', :all => true, :ignore => true,
:version => '< 0.4')
@@ -604,11 +602,12 @@ abort "#{deprecation_message}"
rescue Gem::InstallError
end
- def regenerate_binstubs
- require "rubygems/commands/pristine_command"
+ def regenerate_binstubs(bindir)
+ require_relative "pristine_command"
say "Regenerating binstubs"
args = %w[--all --only-executables --silent]
+ args << "--bindir=#{bindir}"
if options[:env_shebang]
args << "--env-shebang"
end
@@ -617,11 +616,13 @@ abort "#{deprecation_message}"
command.invoke(*args)
end
- def regenerate_plugins
- require "rubygems/commands/pristine_command"
+ def regenerate_plugins(bindir)
+ require_relative "pristine_command"
say "Regenerating plugins"
args = %w[--all --only-plugins --silent]
+ args << "--bindir=#{bindir}"
+ args << "--install-dir=#{default_dir}"
command = Gem::Commands::PristineCommand.new
command.invoke(*args)
@@ -629,6 +630,25 @@ abort "#{deprecation_message}"
private
+ def default_dir
+ prefix = options[:prefix]
+
+ if prefix.empty?
+ dir = Gem.default_dir
+ else
+ dir = prefix
+ end
+
+ prepend_destdir_if_present(dir)
+ end
+
+ def prepend_destdir_if_present(path)
+ destdir = options[:destdir]
+ return path if destdir.empty?
+
+ File.join(options[:destdir], path.gsub(/^[a-zA-Z]:/, ''))
+ end
+
def install_file_list(files, dest_dir)
files.each do |file|
install_file file, dest_dir
diff --git a/lib/rubygems/commands/signin_command.rb b/lib/rubygems/commands/signin_command.rb
index 2e19c8333c..23bb2f937f 100644
--- a/lib/rubygems/commands/signin_command.rb
+++ b/lib/rubygems/commands/signin_command.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/gemcutter_utilities'
+require_relative '../command'
+require_relative '../gemcutter_utilities'
class Gem::Commands::SigninCommand < Gem::Command
include Gem::GemcutterUtilities
diff --git a/lib/rubygems/commands/signout_command.rb b/lib/rubygems/commands/signout_command.rb
index ebbe746cb4..c9485e0c1b 100644
--- a/lib/rubygems/commands/signout_command.rb
+++ b/lib/rubygems/commands/signout_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/command'
+require_relative '../command'
class Gem::Commands::SignoutCommand < Gem::Command
def initialize
diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb
index f74fb12e42..9e74f3c47d 100644
--- a/lib/rubygems/commands/sources_command.rb
+++ b/lib/rubygems/commands/sources_command.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/remote_fetcher'
-require 'rubygems/spec_fetcher'
-require 'rubygems/local_remote_options'
+require_relative '../command'
+require_relative '../remote_fetcher'
+require_relative '../spec_fetcher'
+require_relative '../local_remote_options'
class Gem::Commands::SourcesCommand < Gem::Command
include Gem::LocalRemoteOptions
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
index 3fddaaaf30..473b6e7b19 100644
--- a/lib/rubygems/commands/specification_command.rb
+++ b/lib/rubygems/commands/specification_command.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/local_remote_options'
-require 'rubygems/version_option'
-require 'rubygems/package'
+require_relative '../command'
+require_relative '../local_remote_options'
+require_relative '../version_option'
+require_relative '../package'
class Gem::Commands::SpecificationCommand < Gem::Command
include Gem::LocalRemoteOptions
diff --git a/lib/rubygems/commands/stale_command.rb b/lib/rubygems/commands/stale_command.rb
index badc9905c1..62a97966f1 100644
--- a/lib/rubygems/commands/stale_command.rb
+++ b/lib/rubygems/commands/stale_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/command'
+require_relative '../command'
class Gem::Commands::StaleCommand < Gem::Command
def initialize
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index 1540b2f0fb..467c8bf7ed 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/version_option'
-require 'rubygems/uninstaller'
+require_relative '../command'
+require_relative '../version_option'
+require_relative '../uninstaller'
require 'fileutils'
##
@@ -81,7 +81,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
'Uninstall gem from the vendor directory.',
'Only for use by gem repackagers.') do |value, options|
unless Gem.vendor_dir
- raise OptionParser::InvalidOption.new 'your platform is not supported'
+ raise Gem::OptionParser::InvalidOption.new 'your platform is not supported'
end
alert_warning 'Use your OS package manager to uninstall vendor gems'
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
index 8d90d08eb4..3f1708375f 100644
--- a/lib/rubygems/commands/unpack_command.rb
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/version_option'
-require 'rubygems/security_option'
-require 'rubygems/remote_fetcher'
-require 'rubygems/package'
+require_relative '../command'
+require_relative '../version_option'
+require_relative '../security_option'
+require_relative '../remote_fetcher'
+require_relative '../package'
# forward-declare
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index fcc52c293e..bb356fd610 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/command_manager'
-require 'rubygems/dependency_installer'
-require 'rubygems/install_update_options'
-require 'rubygems/local_remote_options'
-require 'rubygems/spec_fetcher'
-require 'rubygems/version_option'
-require 'rubygems/install_message' # must come before rdoc for messaging
-require 'rubygems/rdoc'
+require_relative '../command'
+require_relative '../command_manager'
+require_relative '../dependency_installer'
+require_relative '../install_update_options'
+require_relative '../local_remote_options'
+require_relative '../spec_fetcher'
+require_relative '../version_option'
+require_relative '../install_message' # must come before rdoc for messaging
+require_relative '../rdoc'
class Gem::Commands::UpdateCommand < Gem::Command
include Gem::InstallUpdateOptions
@@ -25,7 +25,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
add_install_update_options
- OptionParser.accept Gem::Version do |value|
+ Gem::OptionParser.accept Gem::Version do |value|
Gem::Version.new value
value
@@ -76,7 +76,7 @@ command to remove old versions.
def check_oldest_rubygems(version) # :nodoc:
if oldest_supported_version > version
- alert_error "rubygems #{version} is not supported. The oldest supported version is #{oldest_supported_version}"
+ alert_error "rubygems #{version} is not supported on #{RUBY_VERSION}. The oldest version supported by this ruby is #{oldest_supported_version}"
terminate_interaction 1
end
end
@@ -322,8 +322,26 @@ command to remove old versions.
private
+ #
+ # Oldest version we support downgrading to. This is the version that
+ # originally ships with the first patch version of each ruby, because we never
+ # test each ruby against older rubygems, so we can't really guarantee it
+ # works. Version list can be checked here: https://stdgems.org/rubygems
+ #
def oldest_supported_version
- # for Ruby 2.3
- @oldest_supported_version ||= Gem::Version.new("2.5.2")
+ @oldest_supported_version ||=
+ if Gem.ruby_version > Gem::Version.new("3.0.a")
+ Gem::Version.new("3.2.3")
+ elsif Gem.ruby_version > Gem::Version.new("2.7.a")
+ Gem::Version.new("3.1.2")
+ elsif Gem.ruby_version > Gem::Version.new("2.6.a")
+ Gem::Version.new("3.0.1")
+ elsif Gem.ruby_version > Gem::Version.new("2.5.a")
+ Gem::Version.new("2.7.3")
+ elsif Gem.ruby_version > Gem::Version.new("2.4.a")
+ Gem::Version.new("2.6.8")
+ else
+ Gem::Version.new("2.5.2")
+ end
end
end
diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb
index d42ab18395..44e87a2b98 100644
--- a/lib/rubygems/commands/which_command.rb
+++ b/lib/rubygems/commands/which_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/command'
+require_relative '../command'
class Gem::Commands::WhichCommand < Gem::Command
def initialize
diff --git a/lib/rubygems/commands/yank_command.rb b/lib/rubygems/commands/yank_command.rb
index 7e8b66b300..cad78aec5f 100644
--- a/lib/rubygems/commands/yank_command.rb
+++ b/lib/rubygems/commands/yank_command.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-require 'rubygems/command'
-require 'rubygems/local_remote_options'
-require 'rubygems/version_option'
-require 'rubygems/gemcutter_utilities'
+require_relative '../command'
+require_relative '../local_remote_options'
+require_relative '../version_option'
+require_relative '../gemcutter_utilities'
class Gem::Commands::YankCommand < Gem::Command
include Gem::LocalRemoteOptions
@@ -24,7 +24,7 @@ data you will need to change them immediately and yank your gem.
end
def usage # :nodoc:
- "#{program_name} GEM -v VERSION [-p PLATFORM] [--key KEY_NAME] [--host HOST]"
+ "#{program_name} -v VERSION [-p PLATFORM] [--key KEY_NAME] [--host HOST] GEM"
end
def initialize
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index 854d09ef3d..60c1d50ba9 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -5,7 +5,7 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems/user_interaction'
+require_relative 'user_interaction'
require 'rbconfig'
##
@@ -45,6 +45,7 @@ class Gem::ConfigFile
DEFAULT_UPDATE_SOURCES = true
DEFAULT_CONCURRENT_DOWNLOADS = 8
DEFAULT_CERT_EXPIRATION_LENGTH_DAYS = 365
+ DEFAULT_IPV4_FALLBACK_ENABLED = false
##
# For Ruby packagers to set configuration defaults. Set in
@@ -141,6 +142,12 @@ class Gem::ConfigFile
attr_accessor :cert_expiration_length_days
##
+ # == Experimental ==
+ # Fallback to IPv4 when IPv6 is not reachable or slow (default: false)
+
+ attr_accessor :ipv4_fallback_enabled
+
+ ##
# Path name of directory or file of openssl client certificate, used for remote https connection with client authentication
attr_reader :ssl_client_cert
@@ -175,6 +182,7 @@ class Gem::ConfigFile
@update_sources = DEFAULT_UPDATE_SOURCES
@concurrent_downloads = DEFAULT_CONCURRENT_DOWNLOADS
@cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS
+ @ipv4_fallback_enabled = ENV['IPV4_FALLBACK_ENABLED'] == 'true' || DEFAULT_IPV4_FALLBACK_ENABLED
operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
@@ -203,6 +211,7 @@ class Gem::ConfigFile
@disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
@sources = @hash[:sources] if @hash.key? :sources
@cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days
+ @ipv4_fallback_enabled = @hash[:ipv4_fallback_enabled] if @hash.key? :ipv4_fallback_enabled
@ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
@ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
@@ -311,7 +320,8 @@ if you believe they were disclosed to a third party.
config = load_file(credentials_path).merge(host => api_key)
dirname = File.dirname credentials_path
- Dir.mkdir(dirname) unless File.exist? dirname
+ require 'fileutils'
+ FileUtils.mkdir_p(dirname)
Gem.load_yaml
@@ -448,9 +458,8 @@ if you believe they were disclosed to a third party.
# Writes out this config file, replacing its source.
def write
- unless File.exist?(File.dirname(config_file_name))
- FileUtils.mkdir_p File.dirname(config_file_name)
- end
+ require 'fileutils'
+ FileUtils.mkdir_p File.dirname(config_file_name)
File.open config_file_name, 'w' do |io|
io.write to_yaml
diff --git a/lib/rubygems/core_ext/tcpsocket_init.rb b/lib/rubygems/core_ext/tcpsocket_init.rb
new file mode 100644
index 0000000000..2a79b63bd6
--- /dev/null
+++ b/lib/rubygems/core_ext/tcpsocket_init.rb
@@ -0,0 +1,52 @@
+require 'socket'
+
+module CoreExtensions
+ module TCPSocketExt
+ def self.prepended(base)
+ base.prepend Initializer
+ end
+
+ module Initializer
+ CONNECTION_TIMEOUT = 5
+ IPV4_DELAY_SECONDS = 0.1
+
+ def initialize(host, serv, *rest)
+ mutex = Thread::Mutex.new
+ addrs = []
+ threads = []
+ cond_var = Thread::ConditionVariable.new
+
+ Addrinfo.foreach(host, serv, nil, :STREAM) do |addr|
+ Thread.report_on_exception = false if defined? Thread.report_on_exception = ()
+
+ threads << Thread.new(addr) do
+ # give head start to ipv6 addresses
+ sleep IPV4_DELAY_SECONDS if addr.ipv4?
+
+ # raises Errno::ECONNREFUSED when ip:port is unreachable
+ Socket.tcp(addr.ip_address, serv, connect_timeout: CONNECTION_TIMEOUT).close
+ mutex.synchronize do
+ addrs << addr.ip_address
+ cond_var.signal
+ end
+ end
+ end
+
+ mutex.synchronize do
+ timeout_time = CONNECTION_TIMEOUT + Time.now.to_f
+ while addrs.empty? && (remaining_time = timeout_time - Time.now.to_f) > 0
+ cond_var.wait(mutex, remaining_time)
+ end
+
+ host = addrs.shift unless addrs.empty?
+ end
+
+ threads.each {|t| t.kill.join if t.alive? }
+
+ super(host, serv, *rest)
+ end
+ end
+ end
+end
+
+TCPSocket.prepend CoreExtensions::TCPSocketExt
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index 8aae67cd6b..923b60f31f 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -73,7 +73,7 @@ module Gem
# Path to specification files of default gems.
def self.default_specifications_dir
- File.join(Gem.default_dir, "specifications", "default")
+ @default_specifications_dir ||= File.join(Gem.default_dir, "specifications", "default")
end
##
@@ -198,7 +198,7 @@ module Gem
def self.default_bindir
if defined? RUBY_FRAMEWORK_VERSION # mac framework support
- '/usr/bin'
+ '/usr/local/bin'
else # generic install
RbConfig::CONFIG['bindir']
end
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 400a5de5cf..913bba32eb 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -1,12 +1,12 @@
# frozen_string_literal: true
-require 'rubygems'
-require 'rubygems/dependency_list'
-require 'rubygems/package'
-require 'rubygems/installer'
-require 'rubygems/spec_fetcher'
-require 'rubygems/user_interaction'
-require 'rubygems/available_set'
-require 'rubygems/deprecate'
+require_relative '../rubygems'
+require_relative 'dependency_list'
+require_relative 'package'
+require_relative 'installer'
+require_relative 'spec_fetcher'
+require_relative 'user_interaction'
+require_relative 'available_set'
+require_relative 'deprecate'
##
# Installs a gem along with all its dependencies from local and remote gems.
diff --git a/lib/rubygems/dependency_list.rb b/lib/rubygems/dependency_list.rb
index bcf436cd03..10e08fc703 100644
--- a/lib/rubygems/dependency_list.rb
+++ b/lib/rubygems/dependency_list.rb
@@ -5,8 +5,8 @@
# See LICENSE.txt for permissions.
#++
-require 'tsort'
-require 'rubygems/deprecate'
+require_relative 'tsort'
+require_relative 'deprecate'
##
# Gem::DependencyList is used for installing and uninstalling gems in the
@@ -20,7 +20,7 @@ class Gem::DependencyList
attr_reader :specs
include Enumerable
- include TSort
+ include Gem::TSort
##
# Allows enabling/disabling use of development dependencies
diff --git a/lib/rubygems/deprecate.rb b/lib/rubygems/deprecate.rb
index 67285d4161..8c822cda95 100644
--- a/lib/rubygems/deprecate.rb
+++ b/lib/rubygems/deprecate.rb
@@ -60,12 +60,13 @@ module Gem::Deprecate
target = klass ? "#{self}." : "#{self.class}#"
msg = [ "NOTE: #{target}#{name} is deprecated",
repl == :none ? " with no replacement" : "; use #{repl} instead",
- ". It will be removed on or after %4d-%02d-01." % [year, month],
+ ". It will be removed on or after %4d-%02d." % [year, month],
"\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
]
warn "#{msg.join}." unless Gem::Deprecate.skip
send old, *args, &block
end
+ ruby2_keywords name if respond_to?(:ruby2_keywords, true)
end
end
@@ -90,6 +91,7 @@ module Gem::Deprecate
warn "#{msg.join}." unless Gem::Deprecate.skip
send old, *args, &block
end
+ ruby2_keywords name if respond_to?(:ruby2_keywords, true)
end
end
diff --git a/lib/rubygems/doctor.rb b/lib/rubygems/doctor.rb
index ef31aeddd0..41bcda9804 100644
--- a/lib/rubygems/doctor.rb
+++ b/lib/rubygems/doctor.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems'
-require 'rubygems/user_interaction'
+require_relative '../rubygems'
+require_relative 'user_interaction'
##
# Cleans up after a partially-failed uninstall or for an invalid
diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb
index abee20651e..86f0d1da14 100644
--- a/lib/rubygems/errors.rb
+++ b/lib/rubygems/errors.rb
@@ -171,8 +171,7 @@ module Gem
# An English description of the error.
def wordy
- @source.uri.password = 'REDACTED' unless @source.uri.password.nil?
- "Unable to download data from #{@source.uri} - #{@error.message}"
+ "Unable to download data from #{Gem::Uri.new(@source.uri).redacted} - #{@error.message}"
end
##
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index 804863f693..5f20dfaed0 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/deprecate'
+require_relative 'deprecate'
##
# Base exception class for RubyGems. All exception raised by RubyGems are a
@@ -259,3 +259,4 @@ end
# Backwards compatible typo'd exception class for early RubyGems 2.0.x
Gem::UnsatisfiableDepedencyError = Gem::UnsatisfiableDependencyError # :nodoc:
+Gem.deprecate_constant :UnsatisfiableDepedencyError
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index e4af450565..c1a1bc9c00 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -24,13 +24,14 @@ class Gem::Ext::Builder
# try to find make program from Ruby configure arguments first
RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/
- make_program = ENV['MAKE'] || ENV['make'] || $1
- unless make_program
- make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
+ make_program_name = ENV['MAKE'] || ENV['make'] || $1
+ unless make_program_name
+ make_program_name = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
end
- make_program = Shellwords.split(make_program)
+ make_program = Shellwords.split(make_program_name)
- destdir = 'DESTDIR=%s' % ENV['DESTDIR']
+ # The installation of the bundled gems is failed when DESTDIR is empty in mswin platform.
+ destdir = (/\bnmake/i !~ make_program_name || ENV['DESTDIR'] && ENV['DESTDIR'] != "") ? 'DESTDIR=%s' % ENV['DESTDIR'] : ''
['clean', '', 'install'].each do |target|
# Pass DESTDIR via command line to override what's in MAKEFLAGS
@@ -57,6 +58,7 @@ class Gem::Ext::Builder
p(command)
end
results << "current directory: #{dir}"
+ require "shellwords"
results << command.shelljoin
require "open3"
diff --git a/lib/rubygems/ext/cmake_builder.rb b/lib/rubygems/ext/cmake_builder.rb
index 269e876cfa..e47cabef84 100644
--- a/lib/rubygems/ext/cmake_builder.rb
+++ b/lib/rubygems/ext/cmake_builder.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-require_relative '../command'
class Gem::Ext::CmakeBuilder < Gem::Ext::Builder
def self.build(extension, dest_path, results, args=[], lib_dir=nil, cmake_dir=Dir.pwd)
unless File.exist?(File.join(cmake_dir, 'Makefile'))
+ require_relative '../command'
cmd = ["cmake", ".", "-DCMAKE_INSTALL_PREFIX=#{dest_path}", *Gem::Command.build_args]
run cmd, results, class_name, cmake_dir
diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb
index fede270417..3ca3463615 100644
--- a/lib/rubygems/ext/ext_conf_builder.rb
+++ b/lib/rubygems/ext/ext_conf_builder.rb
@@ -5,8 +5,6 @@
# See LICENSE.txt for permissions.
#++
-require 'shellwords'
-
class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd)
require 'fileutils'
@@ -23,11 +21,11 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
# spaces do not work.
#
# Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940
- tmp_dest = get_relative_path(tmp_dest, extension_dir)
+ tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir)
Tempfile.open %w[siteconf .rb], extension_dir do |siteconf|
siteconf.puts "require 'rbconfig'"
- siteconf.puts "dest_path = #{tmp_dest.dump}"
+ siteconf.puts "dest_path = #{tmp_dest_relative.dump}"
%w[sitearchdir sitelibdir].each do |dir|
siteconf.puts "RbConfig::MAKEFILE_CONFIG['#{dir}'] = dest_path"
siteconf.puts "RbConfig::CONFIG['#{dir}'] = dest_path"
@@ -40,6 +38,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
begin
# workaround for https://github.com/oracle/truffleruby/issues/2115
siteconf_path = RUBY_ENGINE == "truffleruby" ? siteconf.path.dup : siteconf.path
+ require "shellwords"
cmd = Gem.ruby.shellsplit << "-I" << File.expand_path("../../..", __FILE__) <<
"-r" << get_relative_path(siteconf_path, extension_dir) << File.basename(extension)
cmd.push(*args)
@@ -63,8 +62,8 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
make dest_path, results, extension_dir
- if tmp_dest
- full_tmp_dest = File.join(extension_dir, tmp_dest)
+ if tmp_dest_relative
+ full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
# TODO remove in RubyGems 3
if Gem.install_extension_in_lib and lib_dir
diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb
index 64a6c0eb80..fed98e741c 100644
--- a/lib/rubygems/ext/rake_builder.rb
+++ b/lib/rubygems/ext/rake_builder.rb
@@ -5,8 +5,6 @@
# See LICENSE.txt for permissions.
#++
-require "shellwords"
-
class Gem::Ext::RakeBuilder < Gem::Ext::Builder
def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd)
if File.basename(extension) =~ /mkrf_conf/i
@@ -16,6 +14,7 @@ class Gem::Ext::RakeBuilder < Gem::Ext::Builder
rake = ENV['rake']
if rake
+ require "shellwords"
rake = rake.shellsplit
else
begin
diff --git a/lib/rubygems/gem_runner.rb b/lib/rubygems/gem_runner.rb
index a36674503e..55b5a7d067 100644
--- a/lib/rubygems/gem_runner.rb
+++ b/lib/rubygems/gem_runner.rb
@@ -5,9 +5,9 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems'
-require 'rubygems/command_manager'
-require 'rubygems/deprecate'
+require_relative '../rubygems'
+require_relative 'command_manager'
+require_relative 'deprecate'
##
# Load additional plugins from $LOAD_PATH
diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb
index 3687e776e2..0968e1a6f9 100644
--- a/lib/rubygems/gemcutter_utilities.rb
+++ b/lib/rubygems/gemcutter_utilities.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/remote_fetcher'
-require 'rubygems/text'
+require_relative 'remote_fetcher'
+require_relative 'text'
##
# Utility methods for using the RubyGems API.
@@ -31,7 +31,8 @@ module Gem::GemcutterUtilities
def add_otp_option
add_option('--otp CODE',
- 'Digit code for multifactor authentication') do |value, options|
+ 'Digit code for multifactor authentication',
+ 'You can also use the environment variable GEM_HOST_OTP_CODE') do |value, options|
options[:otp] = value
end
end
@@ -52,6 +53,13 @@ module Gem::GemcutterUtilities
end
##
+ # The OTP code from the command options or from the user's configuration.
+
+ def otp
+ options[:otp] || ENV["GEM_HOST_OTP_CODE"]
+ end
+
+ ##
# The host to connect to either from the RUBYGEMS_HOST environment variable
# or from the user's configuration
@@ -126,7 +134,7 @@ module Gem::GemcutterUtilities
response = rubygems_api_request(:put, "api/v1/api_key",
sign_in_host, scope: scope) do |request|
request.basic_auth email, password
- request["OTP"] = options[:otp] if options[:otp]
+ request["OTP"] = otp if otp
request.body = URI.encode_www_form({:api_key => api_key }.merge(update_scope_params))
end
@@ -159,7 +167,7 @@ module Gem::GemcutterUtilities
response = rubygems_api_request(:post, "api/v1/api_key",
sign_in_host, scope: scope) do |request|
request.basic_auth email, password
- request["OTP"] = options[:otp] if options[:otp]
+ request["OTP"] = otp if otp
request.body = URI.encode_www_form({ name: key_name }.merge(scope_params))
end
@@ -224,7 +232,7 @@ module Gem::GemcutterUtilities
request_method = Net::HTTP.const_get method.to_s.capitalize
Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req|
- req["OTP"] = options[:otp] if options[:otp]
+ req["OTP"] = otp if otp
block.call(req)
end
end
diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb
index 31285ca962..6e8dade640 100644
--- a/lib/rubygems/indexer.rb
+++ b/lib/rubygems/indexer.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems'
-require 'rubygems/package'
+require_relative '../rubygems'
+require_relative 'package'
require 'tmpdir'
##
@@ -136,7 +136,7 @@ class Gem::Indexer
say "Generating #{name} index"
Gem.time "Generated #{name} index" do
- open(file, 'wb') do |io|
+ File.open(file, 'wb') do |io|
specs = index.map do |*spec|
# We have to splat here because latest_specs is an array, while the
# others are hashes.
diff --git a/lib/rubygems/install_default_message.rb b/lib/rubygems/install_default_message.rb
index f68fd2fd04..052ef528e1 100644
--- a/lib/rubygems/install_default_message.rb
+++ b/lib/rubygems/install_default_message.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems'
-require 'rubygems/user_interaction'
+require_relative '../rubygems'
+require_relative 'user_interaction'
##
# A post-install hook that displays "Successfully installed
diff --git a/lib/rubygems/install_message.rb b/lib/rubygems/install_message.rb
index 3c13888a84..861ead3770 100644
--- a/lib/rubygems/install_message.rb
+++ b/lib/rubygems/install_message.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems'
-require 'rubygems/user_interaction'
+require_relative '../rubygems'
+require_relative 'user_interaction'
##
# A default post-install hook that displays "Successfully installed
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index ef1ad1edcb..d890c526bc 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -5,8 +5,8 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems'
-require 'rubygems/security_option'
+require_relative '../rubygems'
+require_relative 'security_option'
##
# Mixin methods for install and update options for Gem::Commands
@@ -51,7 +51,7 @@ module Gem::InstallUpdateOptions
'Install gem into the vendor directory.',
'Only for use by gem repackagers.') do |value, options|
unless Gem.vendor_dir
- raise OptionParser::InvalidOption.new 'your platform is not supported'
+ raise Gem::OptionParser::InvalidOption.new 'your platform is not supported'
end
options[:vendor] = true
@@ -143,7 +143,7 @@ module Gem::InstallUpdateOptions
unless v
message = v ? v : "(tried #{Gem::GEM_DEP_FILES.join ', '})"
- raise OptionParser::InvalidArgument,
+ raise Gem::OptionParser::InvalidArgument,
"cannot find gem dependencies file #{message}"
end
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 3ec6d743a2..8e3965ef92 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -5,13 +5,12 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems/command'
-require 'rubygems/installer_uninstaller_utils'
-require 'rubygems/exceptions'
-require 'rubygems/deprecate'
-require 'rubygems/package'
-require 'rubygems/ext'
-require 'rubygems/user_interaction'
+require_relative 'installer_uninstaller_utils'
+require_relative 'exceptions'
+require_relative 'deprecate'
+require_relative 'package'
+require_relative 'ext'
+require_relative 'user_interaction'
##
# The installer installs the files contained in the .gem into the Gem.home.
@@ -68,19 +67,28 @@ class Gem::Installer
@path_warning = false
- @install_lock = Mutex.new
-
class << self
- ##
- # True if we've warned about PATH not including Gem.bindir
+ #
+ # Changes in rubygems to lazily loading `rubygems/command` (in order to
+ # lazily load `optparse` as a side effect) affect bundler's custom installer
+ # which uses `Gem::Command` without requiring it (up until bundler 2.2.29).
+ # This hook is to compensate for that missing require.
+ #
+ # TODO: Remove when rubygems no longer supports running on bundler older
+ # than 2.2.29.
- attr_accessor :path_warning
+ def inherited(klass)
+ if klass.name == "Bundler::RubyGemsGemInstaller"
+ require "rubygems/command"
+ end
+
+ super(klass)
+ end
##
- # Certain aspects of the install process are not thread-safe. This lock is
- # used to allow multiple threads to install Gems at the same time.
+ # True if we've warned about PATH not including Gem.bindir
- attr_reader :install_lock
+ attr_accessor :path_warning
##
# Overrides the executable format.
@@ -285,8 +293,6 @@ class Gem::Installer
def install
pre_install_checks
- FileUtils.rm_f File.join gem_home, 'specifications', spec.spec_name
-
run_pre_install_hooks
# Set loaded_from to ensure extension_dir is correct
@@ -326,7 +332,7 @@ class Gem::Installer
say spec.post_install_message if options[:post_install_message] && !spec.post_install_message.nil?
- Gem::Installer.install_lock.synchronize { Gem::Specification.reset }
+ Gem::Specification.reset
run_post_install_hooks
@@ -440,13 +446,9 @@ class Gem::Installer
# specifications directory.
def write_spec
- File.open spec_file, 'w' do |file|
- spec.installed_by_version = Gem.rubygems_version
-
- file.puts spec.to_ruby_for_cache
+ spec.installed_by_version = Gem.rubygems_version
- file.fsync rescue nil # for filesystems without fsync(2)
- end
+ Gem.write_binary(spec_file, spec.to_ruby_for_cache)
end
##
@@ -454,9 +456,7 @@ class Gem::Installer
# specifications/default directory.
def write_default_spec
- File.open(default_spec_file, "w") do |file|
- file.puts spec.to_ruby
- end
+ Gem.write_binary(default_spec_file, spec.to_ruby)
end
##
@@ -484,8 +484,11 @@ class Gem::Installer
bin_path = File.join gem_dir, spec.bindir, filename
unless File.exist? bin_path
- # TODO change this to a more useful warning
- warn "`#{bin_path}` does not exist, maybe `gem pristine #{spec.name}` will fix it?"
+ if File.symlink? bin_path
+ alert_warning "`#{bin_path}` is dangling symlink pointing to `#{File.readlink bin_path}`"
+ else
+ alert_warning "`#{bin_path}` does not exist, maybe `gem pristine #{spec.name}` will fix it?"
+ end
next
end
@@ -508,7 +511,7 @@ class Gem::Installer
end
def generate_plugins # :nodoc:
- latest = Gem::Installer.install_lock.synchronize { Gem::Specification.latest_spec_for(spec.name) }
+ latest = Gem::Specification.latest_spec_for(spec.name)
return if latest && latest.version > spec.version
ensure_writable_dir @plugins_dir
@@ -673,7 +676,7 @@ class Gem::Installer
@development = options[:development]
@build_root = options[:build_root]
- @build_args = options[:build_args] || Gem::Command.build_args
+ @build_args = options[:build_args]
unless @build_root.nil?
@bin_dir = File.join(@build_root, @bin_dir.gsub(/^[a-zA-Z]:/, ''))
@@ -725,6 +728,10 @@ class Gem::Installer
raise Gem::InstallError, "#{spec} has an invalid extensions"
end
+ if spec.platform.to_s =~ /\R/
+ raise Gem::InstallError, "#{spec.platform} is an invalid platform"
+ end
+
unless spec.specification_version.to_s =~ /\A\d+\z/
raise Gem::InstallError, "#{spec} has an invalid specification_version"
end
@@ -754,7 +761,7 @@ class Gem::Installer
#
require 'rubygems'
-
+#{gemdeps_load(spec.name)}
version = "#{Gem::Requirement.default_prerelease}"
str = ARGV.first
@@ -775,6 +782,15 @@ end
TEXT
end
+ def gemdeps_load(name)
+ return '' if name == "bundler"
+
+ <<-TEXT
+
+Gem.use_gemdeps
+TEXT
+ end
+
##
# return the stub script text used to launch the true Ruby script
@@ -816,7 +832,7 @@ TEXT
# configure scripts and rakefiles or mkrf_conf files.
def build_extensions
- builder = Gem::Ext::Builder.new spec, @build_args
+ builder = Gem::Ext::Builder.new spec, build_args
builder.build_extensions
end
@@ -903,7 +919,7 @@ TEXT
# extensions.
def write_build_info_file
- return if @build_args.empty?
+ return if build_args.empty?
build_info_dir = File.join gem_home, 'build_info'
@@ -913,7 +929,7 @@ TEXT
build_info_file = File.join build_info_dir, "#{spec.full_name}.info"
File.open build_info_file, 'w' do |io|
- @build_args.each do |arg|
+ build_args.each do |arg|
io.puts arg
end
end
@@ -938,4 +954,13 @@ TEXT
raise Gem::FilePermissionError.new(dir) unless File.writable? dir
end
+
+ private
+
+ def build_args
+ @build_args ||= begin
+ require_relative "command"
+ Gem::Command.build_args
+ end
+ end
end
diff --git a/lib/rubygems/local_remote_options.rb b/lib/rubygems/local_remote_options.rb
index 2d0509eb03..0b8b0ee1a6 100644
--- a/lib/rubygems/local_remote_options.rb
+++ b/lib/rubygems/local_remote_options.rb
@@ -6,7 +6,7 @@
#++
require 'uri'
-require 'rubygems'
+require_relative '../rubygems'
##
# Mixin methods for local and remote Gem::Command options.
@@ -14,14 +14,14 @@ require 'rubygems'
module Gem::LocalRemoteOptions
##
- # Allows OptionParser to handle HTTP URIs.
+ # Allows Gem::OptionParser to handle HTTP URIs.
def accept_uri_http
- OptionParser.accept URI::HTTP do |value|
+ Gem::OptionParser.accept URI::HTTP do |value|
begin
uri = URI.parse value
rescue URI::InvalidURIError
- raise OptionParser::InvalidArgument, value
+ raise Gem::OptionParser::InvalidArgument, value
end
valid_uri_schemes = ["http", "https", "file", "s3"]
diff --git a/lib/rubygems/mock_gem_ui.rb b/lib/rubygems/mock_gem_ui.rb
index ec244fb7c6..914ecb9a71 100644
--- a/lib/rubygems/mock_gem_ui.rb
+++ b/lib/rubygems/mock_gem_ui.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/user_interaction'
+require_relative 'user_interaction'
##
# This Gem::StreamUI subclass records input and output to StringIO for
diff --git a/lib/rubygems/optparse.rb b/lib/rubygems/optparse.rb
new file mode 100644
index 0000000000..65be9f6b74
--- /dev/null
+++ b/lib/rubygems/optparse.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require_relative 'optparse/lib/optparse'
diff --git a/lib/rubygems/optparse/.document b/lib/rubygems/optparse/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/rubygems/optparse/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/rubygems/optparse/COPYING b/lib/rubygems/optparse/COPYING
new file mode 100644
index 0000000000..48e5a96de7
--- /dev/null
+++ b/lib/rubygems/optparse/COPYING
@@ -0,0 +1,56 @@
+Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
+You can redistribute it and/or modify it under either the terms of the
+2-clause BSDL (see the file BSDL), or the conditions below:
+
+1. You may make and give away verbatim copies of the source form of the
+ software without restriction, provided that you duplicate all of the
+ original copyright notices and associated disclaimers.
+
+2. You may modify your copy of the software in any way, provided that
+ you do at least ONE of the following:
+
+ a. place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said
+ modifications to Usenet or an equivalent medium, or by allowing
+ the author to include your modifications in the software.
+
+ b. use the modified software only within your corporation or
+ organization.
+
+ c. give non-standard binaries non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d. make other distribution arrangements with the author.
+
+3. You may distribute the software in object code or binary form,
+ provided that you do at least ONE of the following:
+
+ a. distribute the binaries and library files of the software,
+ together with instructions (in the manual page or equivalent)
+ on where to get the original distribution.
+
+ b. accompany the distribution with the machine-readable source of
+ the software.
+
+ c. give non-standard binaries non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d. make other distribution arrangements with the author.
+
+4. You may modify and include the part of the software into any other
+ software (possibly commercial). But some files in the distribution
+ are not written by the author, so that they are not under these terms.
+
+ For the list of those files and their copying conditions, see the
+ file LEGAL.
+
+5. The scripts and library files supplied as input to or produced as
+ output from the software do not automatically fall under the
+ copyright of the software, but belong to whomever generated them,
+ and may be sold commercially, and may be aggregated with this
+ software.
+
+6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE.
diff --git a/lib/rubygems/optparse/lib/optionparser.rb b/lib/rubygems/optparse/lib/optionparser.rb
new file mode 100644
index 0000000000..4b9b40d82a
--- /dev/null
+++ b/lib/rubygems/optparse/lib/optionparser.rb
@@ -0,0 +1,2 @@
+# frozen_string_literal: false
+require_relative 'optparse'
diff --git a/lib/rubygems/optparse/lib/optparse.rb b/lib/rubygems/optparse/lib/optparse.rb
new file mode 100644
index 0000000000..e4b1c61f79
--- /dev/null
+++ b/lib/rubygems/optparse/lib/optparse.rb
@@ -0,0 +1,2230 @@
+# frozen_string_literal: true
+#
+# optparse.rb - command-line option analysis with the Gem::OptionParser class.
+#
+# Author:: Nobu Nakada
+# Documentation:: Nobu Nakada and Gavin Sinclair.
+#
+# See Gem::OptionParser for documentation.
+#
+
+
+#--
+# == Developer Documentation (not for RDoc output)
+#
+# === Class tree
+#
+# - Gem::OptionParser:: front end
+# - Gem::OptionParser::Switch:: each switches
+# - Gem::OptionParser::List:: options list
+# - Gem::OptionParser::ParseError:: errors on parsing
+# - Gem::OptionParser::AmbiguousOption
+# - Gem::OptionParser::NeedlessArgument
+# - Gem::OptionParser::MissingArgument
+# - Gem::OptionParser::InvalidOption
+# - Gem::OptionParser::InvalidArgument
+# - Gem::OptionParser::AmbiguousArgument
+#
+# === Object relationship diagram
+#
+# +--------------+
+# | Gem::OptionParser |<>-----+
+# +--------------+ | +--------+
+# | ,-| Switch |
+# on_head -------->+---------------+ / +--------+
+# accept/reject -->| List |<|>-
+# | |<|>- +----------+
+# on ------------->+---------------+ `-| argument |
+# : : | class |
+# +---------------+ |==========|
+# on_tail -------->| | |pattern |
+# +---------------+ |----------|
+# Gem::OptionParser.accept ->| DefaultList | |converter |
+# reject |(shared between| +----------+
+# | all instances)|
+# +---------------+
+#
+#++
+#
+# == Gem::OptionParser
+#
+# === New to \Gem::OptionParser?
+#
+# See the {Tutorial}[./doc/optparse/tutorial_rdoc.html].
+#
+# === Introduction
+#
+# Gem::OptionParser is a class for command-line option analysis. It is much more
+# advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented
+# solution.
+#
+# === Features
+#
+# 1. The argument specification and the code to handle it are written in the
+# same place.
+# 2. It can output an option summary; you don't need to maintain this string
+# separately.
+# 3. Optional and mandatory arguments are specified very gracefully.
+# 4. Arguments can be automatically converted to a specified class.
+# 5. Arguments can be restricted to a certain set.
+#
+# All of these features are demonstrated in the examples below. See
+# #make_switch for full documentation.
+#
+# === Minimal example
+#
+# require 'rubygems/optparse/lib/optparse'
+#
+# options = {}
+# Gem::OptionParser.new do |parser|
+# parser.banner = "Usage: example.rb [options]"
+#
+# parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
+# options[:verbose] = v
+# end
+# end.parse!
+#
+# p options
+# p ARGV
+#
+# === Generating Help
+#
+# Gem::OptionParser can be used to automatically generate help for the commands you
+# write:
+#
+# require 'rubygems/optparse/lib/optparse'
+#
+# Options = Struct.new(:name)
+#
+# class Parser
+# def self.parse(options)
+# args = Options.new("world")
+#
+# opt_parser = Gem::OptionParser.new do |parser|
+# parser.banner = "Usage: example.rb [options]"
+#
+# parser.on("-nNAME", "--name=NAME", "Name to say hello to") do |n|
+# args.name = n
+# end
+#
+# parser.on("-h", "--help", "Prints this help") do
+# puts parser
+# exit
+# end
+# end
+#
+# opt_parser.parse!(options)
+# return args
+# end
+# end
+# options = Parser.parse %w[--help]
+#
+# #=>
+# # Usage: example.rb [options]
+# # -n, --name=NAME Name to say hello to
+# # -h, --help Prints this help
+#
+# === Required Arguments
+#
+# For options that require an argument, option specification strings may include an
+# option name in all caps. If an option is used without the required argument,
+# an exception will be raised.
+#
+# require 'rubygems/optparse/lib/optparse'
+#
+# options = {}
+# Gem::OptionParser.new do |parser|
+# parser.on("-r", "--require LIBRARY",
+# "Require the LIBRARY before executing your script") do |lib|
+# puts "You required #{lib}!"
+# end
+# end.parse!
+#
+# Used:
+#
+# $ ruby optparse-test.rb -r
+# optparse-test.rb:9:in `<main>': missing argument: -r (Gem::OptionParser::MissingArgument)
+# $ ruby optparse-test.rb -r my-library
+# You required my-library!
+#
+# === Type Coercion
+#
+# Gem::OptionParser supports the ability to coerce command line arguments
+# into objects for us.
+#
+# Gem::OptionParser comes with a few ready-to-use kinds of type
+# coercion. They are:
+#
+# - Date -- Anything accepted by +Date.parse+
+# - DateTime -- Anything accepted by +DateTime.parse+
+# - Time -- Anything accepted by +Time.httpdate+ or +Time.parse+
+# - URI -- Anything accepted by +URI.parse+
+# - Shellwords -- Anything accepted by +Shellwords.shellwords+
+# - String -- Any non-empty string
+# - Integer -- Any integer. Will convert octal. (e.g. 124, -3, 040)
+# - Float -- Any float. (e.g. 10, 3.14, -100E+13)
+# - Numeric -- Any integer, float, or rational (1, 3.4, 1/3)
+# - DecimalInteger -- Like +Integer+, but no octal format.
+# - OctalInteger -- Like +Integer+, but no decimal format.
+# - DecimalNumeric -- Decimal integer or float.
+# - TrueClass -- Accepts '+, yes, true, -, no, false' and
+# defaults as +true+
+# - FalseClass -- Same as +TrueClass+, but defaults to +false+
+# - Array -- Strings separated by ',' (e.g. 1,2,3)
+# - Regexp -- Regular expressions. Also includes options.
+#
+# We can also add our own coercions, which we will cover below.
+#
+# ==== Using Built-in Conversions
+#
+# As an example, the built-in +Time+ conversion is used. The other built-in
+# conversions behave in the same way.
+# Gem::OptionParser will attempt to parse the argument
+# as a +Time+. If it succeeds, that time will be passed to the
+# handler block. Otherwise, an exception will be raised.
+#
+# require 'rubygems/optparse/lib/optparse'
+# require 'rubygems/optparse/lib/optparse/time'
+# Gem::OptionParser.new do |parser|
+# parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
+# p time
+# end
+# end.parse!
+#
+# Used:
+#
+# $ ruby optparse-test.rb -t nonsense
+# ... invalid argument: -t nonsense (Gem::OptionParser::InvalidArgument)
+# $ ruby optparse-test.rb -t 10-11-12
+# 2010-11-12 00:00:00 -0500
+# $ ruby optparse-test.rb -t 9:30
+# 2014-08-13 09:30:00 -0400
+#
+# ==== Creating Custom Conversions
+#
+# The +accept+ method on Gem::OptionParser may be used to create converters.
+# It specifies which conversion block to call whenever a class is specified.
+# The example below uses it to fetch a +User+ object before the +on+ handler receives it.
+#
+# require 'rubygems/optparse/lib/optparse'
+#
+# User = Struct.new(:id, :name)
+#
+# def find_user id
+# not_found = ->{ raise "No User Found for id #{id}" }
+# [ User.new(1, "Sam"),
+# User.new(2, "Gandalf") ].find(not_found) do |u|
+# u.id == id
+# end
+# end
+#
+# op = Gem::OptionParser.new
+# op.accept(User) do |user_id|
+# find_user user_id.to_i
+# end
+#
+# op.on("--user ID", User) do |user|
+# puts user
+# end
+#
+# op.parse!
+#
+# Used:
+#
+# $ ruby optparse-test.rb --user 1
+# #<struct User id=1, name="Sam">
+# $ ruby optparse-test.rb --user 2
+# #<struct User id=2, name="Gandalf">
+# $ ruby optparse-test.rb --user 3
+# optparse-test.rb:15:in `block in find_user': No User Found for id 3 (RuntimeError)
+#
+# === Store options to a Hash
+#
+# The +into+ option of +order+, +parse+ and so on methods stores command line options into a Hash.
+#
+# require 'rubygems/optparse/lib/optparse'
+#
+# options = {}
+# Gem::OptionParser.new do |parser|
+# parser.on('-a')
+# parser.on('-b NUM', Integer)
+# parser.on('-v', '--verbose')
+# end.parse!(into: options)
+#
+# p options
+#
+# Used:
+#
+# $ ruby optparse-test.rb -a
+# {:a=>true}
+# $ ruby optparse-test.rb -a -v
+# {:a=>true, :verbose=>true}
+# $ ruby optparse-test.rb -a -b 100
+# {:a=>true, :b=>100}
+#
+# === Complete example
+#
+# The following example is a complete Ruby program. You can run it and see the
+# effect of specifying various options. This is probably the best way to learn
+# the features of +optparse+.
+#
+# require 'rubygems/optparse/lib/optparse'
+# require 'rubygems/optparse/lib/optparse/time'
+# require 'ostruct'
+# require 'pp'
+#
+# class OptparseExample
+# Version = '1.0.0'
+#
+# CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
+# CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
+#
+# class ScriptOptions
+# attr_accessor :library, :inplace, :encoding, :transfer_type,
+# :verbose, :extension, :delay, :time, :record_separator,
+# :list
+#
+# def initialize
+# self.library = []
+# self.inplace = false
+# self.encoding = "utf8"
+# self.transfer_type = :auto
+# self.verbose = false
+# end
+#
+# def define_options(parser)
+# parser.banner = "Usage: example.rb [options]"
+# parser.separator ""
+# parser.separator "Specific options:"
+#
+# # add additional options
+# perform_inplace_option(parser)
+# delay_execution_option(parser)
+# execute_at_time_option(parser)
+# specify_record_separator_option(parser)
+# list_example_option(parser)
+# specify_encoding_option(parser)
+# optional_option_argument_with_keyword_completion_option(parser)
+# boolean_verbose_option(parser)
+#
+# parser.separator ""
+# parser.separator "Common options:"
+# # No argument, shows at tail. This will print an options summary.
+# # Try it and see!
+# parser.on_tail("-h", "--help", "Show this message") do
+# puts parser
+# exit
+# end
+# # Another typical switch to print the version.
+# parser.on_tail("--version", "Show version") do
+# puts Version
+# exit
+# end
+# end
+#
+# def perform_inplace_option(parser)
+# # Specifies an optional option argument
+# parser.on("-i", "--inplace [EXTENSION]",
+# "Edit ARGV files in place",
+# "(make backup if EXTENSION supplied)") do |ext|
+# self.inplace = true
+# self.extension = ext || ''
+# self.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot.
+# end
+# end
+#
+# def delay_execution_option(parser)
+# # Cast 'delay' argument to a Float.
+# parser.on("--delay N", Float, "Delay N seconds before executing") do |n|
+# self.delay = n
+# end
+# end
+#
+# def execute_at_time_option(parser)
+# # Cast 'time' argument to a Time object.
+# parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
+# self.time = time
+# end
+# end
+#
+# def specify_record_separator_option(parser)
+# # Cast to octal integer.
+# parser.on("-F", "--irs [OCTAL]", Gem::OptionParser::OctalInteger,
+# "Specify record separator (default \\0)") do |rs|
+# self.record_separator = rs
+# end
+# end
+#
+# def list_example_option(parser)
+# # List of arguments.
+# parser.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
+# self.list = list
+# end
+# end
+#
+# def specify_encoding_option(parser)
+# # Keyword completion. We are specifying a specific set of arguments (CODES
+# # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
+# # the shortest unambiguous text.
+# code_list = (CODE_ALIASES.keys + CODES).join(', ')
+# parser.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
+# "(#{code_list})") do |encoding|
+# self.encoding = encoding
+# end
+# end
+#
+# def optional_option_argument_with_keyword_completion_option(parser)
+# # Optional '--type' option argument with keyword completion.
+# parser.on("--type [TYPE]", [:text, :binary, :auto],
+# "Select transfer type (text, binary, auto)") do |t|
+# self.transfer_type = t
+# end
+# end
+#
+# def boolean_verbose_option(parser)
+# # Boolean switch.
+# parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
+# self.verbose = v
+# end
+# end
+# end
+#
+# #
+# # Return a structure describing the options.
+# #
+# def parse(args)
+# # The options specified on the command line will be collected in
+# # *options*.
+#
+# @options = ScriptOptions.new
+# @args = Gem::OptionParser.new do |parser|
+# @options.define_options(parser)
+# parser.parse!(args)
+# end
+# @options
+# end
+#
+# attr_reader :parser, :options
+# end # class OptparseExample
+#
+# example = OptparseExample.new
+# options = example.parse(ARGV)
+# pp options # example.options
+# pp ARGV
+#
+# === Shell Completion
+#
+# For modern shells (e.g. bash, zsh, etc.), you can use shell
+# completion for command line options.
+#
+# === Further documentation
+#
+# The above examples, along with the accompanying
+# {Tutorial}[./doc/optparse/tutorial_rdoc.html],
+# should be enough to learn how to use this class.
+# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
+#
+class Gem::OptionParser
+ Gem::OptionParser::Version = "0.2.0"
+
+ # :stopdoc:
+ NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
+ RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze
+ OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze
+ # :startdoc:
+
+ #
+ # Keyword completion module. This allows partial arguments to be specified
+ # and resolved against a list of acceptable values.
+ #
+ module Completion
+ def self.regexp(key, icase)
+ Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), icase)
+ end
+
+ def self.candidate(key, icase = false, pat = nil, &block)
+ pat ||= Completion.regexp(key, icase)
+ candidates = []
+ block.call do |k, *v|
+ (if Regexp === k
+ kn = ""
+ k === key
+ else
+ kn = defined?(k.id2name) ? k.id2name : k
+ pat === kn
+ end) or next
+ v << k if v.empty?
+ candidates << [k, v, kn]
+ end
+ candidates
+ end
+
+ def candidate(key, icase = false, pat = nil)
+ Completion.candidate(key, icase, pat, &method(:each))
+ end
+
+ public
+ def complete(key, icase = false, pat = nil)
+ candidates = candidate(key, icase, pat, &method(:each)).sort_by {|k, v, kn| kn.size}
+ if candidates.size == 1
+ canon, sw, * = candidates[0]
+ elsif candidates.size > 1
+ canon, sw, cn = candidates.shift
+ candidates.each do |k, v, kn|
+ next if sw == v
+ if String === cn and String === kn
+ if cn.rindex(kn, 0)
+ canon, sw, cn = k, v, kn
+ next
+ elsif kn.rindex(cn, 0)
+ next
+ end
+ end
+ throw :ambiguous, key
+ end
+ end
+ if canon
+ block_given? or return key, *sw
+ yield(key, *sw)
+ end
+ end
+
+ def convert(opt = nil, val = nil, *)
+ val
+ end
+ end
+
+
+ #
+ # Map from option/keyword string to object with completion.
+ #
+ class OptionMap < Hash
+ include Completion
+ end
+
+
+ #
+ # Individual switch class. Not important to the user.
+ #
+ # Defined within Switch are several Switch-derived classes: NoArgument,
+ # RequiredArgument, etc.
+ #
+ class Switch
+ attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
+
+ #
+ # Guesses argument style from +arg+. Returns corresponding
+ # Gem::OptionParser::Switch class (OptionalArgument, etc.).
+ #
+ def self.guess(arg)
+ case arg
+ when ""
+ t = self
+ when /\A=?\[/
+ t = Switch::OptionalArgument
+ when /\A\s+\[/
+ t = Switch::PlacedArgument
+ else
+ t = Switch::RequiredArgument
+ end
+ self >= t or incompatible_argument_styles(arg, t)
+ t
+ end
+
+ def self.incompatible_argument_styles(arg, t)
+ raise(ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}",
+ ParseError.filter_backtrace(caller(2)))
+ end
+
+ def self.pattern
+ NilClass
+ end
+
+ def initialize(pattern = nil, conv = nil,
+ short = nil, long = nil, arg = nil,
+ desc = ([] if short or long), block = nil, &_block)
+ raise if Array === pattern
+ block ||= _block
+ @pattern, @conv, @short, @long, @arg, @desc, @block =
+ pattern, conv, short, long, arg, desc, block
+ end
+
+ #
+ # Parses +arg+ and returns rest of +arg+ and matched portion to the
+ # argument pattern. Yields when the pattern doesn't match substring.
+ #
+ def parse_arg(arg) # :nodoc:
+ pattern or return nil, [arg]
+ unless m = pattern.match(arg)
+ yield(InvalidArgument, arg)
+ return arg, []
+ end
+ if String === m
+ m = [s = m]
+ else
+ m = m.to_a
+ s = m[0]
+ return nil, m unless String === s
+ end
+ raise InvalidArgument, arg unless arg.rindex(s, 0)
+ return nil, m if s.length == arg.length
+ yield(InvalidArgument, arg) # didn't match whole arg
+ return arg[s.length..-1], m
+ end
+ private :parse_arg
+
+ #
+ # Parses argument, converts and returns +arg+, +block+ and result of
+ # conversion. Yields at semi-error condition instead of raising an
+ # exception.
+ #
+ def conv_arg(arg, val = []) # :nodoc:
+ if conv
+ val = conv.call(*val)
+ else
+ val = proc {|v| v}.call(*val)
+ end
+ return arg, block, val
+ end
+ private :conv_arg
+
+ #
+ # Produces the summary text. Each line of the summary is yielded to the
+ # block (without newline).
+ #
+ # +sdone+:: Already summarized short style options keyed hash.
+ # +ldone+:: Already summarized long style options keyed hash.
+ # +width+:: Width of left side (option part). In other words, the right
+ # side (description part) starts after +width+ columns.
+ # +max+:: Maximum width of left side -> the options are filled within
+ # +max+ columns.
+ # +indent+:: Prefix string indents all summarized lines.
+ #
+ def summarize(sdone = {}, ldone = {}, width = 1, max = width - 1, indent = "")
+ sopts, lopts = [], [], nil
+ @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
+ @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
+ return if sopts.empty? and lopts.empty? # completely hidden
+
+ left = [sopts.join(', ')]
+ right = desc.dup
+
+ while s = lopts.shift
+ l = left[-1].length + s.length
+ l += arg.length if left.size == 1 && arg
+ l < max or sopts.empty? or left << +''
+ left[-1] << (left[-1].empty? ? ' ' * 4 : ', ') << s
+ end
+
+ if arg
+ left[0] << (left[1] ? arg.sub(/\A(\[?)=/, '\1') + ',' : arg)
+ end
+ mlen = left.collect {|ss| ss.length}.max.to_i
+ while mlen > width and l = left.shift
+ mlen = left.collect {|ss| ss.length}.max.to_i if l.length == mlen
+ if l.length < width and (r = right[0]) and !r.empty?
+ l = l.to_s.ljust(width) + ' ' + r
+ right.shift
+ end
+ yield(indent + l)
+ end
+
+ while begin l = left.shift; r = right.shift; l or r end
+ l = l.to_s.ljust(width) + ' ' + r if r and !r.empty?
+ yield(indent + l)
+ end
+
+ self
+ end
+
+ def add_banner(to) # :nodoc:
+ unless @short or @long
+ s = desc.join
+ to << " [" + s + "]..." unless s.empty?
+ end
+ to
+ end
+
+ def match_nonswitch?(str) # :nodoc:
+ @pattern =~ str unless @short or @long
+ end
+
+ #
+ # Main name of the switch.
+ #
+ def switch_name
+ (long.first || short.first).sub(/\A-+(?:\[no-\])?/, '')
+ end
+
+ def compsys(sdone, ldone) # :nodoc:
+ sopts, lopts = [], []
+ @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
+ @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
+ return if sopts.empty? and lopts.empty? # completely hidden
+
+ (sopts+lopts).each do |opt|
+ # "(-x -c -r)-l[left justify]"
+ if /^--\[no-\](.+)$/ =~ opt
+ o = $1
+ yield("--#{o}", desc.join(""))
+ yield("--no-#{o}", desc.join(""))
+ else
+ yield("#{opt}", desc.join(""))
+ end
+ end
+ end
+
+ #
+ # Switch that takes no arguments.
+ #
+ class NoArgument < self
+
+ #
+ # Raises an exception if any arguments given.
+ #
+ def parse(arg, argv)
+ yield(NeedlessArgument, arg) if arg
+ conv_arg(arg)
+ end
+
+ def self.incompatible_argument_styles(*)
+ end
+
+ def self.pattern
+ Object
+ end
+ end
+
+ #
+ # Switch that takes an argument.
+ #
+ class RequiredArgument < self
+
+ #
+ # Raises an exception if argument is not present.
+ #
+ def parse(arg, argv)
+ unless arg
+ raise MissingArgument if argv.empty?
+ arg = argv.shift
+ end
+ conv_arg(*parse_arg(arg, &method(:raise)))
+ end
+ end
+
+ #
+ # Switch that can omit argument.
+ #
+ class OptionalArgument < self
+
+ #
+ # Parses argument if given, or uses default value.
+ #
+ def parse(arg, argv, &error)
+ if arg
+ conv_arg(*parse_arg(arg, &error))
+ else
+ conv_arg(arg)
+ end
+ end
+ end
+
+ #
+ # Switch that takes an argument, which does not begin with '-'.
+ #
+ class PlacedArgument < self
+
+ #
+ # Returns nil if argument is not present or begins with '-'.
+ #
+ def parse(arg, argv, &error)
+ if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
+ return nil, block, nil
+ end
+ opt = (val = parse_arg(val, &error))[1]
+ val = conv_arg(*val)
+ if opt and !arg
+ argv.shift
+ else
+ val[0] = nil
+ end
+ val
+ end
+ end
+ end
+
+ #
+ # Simple option list providing mapping from short and/or long option
+ # string to Gem::OptionParser::Switch and mapping from acceptable argument to
+ # matching pattern and converter pair. Also provides summary feature.
+ #
+ class List
+ # Map from acceptable argument types to pattern and converter pairs.
+ attr_reader :atype
+
+ # Map from short style option switches to actual switch objects.
+ attr_reader :short
+
+ # Map from long style option switches to actual switch objects.
+ attr_reader :long
+
+ # List of all switches and summary string.
+ attr_reader :list
+
+ #
+ # Just initializes all instance variables.
+ #
+ def initialize
+ @atype = {}
+ @short = OptionMap.new
+ @long = OptionMap.new
+ @list = []
+ end
+
+ #
+ # See Gem::OptionParser.accept.
+ #
+ def accept(t, pat = /.*/m, &block)
+ if pat
+ pat.respond_to?(:match) or
+ raise TypeError, "has no `match'", ParseError.filter_backtrace(caller(2))
+ else
+ pat = t if t.respond_to?(:match)
+ end
+ unless block
+ block = pat.method(:convert).to_proc if pat.respond_to?(:convert)
+ end
+ @atype[t] = [pat, block]
+ end
+
+ #
+ # See Gem::OptionParser.reject.
+ #
+ def reject(t)
+ @atype.delete(t)
+ end
+
+ #
+ # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+.
+ #
+ # +sw+:: Gem::OptionParser::Switch instance to be added.
+ # +sopts+:: Short style option list.
+ # +lopts+:: Long style option list.
+ # +nlopts+:: Negated long style options list.
+ #
+ def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc:
+ sopts.each {|o| @short[o] = sw} if sopts
+ lopts.each {|o| @long[o] = sw} if lopts
+ nlopts.each {|o| @long[o] = nsw} if nsw and nlopts
+ used = @short.invert.update(@long.invert)
+ @list.delete_if {|o| Switch === o and !used[o]}
+ end
+ private :update
+
+ #
+ # Inserts +switch+ at the head of the list, and associates short, long
+ # and negated long options. Arguments are:
+ #
+ # +switch+:: Gem::OptionParser::Switch instance to be inserted.
+ # +short_opts+:: List of short style options.
+ # +long_opts+:: List of long style options.
+ # +nolong_opts+:: List of long style options with "no-" prefix.
+ #
+ # prepend(switch, short_opts, long_opts, nolong_opts)
+ #
+ def prepend(*args)
+ update(*args)
+ @list.unshift(args[0])
+ end
+
+ #
+ # Appends +switch+ at the tail of the list, and associates short, long
+ # and negated long options. Arguments are:
+ #
+ # +switch+:: Gem::OptionParser::Switch instance to be inserted.
+ # +short_opts+:: List of short style options.
+ # +long_opts+:: List of long style options.
+ # +nolong_opts+:: List of long style options with "no-" prefix.
+ #
+ # append(switch, short_opts, long_opts, nolong_opts)
+ #
+ def append(*args)
+ update(*args)
+ @list.push(args[0])
+ end
+
+ #
+ # Searches +key+ in +id+ list. The result is returned or yielded if a
+ # block is given. If it isn't found, nil is returned.
+ #
+ def search(id, key)
+ if list = __send__(id)
+ val = list.fetch(key) {return nil}
+ block_given? ? yield(val) : val
+ end
+ end
+
+ #
+ # Searches list +id+ for +opt+ and the optional patterns for completion
+ # +pat+. If +icase+ is true, the search is case insensitive. The result
+ # is returned or yielded if a block is given. If it isn't found, nil is
+ # returned.
+ #
+ def complete(id, opt, icase = false, *pat, &block)
+ __send__(id).complete(opt, icase, *pat, &block)
+ end
+
+ def get_candidates(id)
+ yield __send__(id).keys
+ end
+
+ #
+ # Iterates over each option, passing the option to the +block+.
+ #
+ def each_option(&block)
+ list.each(&block)
+ end
+
+ #
+ # Creates the summary table, passing each line to the +block+ (without
+ # newline). The arguments +args+ are passed along to the summarize
+ # method which is called on every option.
+ #
+ def summarize(*args, &block)
+ sum = []
+ list.reverse_each do |opt|
+ if opt.respond_to?(:summarize) # perhaps Gem::OptionParser::Switch
+ s = []
+ opt.summarize(*args) {|l| s << l}
+ sum.concat(s.reverse)
+ elsif !opt or opt.empty?
+ sum << ""
+ elsif opt.respond_to?(:each_line)
+ sum.concat([*opt.each_line].reverse)
+ else
+ sum.concat([*opt.each].reverse)
+ end
+ end
+ sum.reverse_each(&block)
+ end
+
+ def add_banner(to) # :nodoc:
+ list.each do |opt|
+ if opt.respond_to?(:add_banner)
+ opt.add_banner(to)
+ end
+ end
+ to
+ end
+
+ def compsys(*args, &block) # :nodoc:
+ list.each do |opt|
+ if opt.respond_to?(:compsys)
+ opt.compsys(*args, &block)
+ end
+ end
+ end
+ end
+
+ #
+ # Hash with completion search feature. See Gem::OptionParser::Completion.
+ #
+ class CompletingHash < Hash
+ include Completion
+
+ #
+ # Completion for hash key.
+ #
+ def match(key)
+ *values = fetch(key) {
+ raise AmbiguousArgument, catch(:ambiguous) {return complete(key)}
+ }
+ return key, *values
+ end
+ end
+
+ # :stopdoc:
+
+ #
+ # Enumeration of acceptable argument styles. Possible values are:
+ #
+ # NO_ARGUMENT:: The switch takes no arguments. (:NONE)
+ # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED)
+ # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL)
+ #
+ # Use like --switch=argument (long style) or -Xargument (short style). For
+ # short style, only portion matched to argument pattern is treated as
+ # argument.
+ #
+ ArgumentStyle = {}
+ NoArgument.each {|el| ArgumentStyle[el] = Switch::NoArgument}
+ RequiredArgument.each {|el| ArgumentStyle[el] = Switch::RequiredArgument}
+ OptionalArgument.each {|el| ArgumentStyle[el] = Switch::OptionalArgument}
+ ArgumentStyle.freeze
+
+ #
+ # Switches common used such as '--', and also provides default
+ # argument classes
+ #
+ DefaultList = List.new
+ DefaultList.short['-'] = Switch::NoArgument.new {}
+ DefaultList.long[''] = Switch::NoArgument.new {throw :terminate}
+
+
+ COMPSYS_HEADER = <<'XXX' # :nodoc:
+
+typeset -A opt_args
+local context state line
+
+_arguments -s -S \
+XXX
+
+ def compsys(to, name = File.basename($0)) # :nodoc:
+ to << "#compdef #{name}\n"
+ to << COMPSYS_HEADER
+ visit(:compsys, {}, {}) {|o, d|
+ to << %Q[ "#{o}[#{d.gsub(/[\"\[\]]/, '\\\\\&')}]" \\\n]
+ }
+ to << " '*:file:_files' && return 0\n"
+ end
+
+ #
+ # Default options for ARGV, which never appear in option summary.
+ #
+ Officious = {}
+
+ #
+ # --help
+ # Shows option summary.
+ #
+ Officious['help'] = proc do |parser|
+ Switch::NoArgument.new do |arg|
+ puts parser.help
+ exit
+ end
+ end
+
+ #
+ # --*-completion-bash=WORD
+ # Shows candidates for command line completion.
+ #
+ Officious['*-completion-bash'] = proc do |parser|
+ Switch::RequiredArgument.new do |arg|
+ puts parser.candidate(arg)
+ exit
+ end
+ end
+
+ #
+ # --*-completion-zsh[=NAME:FILE]
+ # Creates zsh completion file.
+ #
+ Officious['*-completion-zsh'] = proc do |parser|
+ Switch::OptionalArgument.new do |arg|
+ parser.compsys(STDOUT, arg)
+ exit
+ end
+ end
+
+ #
+ # --version
+ # Shows version string if Version is defined.
+ #
+ Officious['version'] = proc do |parser|
+ Switch::OptionalArgument.new do |pkg|
+ if pkg
+ begin
+ require 'rubygems/optparse/lib/optparse/version'
+ rescue LoadError
+ else
+ show_version(*pkg.split(/,/)) or
+ abort("#{parser.program_name}: no version found in package #{pkg}")
+ exit
+ end
+ end
+ v = parser.ver or abort("#{parser.program_name}: version unknown")
+ puts v
+ exit
+ end
+ end
+
+ # :startdoc:
+
+ #
+ # Class methods
+ #
+
+ #
+ # Initializes a new instance and evaluates the optional block in context
+ # of the instance. Arguments +args+ are passed to #new, see there for
+ # description of parameters.
+ #
+ # This method is *deprecated*, its behavior corresponds to the older #new
+ # method.
+ #
+ def self.with(*args, &block)
+ opts = new(*args)
+ opts.instance_eval(&block)
+ opts
+ end
+
+ #
+ # Returns an incremented value of +default+ according to +arg+.
+ #
+ def self.inc(arg, default = nil)
+ case arg
+ when Integer
+ arg.nonzero?
+ when nil
+ default.to_i + 1
+ end
+ end
+ def inc(*args)
+ self.class.inc(*args)
+ end
+
+ #
+ # Initializes the instance and yields itself if called with a block.
+ #
+ # +banner+:: Banner message.
+ # +width+:: Summary width.
+ # +indent+:: Summary indent.
+ #
+ def initialize(banner = nil, width = 32, indent = ' ' * 4)
+ @stack = [DefaultList, List.new, List.new]
+ @program_name = nil
+ @banner = banner
+ @summary_width = width
+ @summary_indent = indent
+ @default_argv = ARGV
+ @require_exact = false
+ add_officious
+ yield self if block_given?
+ end
+
+ def add_officious # :nodoc:
+ list = base()
+ Officious.each do |opt, block|
+ list.long[opt] ||= block.call(self)
+ end
+ end
+
+ #
+ # Terminates option parsing. Optional parameter +arg+ is a string pushed
+ # back to be the first non-option argument.
+ #
+ def terminate(arg = nil)
+ self.class.terminate(arg)
+ end
+ def self.terminate(arg = nil)
+ throw :terminate, arg
+ end
+
+ @stack = [DefaultList]
+ def self.top() DefaultList end
+
+ #
+ # Directs to accept specified class +t+. The argument string is passed to
+ # the block in which it should be converted to the desired class.
+ #
+ # +t+:: Argument class specifier, any object including Class.
+ # +pat+:: Pattern for argument, defaults to +t+ if it responds to match.
+ #
+ # accept(t, pat, &block)
+ #
+ def accept(*args, &blk) top.accept(*args, &blk) end
+ #
+ # See #accept.
+ #
+ def self.accept(*args, &blk) top.accept(*args, &blk) end
+
+ #
+ # Directs to reject specified class argument.
+ #
+ # +t+:: Argument class specifier, any object including Class.
+ #
+ # reject(t)
+ #
+ def reject(*args, &blk) top.reject(*args, &blk) end
+ #
+ # See #reject.
+ #
+ def self.reject(*args, &blk) top.reject(*args, &blk) end
+
+ #
+ # Instance methods
+ #
+
+ # Heading banner preceding summary.
+ attr_writer :banner
+
+ # Program name to be emitted in error message and default banner,
+ # defaults to $0.
+ attr_writer :program_name
+
+ # Width for option list portion of summary. Must be Numeric.
+ attr_accessor :summary_width
+
+ # Indentation for summary. Must be String (or have + String method).
+ attr_accessor :summary_indent
+
+ # Strings to be parsed in default.
+ attr_accessor :default_argv
+
+ # Whether to require that options match exactly (disallows providing
+ # abbreviated long option as short option).
+ attr_accessor :require_exact
+
+ #
+ # Heading banner preceding summary.
+ #
+ def banner
+ unless @banner
+ @banner = +"Usage: #{program_name} [options]"
+ visit(:add_banner, @banner)
+ end
+ @banner
+ end
+
+ #
+ # Program name to be emitted in error message and default banner, defaults
+ # to $0.
+ #
+ def program_name
+ @program_name || File.basename($0, '.*')
+ end
+
+ # for experimental cascading :-)
+ alias set_banner banner=
+ alias set_program_name program_name=
+ alias set_summary_width summary_width=
+ alias set_summary_indent summary_indent=
+
+ # Version
+ attr_writer :version
+ # Release code
+ attr_writer :release
+
+ #
+ # Version
+ #
+ def version
+ (defined?(@version) && @version) || (defined?(::Version) && ::Version)
+ end
+
+ #
+ # Release code
+ #
+ def release
+ (defined?(@release) && @release) || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
+ end
+
+ #
+ # Returns version string from program_name, version and release.
+ #
+ def ver
+ if v = version
+ str = +"#{program_name} #{[v].join('.')}"
+ str << " (#{v})" if v = release
+ str
+ end
+ end
+
+ def warn(mesg = $!)
+ super("#{program_name}: #{mesg}")
+ end
+
+ def abort(mesg = $!)
+ super("#{program_name}: #{mesg}")
+ end
+
+ #
+ # Subject of #on / #on_head, #accept / #reject
+ #
+ def top
+ @stack[-1]
+ end
+
+ #
+ # Subject of #on_tail.
+ #
+ def base
+ @stack[1]
+ end
+
+ #
+ # Pushes a new List.
+ #
+ def new
+ @stack.push(List.new)
+ if block_given?
+ yield self
+ else
+ self
+ end
+ end
+
+ #
+ # Removes the last List.
+ #
+ def remove
+ @stack.pop
+ end
+
+ #
+ # Puts option summary into +to+ and returns +to+. Yields each line if
+ # a block is given.
+ #
+ # +to+:: Output destination, which must have method <<. Defaults to [].
+ # +width+:: Width of left side, defaults to @summary_width.
+ # +max+:: Maximum length allowed for left side, defaults to +width+ - 1.
+ # +indent+:: Indentation, defaults to @summary_indent.
+ #
+ def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
+ nl = "\n"
+ blk ||= proc {|l| to << (l.index(nl, -1) ? l : l + nl)}
+ visit(:summarize, {}, {}, width, max, indent, &blk)
+ to
+ end
+
+ #
+ # Returns option summary string.
+ #
+ def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end
+ alias to_s help
+
+ #
+ # Returns option summary list.
+ #
+ def to_a; summarize("#{banner}".split(/^/)) end
+
+ #
+ # Checks if an argument is given twice, in which case an ArgumentError is
+ # raised. Called from Gem::OptionParser#switch only.
+ #
+ # +obj+:: New argument.
+ # +prv+:: Previously specified argument.
+ # +msg+:: Exception message.
+ #
+ def notwice(obj, prv, msg) # :nodoc:
+ unless !prv or prv == obj
+ raise(ArgumentError, "argument #{msg} given twice: #{obj}",
+ ParseError.filter_backtrace(caller(2)))
+ end
+ obj
+ end
+ private :notwice
+
+ SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a} # :nodoc:
+
+ # :call-seq:
+ # make_switch(params, block = nil)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def make_switch(opts, block = nil)
+ short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
+ ldesc, sdesc, desc, arg = [], [], []
+ default_style = Switch::NoArgument
+ default_pattern = nil
+ klass = nil
+ q, a = nil
+ has_arg = false
+
+ opts.each do |o|
+ # argument class
+ next if search(:atype, o) do |pat, c|
+ klass = notwice(o, klass, 'type')
+ if not_style and not_style != Switch::NoArgument
+ not_pattern, not_conv = pat, c
+ else
+ default_pattern, conv = pat, c
+ end
+ end
+
+ # directly specified pattern(any object possible to match)
+ if (!(String === o || Symbol === o)) and o.respond_to?(:match)
+ pattern = notwice(o, pattern, 'pattern')
+ if pattern.respond_to?(:convert)
+ conv = pattern.method(:convert).to_proc
+ else
+ conv = SPLAT_PROC
+ end
+ next
+ end
+
+ # anything others
+ case o
+ when Proc, Method
+ block = notwice(o, block, 'block')
+ when Array, Hash
+ case pattern
+ when CompletingHash
+ when nil
+ pattern = CompletingHash.new
+ conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert)
+ else
+ raise ArgumentError, "argument pattern given twice"
+ end
+ o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}}
+ when Module
+ raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4))
+ when *ArgumentStyle.keys
+ style = notwice(ArgumentStyle[o], style, 'style')
+ when /^--no-([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ o = notwice(a ? Object : TrueClass, klass, 'type')
+ not_pattern, not_conv = search(:atype, o) unless not_style
+ not_style = (not_style || default_style).guess(arg = a) if a
+ default_style = Switch::NoArgument
+ default_pattern, conv = search(:atype, FalseClass) unless default_pattern
+ ldesc << "--no-#{q}"
+ (q = q.downcase).tr!('_', '-')
+ long << "no-#{q}"
+ nolong << q
+ when /^--\[no-\]([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ o = notwice(a ? Object : TrueClass, klass, 'type')
+ if a
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ ldesc << "--[no-]#{q}"
+ (o = q.downcase).tr!('_', '-')
+ long << o
+ not_pattern, not_conv = search(:atype, FalseClass) unless not_style
+ not_style = Switch::NoArgument
+ nolong << "no-#{o}"
+ when /^--([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ if a
+ o = notwice(NilClass, klass, 'type')
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ ldesc << "--#{q}"
+ (o = q.downcase).tr!('_', '-')
+ long << o
+ when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
+ q, a = $1, $2
+ o = notwice(Object, klass, 'type')
+ if a
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ else
+ has_arg = true
+ end
+ sdesc << "-#{q}"
+ short << Regexp.new(q)
+ when /^-(.)(.+)?/
+ q, a = $1, $2
+ if a
+ o = notwice(NilClass, klass, 'type')
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ sdesc << "-#{q}"
+ short << q
+ when /^=/
+ style = notwice(default_style.guess(arg = o), style, 'style')
+ default_pattern, conv = search(:atype, Object) unless default_pattern
+ else
+ desc.push(o)
+ end
+ end
+
+ default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
+ if !(short.empty? and long.empty?)
+ if has_arg and default_style == Switch::NoArgument
+ default_style = Switch::RequiredArgument
+ end
+ s = (style || default_style).new(pattern || default_pattern,
+ conv, sdesc, ldesc, arg, desc, block)
+ elsif !block
+ if style or pattern
+ raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller)
+ end
+ s = desc
+ else
+ short << pattern
+ s = (style || default_style).new(pattern,
+ conv, nil, nil, arg, desc, block)
+ end
+ return s, short, long,
+ (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
+ nolong
+ end
+
+ # :call-seq:
+ # define(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def define(*opts, &block)
+ top.append(*(sw = make_switch(opts, block)))
+ sw[0]
+ end
+
+ # :call-seq:
+ # on(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def on(*opts, &block)
+ define(*opts, &block)
+ self
+ end
+ alias def_option define
+
+ # :call-seq:
+ # define_head(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def define_head(*opts, &block)
+ top.prepend(*(sw = make_switch(opts, block)))
+ sw[0]
+ end
+
+ # :call-seq:
+ # on_head(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ # The new option is added at the head of the summary.
+ #
+ def on_head(*opts, &block)
+ define_head(*opts, &block)
+ self
+ end
+ alias def_head_option define_head
+
+ # :call-seq:
+ # define_tail(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ def define_tail(*opts, &block)
+ base.append(*(sw = make_switch(opts, block)))
+ sw[0]
+ end
+
+ #
+ # :call-seq:
+ # on_tail(*params, &block)
+ #
+ # :include: ../doc/optparse/creates_option.rdoc
+ #
+ # The new option is added at the tail of the summary.
+ #
+ def on_tail(*opts, &block)
+ define_tail(*opts, &block)
+ self
+ end
+ alias def_tail_option define_tail
+
+ #
+ # Add separator in summary.
+ #
+ def separator(string)
+ top.append(string, nil, nil)
+ end
+
+ #
+ # Parses command line arguments +argv+ in order. When a block is given,
+ # each non-option argument is yielded. When optional +into+ keyword
+ # argument is provided, the parsed option values are stored there via
+ # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
+ # similar object).
+ #
+ # Returns the rest of +argv+ left unparsed.
+ #
+ def order(*argv, into: nil, &nonopt)
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
+ order!(argv, into: into, &nonopt)
+ end
+
+ #
+ # Same as #order, but removes switches destructively.
+ # Non-option arguments remain in +argv+.
+ #
+ def order!(argv = default_argv, into: nil, &nonopt)
+ setter = ->(name, val) {into[name.to_sym] = val} if into
+ parse_in_order(argv, setter, &nonopt)
+ end
+
+ def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
+ opt, arg, val, rest = nil
+ nonopt ||= proc {|a| throw :terminate, a}
+ argv.unshift(arg) if arg = catch(:terminate) {
+ while arg = argv.shift
+ case arg
+ # long option
+ when /\A--([^=]*)(?:=(.*))?/m
+ opt, rest = $1, $2
+ opt.tr!('_', '-')
+ begin
+ sw, = complete(:long, opt, true)
+ if require_exact && !sw.long.include?(arg)
+ raise InvalidOption, arg
+ end
+ rescue ParseError
+ raise $!.set_option(arg, true)
+ end
+ begin
+ opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
+ val = cb.call(val) if cb
+ setter.call(sw.switch_name, val) if setter
+ rescue ParseError
+ raise $!.set_option(arg, rest)
+ end
+
+ # short option
+ when /\A-(.)((=).*|.+)?/m
+ eq, rest, opt = $3, $2, $1
+ has_arg, val = eq, rest
+ begin
+ sw, = search(:short, opt)
+ unless sw
+ begin
+ sw, = complete(:short, opt)
+ # short option matched.
+ val = arg.delete_prefix('-')
+ has_arg = true
+ rescue InvalidOption
+ raise if require_exact
+ # if no short options match, try completion with long
+ # options.
+ sw, = complete(:long, opt)
+ eq ||= !rest
+ end
+ end
+ rescue ParseError
+ raise $!.set_option(arg, true)
+ end
+ begin
+ opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
+ rescue ParseError
+ raise $!.set_option(arg, arg.length > 2)
+ else
+ raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
+ end
+ begin
+ argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-')
+ val = cb.call(val) if cb
+ setter.call(sw.switch_name, val) if setter
+ rescue ParseError
+ raise $!.set_option(arg, arg.length > 2)
+ end
+
+ # non-option argument
+ else
+ catch(:prune) do
+ visit(:each_option) do |sw0|
+ sw = sw0
+ sw.block.call(arg) if Switch === sw and sw.match_nonswitch?(arg)
+ end
+ nonopt.call(arg)
+ end
+ end
+ end
+
+ nil
+ }
+
+ visit(:search, :short, nil) {|sw| sw.block.call(*argv) if !sw.pattern}
+
+ argv
+ end
+ private :parse_in_order
+
+ #
+ # Parses command line arguments +argv+ in permutation mode and returns
+ # list of non-option arguments. When optional +into+ keyword
+ # argument is provided, the parsed option values are stored there via
+ # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
+ # similar object).
+ #
+ def permute(*argv, into: nil)
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
+ permute!(argv, into: into)
+ end
+
+ #
+ # Same as #permute, but removes switches destructively.
+ # Non-option arguments remain in +argv+.
+ #
+ def permute!(argv = default_argv, into: nil)
+ nonopts = []
+ order!(argv, into: into, &nonopts.method(:<<))
+ argv[0, 0] = nonopts
+ argv
+ end
+
+ #
+ # Parses command line arguments +argv+ in order when environment variable
+ # POSIXLY_CORRECT is set, and in permutation mode otherwise.
+ # When optional +into+ keyword argument is provided, the parsed option
+ # values are stored there via <code>[]=</code> method (so it can be Hash,
+ # or OpenStruct, or other similar object).
+ #
+ def parse(*argv, into: nil)
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
+ parse!(argv, into: into)
+ end
+
+ #
+ # Same as #parse, but removes switches destructively.
+ # Non-option arguments remain in +argv+.
+ #
+ def parse!(argv = default_argv, into: nil)
+ if ENV.include?('POSIXLY_CORRECT')
+ order!(argv, into: into)
+ else
+ permute!(argv, into: into)
+ end
+ end
+
+ #
+ # Wrapper method for getopts.rb.
+ #
+ # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option")
+ # # params["a"] = true # -a
+ # # params["b"] = "1" # -b1
+ # # params["foo"] = "1" # --foo
+ # # params["bar"] = "x" # --bar x
+ # # params["zot"] = "z" # --zot Z
+ #
+ def getopts(*args)
+ argv = Array === args.first ? args.shift : default_argv
+ single_options, *long_options = *args
+
+ result = {}
+
+ single_options.scan(/(.)(:)?/) do |opt, val|
+ if val
+ result[opt] = nil
+ define("-#{opt} VAL")
+ else
+ result[opt] = false
+ define("-#{opt}")
+ end
+ end if single_options
+
+ long_options.each do |arg|
+ arg, desc = arg.split(';', 2)
+ opt, val = arg.split(':', 2)
+ if val
+ result[opt] = val.empty? ? nil : val
+ define("--#{opt}=#{result[opt] || "VAL"}", *[desc].compact)
+ else
+ result[opt] = false
+ define("--#{opt}", *[desc].compact)
+ end
+ end
+
+ parse_in_order(argv, result.method(:[]=))
+ result
+ end
+
+ #
+ # See #getopts.
+ #
+ def self.getopts(*args)
+ new.getopts(*args)
+ end
+
+ #
+ # Traverses @stack, sending each element method +id+ with +args+ and
+ # +block+.
+ #
+ def visit(id, *args, &block) # :nodoc:
+ @stack.reverse_each do |el|
+ el.__send__(id, *args, &block)
+ end
+ nil
+ end
+ private :visit
+
+ #
+ # Searches +key+ in @stack for +id+ hash and returns or yields the result.
+ #
+ def search(id, key) # :nodoc:
+ block_given = block_given?
+ visit(:search, id, key) do |k|
+ return block_given ? yield(k) : k
+ end
+ end
+ private :search
+
+ #
+ # Completes shortened long style option switch and returns pair of
+ # canonical switch and switch descriptor Gem::OptionParser::Switch.
+ #
+ # +typ+:: Searching table.
+ # +opt+:: Searching key.
+ # +icase+:: Search case insensitive if true.
+ # +pat+:: Optional pattern for completion.
+ #
+ def complete(typ, opt, icase = false, *pat) # :nodoc:
+ if pat.empty?
+ search(typ, opt) {|sw| return [sw, opt]} # exact match or...
+ end
+ ambiguous = catch(:ambiguous) {
+ visit(:complete, typ, opt, icase, *pat) {|o, *sw| return sw}
+ }
+ exc = ambiguous ? AmbiguousOption : InvalidOption
+ raise exc.new(opt, additional: self.method(:additional_message).curry[typ])
+ end
+ private :complete
+
+ #
+ # Returns additional info.
+ #
+ def additional_message(typ, opt)
+ return unless typ and opt and defined?(DidYouMean::SpellChecker)
+ all_candidates = []
+ visit(:get_candidates, typ) do |candidates|
+ all_candidates.concat(candidates)
+ end
+ all_candidates.select! {|cand| cand.is_a?(String) }
+ checker = DidYouMean::SpellChecker.new(dictionary: all_candidates)
+ suggestions = all_candidates & checker.correct(opt)
+ if DidYouMean.respond_to?(:formatter)
+ DidYouMean.formatter.message_for(suggestions)
+ else
+ "\nDid you mean? #{suggestions.join("\n ")}"
+ end
+ end
+
+ def candidate(word)
+ list = []
+ case word
+ when '-'
+ long = short = true
+ when /\A--/
+ word, arg = word.split(/=/, 2)
+ argpat = Completion.regexp(arg, false) if arg and !arg.empty?
+ long = true
+ when /\A-/
+ short = true
+ end
+ pat = Completion.regexp(word, long)
+ visit(:each_option) do |opt|
+ next unless Switch === opt
+ opts = (long ? opt.long : []) + (short ? opt.short : [])
+ opts = Completion.candidate(word, true, pat, &opts.method(:each)).map(&:first) if pat
+ if /\A=/ =~ opt.arg
+ opts.map! {|sw| sw + "="}
+ if arg and CompletingHash === opt.pattern
+ if opts = opt.pattern.candidate(arg, false, argpat)
+ opts.map!(&:last)
+ end
+ end
+ end
+ list.concat(opts)
+ end
+ list
+ end
+
+ #
+ # Loads options from file names as +filename+. Does nothing when the file
+ # is not present. Returns whether successfully loaded.
+ #
+ # +filename+ defaults to basename of the program without suffix in a
+ # directory ~/.options, then the basename with '.options' suffix
+ # under XDG and Haiku standard places.
+ #
+ def load(filename = nil)
+ unless filename
+ basename = File.basename($0, '.*')
+ return true if load(File.expand_path(basename, '~/.options')) rescue nil
+ basename << ".options"
+ return [
+ # XDG
+ ENV['XDG_CONFIG_HOME'],
+ '~/.config',
+ *ENV['XDG_CONFIG_DIRS']&.split(File::PATH_SEPARATOR),
+
+ # Haiku
+ '~/config/settings',
+ ].any? {|dir|
+ next if !dir or dir.empty?
+ load(File.expand_path(basename, dir)) rescue nil
+ }
+ end
+ begin
+ parse(*IO.readlines(filename).each {|s| s.chomp!})
+ true
+ rescue Errno::ENOENT, Errno::ENOTDIR
+ false
+ end
+ end
+
+ #
+ # Parses environment variable +env+ or its uppercase with splitting like a
+ # shell.
+ #
+ # +env+ defaults to the basename of the program.
+ #
+ def environment(env = File.basename($0, '.*'))
+ env = ENV[env] || ENV[env.upcase] or return
+ require 'shellwords'
+ parse(*Shellwords.shellwords(env))
+ end
+
+ #
+ # Acceptable argument classes
+ #
+
+ #
+ # Any string and no conversion. This is fall-back.
+ #
+ accept(Object) {|s,|s or s.nil?}
+
+ accept(NilClass) {|s,|s}
+
+ #
+ # Any non-empty string, and no conversion.
+ #
+ accept(String, /.+/m) {|s,*|s}
+
+ #
+ # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal
+ # for 0x, and decimal for others; with optional sign prefix. Converts to
+ # Integer.
+ #
+ decimal = '\d+(?:_\d+)*'
+ binary = 'b[01]+(?:_[01]+)*'
+ hex = 'x[\da-f]+(?:_[\da-f]+)*'
+ octal = "0(?:[0-7]+(?:_[0-7]+)*|#{binary}|#{hex})?"
+ integer = "#{octal}|#{decimal}"
+
+ accept(Integer, %r"\A[-+]?(?:#{integer})\z"io) {|s,|
+ begin
+ Integer(s)
+ rescue ArgumentError
+ raise Gem::OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Float number format, and converts to Float.
+ #
+ float = "(?:#{decimal}(?=(.)?)(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
+ floatpat = %r"\A[-+]?#{float}\z"io
+ accept(Float, floatpat) {|s,| s.to_f if s}
+
+ #
+ # Generic numeric format, converts to Integer for integer format, Float
+ # for float format, and Rational for rational format.
+ #
+ real = "[-+]?(?:#{octal}|#{float})"
+ accept(Numeric, /\A(#{real})(?:\/(#{real}))?\z/io) {|s, d, f, n,|
+ if n
+ Rational(d, n)
+ elsif f
+ Float(s)
+ else
+ Integer(s)
+ end
+ }
+
+ #
+ # Decimal integer format, to be converted to Integer.
+ #
+ DecimalInteger = /\A[-+]?#{decimal}\z/io
+ accept(DecimalInteger, DecimalInteger) {|s,|
+ begin
+ Integer(s, 10)
+ rescue ArgumentError
+ raise Gem::OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Ruby/C like octal/hexadecimal/binary integer format, to be converted to
+ # Integer.
+ #
+ OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))\z/io
+ accept(OctalInteger, OctalInteger) {|s,|
+ begin
+ Integer(s, 8)
+ rescue ArgumentError
+ raise Gem::OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Decimal integer/float number format, to be converted to Integer for
+ # integer format, Float for float format.
+ #
+ DecimalNumeric = floatpat # decimal integer is allowed as float also.
+ accept(DecimalNumeric, floatpat) {|s, f|
+ begin
+ if f
+ Float(s)
+ else
+ Integer(s)
+ end
+ rescue ArgumentError
+ raise Gem::OptionParser::InvalidArgument, s
+ end if s
+ }
+
+ #
+ # Boolean switch, which means whether it is present or not, whether it is
+ # absent or not with prefix no-, or it takes an argument
+ # yes/no/true/false/+/-.
+ #
+ yesno = CompletingHash.new
+ %w[- no false].each {|el| yesno[el] = false}
+ %w[+ yes true].each {|el| yesno[el] = true}
+ yesno['nil'] = false # should be nil?
+ accept(TrueClass, yesno) {|arg, val| val == nil or val}
+ #
+ # Similar to TrueClass, but defaults to false.
+ #
+ accept(FalseClass, yesno) {|arg, val| val != nil and val}
+
+ #
+ # List of strings separated by ",".
+ #
+ accept(Array) do |s, |
+ if s
+ s = s.split(',').collect {|ss| ss unless ss.empty?}
+ end
+ s
+ end
+
+ #
+ # Regular expression with options.
+ #
+ accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o|
+ f = 0
+ if o
+ f |= Regexp::IGNORECASE if /i/ =~ o
+ f |= Regexp::MULTILINE if /m/ =~ o
+ f |= Regexp::EXTENDED if /x/ =~ o
+ k = o.delete("imx")
+ k = nil if k.empty?
+ end
+ Regexp.new(s || all, f, k)
+ end
+
+ #
+ # Exceptions
+ #
+
+ #
+ # Base class of exceptions from Gem::OptionParser.
+ #
+ class ParseError < RuntimeError
+ # Reason which caused the error.
+ Reason = 'parse error'
+
+ def initialize(*args, additional: nil)
+ @additional = additional
+ @arg0, = args
+ @args = args
+ @reason = nil
+ end
+
+ attr_reader :args
+ attr_writer :reason
+ attr_accessor :additional
+
+ #
+ # Pushes back erred argument(s) to +argv+.
+ #
+ def recover(argv)
+ argv[0, 0] = @args
+ argv
+ end
+
+ def self.filter_backtrace(array)
+ unless $DEBUG
+ array.delete_if(&%r"\A#{Regexp.quote(__FILE__)}:"o.method(:=~))
+ end
+ array
+ end
+
+ def set_backtrace(array)
+ super(self.class.filter_backtrace(array))
+ end
+
+ def set_option(opt, eq)
+ if eq
+ @args[0] = opt
+ else
+ @args.unshift(opt)
+ end
+ self
+ end
+
+ #
+ # Returns error reason. Override this for I18N.
+ #
+ def reason
+ @reason || self.class::Reason
+ end
+
+ def inspect
+ "#<#{self.class}: #{args.join(' ')}>"
+ end
+
+ #
+ # Default stringizing method to emit standard error message.
+ #
+ def message
+ "#{reason}: #{args.join(' ')}#{additional[@arg0] if additional}"
+ end
+
+ alias to_s message
+ end
+
+ #
+ # Raises when ambiguously completable string is encountered.
+ #
+ class AmbiguousOption < ParseError
+ const_set(:Reason, 'ambiguous option')
+ end
+
+ #
+ # Raises when there is an argument for a switch which takes no argument.
+ #
+ class NeedlessArgument < ParseError
+ const_set(:Reason, 'needless argument')
+ end
+
+ #
+ # Raises when a switch with mandatory argument has no argument.
+ #
+ class MissingArgument < ParseError
+ const_set(:Reason, 'missing argument')
+ end
+
+ #
+ # Raises when switch is undefined.
+ #
+ class InvalidOption < ParseError
+ const_set(:Reason, 'invalid option')
+ end
+
+ #
+ # Raises when the given argument does not match required format.
+ #
+ class InvalidArgument < ParseError
+ const_set(:Reason, 'invalid argument')
+ end
+
+ #
+ # Raises when the given argument word can't be completed uniquely.
+ #
+ class AmbiguousArgument < InvalidArgument
+ const_set(:Reason, 'ambiguous argument')
+ end
+
+ #
+ # Miscellaneous
+ #
+
+ #
+ # Extends command line arguments array (ARGV) to parse itself.
+ #
+ module Arguable
+
+ #
+ # Sets Gem::OptionParser object, when +opt+ is +false+ or +nil+, methods
+ # Gem::OptionParser::Arguable#options and Gem::OptionParser::Arguable#options= are
+ # undefined. Thus, there is no ways to access the Gem::OptionParser object
+ # via the receiver object.
+ #
+ def options=(opt)
+ unless @optparse = opt
+ class << self
+ undef_method(:options)
+ undef_method(:options=)
+ end
+ end
+ end
+
+ #
+ # Actual Gem::OptionParser object, automatically created if nonexistent.
+ #
+ # If called with a block, yields the Gem::OptionParser object and returns the
+ # result of the block. If an Gem::OptionParser::ParseError exception occurs
+ # in the block, it is rescued, a error message printed to STDERR and
+ # +nil+ returned.
+ #
+ def options
+ @optparse ||= Gem::OptionParser.new
+ @optparse.default_argv = self
+ block_given? or return @optparse
+ begin
+ yield @optparse
+ rescue ParseError
+ @optparse.warn $!
+ nil
+ end
+ end
+
+ #
+ # Parses +self+ destructively in order and returns +self+ containing the
+ # rest arguments left unparsed.
+ #
+ def order!(&blk) options.order!(self, &blk) end
+
+ #
+ # Parses +self+ destructively in permutation mode and returns +self+
+ # containing the rest arguments left unparsed.
+ #
+ def permute!() options.permute!(self) end
+
+ #
+ # Parses +self+ destructively and returns +self+ containing the
+ # rest arguments left unparsed.
+ #
+ def parse!() options.parse!(self) end
+
+ #
+ # Substitution of getopts is possible as follows. Also see
+ # Gem::OptionParser#getopts.
+ #
+ # def getopts(*args)
+ # ($OPT = ARGV.getopts(*args)).each do |opt, val|
+ # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val"
+ # end
+ # rescue Gem::OptionParser::ParseError
+ # end
+ #
+ def getopts(*args)
+ options.getopts(self, *args)
+ end
+
+ #
+ # Initializes instance variable.
+ #
+ def self.extend_object(obj)
+ super
+ obj.instance_eval {@optparse = nil}
+ end
+ def initialize(*args)
+ super
+ @optparse = nil
+ end
+ end
+
+ #
+ # Acceptable argument classes. Now contains DecimalInteger, OctalInteger
+ # and DecimalNumeric. See Acceptable argument classes (in source code).
+ #
+ module Acceptables
+ const_set(:DecimalInteger, Gem::OptionParser::DecimalInteger)
+ const_set(:OctalInteger, Gem::OptionParser::OctalInteger)
+ const_set(:DecimalNumeric, Gem::OptionParser::DecimalNumeric)
+ end
+end
+
+# ARGV is arguable by Gem::OptionParser
+ARGV.extend(Gem::OptionParser::Arguable)
diff --git a/lib/rubygems/optparse/lib/optparse/ac.rb b/lib/rubygems/optparse/lib/optparse/ac.rb
new file mode 100644
index 0000000000..ff2f4c2dc3
--- /dev/null
+++ b/lib/rubygems/optparse/lib/optparse/ac.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: false
+require 'rubygems/optparse/lib/optparse'
+
+class Gem::OptionParser::AC < Gem::OptionParser
+ private
+
+ def _check_ac_args(name, block)
+ unless /\A\w[-\w]*\z/ =~ name
+ raise ArgumentError, name
+ end
+ unless block
+ raise ArgumentError, "no block given", ParseError.filter_backtrace(caller)
+ end
+ end
+
+ ARG_CONV = proc {|val| val.nil? ? true : val}
+
+ def _ac_arg_enable(prefix, name, help_string, block)
+ _check_ac_args(name, block)
+
+ sdesc = []
+ ldesc = ["--#{prefix}-#{name}"]
+ desc = [help_string]
+ q = name.downcase
+ ac_block = proc {|val| block.call(ARG_CONV.call(val))}
+ enable = Switch::PlacedArgument.new(nil, ARG_CONV, sdesc, ldesc, nil, desc, ac_block)
+ disable = Switch::NoArgument.new(nil, proc {false}, sdesc, ldesc, nil, desc, ac_block)
+ top.append(enable, [], ["enable-" + q], disable, ['disable-' + q])
+ enable
+ end
+
+ public
+
+ def ac_arg_enable(name, help_string, &block)
+ _ac_arg_enable("enable", name, help_string, block)
+ end
+
+ def ac_arg_disable(name, help_string, &block)
+ _ac_arg_enable("disable", name, help_string, block)
+ end
+
+ def ac_arg_with(name, help_string, &block)
+ _check_ac_args(name, block)
+
+ sdesc = []
+ ldesc = ["--with-#{name}"]
+ desc = [help_string]
+ q = name.downcase
+ with = Switch::PlacedArgument.new(*search(:atype, String), sdesc, ldesc, nil, desc, block)
+ without = Switch::NoArgument.new(nil, proc {}, sdesc, ldesc, nil, desc, block)
+ top.append(with, [], ["with-" + q], without, ['without-' + q])
+ with
+ end
+end
diff --git a/lib/rubygems/optparse/lib/optparse/date.rb b/lib/rubygems/optparse/lib/optparse/date.rb
new file mode 100644
index 0000000000..11131e92c2
--- /dev/null
+++ b/lib/rubygems/optparse/lib/optparse/date.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: false
+require 'rubygems/optparse/lib/optparse'
+require 'date'
+
+Gem::OptionParser.accept(DateTime) do |s,|
+ begin
+ DateTime.parse(s) if s
+ rescue ArgumentError
+ raise Gem::OptionParser::InvalidArgument, s
+ end
+end
+Gem::OptionParser.accept(Date) do |s,|
+ begin
+ Date.parse(s) if s
+ rescue ArgumentError
+ raise Gem::OptionParser::InvalidArgument, s
+ end
+end
diff --git a/lib/rubygems/optparse/lib/optparse/kwargs.rb b/lib/rubygems/optparse/lib/optparse/kwargs.rb
new file mode 100644
index 0000000000..9290344c39
--- /dev/null
+++ b/lib/rubygems/optparse/lib/optparse/kwargs.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+require 'rubygems/optparse/lib/optparse'
+
+class Gem::OptionParser
+ # :call-seq:
+ # define_by_keywords(options, method, **params)
+ #
+ # :include: ../../doc/optparse/creates_option.rdoc
+ #
+ def define_by_keywords(options, meth, **opts)
+ meth.parameters.each do |type, name|
+ case type
+ when :key, :keyreq
+ op, cl = *(type == :key ? %w"[ ]" : ["", ""])
+ define("--#{name}=#{op}#{name.upcase}#{cl}", *opts[name]) do |o|
+ options[name] = o
+ end
+ end
+ end
+ options
+ end
+end
diff --git a/lib/rubygems/optparse/lib/optparse/shellwords.rb b/lib/rubygems/optparse/lib/optparse/shellwords.rb
new file mode 100644
index 0000000000..60dd91990c
--- /dev/null
+++ b/lib/rubygems/optparse/lib/optparse/shellwords.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: false
+# -*- ruby -*-
+
+require 'shellwords'
+require 'rubygems/optparse/lib/optparse'
+
+Gem::OptionParser.accept(Shellwords) {|s,| Shellwords.shellwords(s)}
diff --git a/lib/rubygems/optparse/lib/optparse/time.rb b/lib/rubygems/optparse/lib/optparse/time.rb
new file mode 100644
index 0000000000..cb19f6e998
--- /dev/null
+++ b/lib/rubygems/optparse/lib/optparse/time.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: false
+require 'rubygems/optparse/lib/optparse'
+require 'time'
+
+Gem::OptionParser.accept(Time) do |s,|
+ begin
+ (Time.httpdate(s) rescue Time.parse(s)) if s
+ rescue
+ raise Gem::OptionParser::InvalidArgument, s
+ end
+end
diff --git a/lib/rubygems/optparse/lib/optparse/uri.rb b/lib/rubygems/optparse/lib/optparse/uri.rb
new file mode 100644
index 0000000000..088f309992
--- /dev/null
+++ b/lib/rubygems/optparse/lib/optparse/uri.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: false
+# -*- ruby -*-
+
+require 'rubygems/optparse/lib/optparse'
+require 'uri'
+
+Gem::OptionParser.accept(URI) {|s,| URI.parse(s) if s}
diff --git a/lib/rubygems/optparse/lib/optparse/version.rb b/lib/rubygems/optparse/lib/optparse/version.rb
new file mode 100644
index 0000000000..5d79e9db44
--- /dev/null
+++ b/lib/rubygems/optparse/lib/optparse/version.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: false
+# Gem::OptionParser internal utility
+
+class << Gem::OptionParser
+ def show_version(*pkgs)
+ progname = ARGV.options.program_name
+ result = false
+ show = proc do |klass, cname, version|
+ str = "#{progname}"
+ unless klass == ::Object and cname == :VERSION
+ version = version.join(".") if Array === version
+ str << ": #{klass}" unless klass == Object
+ str << " version #{version}"
+ end
+ [:Release, :RELEASE].find do |rel|
+ if klass.const_defined?(rel)
+ str << " (#{klass.const_get(rel)})"
+ end
+ end
+ puts str
+ result = true
+ end
+ if pkgs.size == 1 and pkgs[0] == "all"
+ self.search_const(::Object, /\AV(?:ERSION|ersion)\z/) do |klass, cname, version|
+ unless cname[1] == ?e and klass.const_defined?(:Version)
+ show.call(klass, cname.intern, version)
+ end
+ end
+ else
+ pkgs.each do |pkg|
+ begin
+ pkg = pkg.split(/::|\//).inject(::Object) {|m, c| m.const_get(c)}
+ v = case
+ when pkg.const_defined?(:Version)
+ pkg.const_get(n = :Version)
+ when pkg.const_defined?(:VERSION)
+ pkg.const_get(n = :VERSION)
+ else
+ n = nil
+ "unknown"
+ end
+ show.call(pkg, n, v)
+ rescue NameError
+ end
+ end
+ end
+ result
+ end
+
+ def each_const(path, base = ::Object)
+ path.split(/::|\//).inject(base) do |klass, name|
+ raise NameError, path unless Module === klass
+ klass.constants.grep(/#{name}/i) do |c|
+ klass.const_defined?(c) or next
+ klass.const_get(c)
+ end
+ end
+ end
+
+ def search_const(klass, name)
+ klasses = [klass]
+ while klass = klasses.shift
+ klass.constants.each do |cname|
+ klass.const_defined?(cname) or next
+ const = klass.const_get(cname)
+ yield klass, cname, const if name === cname
+ klasses << const if Module === const and const != ::Object
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index 0587cd212b..94705914af 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -41,9 +41,9 @@
# #files are the files in the .gem tar file, not the Ruby files in the gem
# #extract_files and #contents automatically call #verify
-require "rubygems"
-require 'rubygems/security'
-require 'rubygems/user_interaction'
+require_relative "../rubygems"
+require_relative 'security'
+require_relative 'user_interaction'
class Gem::Package
include Gem::UserInteraction
@@ -71,6 +71,13 @@ class Gem::Package
end
end
+ class SymlinkError < Error
+ def initialize(name, destination, destination_dir)
+ super "installing symlink '%s' pointing to parent path %s of %s is not allowed" %
+ [name, destination, destination_dir]
+ end
+ end
+
class NonSeekableIO < Error; end
class TooLongFileName < Error; end
@@ -252,14 +259,7 @@ class Gem::Package
stat = File.lstat file
if stat.symlink?
- target_path = File.readlink(file)
-
- unless target_path.start_with? '.'
- relative_dir = File.dirname(file).sub("#{Dir.pwd}/", '')
- target_path = File.join(relative_dir, target_path)
- end
-
- tar.add_symlink file, target_path, stat.mode
+ tar.add_symlink file, File.readlink(file), stat.mode
end
next unless stat.file?
@@ -407,13 +407,21 @@ EOM
# extracted.
def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc:
- directories = [] if dir_mode
+ directories = []
open_tar_gz io do |tar|
tar.each do |entry|
next unless File.fnmatch pattern, entry.full_name, File::FNM_DOTMATCH
destination = install_location entry.full_name, destination_dir
+ if entry.symlink?
+ link_target = entry.header.linkname
+ real_destination = link_target.start_with?("/") ? link_target : File.expand_path(link_target, File.dirname(destination))
+
+ raise Gem::Package::SymlinkError.new(entry.full_name, real_destination, destination_dir) unless
+ normalize_path(real_destination).start_with? normalize_path(destination_dir + '/')
+ end
+
FileUtils.rm_rf destination
mkdir_options = {}
@@ -424,9 +432,11 @@ EOM
else
File.dirname destination
end
- directories << mkdir if directories
- mkdir_p_safe mkdir, mkdir_options, destination_dir, entry.full_name
+ unless directories.include?(mkdir)
+ FileUtils.mkdir_p mkdir, **mkdir_options
+ directories << mkdir
+ end
File.open destination, 'wb' do |out|
out.write entry.read
@@ -439,8 +449,7 @@ EOM
end
end
- if directories
- directories.uniq!
+ if dir_mode
File.chmod(dir_mode, *directories)
end
end
@@ -473,21 +482,11 @@ EOM
raise Gem::Package::PathError.new(filename, destination_dir) if
filename.start_with? '/'
- destination_dir = File.expand_path(File.realpath(destination_dir))
- destination = File.expand_path(File.join(destination_dir, filename))
+ destination_dir = File.realpath(destination_dir)
+ destination = File.expand_path(filename, destination_dir)
raise Gem::Package::PathError.new(destination, destination_dir) unless
- destination.start_with? destination_dir + '/'
-
- begin
- real_destination = File.expand_path(File.realpath(destination))
- rescue
- # it's fine if the destination doesn't exist, because rm -rf'ing it can't cause any damage
- nil
- else
- raise Gem::Package::PathError.new(real_destination, destination_dir) unless
- real_destination.start_with? destination_dir + '/'
- end
+ normalize_path(destination).start_with? normalize_path(destination_dir + '/')
destination.tap(&Gem::UNTAINT)
destination
@@ -501,22 +500,6 @@ EOM
end
end
- def mkdir_p_safe(mkdir, mkdir_options, destination_dir, file_name)
- destination_dir = File.realpath(File.expand_path(destination_dir))
- parts = mkdir.split(File::SEPARATOR)
- parts.reduce do |path, basename|
- path = File.realpath(path) unless path == ""
- path = File.expand_path(path + File::SEPARATOR + basename)
- lstat = File.lstat path rescue nil
- if !lstat || !lstat.directory?
- unless normalize_path(path).start_with? normalize_path(destination_dir) and (FileUtils.mkdir path, **mkdir_options rescue false)
- raise Gem::Package::PathError.new(file_name, destination_dir)
- end
- end
- path
- end
- end
-
##
# Loads a Gem::Specification from the TarEntry +entry+
@@ -709,12 +692,12 @@ EOM
end
end
-require 'rubygems/package/digest_io'
-require 'rubygems/package/source'
-require 'rubygems/package/file_source'
-require 'rubygems/package/io_source'
-require 'rubygems/package/old'
-require 'rubygems/package/tar_header'
-require 'rubygems/package/tar_reader'
-require 'rubygems/package/tar_reader/entry'
-require 'rubygems/package/tar_writer'
+require_relative 'package/digest_io'
+require_relative 'package/source'
+require_relative 'package/file_source'
+require_relative 'package/io_source'
+require_relative 'package/old'
+require_relative 'package/tar_header'
+require_relative 'package/tar_reader'
+require_relative 'package/tar_reader/entry'
+require_relative 'package/tar_writer'
diff --git a/lib/rubygems/package/io_source.rb b/lib/rubygems/package/io_source.rb
index 7d7383110b..03d7714524 100644
--- a/lib/rubygems/package/io_source.rb
+++ b/lib/rubygems/package/io_source.rb
@@ -32,10 +32,14 @@ class Gem::Package::IOSource < Gem::Package::Source # :nodoc: all
def with_read_io
yield io
+ ensure
+ io.rewind
end
def with_write_io
yield io
+ ensure
+ io.rewind
end
def path
diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb
index e7c5620533..41121f3bfb 100644
--- a/lib/rubygems/package/tar_reader.rb
+++ b/lib/rubygems/package/tar_reader.rb
@@ -121,4 +121,4 @@ class Gem::Package::TarReader
end
end
-require 'rubygems/package/tar_reader/entry'
+require_relative 'tar_reader/entry'
diff --git a/lib/rubygems/package_task.rb b/lib/rubygems/package_task.rb
index d5a2885a64..bb48616b0e 100644
--- a/lib/rubygems/package_task.rb
+++ b/lib/rubygems/package_task.rb
@@ -20,8 +20,8 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-require 'rubygems'
-require 'rubygems/package'
+require_relative '../rubygems'
+require_relative 'package'
require 'rake/packagetask'
##
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index fd1c0a62ac..efb046c7aa 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require "rubygems/deprecate"
+require_relative "deprecate"
##
# Available list of platforms for targeting Gem installations.
@@ -100,6 +100,7 @@ class Gem::Platform
when /^dotnet([\d.]*)/ then [ 'dotnet', $1 ]
when /linux-?((?!gnu)\w+)?/ then [ 'linux', $1 ]
when /mingw32/ then [ 'mingw32', nil ]
+ when /mingw-?(\w+)?/ then [ 'mingw', $1 ]
when /(mswin\d+)(\_(\d+))?/ then
os, version = $1, $3
@cpu = 'x86' if @cpu.nil? and os =~ /32$/
diff --git a/lib/rubygems/query_utils.rb b/lib/rubygems/query_utils.rb
index ea0f260ab4..0acd5bf9c8 100644
--- a/lib/rubygems/query_utils.rb
+++ b/lib/rubygems/query_utils.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-require 'rubygems/local_remote_options'
-require 'rubygems/spec_fetcher'
-require 'rubygems/version_option'
-require 'rubygems/text'
+require_relative 'local_remote_options'
+require_relative 'spec_fetcher'
+require_relative 'version_option'
+require_relative 'text'
module Gem::QueryUtils
diff --git a/lib/rubygems/rdoc.rb b/lib/rubygems/rdoc.rb
index c40bb7d9f1..ac5e8f0822 100644
--- a/lib/rubygems/rdoc.rb
+++ b/lib/rubygems/rdoc.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems'
+require_relative '../rubygems'
begin
require 'rdoc/rubygems_hook'
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index 53e840978c..de6f88f39a 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -1,12 +1,11 @@
# frozen_string_literal: true
-require 'rubygems'
-require 'rubygems/request'
-require 'rubygems/request/connection_pools'
-require 'rubygems/s3_uri_signer'
-require 'rubygems/uri_formatter'
-require 'rubygems/uri_parsing'
-require 'rubygems/user_interaction'
-require 'resolv'
+require_relative '../rubygems'
+require_relative 'request'
+require_relative 'request/connection_pools'
+require_relative 's3_uri_signer'
+require_relative 'uri_formatter'
+require_relative 'uri'
+require_relative 'user_interaction'
##
# RemoteFetcher handles the details of fetching gems and gem information from
@@ -14,30 +13,24 @@ require 'resolv'
class Gem::RemoteFetcher
include Gem::UserInteraction
- include Gem::UriParsing
##
# A FetchError exception wraps up the various possible IO and HTTP failures
# that could happen while downloading from the internet.
class FetchError < Gem::Exception
- include Gem::UriParsing
-
##
# The URI which was being accessed when the exception happened.
attr_accessor :uri, :original_uri
def initialize(message, uri)
- super message
-
- uri = parse_uri(uri)
-
- @original_uri = uri.dup
+ uri = Gem::Uri.new(uri)
- uri.password = 'REDACTED' if uri.respond_to?(:password) && uri.password
+ super uri.redact_credentials_from(message)
- @uri = uri.to_s
+ @original_uri = uri.to_s
+ @uri = uri.redacted.to_s
end
def to_s # :nodoc:
@@ -51,6 +44,7 @@ class Gem::RemoteFetcher
class UnknownHostError < FetchError
end
+ deprecate_constant(:UnknownHostError)
@fetcher = nil
@@ -78,6 +72,7 @@ class Gem::RemoteFetcher
# fetching the gem.
def initialize(proxy=nil, dns=nil, headers={})
+ require_relative 'core_ext/tcpsocket_init' if Gem.configuration.ipv4_fallback_enabled
require 'net/http'
require 'stringio'
require 'uri'
@@ -86,7 +81,7 @@ class Gem::RemoteFetcher
@proxy = proxy
@pools = {}
- @pool_lock = Mutex.new
+ @pool_lock = Thread::Mutex.new
@cert_files = Gem::Request.get_cert_files
@headers = headers
@@ -131,7 +126,7 @@ class Gem::RemoteFetcher
require "fileutils"
FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
- source_uri = parse_uri(source_uri)
+ source_uri = Gem::Uri.new(source_uri)
scheme = source_uri.scheme
@@ -226,7 +221,7 @@ class Gem::RemoteFetcher
unless location = response['Location']
raise FetchError.new("redirecting but no redirect location was given", uri)
end
- location = parse_uri location
+ location = Gem::Uri.new location
if https?(uri) && !https?(location)
raise FetchError.new("redirecting to non-https resource: #{location}", uri)
@@ -244,7 +239,7 @@ class Gem::RemoteFetcher
# Downloads +uri+ and returns it as a String.
def fetch_path(uri, mtime = nil, head = false)
- uri = parse_uri uri
+ uri = Gem::Uri.new uri
unless uri.scheme
raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}"
@@ -261,15 +256,9 @@ class Gem::RemoteFetcher
end
data
- rescue Timeout::Error
- raise UnknownHostError.new('timed out', uri)
- rescue IOError, SocketError, SystemCallError,
+ rescue Timeout::Error, IOError, SocketError, SystemCallError,
*(OpenSSL::SSL::SSLError if Gem::HAVE_OPENSSL) => e
- if e.message =~ /getaddrinfo/
- raise UnknownHostError.new('no such name', uri)
- else
- raise FetchError.new("#{e.class}: #{e}", uri)
- end
+ raise FetchError.new("#{e.class}: #{e}", uri)
end
def fetch_s3(uri, mtime = nil, head = false)
diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb
index 1ed0fbcb99..d6100c914b 100644
--- a/lib/rubygems/request.rb
+++ b/lib/rubygems/request.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
require 'net/http'
-require 'rubygems/user_interaction'
+require_relative 'user_interaction'
class Gem::Request
extend Gem::UserInteraction
@@ -44,7 +44,7 @@ class Gem::Request
end
def self.configure_connection_for_https(connection, cert_files)
- raise Gem::Exception.new('OpenSSl is not available. Install OpenSSL and rebuild Ruby (preferred) or use non-HTTPS sources') unless Gem::HAVE_OPENSSL
+ raise Gem::Exception.new('OpenSSL is not available. Install OpenSSL and rebuild Ruby (preferred) or use non-HTTPS sources') unless Gem::HAVE_OPENSSL
connection.use_ssl = true
connection.verify_mode =
@@ -96,8 +96,10 @@ class Gem::Request
return unless cert
case error_number
when OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED then
+ require 'time'
"Certificate #{cert.subject} expired at #{cert.not_after.iso8601}"
when OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID then
+ require 'time'
"Certificate #{cert.subject} not valid until #{cert.not_before.iso8601}"
when OpenSSL::X509::V_ERR_CERT_REJECTED then
"Certificate #{cert.subject} is rejected"
@@ -191,7 +193,7 @@ class Gem::Request
begin
@requests[connection.object_id] += 1
- verbose "#{request.method} #{@uri}"
+ verbose "#{request.method} #{Gem::Uri.new(@uri).redacted}"
file_name = File.basename(@uri.path)
# perform download progress reporter only for gems
@@ -287,6 +289,6 @@ class Gem::Request
end
end
-require 'rubygems/request/http_pool'
-require 'rubygems/request/https_pool'
-require 'rubygems/request/connection_pools'
+require_relative 'request/http_pool'
+require_relative 'request/https_pool'
+require_relative 'request/connection_pools'
diff --git a/lib/rubygems/request/connection_pools.rb b/lib/rubygems/request/connection_pools.rb
index 7f3988952c..a4c2929b38 100644
--- a/lib/rubygems/request/connection_pools.rb
+++ b/lib/rubygems/request/connection_pools.rb
@@ -11,7 +11,7 @@ class Gem::Request::ConnectionPools # :nodoc:
@proxy_uri = proxy_uri
@cert_files = cert_files
@pools = {}
- @pool_mutex = Mutex.new
+ @pool_mutex = Thread::Mutex.new
end
def pool_for(uri)
diff --git a/lib/rubygems/request/http_pool.rb b/lib/rubygems/request/http_pool.rb
index 9985bbafa6..f028516db8 100644
--- a/lib/rubygems/request/http_pool.rb
+++ b/lib/rubygems/request/http_pool.rb
@@ -12,7 +12,7 @@ class Gem::Request::HTTPPool # :nodoc:
@http_args = http_args
@cert_files = cert_files
@proxy_uri = proxy_uri
- @queue = SizedQueue.new 1
+ @queue = Thread::SizedQueue.new 1
@queue << nil
end
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 5190cfc904..01b01599a8 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'tsort'
+require_relative 'tsort'
##
# A RequestSet groups a request to activate a set of dependencies.
@@ -15,7 +15,7 @@ require 'tsort'
# #=> ["nokogiri-1.6.0", "mini_portile-0.5.1", "pg-0.17.0"]
class Gem::RequestSet
- include TSort
+ include Gem::TSort
##
# Array of gems to install even if already installed
@@ -151,7 +151,7 @@ class Gem::RequestSet
@prerelease = options[:prerelease]
requests = []
- download_queue = Queue.new
+ download_queue = Thread::Queue.new
# Create a thread-safe list of gems to download
sorted_requests.each do |req|
@@ -303,7 +303,7 @@ class Gem::RequestSet
end
end
- require "rubygems/dependency_installer"
+ require_relative "dependency_installer"
inst = Gem::DependencyInstaller.new options
inst.installed_gems.replace specs
@@ -461,6 +461,6 @@ class Gem::RequestSet
end
end
-require 'rubygems/request_set/gem_dependency_api'
-require 'rubygems/request_set/lockfile'
-require 'rubygems/request_set/lockfile/tokenizer'
+require_relative 'request_set/gem_dependency_api'
+require_relative 'request_set/lockfile'
+require_relative 'request_set/lockfile/tokenizer'
diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb
index 8f8f142fff..bec29ef1b9 100644
--- a/lib/rubygems/request_set/lockfile.rb
+++ b/lib/rubygems/request_set/lockfile.rb
@@ -236,4 +236,4 @@ class Gem::RequestSet::Lockfile
end
end
-require 'rubygems/request_set/lockfile/tokenizer'
+require_relative 'lockfile/tokenizer'
diff --git a/lib/rubygems/request_set/lockfile/tokenizer.rb b/lib/rubygems/request_set/lockfile/tokenizer.rb
index 6918e8e1a5..cb8030c143 100644
--- a/lib/rubygems/request_set/lockfile/tokenizer.rb
+++ b/lib/rubygems/request_set/lockfile/tokenizer.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/request_set/lockfile/parser'
+require_relative 'parser'
class Gem::RequestSet::Lockfile::Tokenizer
Token = Struct.new :type, :value, :column, :line
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 6721de4055..d2e28fab5b 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require "rubygems/deprecate"
+require_relative "deprecate"
##
# A Requirement is a set of one or more version restrictions. It supports a
@@ -194,24 +194,19 @@ class Gem::Requirement
end
def marshal_dump # :nodoc:
- fix_syck_default_key_in_requirements
-
[@requirements]
end
def marshal_load(array) # :nodoc:
@requirements = array[0]
- fix_syck_default_key_in_requirements
+ raise TypeError, "wrong @requirements" unless Array === @requirements
end
def yaml_initialize(tag, vals) # :nodoc:
vals.each do |ivar, val|
instance_variable_set "@#{ivar}", val
end
-
- Gem.load_yaml
- fix_syck_default_key_in_requirements
end
def init_with(coder) # :nodoc:
@@ -246,8 +241,7 @@ class Gem::Requirement
def satisfied_by?(version)
raise ArgumentError, "Need a Gem::Version: #{version.inspect}" unless
Gem::Version === version
- # #28965: syck has a bug with unquoted '=' YAML.loading as YAML::DefaultKey
- requirements.all? {|op, rv| (OPS[op] || OPS["="]).call version, rv }
+ requirements.all? {|op, rv| OPS[op].call version, rv }
end
alias :=== :satisfied_by?
@@ -289,19 +283,6 @@ class Gem::Requirement
def _tilde_requirements
@_tilde_requirements ||= _sorted_requirements.select {|r| r.first == "~>" }
end
-
- private
-
- def fix_syck_default_key_in_requirements # :nodoc:
- Gem.load_yaml
-
- # Fixup the Syck DefaultKey bug
- @requirements.each do |r|
- if r[0].kind_of? Gem::SyckDefaultKey
- r[0] = "="
- end
- end
- end
end
class Gem::Version
diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb
index 71c35ea3d3..51a11fed47 100644
--- a/lib/rubygems/resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/dependency'
-require 'rubygems/exceptions'
-require 'rubygems/util/list'
+require_relative 'dependency'
+require_relative 'exceptions'
+require_relative 'util/list'
##
# Given a set of Gem::Dependency objects as +needed+ and a way to query the
@@ -10,7 +10,7 @@ require 'rubygems/util/list'
# all the requirements.
class Gem::Resolver
- require 'rubygems/resolver/molinillo'
+ require_relative 'resolver/molinillo'
##
# If the DEBUG_RESOLVER environment variable is set then debugging mode is
@@ -318,30 +318,30 @@ class Gem::Resolver
private :amount_constrained
end
-require 'rubygems/resolver/activation_request'
-require 'rubygems/resolver/conflict'
-require 'rubygems/resolver/dependency_request'
-require 'rubygems/resolver/requirement_list'
-require 'rubygems/resolver/stats'
-
-require 'rubygems/resolver/set'
-require 'rubygems/resolver/api_set'
-require 'rubygems/resolver/composed_set'
-require 'rubygems/resolver/best_set'
-require 'rubygems/resolver/current_set'
-require 'rubygems/resolver/git_set'
-require 'rubygems/resolver/index_set'
-require 'rubygems/resolver/installer_set'
-require 'rubygems/resolver/lock_set'
-require 'rubygems/resolver/vendor_set'
-require 'rubygems/resolver/source_set'
-
-require 'rubygems/resolver/specification'
-require 'rubygems/resolver/spec_specification'
-require 'rubygems/resolver/api_specification'
-require 'rubygems/resolver/git_specification'
-require 'rubygems/resolver/index_specification'
-require 'rubygems/resolver/installed_specification'
-require 'rubygems/resolver/local_specification'
-require 'rubygems/resolver/lock_specification'
-require 'rubygems/resolver/vendor_specification'
+require_relative 'resolver/activation_request'
+require_relative 'resolver/conflict'
+require_relative 'resolver/dependency_request'
+require_relative 'resolver/requirement_list'
+require_relative 'resolver/stats'
+
+require_relative 'resolver/set'
+require_relative 'resolver/api_set'
+require_relative 'resolver/composed_set'
+require_relative 'resolver/best_set'
+require_relative 'resolver/current_set'
+require_relative 'resolver/git_set'
+require_relative 'resolver/index_set'
+require_relative 'resolver/installer_set'
+require_relative 'resolver/lock_set'
+require_relative 'resolver/vendor_set'
+require_relative 'resolver/source_set'
+
+require_relative 'resolver/specification'
+require_relative 'resolver/spec_specification'
+require_relative 'resolver/api_specification'
+require_relative 'resolver/git_specification'
+require_relative 'resolver/index_specification'
+require_relative 'resolver/installed_specification'
+require_relative 'resolver/local_specification'
+require_relative 'resolver/lock_specification'
+require_relative 'resolver/vendor_specification'
diff --git a/lib/rubygems/resolver/git_specification.rb b/lib/rubygems/resolver/git_specification.rb
index 555dcffc22..ee47080ab4 100644
--- a/lib/rubygems/resolver/git_specification.rb
+++ b/lib/rubygems/resolver/git_specification.rb
@@ -21,7 +21,7 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
# the executables.
def install(options = {})
- require 'rubygems/installer'
+ require_relative '../installer'
installer = Gem::Installer.for_spec spec, options
diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb
index 60181315b0..f4fee351a5 100644
--- a/lib/rubygems/resolver/installer_set.rb
+++ b/lib/rubygems/resolver/installer_set.rb
@@ -77,11 +77,11 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
newest = found.last
unless @force
- found_matching_metadata = found.select do |spec|
+ found_matching_metadata = found.reverse.find do |spec|
metadata_satisfied?(spec)
end
- if found_matching_metadata.empty?
+ if found_matching_metadata.nil?
if newest
ensure_required_ruby_version_met(newest.spec)
ensure_required_rubygems_version_met(newest.spec)
@@ -92,7 +92,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
raise exc
end
else
- newest = found_matching_metadata.last
+ newest = found_matching_metadata
end
end
diff --git a/lib/rubygems/resolver/molinillo.rb b/lib/rubygems/resolver/molinillo.rb
index 2357f41bee..12ca740e5a 100644
--- a/lib/rubygems/resolver/molinillo.rb
+++ b/lib/rubygems/resolver/molinillo.rb
@@ -1,2 +1,2 @@
# frozen_string_literal: true
-require 'rubygems/resolver/molinillo/lib/molinillo'
+require_relative 'molinillo/lib/molinillo'
diff --git a/lib/rubygems/resolver/molinillo/LICENSE b/lib/rubygems/resolver/molinillo/LICENSE
new file mode 100644
index 0000000000..01feffa088
--- /dev/null
+++ b/lib/rubygems/resolver/molinillo/LICENSE
@@ -0,0 +1,9 @@
+This project is licensed under the MIT license.
+
+Copyright (c) 2014 Samuel E. Giddins segiddins@segiddins.me
+
+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/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
index 16430a79f5..95f8416b96 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'tsort'
+require_relative '../../../../tsort'
require_relative 'dependency_graph/log'
require_relative 'dependency_graph/vertex'
@@ -17,7 +17,7 @@ module Gem::Resolver::Molinillo
vertices.values.each { |v| yield v }
end
- include TSort
+ include Gem::TSort
# @!visibility private
alias tsort_each_node each
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb
index 9448dc7bf3..1067bf7439 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module Gem::Resolver::Molinillo
- # Provides information about specifcations and dependencies to the resolver,
+ # Provides information about specifications and dependencies to the resolver,
# allowing the {Resolver} class to remain generic while still providing power
# and flexibility.
#
diff --git a/lib/rubygems/resolver/set.rb b/lib/rubygems/resolver/set.rb
index 8046e18ea1..5d8dd51eaa 100644
--- a/lib/rubygems/resolver/set.rb
+++ b/lib/rubygems/resolver/set.rb
@@ -20,7 +20,6 @@ class Gem::Resolver::Set
attr_accessor :prerelease
def initialize # :nodoc:
- require 'uri'
@prerelease = false
@remote = true
@errors = []
diff --git a/lib/rubygems/resolver/specification.rb b/lib/rubygems/resolver/specification.rb
index 8c6fc9afcf..dfcb7eb057 100644
--- a/lib/rubygems/resolver/specification.rb
+++ b/lib/rubygems/resolver/specification.rb
@@ -93,7 +93,7 @@ class Gem::Resolver::Specification
# specification.
def install(options = {})
- require 'rubygems/installer'
+ require_relative '../installer'
gem = download options
diff --git a/lib/rubygems/s3_uri_signer.rb b/lib/rubygems/s3_uri_signer.rb
index f1f9229ca5..4d1deee997 100644
--- a/lib/rubygems/s3_uri_signer.rb
+++ b/lib/rubygems/s3_uri_signer.rb
@@ -1,6 +1,4 @@
-require 'base64'
-require 'digest'
-require 'rubygems/openssl'
+require_relative 'openssl'
##
# S3URISigner implements AWS SigV4 for S3 Source to avoid a dependency on the aws-sdk-* gems
@@ -88,7 +86,7 @@ class Gem::S3URISigner
"AWS4-HMAC-SHA256",
date_time,
credential_info,
- Digest::SHA256.hexdigest(canonical_request),
+ OpenSSL::Digest::SHA256.hexdigest(canonical_request),
].join("\n")
end
@@ -141,8 +139,8 @@ class Gem::S3URISigner
def ec2_metadata_credentials_json
require 'net/http'
- require 'rubygems/request'
- require 'rubygems/request/connection_pools'
+ require_relative 'request'
+ require_relative 'request/connection_pools'
require 'json'
iam_info = ec2_metadata_request(EC2_IAM_INFO)
diff --git a/lib/rubygems/safe_yaml.rb b/lib/rubygems/safe_yaml.rb
index 29312ad5a1..e905052e1c 100644
--- a/lib/rubygems/safe_yaml.rb
+++ b/lib/rubygems/safe_yaml.rb
@@ -17,8 +17,6 @@ module Gem
Gem::Specification
Gem::Version
Gem::Version::Requirement
- YAML::Syck::DefaultKey
- Syck::DefaultKey
].freeze
PERMITTED_SYMBOLS = %w[
diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb
index c80639af6d..8240a1a059 100644
--- a/lib/rubygems/security.rb
+++ b/lib/rubygems/security.rb
@@ -5,7 +5,7 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems/exceptions'
+require_relative 'exceptions'
require_relative 'openssl'
##
@@ -152,6 +152,7 @@ require_relative 'openssl'
# certificate for EMAIL_ADDR
# -C, --certificate CERT Signing certificate for --sign
# -K, --private-key KEY Key for --sign or --build
+# -A, --key-algorithm ALGORITHM Select key algorithm for --build from RSA, DSA, or EC. Defaults to RSA.
# -s, --sign CERT Signs CERT with the key from -K
# and the certificate from -C
# -d, --days NUMBER_OF_DAYS Days before the certificate expires
@@ -317,7 +318,6 @@ require_relative 'openssl'
# * Honor extension restrictions
# * Might be better to store the certificate chain as a PKCS#7 or PKCS#12
# file, instead of an array embedded in the metadata.
-# * Flexible signature and key algorithms, not hard-coded to RSA and SHA1.
#
# == Original author
#
@@ -337,17 +337,19 @@ module Gem::Security
DIGEST_NAME = 'SHA256' # :nodoc:
##
- # Algorithm for creating the key pair used to sign gems
+ # Length of keys created by RSA and DSA keys
- KEY_ALGORITHM =
- if defined?(OpenSSL::PKey::RSA)
- OpenSSL::PKey::RSA
- end
+ RSA_DSA_KEY_LENGTH = 3072
##
- # Length of keys created by KEY_ALGORITHM
+ # Default algorithm to use when building a key pair
- KEY_LENGTH = 3072
+ DEFAULT_KEY_ALGORITHM = 'RSA'
+
+ ##
+ # Named curve used for Elliptic Curve
+
+ EC_NAME = 'secp384r1'
##
# Cipher used to encrypt the key pair used to sign gems.
@@ -400,7 +402,7 @@ module Gem::Security
serial = 1)
cert = OpenSSL::X509::Certificate.new
- cert.public_key = key.public_key
+ cert.public_key = get_public_key(key)
cert.version = 2
cert.serial = serial
@@ -419,6 +421,24 @@ module Gem::Security
end
##
+ # Gets the right public key from a PKey instance
+
+ def self.get_public_key(key)
+ return key.public_key unless key.is_a?(OpenSSL::PKey::EC)
+
+ ec_key = OpenSSL::PKey::EC.new(key.group.curve_name)
+ ec_key.public_key = key.public_key
+ ec_key
+ end
+
+ ##
+ # In Ruby 2.3 EC doesn't implement the private_key? but not the private? method
+
+ if defined?(OpenSSL::PKey::EC) && Gem::Version.new(String.new(RUBY_VERSION)) < Gem::Version.new("2.4.0")
+ OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?)
+ end
+
+ ##
# Creates a self-signed certificate with an issuer and subject from +email+,
# a subject alternative name of +email+ and the given +extensions+ for the
# +key+.
@@ -459,11 +479,25 @@ module Gem::Security
end
##
- # Creates a new key pair of the specified +length+ and +algorithm+. The
- # default is a 3072 bit RSA key.
-
- def self.create_key(length = KEY_LENGTH, algorithm = KEY_ALGORITHM)
- algorithm.new length
+ # Creates a new key pair of the specified +algorithm+. RSA, DSA, and EC
+ # are supported.
+
+ def self.create_key(algorithm)
+ if defined?(OpenSSL::PKey)
+ case algorithm.downcase
+ when 'dsa'
+ OpenSSL::PKey::DSA.new(RSA_DSA_KEY_LENGTH)
+ when 'rsa'
+ OpenSSL::PKey::RSA.new(RSA_DSA_KEY_LENGTH)
+ when 'ec'
+ domain_key = OpenSSL::PKey::EC.new(EC_NAME)
+ domain_key.generate_key
+ domain_key
+ else
+ raise Gem::Security::Exception,
+ "#{algorithm} algorithm not found. RSA, DSA, and EC algorithms are supported."
+ end
+ end
end
##
@@ -492,7 +526,7 @@ module Gem::Security
raise Gem::Security::Exception,
"incorrect signing key for re-signing " +
"#{expired_certificate.subject}" unless
- expired_certificate.public_key.to_pem == private_key.public_key.to_pem
+ expired_certificate.public_key.to_pem == get_public_key(private_key).to_pem
unless expired_certificate.subject.to_s ==
expired_certificate.issuer.to_s
@@ -592,9 +626,9 @@ module Gem::Security
end
if Gem::HAVE_OPENSSL
- require 'rubygems/security/policy'
- require 'rubygems/security/policies'
- require 'rubygems/security/trust_dir'
+ require_relative 'security/policy'
+ require_relative 'security/policies'
+ require_relative 'security/trust_dir'
end
-require 'rubygems/security/signer'
+require_relative 'security/signer'
diff --git a/lib/rubygems/security/policy.rb b/lib/rubygems/security/policy.rb
index 7629d64796..3c3cb647ee 100644
--- a/lib/rubygems/security/policy.rb
+++ b/lib/rubygems/security/policy.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/user_interaction'
+require_relative '../user_interaction'
##
# A Gem::Security::Policy object encapsulates the settings for verifying
@@ -115,9 +115,11 @@ class Gem::Security::Policy
raise Gem::Security::Exception, 'missing key or signature'
end
+ public_key = Gem::Security.get_public_key(key)
+
raise Gem::Security::Exception,
"certificate #{signer.subject} does not match the signing key" unless
- signer.public_key.to_pem == key.public_key.to_pem
+ signer.public_key.to_pem == public_key.to_pem
true
end
@@ -164,9 +166,9 @@ class Gem::Security::Policy
end
save_cert = OpenSSL::X509::Certificate.new File.read path
- save_dgst = digester.digest save_cert.public_key.to_s
+ save_dgst = digester.digest save_cert.public_key.to_pem
- pkey_str = root.public_key.to_s
+ pkey_str = root.public_key.to_pem
cert_dgst = digester.digest pkey_str
raise Gem::Security::Exception,
diff --git a/lib/rubygems/security/signer.rb b/lib/rubygems/security/signer.rb
index 6c85ab08d2..968cf88973 100644
--- a/lib/rubygems/security/signer.rb
+++ b/lib/rubygems/security/signer.rb
@@ -2,7 +2,7 @@
##
# Basic OpenSSL-based package signing class.
-require "rubygems/user_interaction"
+require_relative "../user_interaction"
class Gem::Security::Signer
include Gem::UserInteraction
@@ -83,8 +83,8 @@ class Gem::Security::Signer
@digest_name = Gem::Security::DIGEST_NAME
@digest_algorithm = Gem::Security.create_digest(@digest_name)
- if @key && !@key.is_a?(OpenSSL::PKey::RSA)
- @key = OpenSSL::PKey::RSA.new(File.read(@key), @passphrase)
+ if @key && !@key.is_a?(OpenSSL::PKey::PKey)
+ @key = OpenSSL::PKey.read(File.read(@key), @passphrase)
end
if @cert_chain
@@ -177,8 +177,7 @@ class Gem::Security::Signer
disk_cert = File.read(disk_cert_path) rescue nil
disk_key_path = File.join(Gem.default_key_path)
- disk_key =
- OpenSSL::PKey::RSA.new(File.read(disk_key_path), @passphrase) rescue nil
+ disk_key = OpenSSL::PKey.read(File.read(disk_key_path), @passphrase) rescue nil
return unless disk_key
diff --git a/lib/rubygems/security/trust_dir.rb b/lib/rubygems/security/trust_dir.rb
index 1d93ceabd1..456947274c 100644
--- a/lib/rubygems/security/trust_dir.rb
+++ b/lib/rubygems/security/trust_dir.rb
@@ -104,6 +104,7 @@ class Gem::Security::TrustDir
# permissions.
def verify
+ require 'fileutils'
if File.exist? @dir
raise Gem::Security::Exception,
"trust directory #{@dir} is not a directory" unless
diff --git a/lib/rubygems/security_option.rb b/lib/rubygems/security_option.rb
index 3403aaaf05..a4c570ded5 100644
--- a/lib/rubygems/security_option.rb
+++ b/lib/rubygems/security_option.rb
@@ -5,7 +5,7 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems'
+require_relative '../rubygems'
# forward-declare
@@ -19,16 +19,16 @@ end
module Gem::SecurityOption
def add_security_option
- OptionParser.accept Gem::Security::Policy do |value|
- require 'rubygems/security'
+ Gem::OptionParser.accept Gem::Security::Policy do |value|
+ require_relative 'security'
- raise OptionParser::InvalidArgument, 'OpenSSL not installed' unless
+ raise Gem::OptionParser::InvalidArgument, 'OpenSSL not installed' unless
defined?(Gem::Security::HighSecurity)
policy = Gem::Security::Policies[value]
unless policy
valid = Gem::Security::Policies.keys.sort
- raise OptionParser::InvalidArgument, "#{value} (#{valid.join ', '} are valid)"
+ raise Gem::OptionParser::InvalidArgument, "#{value} (#{valid.join ', '} are valid)"
end
policy
end
diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb
index 2c2805f31c..45be05bcda 100644
--- a/lib/rubygems/server.rb
+++ b/lib/rubygems/server.rb
@@ -3,8 +3,8 @@ require 'zlib'
require 'erb'
require 'uri'
-require 'rubygems'
-require 'rubygems/rdoc'
+require_relative '../rubygems'
+require_relative 'rdoc'
##
# Gem::Server and allows users to serve gems for consumption by
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index 4ae84cf532..b5bd6b80e6 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "rubygems/text"
+require_relative "text"
##
# A Source knows how to list and fetch gems from a RubyGems marshal index.
#
@@ -238,9 +238,9 @@ class Gem::Source
end
end
-require 'rubygems/source/git'
-require 'rubygems/source/installed'
-require 'rubygems/source/specific_file'
-require 'rubygems/source/local'
-require 'rubygems/source/lock'
-require 'rubygems/source/vendor'
+require_relative 'source/git'
+require_relative 'source/installed'
+require_relative 'source/specific_file'
+require_relative 'source/local'
+require_relative 'source/lock'
+require_relative 'source/vendor'
diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb
index 9876adc24e..cda5aa8073 100644
--- a/lib/rubygems/source/git.rb
+++ b/lib/rubygems/source/git.rb
@@ -225,7 +225,7 @@ class Gem::Source::Git < Gem::Source
# A hash for the git gem based on the git repository URI.
def uri_hash # :nodoc:
- require 'digest' # required here to avoid deadlocking in Gem.activate_bin_path (because digest is a gem on 2.5+)
+ require_relative '../openssl'
normalized =
if @repository =~ %r{^\w+://(\w+@)?}
@@ -235,6 +235,6 @@ class Gem::Source::Git < Gem::Source
@repository
end
- Digest::SHA1.hexdigest normalized
+ OpenSSL::Digest::SHA1.hexdigest normalized
end
end
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index b2bcadc49c..68ff605a83 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-require 'rubygems/remote_fetcher'
-require 'rubygems/user_interaction'
-require 'rubygems/errors'
-require 'rubygems/text'
-require 'rubygems/name_tuple'
+require_relative 'remote_fetcher'
+require_relative 'user_interaction'
+require_relative 'errors'
+require_relative 'text'
+require_relative 'name_tuple'
##
# SpecFetcher handles metadata updates from remote gem repositories.
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index 73062afe53..b493a47ef0 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -6,11 +6,11 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems/deprecate'
-require 'rubygems/basic_specification'
-require 'rubygems/stub_specification'
-require 'rubygems/specification_policy'
-require 'rubygems/util/list'
+require_relative 'deprecate'
+require_relative 'basic_specification'
+require_relative 'stub_specification'
+require_relative 'specification_policy'
+require_relative 'util/list'
##
# The Specification class contains the information for a gem. Typically
@@ -102,12 +102,8 @@ class Gem::Specification < Gem::BasicSpecification
today = Time.now.utc
TODAY = Time.utc(today.year, today.month, today.day) # :nodoc:
- # rubocop:disable Style/MutableConstant
- LOAD_CACHE = {} # :nodoc:
- # rubocop:enable Style/MutableConstant
- LOAD_CACHE_MUTEX = Mutex.new
-
- private_constant :LOAD_CACHE if defined? private_constant
+ @load_cache = {} # :nodoc:
+ @load_cache_mutex = Thread::Mutex.new
VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/.freeze # :nodoc:
@@ -182,13 +178,23 @@ class Gem::Specification < Gem::BasicSpecification
@@default_value[k].nil?
end
- @@stubs = nil
- @@stubs_by_name = {}
+ def self.clear_specs # :nodoc:
+ @@all_specs_mutex.synchronize do
+ @@all = nil
+ @@stubs = nil
+ @@stubs_by_name = {}
+ @@spec_with_requirable_file = {}
+ @@active_stub_with_requirable_file = {}
+ end
+ end
+ private_class_method :clear_specs
+
+ @@all_specs_mutex = Thread::Mutex.new
+
+ clear_specs
# Sentinel object to represent "not found" stubs
NOT_FOUND = Struct.new(:to_spec, :this).new # :nodoc:
- @@spec_with_requirable_file = {}
- @@active_stub_with_requirable_file = {}
# Tracking removed method calls to warn users during build time.
REMOVED_METHODS = [:rubyforge_project=].freeze # :nodoc:
@@ -282,6 +288,15 @@ class Gem::Specification < Gem::BasicSpecification
# :section: Recommended gemspec attributes
##
+ # The version of Ruby required by this gem
+ #
+ # Usage:
+ #
+ # spec.required_ruby_version = '>= 2.7.0'
+
+ attr_reader :required_ruby_version
+
+ ##
# A long description of this gem
#
# The description should be more detailed than the summary but not
@@ -324,17 +339,21 @@ class Gem::Specification < Gem::BasicSpecification
# This should just be the name of your license. The full text of the license
# should be inside of the gem (at the top level) when you build it.
#
- # The simplest way, is to specify the standard SPDX ID
+ # The simplest way is to specify the standard SPDX ID
# https://spdx.org/licenses/ for the license.
- # Ideally you should pick one that is OSI (Open Source Initiative)
+ # Ideally, you should pick one that is OSI (Open Source Initiative)
# http://opensource.org/licenses/alphabetical approved.
#
- # The most commonly used OSI approved licenses are MIT and Apache-2.0.
+ # The most commonly used OSI-approved licenses are MIT and Apache-2.0.
# GitHub also provides a license picker at http://choosealicense.com/.
#
+ # You can also use a custom license file along with your gemspec and specify
+ # a LicenseRef-<idstring>, where idstring is the name of the file containing
+ # the license text.
+ #
# You should specify a license for your gem so that people know how they are
- # permitted to use it, and any restrictions you're placing on it. Not
- # specifying a license means all rights are reserved; others have no rights
+ # permitted to use it and any restrictions you're placing on it. Not
+ # specifying a license means all rights are reserved; others have no right
# to use the code for any purpose.
#
# You can set multiple licenses with #licenses=
@@ -513,11 +532,6 @@ class Gem::Specification < Gem::BasicSpecification
end
##
- # The version of Ruby required by this gem
-
- attr_reader :required_ruby_version
-
- ##
# The RubyGems version required by this gem
attr_reader :required_rubygems_version
@@ -666,6 +680,9 @@ class Gem::Specification < Gem::BasicSpecification
#
# # Only prereleases or final releases after 2.6.0.preview2
# spec.required_ruby_version = '> 2.6.0.preview2'
+ #
+ # # This gem will work with 2.3.0 or greater, including major version 3, but lesser than 4.0.0
+ # spec.required_ruby_version = '>= 2.3', '< 4'
def required_ruby_version=(req)
@required_ruby_version = Gem::Requirement.create req
@@ -741,23 +758,15 @@ class Gem::Specification < Gem::BasicSpecification
attr_accessor :specification_version
def self._all # :nodoc:
- unless defined?(@@all) && @@all
- @@all = stubs.map(&:to_spec)
-
- # After a reset, make sure already loaded specs
- # are still marked as activated.
- specs = {}
- Gem.loaded_specs.each_value{|s| specs[s] = true }
- @@all.each{|s| s.activated = true if specs[s] }
- end
- @@all
+ @@all_specs_mutex.synchronize { @@all ||= Gem.loaded_specs.values | stubs.map(&:to_spec) }
end
- def self._clear_load_cache # :nodoc:
- LOAD_CACHE_MUTEX.synchronize do
- LOAD_CACHE.clear
+ def self.clear_load_cache # :nodoc:
+ @load_cache_mutex.synchronize do
+ @load_cache.clear
end
end
+ private_class_method :clear_load_cache
def self.each_gemspec(dirs) # :nodoc:
dirs.each do |dir|
@@ -1105,7 +1114,7 @@ class Gem::Specification < Gem::BasicSpecification
def self.load(file)
return unless file
- _spec = LOAD_CACHE_MUTEX.synchronize { LOAD_CACHE[file] }
+ _spec = @load_cache_mutex.synchronize { @load_cache[file] }
return _spec if _spec
file = file.dup.tap(&Gem::UNTAINT)
@@ -1120,12 +1129,12 @@ class Gem::Specification < Gem::BasicSpecification
if Gem::Specification === _spec
_spec.loaded_from = File.expand_path file.to_s
- LOAD_CACHE_MUTEX.synchronize do
- prev = LOAD_CACHE[file]
+ @load_cache_mutex.synchronize do
+ prev = @load_cache[file]
if prev
_spec = prev
else
- LOAD_CACHE[file] = _spec
+ @load_cache[file] = _spec
end
end
return _spec
@@ -1223,12 +1232,8 @@ class Gem::Specification < Gem::BasicSpecification
def self.reset
@@dirs = nil
Gem.pre_reset_hooks.each {|hook| hook.call }
- @@all = nil
- @@stubs = nil
- @@stubs_by_name = {}
- @@spec_with_requirable_file = {}
- @@active_stub_with_requirable_file = {}
- _clear_load_cache
+ clear_specs
+ clear_load_cache
unresolved = unresolved_deps
unless unresolved.empty?
w = "W" + "ARN"
@@ -1553,9 +1558,8 @@ class Gem::Specification < Gem::BasicSpecification
# the gem.build_complete file is missing.
def build_extensions # :nodoc:
- return if default_gem?
return if extensions.empty?
- return if installed_by_version < Gem::Version.new('2.2.0.preview.2')
+ return if default_gem?
return if File.exist? gem_build_complete_path
return if !File.writable?(base_dir)
return if !File.exist?(File.join(base_dir, 'extensions'))
@@ -1566,9 +1570,9 @@ class Gem::Specification < Gem::BasicSpecification
unresolved_deps = Gem::Specification.unresolved_deps.dup
Gem::Specification.unresolved_deps.clear
- require 'rubygems/config_file'
- require 'rubygems/ext'
- require 'rubygems/user_interaction'
+ require_relative 'config_file'
+ require_relative 'ext'
+ require_relative 'user_interaction'
ui = Gem::SilentUI.new
Gem::DefaultUserInteraction.use_ui ui do
@@ -1688,12 +1692,6 @@ class Gem::Specification < Gem::BasicSpecification
when String then
if DateTimeFormat =~ date
Time.utc($1.to_i, $2.to_i, $3.to_i)
-
- # Workaround for where the date format output from psych isn't
- # parsed as a Time object by syck and thus comes through as a
- # string.
- elsif /\A(\d{4})-(\d{2})-(\d{2}) \d{2}:\d{2}:\d{2}\.\d+?Z\z/ =~ date
- Time.utc($1.to_i, $2.to_i, $3.to_i)
else
raise(Gem::InvalidSpecificationException,
"invalid date format in specification: #{date.inspect}")
@@ -2124,9 +2122,8 @@ class Gem::Specification < Gem::BasicSpecification
# probably want to build_extensions
def missing_extensions?
- return false if default_gem?
return false if extensions.empty?
- return false if installed_by_version < Gem::Version.new('2.2.0.preview.2')
+ return false if default_gem?
return false if File.exist? gem_build_complete_path
true
@@ -2420,7 +2417,6 @@ class Gem::Specification < Gem::BasicSpecification
# still have their default values are omitted.
def to_ruby
- require_relative 'openssl'
mark_version
result = []
result << "# -*- encoding: utf-8 -*-"
@@ -2454,16 +2450,21 @@ class Gem::Specification < Gem::BasicSpecification
:has_rdoc,
:default_executable,
:metadata,
+ :signing_key,
]
@@attributes.each do |attr_name|
next if handled.include? attr_name
current_value = self.send(attr_name)
if current_value != default_value(attr_name) || self.class.required_attribute?(attr_name)
- result << " s.#{attr_name} = #{ruby_code current_value}" unless defined?(OpenSSL::PKey::RSA) && current_value.is_a?(OpenSSL::PKey::RSA)
+ result << " s.#{attr_name} = #{ruby_code current_value}"
end
end
+ if String === signing_key
+ result << " s.signing_key = #{signing_key.dump}.freeze"
+ end
+
if @installed_by_version
result << nil
result << " s.installed_by_version = \"#{Gem::VERSION}\" if s.respond_to? :installed_by_version"
@@ -2526,7 +2527,7 @@ class Gem::Specification < Gem::BasicSpecification
# back, we have to check again here to make sure that our
# psych code was properly loaded, and load it if not.
unless Gem.const_defined?(:NoAliasYAMLTree)
- require 'rubygems/psych_tree'
+ require_relative 'psych_tree'
end
builder = Gem::NoAliasYAMLTree.create
diff --git a/lib/rubygems/specification_policy.rb b/lib/rubygems/specification_policy.rb
index 2b8b05635e..73bd31970c 100644
--- a/lib/rubygems/specification_policy.rb
+++ b/lib/rubygems/specification_policy.rb
@@ -1,4 +1,4 @@
-require 'rubygems/user_interaction'
+require_relative 'user_interaction'
class Gem::SpecificationPolicy
include Gem::UserInteraction
@@ -124,25 +124,26 @@ class Gem::SpecificationPolicy
end
metadata.each do |key, value|
+ entry = "metadata['#{key}']"
if !key.kind_of?(String)
error "metadata keys must be a String"
end
if key.size > 128
- error "metadata key too large (#{key.size} > 128)"
+ error "metadata key is too large (#{key.size} > 128)"
end
if !value.kind_of?(String)
- error "metadata values must be a String"
+ error "#{entry} value must be a String"
end
if value.size > 1024
- error "metadata value too large (#{value.size} > 1024)"
+ error "#{entry} value is too large (#{value.size} > 1024)"
end
if METADATA_LINK_KEYS.include? key
if value !~ VALID_URI_PATTERN
- error "metadata['#{key}'] has invalid link: #{value.inspect}"
+ error "#{entry} has invalid link: #{value.inspect}"
end
end
end
@@ -380,7 +381,7 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
end
LAZY = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, '')
- LAZY_PATTERN = /FI XME|TO DO/x.freeze
+ LAZY_PATTERN = /\AFI XME|\ATO DO/x.freeze
HOMEPAGE_URI_PATTERN = /\A[a-z][a-z\d+.-]*:/i.freeze
def validate_lazy_metadata
diff --git a/lib/rubygems/syck_hack.rb b/lib/rubygems/syck_hack.rb
deleted file mode 100644
index 051483eac8..0000000000
--- a/lib/rubygems/syck_hack.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-# :stopdoc:
-
-# Hack to handle syck's DefaultKey bug
-#
-# This file is always loaded AFTER either syck or psych are already
-# loaded. It then looks at what constants are available and creates
-# a consistent view on all rubys.
-#
-# All this is so that there is always a YAML::Syck::DefaultKey
-# class no matter if the full yaml library has loaded or not.
-#
-
-module YAML # :nodoc:
- # In newer 1.9.2, there is a Syck toplevel constant instead of it
- # being underneath YAML. If so, reference it back under YAML as
- # well.
- if defined? ::Syck
- # for tests that change YAML::ENGINE
- # 1.8 does not support the second argument to const_defined?
- remove_const :Syck rescue nil
-
- Syck = ::Syck
-
- # JRuby's "Syck" is called "Yecht"
- elsif defined? YAML::Yecht
- Syck = YAML::Yecht
-
- # Otherwise, if there is no YAML::Syck, then we've got just psych
- # loaded, so lets define a stub for DefaultKey.
- elsif !defined? YAML::Syck
- module Syck
- class DefaultKey # :nodoc:
- end
- end
- end
-
- # Now that we've got something that is always here, define #to_s
- # so when code tries to use this, it at least just shows up like it
- # should.
- module Syck
- class DefaultKey
- remove_method :to_s rescue nil
-
- def to_s
- '='
- end
- end
- end
-
- SyntaxError = Error unless defined? SyntaxError
-end
-
-# Sometime in the 1.9 dev cycle, the Syck constant was moved from under YAML
-# to be a toplevel constant. So gemspecs created under these versions of Syck
-# will have references to Syck::DefaultKey.
-#
-# So we need to be sure that we reference Syck at the toplevel too so that
-# we can always load these kind of gemspecs.
-#
-if !defined?(Syck)
- Syck = YAML::Syck
-end
-
-# Now that we've got Syck setup in all the right places, store
-# a reference to the DefaultKey class inside Gem. We do this so that
-# if later on YAML, etc are redefined, we've still got a consistent
-# place to find the DefaultKey class for comparison.
-
-module Gem
- # for tests that change YAML::ENGINE
- remove_const :SyckDefaultKey if const_defined? :SyckDefaultKey
-
- SyckDefaultKey = YAML::Syck::DefaultKey
-end
-
-# :startdoc:
diff --git a/lib/rubygems/tsort.rb b/lib/rubygems/tsort.rb
new file mode 100644
index 0000000000..ebe7c3364b
--- /dev/null
+++ b/lib/rubygems/tsort.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require_relative 'tsort/lib/tsort'
diff --git a/lib/rubygems/tsort/.document b/lib/rubygems/tsort/.document
new file mode 100644
index 0000000000..0c43bbd6b3
--- /dev/null
+++ b/lib/rubygems/tsort/.document
@@ -0,0 +1 @@
+# Vendored files do not need to be documented
diff --git a/lib/rubygems/tsort/LICENSE.txt b/lib/rubygems/tsort/LICENSE.txt
new file mode 100644
index 0000000000..a009caefea
--- /dev/null
+++ b/lib/rubygems/tsort/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/lib/rubygems/tsort/lib/tsort.rb b/lib/rubygems/tsort/lib/tsort.rb
new file mode 100644
index 0000000000..f68c5947d3
--- /dev/null
+++ b/lib/rubygems/tsort/lib/tsort.rb
@@ -0,0 +1,454 @@
+# frozen_string_literal: true
+
+#--
+# tsort.rb - provides a module for topological sorting and strongly connected components.
+#++
+#
+
+#
+# Gem::TSort implements topological sorting using Tarjan's algorithm for
+# strongly connected components.
+#
+# Gem::TSort is designed to be able to be used with any object which can be
+# interpreted as a directed graph.
+#
+# Gem::TSort requires two methods to interpret an object as a graph,
+# tsort_each_node and tsort_each_child.
+#
+# * tsort_each_node is used to iterate for all nodes over a graph.
+# * tsort_each_child is used to iterate for child nodes of a given node.
+#
+# The equality of nodes are defined by eql? and hash since
+# Gem::TSort uses Hash internally.
+#
+# == A Simple Example
+#
+# The following example demonstrates how to mix the Gem::TSort module into an
+# existing class (in this case, Hash). Here, we're treating each key in
+# the hash as a node in the graph, and so we simply alias the required
+# #tsort_each_node method to Hash's #each_key method. For each key in the
+# hash, the associated value is an array of the node's child nodes. This
+# choice in turn leads to our implementation of the required #tsort_each_child
+# method, which fetches the array of child nodes and then iterates over that
+# array using the user-supplied block.
+#
+# require 'rubygems/tsort/lib/tsort'
+#
+# class Hash
+# include Gem::TSort
+# alias tsort_each_node each_key
+# def tsort_each_child(node, &block)
+# fetch(node).each(&block)
+# end
+# end
+#
+# {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
+# #=> [3, 2, 1, 4]
+#
+# {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
+# #=> [[4], [2, 3], [1]]
+#
+# == A More Realistic Example
+#
+# A very simple `make' like tool can be implemented as follows:
+#
+# require 'rubygems/tsort/lib/tsort'
+#
+# class Make
+# def initialize
+# @dep = {}
+# @dep.default = []
+# end
+#
+# def rule(outputs, inputs=[], &block)
+# triple = [outputs, inputs, block]
+# outputs.each {|f| @dep[f] = [triple]}
+# @dep[triple] = inputs
+# end
+#
+# def build(target)
+# each_strongly_connected_component_from(target) {|ns|
+# if ns.length != 1
+# fs = ns.delete_if {|n| Array === n}
+# raise Gem::TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
+# end
+# n = ns.first
+# if Array === n
+# outputs, inputs, block = n
+# inputs_time = inputs.map {|f| File.mtime f}.max
+# begin
+# outputs_time = outputs.map {|f| File.mtime f}.min
+# rescue Errno::ENOENT
+# outputs_time = nil
+# end
+# if outputs_time == nil ||
+# inputs_time != nil && outputs_time <= inputs_time
+# sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i
+# block.call
+# end
+# end
+# }
+# end
+#
+# def tsort_each_child(node, &block)
+# @dep[node].each(&block)
+# end
+# include Gem::TSort
+# end
+#
+# def command(arg)
+# print arg, "\n"
+# system arg
+# end
+#
+# m = Make.new
+# m.rule(%w[t1]) { command 'date > t1' }
+# m.rule(%w[t2]) { command 'date > t2' }
+# m.rule(%w[t3]) { command 'date > t3' }
+# m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' }
+# m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' }
+# m.build('t5')
+#
+# == Bugs
+#
+# * 'tsort.rb' is wrong name because this library uses
+# Tarjan's algorithm for strongly connected components.
+# Although 'strongly_connected_components.rb' is correct but too long.
+#
+# == References
+#
+# R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
+# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
+#
+
+module Gem
+ module TSort
+ class Cyclic < StandardError
+ end
+
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # p graph.tsort #=> [4, 2, 3, 1]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # p graph.tsort # raises Gem::TSort::Cyclic
+ #
+ def tsort
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.tsort(each_node, each_child)
+ end
+
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # 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) }
+ # p Gem::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.tsort(each_node, each_child) # raises Gem::TSort::Cyclic
+ #
+ def TSort.tsort(each_node, each_child)
+ Gem::TSort.tsort_each(each_node, each_child).to_a
+ end
+
+ # The iterator version of the #tsort method.
+ # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #tsort_each returns +nil+.
+ # If there is a cycle, Gem::TSort::Cyclic is raised.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.tsort_each {|n| p n }
+ # #=> 4
+ # # 2
+ # # 3
+ # # 1
+ #
+ def tsort_each(&block) # :yields: node
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.tsort_each(each_node, each_child, &block)
+ end
+
+ # The iterator version of the Gem::TSort.tsort method.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # 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) }
+ # Gem::TSort.tsort_each(each_node, each_child) {|n| p n }
+ # #=> 4
+ # # 2
+ # # 3
+ # # 1
+ #
+ def TSort.tsort_each(each_node, each_child) # :yields: node
+ return to_enum(__method__, each_node, each_child) unless block_given?
+
+ Gem::TSort.each_strongly_connected_component(each_node, each_child) {|component|
+ if component.size == 1
+ yield component.first
+ else
+ raise Cyclic.new("topological sort failed: #{component.inspect}")
+ end
+ }
+ end
+
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
+ #
+ def strongly_connected_components
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.strongly_connected_components(each_node, each_child)
+ end
+
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # 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) }
+ # p Gem::TSort.strongly_connected_components(each_node, each_child)
+ # #=> [[4], [2], [3], [1]]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # p Gem::TSort.strongly_connected_components(each_node, each_child)
+ # #=> [[4], [2, 3], [1]]
+ #
+ def TSort.strongly_connected_components(each_node, each_child)
+ Gem::TSort.each_strongly_connected_component(each_node, each_child).to_a
+ end
+
+ # The iterator version of the #strongly_connected_components method.
+ # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
+ # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #each_strongly_connected_component returns +nil+.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.each_strongly_connected_component {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ # # [3]
+ # # [1]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # graph.each_strongly_connected_component {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def each_strongly_connected_component(&block) # :yields: nodes
+ each_node = method(:tsort_each_node)
+ each_child = method(:tsort_each_child)
+ Gem::TSort.each_strongly_connected_component(each_node, each_child, &block)
+ end
+
+ # The iterator version of the Gem::TSort.strongly_connected_components method.
+ #
+ # The graph is represented by _each_node_ and _each_child_.
+ # _each_node_ should have +call+ method which yields for each node in the graph.
+ # _each_child_ should have +call+ method which takes a node argument and yields for each child node.
+ #
+ # 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) }
+ # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ # # [3]
+ # # [1]
+ #
+ # g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_node = lambda {|&b| g.each_key(&b) }
+ # each_child = lambda {|n, &b| g[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def TSort.each_strongly_connected_component(each_node, each_child) # :yields: nodes
+ return to_enum(__method__, each_node, each_child) unless block_given?
+
+ id_map = {}
+ stack = []
+ each_node.call {|node|
+ unless id_map.include? node
+ Gem::TSort.each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
+ yield c
+ }
+ end
+ }
+ nil
+ end
+
+ # Iterates over strongly connected component in the subgraph reachable from
+ # _node_.
+ #
+ # Return value is unspecified.
+ #
+ # #each_strongly_connected_component_from doesn't call #tsort_each_node.
+ #
+ # class G
+ # include Gem::TSort
+ # def initialize(g)
+ # @g = g
+ # end
+ # def tsort_each_child(n, &b) @g[n].each(&b) end
+ # def tsort_each_node(&b) @g.each_key(&b) end
+ # end
+ #
+ # graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
+ # graph.each_strongly_connected_component_from(2) {|scc| p scc }
+ # #=> [4]
+ # # [2]
+ #
+ # graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
+ # graph.each_strongly_connected_component_from(2) {|scc| p scc }
+ # #=> [4]
+ # # [2, 3]
+ #
+ def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
+ Gem::TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
+ end
+
+ # Iterates over strongly connected components in a graph.
+ # The graph is represented by _node_ and _each_child_.
+ #
+ # _node_ is the first node.
+ # _each_child_ should have +call+ method which takes a node argument
+ # and yields for each child node.
+ #
+ # Return value is unspecified.
+ #
+ # #Gem::TSort.each_strongly_connected_component_from is a class method and
+ # it doesn't need a class to represent a graph which includes Gem::TSort.
+ #
+ # graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
+ # each_child = lambda {|n, &b| graph[n].each(&b) }
+ # Gem::TSort.each_strongly_connected_component_from(1, each_child) {|scc|
+ # p scc
+ # }
+ # #=> [4]
+ # # [2, 3]
+ # # [1]
+ #
+ def TSort.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
+ return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
+
+ minimum_id = node_id = id_map[node] = id_map.size
+ stack_length = stack.length
+ stack << node
+
+ each_child.call(node) {|child|
+ if id_map.include? child
+ child_id = id_map[child]
+ minimum_id = child_id if child_id && child_id < minimum_id
+ else
+ sub_minimum_id =
+ Gem::TSort.each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
+ yield c
+ }
+ minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
+ end
+ }
+
+ if node_id == minimum_id
+ component = stack.slice!(stack_length .. -1)
+ component.each {|n| id_map[n] = nil}
+ yield component
+ end
+
+ minimum_id
+ end
+
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_node is used to iterate for all nodes over a graph.
+ #
+ def tsort_each_node # :yields: node
+ raise NotImplementedError.new
+ end
+
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_child is used to iterate for child nodes of _node_.
+ #
+ def tsort_each_child(node) # :yields: child
+ raise NotImplementedError.new
+ end
+ end
+end
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index 51ac3494f3..1c3bf260c2 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -6,11 +6,11 @@
#++
require 'fileutils'
-require 'rubygems'
-require 'rubygems/installer_uninstaller_utils'
-require 'rubygems/dependency_list'
-require 'rubygems/rdoc'
-require 'rubygems/user_interaction'
+require_relative '../rubygems'
+require_relative 'installer_uninstaller_utils'
+require_relative 'dependency_list'
+require_relative 'rdoc'
+require_relative 'user_interaction'
##
# An Uninstaller.
@@ -70,6 +70,9 @@ class Gem::Uninstaller
# only add user directory if install_dir is not set
@user_install = false
@user_install = options[:user_install] unless options[:install_dir]
+
+ # Optimization: populated during #uninstall
+ @default_specs_matching_uninstall_params = []
end
##
@@ -98,10 +101,8 @@ class Gem::Uninstaller
default_specs, list = list.partition do |spec|
spec.default_gem?
end
-
- default_specs.each do |default_spec|
- say "Gem #{default_spec.full_name} cannot be uninstalled because it is a default gem"
- end
+ warn_cannot_uninstall_default_gems(default_specs - list)
+ @default_specs_matching_uninstall_params = default_specs
list, other_repo_specs = list.partition do |spec|
@gem_home == spec.base_dir or
@@ -270,7 +271,7 @@ class Gem::Uninstaller
end
safe_delete { FileUtils.rm_r gemspec }
- say "Successfully uninstalled #{spec.full_name}"
+ announce_deletion_of(spec)
Gem::Specification.reset
end
@@ -356,7 +357,7 @@ class Gem::Uninstaller
# of what it did for us to find rather than trying to recreate
# it again.
if @format_executable
- require 'rubygems/installer'
+ require_relative 'installer'
Gem::Installer.exec_format % File.basename(filename)
else
filename
@@ -373,4 +374,34 @@ class Gem::Uninstaller
raise e
end
+
+ private
+
+ def announce_deletion_of(spec)
+ name = spec.full_name
+ say "Successfully uninstalled #{name}"
+ if default_spec_matches?(spec)
+ say(
+ "There was both a regular copy and a default copy of #{name}. The " \
+ "regular copy was successfully uninstalled, but the default copy " \
+ "was left around because default gems can't be removed."
+ )
+ end
+ end
+
+ # @return true if the specs of any default gems are `==` to the given `spec`.
+ def default_spec_matches?(spec)
+ !default_specs_that_match(spec).empty?
+ end
+
+ # @return [Array] specs of default gems that are `==` to the given `spec`.
+ def default_specs_that_match(spec)
+ @default_specs_matching_uninstall_params.select {|default_spec| spec == default_spec }
+ end
+
+ def warn_cannot_uninstall_default_gems(specs)
+ specs.each do |spec|
+ say "Gem #{spec.full_name} cannot be uninstalled because it is a default gem"
+ end
+ end
end
diff --git a/lib/rubygems/uri.rb b/lib/rubygems/uri.rb
new file mode 100644
index 0000000000..ba30fac2f5
--- /dev/null
+++ b/lib/rubygems/uri.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+##
+# The Uri handles rubygems source URIs.
+#
+
+class Gem::Uri
+ def initialize(source_uri)
+ @parsed_uri = parse(source_uri)
+ end
+
+ def redacted
+ return self unless valid_uri?
+
+ if token? || oauth_basic?
+ with_redacted_user
+ elsif password?
+ with_redacted_password
+ else
+ self
+ end
+ end
+
+ def to_s
+ @parsed_uri.to_s
+ end
+
+ def redact_credentials_from(text)
+ return text unless valid_uri? && password?
+
+ text.sub(password, 'REDACTED')
+ end
+
+ def method_missing(method_name, *args, &blk)
+ if @parsed_uri.respond_to?(method_name)
+ @parsed_uri.send(method_name, *args, &blk)
+ else
+ super
+ end
+ end
+
+ def respond_to_missing?(method_name, include_private = false)
+ @parsed_uri.respond_to?(method_name, include_private) || super
+ end
+
+ protected
+
+ # Add a protected reader for the cloned instance to access the original object's parsed uri
+ attr_reader :parsed_uri
+
+ private
+
+ ##
+ # Parses the #uri, raising if it's invalid
+
+ def parse!(uri)
+ require "uri"
+
+ raise URI::InvalidURIError unless uri
+
+ # Always escape URI's to deal with potential spaces and such
+ # It should also be considered that source_uri may already be
+ # a valid URI with escaped characters. e.g. "{DESede}" is encoded
+ # as "%7BDESede%7D". If this is escaped again the percentage
+ # symbols will be escaped.
+ begin
+ URI.parse(uri)
+ rescue URI::InvalidURIError
+ URI.parse(URI::DEFAULT_PARSER.escape(uri))
+ end
+ end
+
+ ##
+ # Parses the #uri, returning the original uri if it's invalid
+
+ def parse(uri)
+ return uri unless uri.is_a?(String)
+
+ parse!(uri)
+ rescue URI::InvalidURIError
+ uri
+ end
+
+ def with_redacted_user
+ clone.tap {|uri| uri.user = 'REDACTED' }
+ end
+
+ def with_redacted_password
+ clone.tap {|uri| uri.password = 'REDACTED' }
+ end
+
+ def valid_uri?
+ !@parsed_uri.is_a?(String)
+ end
+
+ def password?
+ !!password
+ end
+
+ def oauth_basic?
+ password == 'x-oauth-basic'
+ end
+
+ def token?
+ !user.nil? && password.nil?
+ end
+
+ def initialize_copy(original)
+ @parsed_uri = original.parsed_uri.clone
+ end
+end
diff --git a/lib/rubygems/uri_parser.rb b/lib/rubygems/uri_parser.rb
deleted file mode 100644
index f350edec8c..0000000000
--- a/lib/rubygems/uri_parser.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-##
-# The UriParser handles parsing URIs.
-#
-
-class Gem::UriParser
- ##
- # Parses the #uri, raising if it's invalid
-
- def parse!(uri)
- raise URI::InvalidURIError unless uri
-
- # Always escape URI's to deal with potential spaces and such
- # It should also be considered that source_uri may already be
- # a valid URI with escaped characters. e.g. "{DESede}" is encoded
- # as "%7BDESede%7D". If this is escaped again the percentage
- # symbols will be escaped.
- begin
- URI.parse(uri)
- rescue URI::InvalidURIError
- URI.parse(URI::DEFAULT_PARSER.escape(uri))
- end
- end
-
- ##
- # Parses the #uri, returning the original uri if it's invalid
-
- def parse(uri)
- parse!(uri)
- rescue URI::InvalidURIError
- uri
- end
-end
diff --git a/lib/rubygems/uri_parsing.rb b/lib/rubygems/uri_parsing.rb
deleted file mode 100644
index 941d7e023a..0000000000
--- a/lib/rubygems/uri_parsing.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-require "rubygems/uri_parser"
-
-module Gem::UriParsing
-
- def parse_uri(source_uri)
- return source_uri unless source_uri.is_a?(String)
-
- uri_parser.parse(source_uri)
- end
-
- private :parse_uri
-
- def uri_parser
- require "uri"
-
- Gem::UriParser.new
- end
-
- private :uri_parser
-
-end
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index 27a9957117..0ab44fbf6c 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -5,8 +5,8 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems/deprecate'
-require 'rubygems/text'
+require_relative 'deprecate'
+require_relative 'text'
##
# Module that defines the default UserInteraction. Any class including this
@@ -543,7 +543,7 @@ class Gem::StreamUI
# A progress reporter that behaves nicely with threaded downloading.
class ThreadedDownloadReporter
- MUTEX = Mutex.new
+ MUTEX = Thread::Mutex.new
##
# The current file name being displayed
diff --git a/lib/rubygems/util.rb b/lib/rubygems/util.rb
index 2a55305172..4363c5adce 100644
--- a/lib/rubygems/util.rb
+++ b/lib/rubygems/util.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/deprecate'
+require_relative 'deprecate'
##
# This module contains various utility methods as module methods.
diff --git a/lib/rubygems/util/licenses.rb b/lib/rubygems/util/licenses.rb
index 29bf310ea0..3f4178c6e0 100644
--- a/lib/rubygems/util/licenses.rb
+++ b/lib/rubygems/util/licenses.rb
@@ -1,10 +1,11 @@
# frozen_string_literal: true
-require 'rubygems/text'
+require_relative '../text'
class Gem::Licenses
extend Gem::Text
NONSTANDARD = 'Nonstandard'.freeze
+ LICENSE_REF = 'LicenseRef-.+'.freeze
# Software Package Data Exchange (SPDX) standard open-source software
# license identifiers
@@ -18,6 +19,8 @@ class Gem::Licenses
AFL-2.1
AFL-3.0
AGPL-1.0
+ AGPL-1.0-only
+ AGPL-1.0-or-later
AGPL-3.0
AGPL-3.0-only
AGPL-3.0-or-later
@@ -25,6 +28,7 @@ class Gem::Licenses
AML
AMPAS
ANTLR-PD
+ ANTLR-PD-fallback
APAFML
APL-1.0
APSL-1.0
@@ -48,29 +52,41 @@ class Gem::Licenses
BSD-2-Clause-FreeBSD
BSD-2-Clause-NetBSD
BSD-2-Clause-Patent
+ BSD-2-Clause-Views
BSD-3-Clause
BSD-3-Clause-Attribution
BSD-3-Clause-Clear
BSD-3-Clause-LBNL
+ BSD-3-Clause-Modification
+ BSD-3-Clause-No-Military-License
BSD-3-Clause-No-Nuclear-License
BSD-3-Clause-No-Nuclear-License-2014
BSD-3-Clause-No-Nuclear-Warranty
+ BSD-3-Clause-Open-MPI
BSD-4-Clause
+ BSD-4-Clause-Shortened
BSD-4-Clause-UC
BSD-Protection
BSD-Source-Code
BSL-1.0
+ BUSL-1.1
Bahyph
Barr
Beerware
BitTorrent-1.0
BitTorrent-1.1
+ BlueOak-1.0.0
Borceux
+ C-UDA-1.0
+ CAL-1.0
+ CAL-1.0-Combined-Work-Exception
CATOSL-1.1
CC-BY-1.0
CC-BY-2.0
CC-BY-2.5
CC-BY-3.0
+ CC-BY-3.0-AT
+ CC-BY-3.0-US
CC-BY-4.0
CC-BY-NC-1.0
CC-BY-NC-2.0
@@ -81,6 +97,7 @@ class Gem::Licenses
CC-BY-NC-ND-2.0
CC-BY-NC-ND-2.5
CC-BY-NC-ND-3.0
+ CC-BY-NC-ND-3.0-IGO
CC-BY-NC-ND-4.0
CC-BY-NC-SA-1.0
CC-BY-NC-SA-2.0
@@ -94,12 +111,17 @@ class Gem::Licenses
CC-BY-ND-4.0
CC-BY-SA-1.0
CC-BY-SA-2.0
+ CC-BY-SA-2.0-UK
+ CC-BY-SA-2.1-JP
CC-BY-SA-2.5
CC-BY-SA-3.0
+ CC-BY-SA-3.0-AT
CC-BY-SA-4.0
+ CC-PDDC
CC0-1.0
CDDL-1.0
CDDL-1.1
+ CDL-1.0
CDLA-Permissive-1.0
CDLA-Sharing-1.0
CECILL-1.0
@@ -108,6 +130,11 @@ class Gem::Licenses
CECILL-2.1
CECILL-B
CECILL-C
+ CERN-OHL-1.1
+ CERN-OHL-1.2
+ CERN-OHL-P-2.0
+ CERN-OHL-S-2.0
+ CERN-OHL-W-2.0
CNRI-Jython
CNRI-Python
CNRI-Python-GPL-Compatible
@@ -123,12 +150,14 @@ class Gem::Licenses
Cube
D-FSL-1.0
DOC
+ DRL-1.0
DSDP
Dotseqn
ECL-1.0
ECL-2.0
EFL-1.0
EFL-2.0
+ EPICS
EPL-1.0
EPL-2.0
EUDatagrid
@@ -144,17 +173,32 @@ class Gem::Licenses
FTL
Fair
Frameworx-1.0
+ FreeBSD-DOC
FreeImage
+ GD
GFDL-1.1
+ GFDL-1.1-invariants-only
+ GFDL-1.1-invariants-or-later
+ GFDL-1.1-no-invariants-only
+ GFDL-1.1-no-invariants-or-later
GFDL-1.1-only
GFDL-1.1-or-later
GFDL-1.2
+ GFDL-1.2-invariants-only
+ GFDL-1.2-invariants-or-later
+ GFDL-1.2-no-invariants-only
+ GFDL-1.2-no-invariants-or-later
GFDL-1.2-only
GFDL-1.2-or-later
GFDL-1.3
+ GFDL-1.3-invariants-only
+ GFDL-1.3-invariants-or-later
+ GFDL-1.3-no-invariants-only
+ GFDL-1.3-no-invariants-or-later
GFDL-1.3-only
GFDL-1.3-or-later
GL2PS
+ GLWTPL
GPL-1.0
GPL-1.0+
GPL-1.0-only
@@ -178,7 +222,10 @@ class Gem::Licenses
Glide
Glulxe
HPND
+ HPND-sell-variant
+ HTMLTIDY
HaskellReport
+ Hippocratic-2.1
IBM-pibs
ICU
IJG
@@ -191,6 +238,7 @@ class Gem::Licenses
Intel
Intel-ACPI
Interbase-1.0
+ JPNIC
JSON
JasPer-2.0
LAL-1.2
@@ -221,11 +269,15 @@ class Gem::Licenses
LiLiQ-R-1.1
LiLiQ-Rplus-1.1
Libpng
+ Linux-OpenIB
MIT
+ MIT-0
MIT-CMU
+ MIT-Modern-Variant
MIT-advertising
MIT-enna
MIT-feh
+ MIT-open-group
MITNFA
MPL-1.0
MPL-1.1
@@ -237,12 +289,18 @@ class Gem::Licenses
MakeIndex
MirOS
Motosoto
+ MulanPSL-1.0
+ MulanPSL-2.0
Multics
Mup
+ NAIST-2003
NASA-1.3
NBPL-1.0
+ NCGL-UK-2.0
NCSA
NGPL
+ NIST-PD
+ NIST-PD-fallback
NLOD-1.0
NLPL
NOSL
@@ -251,6 +309,7 @@ class Gem::Licenses
NPOSL-3.0
NRL
NTP
+ NTP-0
Naumen
Net-SNMP
NetCDF
@@ -258,11 +317,23 @@ class Gem::Licenses
Nokia
Noweb
Nunit
+ O-UDA-1.0
OCCT-PL
OCLC-2.0
+ ODC-By-1.0
ODbL-1.0
OFL-1.0
+ OFL-1.0-RFN
+ OFL-1.0-no-RFN
OFL-1.1
+ OFL-1.1-RFN
+ OFL-1.1-no-RFN
+ OGC-1.0
+ OGDL-Taiwan-1.0
+ OGL-Canada-2.0
+ OGL-UK-1.0
+ OGL-UK-2.0
+ OGL-UK-3.0
OGTSL
OLDAP-1.1
OLDAP-1.2
@@ -292,7 +363,12 @@ class Gem::Licenses
PDDL-1.0
PHP-3.0
PHP-3.01
+ PSF-2.0
+ Parity-6.0.0
+ Parity-7.0.0
Plexus
+ PolyForm-Noncommercial-1.0.0
+ PolyForm-Small-Business-1.0.0
PostgreSQL
Python-2.0
QPL-1.0
@@ -310,15 +386,21 @@ class Gem::Licenses
SGI-B-1.0
SGI-B-1.1
SGI-B-2.0
+ SHL-0.5
+ SHL-0.51
SISSL
SISSL-1.2
SMLNJ
SMPPL
SNIA
SPL-1.0
+ SSH-OpenSSH
+ SSH-short
+ SSPL-1.0
SWL
Saxpath
Sendmail
+ Sendmail-8.23
SimPL-2.0
Sleepycat
Spencer-86
@@ -326,11 +408,15 @@ class Gem::Licenses
Spencer-99
StandardML-NJ
SugarCRM-1.1.3
+ TAPR-OHL-1.0
TCL
TCP-wrappers
TMate
TORQUE-1.1
TOSL
+ TU-Berlin-1.0
+ TU-Berlin-2.0
+ UCL-1.0
UPL-1.0
Unicode-DFS-2015
Unicode-DFS-2016
@@ -360,16 +446,22 @@ class Gem::Licenses
Zimbra-1.3
Zimbra-1.4
Zlib
+ blessing
bzip2-1.0.5
bzip2-1.0.6
+ copyleft-next-0.3.0
+ copyleft-next-0.3.1
curl
diffmark
dvipdfm
eCos-2.0
eGenix
+ etalab-2.0
gSOAP-1.3b
gnuplot
iMatix
+ libpng-2.0
+ libselinux-1.0
libtiff
mpich2
psfrag
@@ -395,12 +487,26 @@ class Gem::Licenses
Font-exception-2.0
GCC-exception-2.0
GCC-exception-3.1
+ GPL-3.0-linking-exception
+ GPL-3.0-linking-source-exception
+ GPL-CC-1.0
+ LGPL-3.0-linking-exception
+ LLVM-exception
LZMA-exception
Libtool-exception
Linux-syscall-note
Nokia-Qt-exception-1.1
OCCT-exception-1.0
+ OCaml-LGPL-linking-exception
+ OpenJDK-assembly-exception-1.0
+ PS-or-PDF-font-exception-20170817
+ Qt-GPL-exception-1.0
+ Qt-LGPL-exception-1.1
Qwt-exception-1.0
+ SHL-2.0
+ SHL-2.1
+ Swift-exception
+ Universal-FOSS-exception-1.0
WxWindows-exception-3.1
eCos-exception-2.0
freertos-exception-2.0
@@ -413,11 +519,12 @@ class Gem::Licenses
REGEXP = %r{
\A
- (
+ (?:
#{Regexp.union(LICENSE_IDENTIFIERS)}
\+?
- (\s WITH \s #{Regexp.union(EXCEPTION_IDENTIFIERS)})?
+ (?:\s WITH \s #{Regexp.union(EXCEPTION_IDENTIFIERS)})?
| #{NONSTANDARD}
+ | #{LICENSE_REF}
)
\Z
}ox.freeze
diff --git a/lib/rubygems/validator.rb b/lib/rubygems/validator.rb
index 30cdd93b5c..728595e778 100644
--- a/lib/rubygems/validator.rb
+++ b/lib/rubygems/validator.rb
@@ -5,8 +5,8 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems/package'
-require 'rubygems/installer'
+require_relative 'package'
+require_relative 'installer'
##
# Validator performs various gem file and gem database validation
diff --git a/lib/rubygems/version_option.rb b/lib/rubygems/version_option.rb
index be71ef409b..1db382fa7f 100644
--- a/lib/rubygems/version_option.rb
+++ b/lib/rubygems/version_option.rb
@@ -5,7 +5,7 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems'
+require_relative '../rubygems'
##
# Mixin methods for --version and --platform Gem::Command options.
@@ -16,7 +16,7 @@ module Gem::VersionOption
# Add the --platform option to the option parser.
def add_platform_option(task = command, *wrap)
- OptionParser.accept Gem::Platform do |value|
+ Gem::OptionParser.accept Gem::Platform do |value|
if value == Gem::Platform::RUBY
value
else
@@ -51,7 +51,7 @@ module Gem::VersionOption
# Add the --version option to the option parser.
def add_version_option(task = command, *wrap)
- OptionParser.accept Gem::Requirement do |value|
+ Gem::OptionParser.accept Gem::Requirement do |value|
Gem::Requirement.new(*value.split(/\s*,\s*/))
end
diff --git a/lib/time.gemspec b/lib/time.gemspec
index 040b9f34cf..be6de35c99 100644
--- a/lib/time.gemspec
+++ b/lib/time.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "time"
- spec.version = "0.1.0"
+ spec.version = "0.1.1"
spec.authors = ["Tanaka Akira"]
spec.email = ["akr@fsij.org"]
@@ -19,6 +19,4 @@ Gem::Specification.new do |spec|
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
-
- spec.add_dependency "date"
end
diff --git a/lib/time.rb b/lib/time.rb
index 625c2c87bb..8af482cb68 100644
--- a/lib/time.rb
+++ b/lib/time.rb
@@ -506,8 +506,8 @@ class Time
(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+
(\d{2,})\s+
(\d{2})\s*
- :\s*(\d{2})\s*
- (?::\s*(\d{2}))?\s+
+ :\s*(\d{2})
+ (?:\s*:\s*(\d\d))?\s+
([+-]\d{4}|
UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Z])/ix =~ date
# Since RFC 2822 permit comments, the regexp has no right anchor.
diff --git a/lib/tmpdir.gemspec b/lib/tmpdir.gemspec
index de30665507..7b76403002 100644
--- a/lib/tmpdir.gemspec
+++ b/lib/tmpdir.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "tmpdir"
- spec.version = "0.1.1"
+ spec.version = "0.1.2"
spec.authors = ["Yukihiro Matsumoto"]
spec.email = ["matz@ruby-lang.org"]
diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb
index 0b1f00aecf..bf7db5282a 100644
--- a/lib/tmpdir.rb
+++ b/lib/tmpdir.rb
@@ -115,7 +115,7 @@ class Dir
Dir.tmpdir
end
- UNUSABLE_CHARS = [File::SEPARATOR, File::ALT_SEPARATOR, File::PATH_SEPARATOR, ":"].uniq.join("").freeze
+ UNUSABLE_CHARS = "^,-.0-9A-Z_a-z~"
class << (RANDOM = Random.new)
MAX = 36**6 # < 0x100000000
diff --git a/lib/uri/rfc2396_parser.rb b/lib/uri/rfc2396_parser.rb
index c719aa0726..253c54b786 100644
--- a/lib/uri/rfc2396_parser.rb
+++ b/lib/uri/rfc2396_parser.rb
@@ -491,8 +491,8 @@ module URI
ret = {}
# for URI::split
- ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
- ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
# for URI::extract
ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb
index 49a594c17d..5a9e44e08c 100644
--- a/lib/uri/rfc3986_parser.rb
+++ b/lib/uri/rfc3986_parser.rb
@@ -3,8 +3,8 @@ module URI
class RFC3986_Parser # :nodoc:
# URI defined in RFC3986
# this regexp is modified not to host is not empty string
- RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
- RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
+ RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*+):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
+ RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?<port>\d*+))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g<segment>)*+)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g<segment>)*+)|(?<path-empty>))(?:\?(?<query>[^#]*+))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/
attr_reader :regexp
def initialize
@@ -95,7 +95,7 @@ module URI
QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
OPAQUE: /\A(?:[^\/].*)?\z/,
- PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/,
+ PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
}
end
diff --git a/lib/uri/version.rb b/lib/uri/version.rb
index 41da51d844..fb195ee686 100644
--- a/lib/uri/version.rb
+++ b/lib/uri/version.rb
@@ -1,6 +1,6 @@
module URI
# :stopdoc:
- VERSION_CODE = '001001'.freeze
+ VERSION_CODE = '001003'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
# :startdoc:
end
diff --git a/load.c b/load.c
index cf3adcbb3e..3dcc40bbe9 100644
--- a/load.c
+++ b/load.c
@@ -35,6 +35,11 @@ static const char *const loadable_ext[] = {
0
};
+static const char *const ruby_ext[] = {
+ ".rb",
+ 0
+};
+
enum expand_type {
EXPAND_ALL,
EXPAND_RELATIVE,
@@ -185,8 +190,17 @@ feature_key(const char *str, size_t len)
return st_hash(str, len, 0xfea7009e);
}
+static bool
+is_rbext_path(VALUE feature_path)
+{
+ long len = RSTRING_LEN(feature_path);
+ long rbext_len = rb_strlen_lit(".rb");
+ if (len <= rbext_len) return false;
+ return IS_RBEXT(RSTRING_PTR(feature_path) + len - rbext_len);
+}
+
static void
-features_index_add_single(const char* str, size_t len, VALUE offset)
+features_index_add_single(const char* str, size_t len, VALUE offset, bool rb)
{
struct st_table *features_index;
VALUE this_feature_index = Qnil;
@@ -202,17 +216,43 @@ features_index_add_single(const char* str, size_t len, VALUE offset)
st_insert(features_index, short_feature_key, (st_data_t)offset);
}
else if (RB_TYPE_P(this_feature_index, T_FIXNUM)) {
+ VALUE loaded_features = get_loaded_features();
+ VALUE this_feature_path = RARRAY_AREF(loaded_features, FIX2LONG(this_feature_index));
VALUE feature_indexes[2];
- feature_indexes[0] = this_feature_index;
- feature_indexes[1] = offset;
+ int top = (rb && !is_rbext_path(this_feature_path)) ? 1 : 0;
+ feature_indexes[top^0] = this_feature_index;
+ feature_indexes[top^1] = offset;
this_feature_index = (VALUE)xcalloc(1, sizeof(struct RArray));
RBASIC(this_feature_index)->flags = T_ARRAY; /* fake VALUE, do not mark/sweep */
rb_ary_cat(this_feature_index, feature_indexes, numberof(feature_indexes));
st_insert(features_index, short_feature_key, (st_data_t)this_feature_index);
}
else {
+ long pos = -1;
+
Check_Type(this_feature_index, T_ARRAY);
+ if (rb) {
+ VALUE loaded_features = get_loaded_features();
+ for (long i = 0; i < RARRAY_LEN(this_feature_index); ++i) {
+ VALUE idx = RARRAY_AREF(this_feature_index, i);
+ VALUE this_feature_path = RARRAY_AREF(loaded_features, FIX2LONG(idx));
+ Check_Type(this_feature_path, T_STRING);
+ if (!is_rbext_path(this_feature_path)) {
+ /* as this_feature_index is a fake VALUE, `push` (which
+ * doesn't wb_unprotect like as rb_ary_splice) first,
+ * then rotate partially. */
+ pos = i;
+ break;
+ }
+ }
+ }
rb_ary_push(this_feature_index, offset);
+ if (pos >= 0) {
+ VALUE *ptr = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(this_feature_index);
+ long len = RARRAY_LEN(this_feature_index);
+ MEMMOVE(ptr + pos, ptr + pos + 1, VALUE, len - pos - 1);
+ ptr[pos] = offset;
+ }
}
}
@@ -228,6 +268,7 @@ static void
features_index_add(VALUE feature, VALUE offset)
{
const char *feature_str, *feature_end, *ext, *p;
+ bool rb = false;
feature_str = StringValuePtr(feature);
feature_end = feature_str + RSTRING_LEN(feature);
@@ -237,6 +278,8 @@ features_index_add(VALUE feature, VALUE offset)
break;
if (*ext != '.')
ext = NULL;
+ else
+ rb = IS_RBEXT(ext);
/* Now `ext` points to the only string matching %r{^\.[^./]*$} that is
at the end of `feature`, or is NULL if there is no such string. */
@@ -248,14 +291,14 @@ features_index_add(VALUE feature, VALUE offset)
if (p < feature_str)
break;
/* Now *p == '/'. We reach this point for every '/' in `feature`. */
- features_index_add_single(p + 1, feature_end - p - 1, offset);
+ features_index_add_single(p + 1, feature_end - p - 1, offset, false);
if (ext) {
- features_index_add_single(p + 1, ext - p - 1, offset);
+ features_index_add_single(p + 1, ext - p - 1, offset, rb);
}
}
- features_index_add_single(feature_str, feature_end - feature_str, offset);
+ features_index_add_single(feature_str, feature_end - feature_str, offset, false);
if (ext) {
- features_index_add_single(feature_str, ext - feature_str, offset);
+ features_index_add_single(feature_str, ext - feature_str, offset, rb);
}
}
@@ -563,6 +606,7 @@ rb_provide_feature(VALUE feature)
}
rb_str_freeze(feature);
+ get_loaded_features_index();
rb_ary_push(features, rb_fstring(feature));
features_index_add(feature, INT2FIX(RARRAY_LEN(features)-1));
reset_loaded_features_snapshot();
@@ -926,7 +970,7 @@ search_required(VALUE fname, volatile VALUE *path, feature_func rb_feature_p)
return 'r';
}
tmp = fname;
- type = rb_find_file_ext(&tmp, loadable_ext);
+ type = rb_find_file_ext(&tmp, ft == 's' ? ruby_ext : loadable_ext);
switch (type) {
case 0:
if (ft)
@@ -1087,7 +1131,7 @@ require_internal(rb_execution_context_t *ec, VALUE fname, int exception)
if (ftptr) load_unlock(RSTRING_PTR(path), !state);
if (state) {
- if (state == TAG_FATAL) {
+ if (state == TAG_FATAL || state == TAG_THROW) {
EC_JUMP_TAG(ec, state);
}
else if (exception) {
diff --git a/marshal.c b/marshal.c
index d629a11046..ed096f52fe 100644
--- a/marshal.c
+++ b/marshal.c
@@ -1137,6 +1137,7 @@ struct load_arg {
long offset;
st_table *symbols;
st_table *data;
+ st_table *partial_objects;
VALUE proc;
st_table *compat_tbl;
};
@@ -1163,6 +1164,7 @@ mark_load_arg(void *ptr)
return;
rb_mark_tbl(p->symbols);
rb_mark_tbl(p->data);
+ rb_mark_tbl(p->partial_objects);
rb_mark_hash(p->compat_tbl);
}
@@ -1424,9 +1426,10 @@ ruby2_keywords_flag_check(VALUE sym)
{
const char *p;
long l;
+ if (rb_enc_get_index(sym) != ENCINDEX_US_ASCII) return 0;
RSTRING_GETMEM(sym, p, l);
if (l <= 0) return 0;
- if (name_equal(name_s_ruby2_keywords_flag, rb_strlen_lit(name_s_ruby2_keywords_flag), p, 1)) {
+ if (name_equal(name_s_ruby2_keywords_flag, rb_strlen_lit(name_s_ruby2_keywords_flag), p, l)) {
return 1;
}
return 0;
@@ -1461,7 +1464,13 @@ r_symreal(struct load_arg *arg, int ivar)
idx = sym2encidx(sym, r_object(arg));
}
}
- if (idx > 0) rb_enc_associate_index(s, idx);
+ if (idx > 0) {
+ rb_enc_associate_index(s, idx);
+ if (rb_enc_str_coderange(s) == ENC_CODERANGE_BROKEN) {
+ rb_raise(rb_eArgError, "invalid byte sequence in %s: %+"PRIsVALUE,
+ rb_enc_name(rb_enc_from_index(idx)), s);
+ }
+ }
return s;
}
@@ -1509,6 +1518,7 @@ r_entry0(VALUE v, st_index_t num, struct load_arg *arg)
st_lookup(arg->compat_tbl, v, &real_obj);
}
st_insert(arg->data, num, real_obj);
+ st_insert(arg->partial_objects, (st_data_t)real_obj, Qtrue);
return v;
}
@@ -1539,10 +1549,15 @@ r_post_proc(VALUE v, struct load_arg *arg)
}
static VALUE
-r_leave(VALUE v, struct load_arg *arg)
+r_leave(VALUE v, struct load_arg *arg, bool partial)
{
v = r_fixup_compat(v, arg);
- v = r_post_proc(v, arg);
+ if (!partial) {
+ st_data_t data;
+ st_data_t key = (st_data_t)v;
+ st_delete(arg->partial_objects, &key, &data);
+ v = r_post_proc(v, arg);
+ }
return v;
}
@@ -1669,7 +1684,7 @@ append_extmod(VALUE obj, VALUE extmod)
} while (0)
static VALUE
-r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
+r_object0(struct load_arg *arg, bool partial, int *ivp, VALUE extmod)
{
VALUE v = Qnil;
int type = r_byte(arg);
@@ -1683,15 +1698,18 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
rb_raise(rb_eArgError, "dump format error (unlinked)");
}
v = (VALUE)link;
- v = r_post_proc(v, arg);
+ if (!st_lookup(arg->partial_objects, (st_data_t)v, &link)) {
+ v = r_post_proc(v, arg);
+ }
break;
case TYPE_IVAR:
{
int ivar = TRUE;
- v = r_object0(arg, &ivar, extmod);
+ v = r_object0(arg, true, &ivar, extmod);
if (ivar) r_ivar(v, NULL, arg);
+ v = r_leave(v, arg, partial);
}
break;
@@ -1704,7 +1722,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
if (RB_TYPE_P(m, T_CLASS)) { /* prepended */
VALUE c;
- v = r_object0(arg, 0, Qnil);
+ v = r_object0(arg, true, 0, Qnil);
c = CLASS_OF(v);
if (c != m || FL_TEST(c, FL_SINGLETON)) {
rb_raise(rb_eArgError,
@@ -1721,7 +1739,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
must_be_module(m, path);
rb_ary_push(extmod, m);
- v = r_object0(arg, 0, extmod);
+ v = r_object0(arg, true, 0, extmod);
while (RARRAY_LEN(extmod) > 0) {
m = rb_ary_pop(extmod);
rb_extend_object(v, m);
@@ -1737,7 +1755,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
if (FL_TEST(c, FL_SINGLETON)) {
rb_raise(rb_eTypeError, "singleton can't be loaded");
}
- v = r_object0(arg, 0, extmod);
+ v = r_object0(arg, partial, 0, extmod);
if (rb_special_const_p(v) || RB_TYPE_P(v, T_OBJECT) || RB_TYPE_P(v, T_CLASS)) {
goto format_error;
}
@@ -1755,17 +1773,17 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
case TYPE_NIL:
v = Qnil;
- v = r_leave(v, arg);
+ v = r_leave(v, arg, false);
break;
case TYPE_TRUE:
v = Qtrue;
- v = r_leave(v, arg);
+ v = r_leave(v, arg, false);
break;
case TYPE_FALSE:
v = Qfalse;
- v = r_leave(v, arg);
+ v = r_leave(v, arg, false);
break;
case TYPE_FIXNUM:
@@ -1773,7 +1791,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
long i = r_long(arg);
v = LONG2FIX(i);
}
- v = r_leave(v, arg);
+ v = r_leave(v, arg, false);
break;
case TYPE_FLOAT:
@@ -1798,7 +1816,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
}
v = DBL2NUM(d);
v = r_entry(v, arg);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, false);
}
break;
@@ -1815,13 +1833,13 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
INTEGER_PACK_LITTLE_ENDIAN | (sign == '-' ? INTEGER_PACK_NEGATIVE : 0));
rb_str_resize(data, 0L);
v = r_entry(v, arg);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, false);
}
break;
case TYPE_STRING:
v = r_entry(r_string(arg), arg);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
break;
case TYPE_REGEXP:
@@ -1856,7 +1874,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
rb_str_set_len(str, dst - ptr);
}
v = r_entry0(rb_reg_new_str(str, options), idx, arg);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
}
break;
@@ -1871,7 +1889,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
rb_ary_push(v, r_object(arg));
arg->readable--;
}
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
arg->readable++;
}
break;
@@ -1894,7 +1912,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
if (type == TYPE_HASH_DEF) {
RHASH_SET_IFNONE(v, r_object(arg));
}
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
}
break;
@@ -1946,7 +1964,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
}
}
rb_struct_initialize(v, values);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
arg->readable += 2;
}
break;
@@ -1973,7 +1991,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
marshal_compat_t *compat = (marshal_compat_t*)d;
v = compat->loader(klass, v);
}
- v = r_post_proc(v, arg);
+ if (!partial) v = r_post_proc(v, arg);
}
break;
@@ -2015,7 +2033,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
}
v = r_entry0(v, idx, arg);
r_ivar(v, NULL, arg);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
}
break;
@@ -2036,9 +2054,9 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
"class %"PRIsVALUE" needs to have instance method `_load_data'",
name);
}
- r = r_object0(arg, 0, extmod);
+ r = r_object0(arg, partial, 0, extmod);
load_funcall(arg, v, s_load_data, 1, &r);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
}
break;
@@ -2049,7 +2067,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
v = rb_path_to_class(str);
prohibit_ivar("class/module", str);
v = r_entry(v, arg);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
}
break;
@@ -2060,7 +2078,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
v = path2class(str);
prohibit_ivar("class", str);
v = r_entry(v, arg);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
}
break;
@@ -2071,7 +2089,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
v = path2module(str);
prohibit_ivar("module", str);
v = r_entry(v, arg);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
}
break;
@@ -2084,7 +2102,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
v = r_symreal(arg, 0);
}
v = rb_str_intern(v);
- v = r_leave(v, arg);
+ v = r_leave(v, arg, partial);
break;
case TYPE_SYMLINK:
@@ -2106,7 +2124,7 @@ r_object0(struct load_arg *arg, int *ivp, VALUE extmod)
static VALUE
r_object(struct load_arg *arg)
{
- return r_object0(arg, 0, Qnil);
+ return r_object0(arg, false, 0, Qnil);
}
static void
@@ -2124,6 +2142,8 @@ clear_load_arg(struct load_arg *arg)
arg->symbols = 0;
st_free_table(arg->data);
arg->data = 0;
+ st_free_table(arg->partial_objects);
+ arg->partial_objects = 0;
if (arg->compat_tbl) {
st_free_table(arg->compat_tbl);
arg->compat_tbl = 0;
@@ -2178,6 +2198,7 @@ rb_marshal_load_with_proc(VALUE port, VALUE proc)
arg->offset = 0;
arg->symbols = st_init_numtable();
arg->data = rb_init_identtable();
+ arg->partial_objects = rb_init_identtable();
arg->compat_tbl = 0;
arg->proc = 0;
arg->readable = 0;
diff --git a/method.h b/method.h
index 5b6fe2d800..16a48ccf88 100644
--- a/method.h
+++ b/method.h
@@ -173,6 +173,7 @@ struct rb_method_definition_struct {
BITFIELD(rb_method_type_t, type, VM_METHOD_TYPE_MINIMUM_BITS);
int alias_count : 28;
int complemented_count : 28;
+ unsigned int no_redef_warning: 1;
union {
rb_method_iseq_t iseq;
diff --git a/missing/dtoa.c b/missing/dtoa.c
index cbee13ee81..b7a8302875 100644
--- a/missing/dtoa.c
+++ b/missing/dtoa.c
@@ -183,12 +183,16 @@
#undef Long
#undef ULong
-#if SIZEOF_INT == 4
+#include <limits.h>
+
+#if (INT_MAX >> 30) && !(INT_MAX >> 31)
#define Long int
#define ULong unsigned int
-#elif SIZEOF_LONG == 4
+#elif (LONG_MAX >> 30) && !(LONG_MAX >> 31)
#define Long long int
#define ULong unsigned long int
+#else
+#error No 32bit integer
#endif
#if HAVE_LONG_LONG
@@ -202,6 +206,11 @@
#define Bug(x) {fprintf(stderr, "%s\n", (x)); exit(EXIT_FAILURE);}
#endif
+#ifndef ISDIGIT
+#include <ctype.h>
+#define ISDIGIT(c) isdigit(c)
+#endif
+#include <errno.h>
#include <stdlib.h>
#include <string.h>
@@ -219,6 +228,9 @@ extern void FREE(void*);
#else
#define FREE xfree
#endif
+#ifndef NO_SANITIZE
+#define NO_SANITIZE(x, y) y
+#endif
#ifndef Omit_Private_Memory
#ifndef PRIVATE_MEM
@@ -280,7 +292,7 @@ extern "C" {
#endif
#ifndef hexdigit
-static const char hexdigits[] = "0123456789abcdef0123456789ABCDEF";
+static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF";
#endif
#if defined(IEEE_LITTLE_ENDIAN) + defined(IEEE_BIG_ENDIAN) + defined(VAX) + defined(IBM) != 1
@@ -489,6 +501,19 @@ extern double rnd_prod(double, double), rnd_quot(double, double);
#define FREE_DTOA_LOCK(n) /*unused right now*/
#endif
+#ifndef ATOMIC_PTR_CAS
+#define ATOMIC_PTR_CAS(var, old, new) ((var) = (new), (old))
+#endif
+#ifndef LIKELY
+#define LIKELY(x) (x)
+#endif
+#ifndef UNLIKELY
+#define UNLIKELY(x) (x)
+#endif
+#ifndef ASSUME
+#define ASSUME(x) (void)(x)
+#endif
+
#define Kmax 15
struct Bigint {
@@ -501,6 +526,8 @@ typedef struct Bigint Bigint;
static Bigint *freelist[Kmax+1];
+#define BLOCKING_BIGINT ((Bigint *)(-1))
+
static Bigint *
Balloc(int k)
{
@@ -510,22 +537,41 @@ Balloc(int k)
size_t len;
#endif
+ rv = 0;
ACQUIRE_DTOA_LOCK(0);
- if (k <= Kmax && (rv = freelist[k]) != 0) {
- freelist[k] = rv->next;
+ if (k <= Kmax) {
+ rv = freelist[k];
+ while (rv) {
+ Bigint *rvn = rv;
+ rv = ATOMIC_PTR_CAS(freelist[k], rv, BLOCKING_BIGINT);
+ if (LIKELY(rv != BLOCKING_BIGINT && rvn == rv)) {
+ rvn = ATOMIC_PTR_CAS(freelist[k], BLOCKING_BIGINT, rv->next);
+ assert(rvn == BLOCKING_BIGINT);
+ ASSUME(rv);
+ break;
+ }
+ }
}
- else {
+ if (!rv) {
x = 1 << k;
#ifdef Omit_Private_Memory
rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));
#else
len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1)
/sizeof(double);
- if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) {
- rv = (Bigint*)pmem_next;
- pmem_next += len;
+ if (k <= Kmax) {
+ double *pnext = pmem_next;
+ while (pnext - private_mem + len <= PRIVATE_mem) {
+ double *p = pnext;
+ pnext = ATOMIC_PTR_CAS(pmem_next, pnext, pnext + len);
+ if (LIKELY(p == pnext)) {
+ rv = (Bigint*)pnext;
+ ASSUME(rv);
+ break;
+ }
+ }
}
- else
+ if (!rv)
rv = (Bigint*)MALLOC(len*sizeof(double));
#endif
rv->k = k;
@@ -539,14 +585,19 @@ Balloc(int k)
static void
Bfree(Bigint *v)
{
+ Bigint *vn;
if (v) {
if (v->k > Kmax) {
FREE(v);
return;
}
ACQUIRE_DTOA_LOCK(0);
- v->next = freelist[v->k];
- freelist[v->k] = v;
+ do {
+ do {
+ vn = ATOMIC_PTR_CAS(freelist[v->k], 0, 0);
+ } while (UNLIKELY(vn == BLOCKING_BIGINT));
+ v->next = vn;
+ } while (UNLIKELY(ATOMIC_PTR_CAS(freelist[v->k], vn, v) != vn));
FREE_DTOA_LOCK(0);
}
}
@@ -829,8 +880,9 @@ static Bigint *
pow5mult(Bigint *b, int k)
{
Bigint *b1, *p5, *p51;
+ Bigint *p5tmp;
int i;
- static int p05[3] = { 5, 25, 125 };
+ static const int p05[3] = { 5, 25, 125 };
if ((i = k & 3) != 0)
b = multadd(b, p05[i-1], 0);
@@ -839,17 +891,17 @@ pow5mult(Bigint *b, int k)
return b;
if (!(p5 = p5s)) {
/* first time */
-#ifdef MULTIPLE_THREADS
ACQUIRE_DTOA_LOCK(1);
if (!(p5 = p5s)) {
- p5 = p5s = i2b(625);
+ p5 = i2b(625);
p5->next = 0;
+ p5tmp = ATOMIC_PTR_CAS(p5s, NULL, p5);
+ if (UNLIKELY(p5tmp)) {
+ Bfree(p5);
+ p5 = p5tmp;
+ }
}
FREE_DTOA_LOCK(1);
-#else
- p5 = p5s = i2b(625);
- p5->next = 0;
-#endif
}
for (;;) {
if (k & 1) {
@@ -860,17 +912,17 @@ pow5mult(Bigint *b, int k)
if (!(k >>= 1))
break;
if (!(p51 = p5->next)) {
-#ifdef MULTIPLE_THREADS
ACQUIRE_DTOA_LOCK(1);
if (!(p51 = p5->next)) {
- p51 = p5->next = mult(p5,p5);
+ p51 = mult(p5,p5);
p51->next = 0;
+ p5tmp = ATOMIC_PTR_CAS(p5->next, NULL, p51);
+ if (UNLIKELY(p5tmp)) {
+ Bfree(p51);
+ p51 = p5tmp;
+ }
}
FREE_DTOA_LOCK(1);
-#else
- p51 = p5->next = mult(p5,p5);
- p51->next = 0;
-#endif
}
p5 = p51;
}
@@ -1500,6 +1552,7 @@ break2:
if (!*++s || !(s1 = strchr(hexdigit, *s))) goto ret0;
if (*s == '0') {
while (*++s == '0');
+ if (!*s) goto ret;
s1 = strchr(hexdigit, *s);
}
if (s1 != NULL) {
@@ -1522,7 +1575,7 @@ break2:
for (; *s && (s1 = strchr(hexdigit, *s)); ++s) {
adj += aadj * ((s1 - hexdigit) & 15);
if ((aadj /= 16) == 0.0) {
- while (strchr(hexdigit, *++s));
+ while (*++s && strchr(hexdigit, *s));
break;
}
}
@@ -2520,10 +2573,10 @@ static char *dtoa_result;
static char *
rv_alloc(int i)
{
- return dtoa_result = xmalloc(i);
+ return dtoa_result = MALLOC(i);
}
#else
-#define rv_alloc(i) xmalloc(i)
+#define rv_alloc(i) MALLOC(i)
#endif
static char *
@@ -2550,7 +2603,7 @@ nrv_alloc(const char *s, char **rve, size_t n)
static void
freedtoa(char *s)
{
- xfree(s);
+ FREE(s);
}
#endif
diff --git a/mjit.c b/mjit.c
index 4a14fa7ae1..db0efb76a3 100644
--- a/mjit.c
+++ b/mjit.c
@@ -230,13 +230,13 @@ finish_conts(void)
}
}
-// Create unit for `iseq`.
+// Create unit for `iseq`. This function may be called from an MJIT worker.
static void
create_unit(const rb_iseq_t *iseq)
{
struct rb_mjit_unit *unit;
- unit = ZALLOC(struct rb_mjit_unit);
+ unit = calloc(1, sizeof(struct rb_mjit_unit));
if (unit == NULL)
return;
@@ -245,8 +245,9 @@ create_unit(const rb_iseq_t *iseq)
iseq->body->jit_unit = unit;
}
+// This is called from an MJIT worker when worker_p is true.
static void
-mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info)
+mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info, bool worker_p)
{
if (!mjit_enabled || pch_status == PCH_FAILED)
return;
@@ -260,14 +261,18 @@ mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_inf
if (compile_info != NULL)
iseq->body->jit_unit->compile_info = *compile_info;
- CRITICAL_SECTION_START(3, "in add_iseq_to_process");
+ if (!worker_p) {
+ CRITICAL_SECTION_START(3, "in add_iseq_to_process");
+ }
add_to_list(iseq->body->jit_unit, &unit_queue);
if (active_units.length >= mjit_opts.max_cache_size) {
unload_requests++;
}
- verbose(3, "Sending wakeup signal to workers in mjit_add_iseq_to_process");
- rb_native_cond_broadcast(&mjit_worker_wakeup);
- CRITICAL_SECTION_FINISH(3, "in add_iseq_to_process");
+ if (!worker_p) {
+ verbose(3, "Sending wakeup signal to workers in mjit_add_iseq_to_process");
+ rb_native_cond_broadcast(&mjit_worker_wakeup);
+ CRITICAL_SECTION_FINISH(3, "in add_iseq_to_process");
+ }
}
// Add ISEQ to be JITed in parallel with the current thread.
@@ -275,7 +280,7 @@ mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_inf
void
rb_mjit_add_iseq_to_process(const rb_iseq_t *iseq)
{
- mjit_add_iseq_to_process(iseq, NULL);
+ mjit_add_iseq_to_process(iseq, NULL, false);
}
// For this timeout seconds, --jit-wait will wait for JIT compilation finish.
@@ -334,17 +339,22 @@ mjit_recompile(const rb_iseq_t *iseq)
RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(iseq->body->location.first_lineno));
assert(iseq->body->jit_unit != NULL);
- // Lazily move active_units to stale_units to avoid race conditions around active_units with compaction
- CRITICAL_SECTION_START(3, "in rb_mjit_recompile_iseq");
- iseq->body->jit_unit->stale_p = true;
- pending_stale_p = true;
- CRITICAL_SECTION_FINISH(3, "in rb_mjit_recompile_iseq");
-
- iseq->body->jit_func = (mjit_func_t)NOT_ADDED_JIT_ISEQ_FUNC;
- mjit_add_iseq_to_process(iseq, &iseq->body->jit_unit->compile_info);
if (UNLIKELY(mjit_opts.wait)) {
+ remove_from_list(iseq->body->jit_unit, &active_units);
+ add_to_list(iseq->body->jit_unit, &stale_units);
+ mjit_add_iseq_to_process(iseq, &iseq->body->jit_unit->compile_info, false);
mjit_wait(iseq->body);
}
+ else {
+ // Lazily move active_units to stale_units to avoid race conditions around active_units with compaction.
+ // Also, it's lazily moved to unit_queue as well because otherwise it won't be added to stale_units properly.
+ // It's good to avoid a race condition between mjit_add_iseq_to_process and mjit_compile around jit_unit as well.
+ CRITICAL_SECTION_START(3, "in rb_mjit_recompile_iseq");
+ iseq->body->jit_unit->stale_p = true;
+ iseq->body->jit_func = (mjit_func_t)NOT_ADDED_JIT_ISEQ_FUNC;
+ pending_stale_p = true;
+ CRITICAL_SECTION_FINISH(3, "in rb_mjit_recompile_iseq");
+ }
}
// Recompile iseq, disabling send optimization
@@ -931,24 +941,32 @@ mjit_mark(void)
return;
RUBY_MARK_ENTER("mjit");
- if (compiling_iseq != NULL)
- rb_gc_mark((VALUE)compiling_iseq);
-
// We need to release a lock when calling rb_gc_mark to avoid doubly acquiring
// a lock by by mjit_gc_start_hook inside rb_gc_mark.
//
// Because an MJIT worker may modify active_units anytime, we need to convert
// the linked list to an array to safely loop its ISeqs without keeping a lock.
CRITICAL_SECTION_START(4, "mjit_mark");
- int length = active_units.length;
- rb_iseq_t **iseqs = ALLOCA_N(rb_iseq_t *, length);
+ int length = 0;
+ if (compiling_iseqs != NULL) {
+ while (compiling_iseqs[length]) length++;
+ }
+ length += active_units.length;
+ const rb_iseq_t **iseqs = ALLOCA_N(const rb_iseq_t *, length);
struct rb_mjit_unit *unit = NULL;
int i = 0;
+ if (compiling_iseqs != NULL) {
+ while (compiling_iseqs[i]) {
+ iseqs[i] = compiling_iseqs[i];
+ i++;
+ }
+ }
list_for_each(&active_units.head, unit, unode) {
iseqs[i] = unit->iseq;
i++;
}
+ assert(i == length);
CRITICAL_SECTION_FINISH(4, "mjit_mark");
for (i = 0; i < length; i++) {
diff --git a/mjit_compile.c b/mjit_compile.c
index dc188864ca..c857153e35 100644
--- a/mjit_compile.c
+++ b/mjit_compile.c
@@ -441,6 +441,22 @@ inlinable_iseq_p(const struct rb_iseq_constant_body *body)
return true;
}
+// Return an iseq pointer if cc has inlinable iseq.
+const rb_iseq_t *
+rb_mjit_inlinable_iseq(const struct rb_callinfo *ci, const struct rb_callcache *cc)
+{
+ const rb_iseq_t *iseq;
+ if (has_valid_method_type(cc) &&
+ !(vm_ci_flag(ci) & VM_CALL_TAILCALL) && // inlining only non-tailcall path
+ vm_cc_cme(cc)->def->type == VM_METHOD_TYPE_ISEQ &&
+ fastpath_applied_iseq_p(ci, cc, iseq = def_iseq_ptr(vm_cc_cme(cc)->def)) &&
+ // CC_SET_FASTPATH in vm_callee_setup_arg
+ inlinable_iseq_p(iseq->body)) {
+ return iseq;
+ }
+ return NULL;
+}
+
static void
init_ivar_compile_status(const struct rb_iseq_constant_body *body, struct compile_status *status)
{
@@ -521,13 +537,9 @@ precompile_inlinable_iseqs(FILE *f, const rb_iseq_t *iseq, struct compile_status
const struct rb_callinfo *ci = cd->ci;
const struct rb_callcache *cc = captured_cc_entries(status)[call_data_index(cd, body)]; // use copy to avoid race condition
+ extern bool rb_mjit_compiling_iseq_p(const rb_iseq_t *iseq);
const rb_iseq_t *child_iseq;
- if (has_valid_method_type(cc) &&
- !(vm_ci_flag(ci) & VM_CALL_TAILCALL) && // inlining only non-tailcall path
- vm_cc_cme(cc)->def->type == VM_METHOD_TYPE_ISEQ &&
- fastpath_applied_iseq_p(ci, cc, child_iseq = def_iseq_ptr(vm_cc_cme(cc)->def)) &&
- // CC_SET_FASTPATH in vm_callee_setup_arg
- inlinable_iseq_p(child_iseq->body)) {
+ if ((child_iseq = rb_mjit_inlinable_iseq(ci, cc)) != NULL && rb_mjit_compiling_iseq_p(child_iseq)) {
status->inlined_iseqs[pos] = child_iseq->body;
if (mjit_opts.verbose >= 1) // print beforehand because ISeq may be GCed during copy job.
diff --git a/mjit_worker.c b/mjit_worker.c
index ba90cca7b6..f7f0b6bced 100644
--- a/mjit_worker.c
+++ b/mjit_worker.c
@@ -93,6 +93,10 @@
#include "ruby/debug.h"
#include "ruby/thread.h"
#include "ruby/version.h"
+#include "builtin.h"
+#include "insns.inc"
+#include "insns_info.inc"
+#include "internal/compile.h"
#ifdef _WIN32
#include <winsock2.h>
@@ -729,6 +733,51 @@ sprint_funcname(char *funcname, const struct rb_mjit_unit *unit)
}
}
+static const rb_iseq_t **compiling_iseqs = NULL;
+
+static bool
+set_compiling_iseqs(const rb_iseq_t *iseq)
+{
+ compiling_iseqs = calloc(iseq->body->iseq_size + 2, sizeof(rb_iseq_t *)); // 2: 1 (unit->iseq) + 1 (NULL end)
+ if (compiling_iseqs == NULL)
+ return false;
+
+ compiling_iseqs[0] = iseq;
+ int i = 1;
+
+ unsigned int pos = 0;
+ while (pos < iseq->body->iseq_size) {
+#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
+ int insn = rb_vm_insn_addr2insn((void *)iseq->body->iseq_encoded[pos]);
+#else
+ int insn = (int)iseq->body->iseq_encoded[pos];
+#endif
+ if (insn == BIN(opt_send_without_block)) {
+ CALL_DATA cd = (CALL_DATA)iseq->body->iseq_encoded[pos + 1];
+ extern const rb_iseq_t *rb_mjit_inlinable_iseq(const struct rb_callinfo *ci, const struct rb_callcache *cc);
+ const rb_iseq_t *iseq = rb_mjit_inlinable_iseq(cd->ci, cd->cc);
+ if (iseq != NULL) {
+ compiling_iseqs[i] = iseq;
+ i++;
+ }
+ }
+ pos += insn_len(insn);
+ }
+ return true;
+}
+
+bool
+rb_mjit_compiling_iseq_p(const rb_iseq_t *iseq)
+{
+ assert(compiling_iseqs != NULL);
+ int i = 0;
+ while (compiling_iseqs[i]) {
+ if (compiling_iseqs[i] == iseq) return true;
+ i++;
+ }
+ return false;
+}
+
static const int c_file_access_mode =
#ifdef O_BINARY
O_BINARY|
@@ -820,6 +869,7 @@ make_pch(void)
const char *rest_args[] = {
# ifdef __clang__
"-emit-pch",
+ "-c",
# endif
// -nodefaultlibs is a linker flag, but it may affect cc1 behavior on Gentoo, which should NOT be changed on pch:
// https://gitweb.gentoo.org/proj/gcc-patches.git/tree/7.3.0/gentoo/13_all_default-ssp-fix.patch
@@ -887,7 +937,13 @@ compile_c_to_so(const char *c_file, const char *so_file)
# endif
o_file, NULL
};
- args = form_args(6, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS, so_args, CC_LIBS, CC_DLDFLAGS_ARGS, CC_LINKER_ARGS);
+# if defined(__MACH__)
+ extern VALUE rb_libruby_selfpath;
+ const char *loader_args[] = {"-bundle_loader", StringValuePtr(rb_libruby_selfpath), NULL};
+# else
+ const char *loader_args[] = {NULL};
+# endif
+ args = form_args(7, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS, so_args, loader_args, CC_LIBS, CC_DLDFLAGS_ARGS, CC_LINKER_ARGS);
if (args == NULL) return false;
exit_code = exec_process(cc_path, args);
free(args);
@@ -951,6 +1007,11 @@ compile_compact_jit_code(char* c_file)
// compacted functions (not done yet).
bool success = true;
list_for_each(&active_units.head, child_unit, unode) {
+ CRITICAL_SECTION_START(3, "before set_compiling_iseqs");
+ success &= set_compiling_iseqs(child_unit->iseq);
+ CRITICAL_SECTION_FINISH(3, "after set_compiling_iseqs");
+ if (!success) continue;
+
char funcname[MAXPATHLEN];
sprint_funcname(funcname, child_unit);
@@ -964,6 +1025,11 @@ compile_compact_jit_code(char* c_file)
if (!iseq_label) iseq_label = sep = "";
fprintf(f, "\n/* %s%s%s:%ld */\n", iseq_label, sep, iseq_path, iseq_lineno);
success &= mjit_compile(f, child_unit->iseq, funcname, child_unit->id);
+
+ CRITICAL_SECTION_START(3, "before compiling_iseqs free");
+ free(compiling_iseqs);
+ compiling_iseqs = NULL;
+ CRITICAL_SECTION_FINISH(3, "after compiling_iseqs free");
}
// release blocking mjit_gc_start_hook
@@ -1103,8 +1169,6 @@ compile_prelude(FILE *f)
#endif
}
-static rb_iseq_t *compiling_iseq = NULL;
-
// Compile ISeq in UNIT and return function pointer of JIT-ed code.
// It may return NOT_COMPILED_JIT_ISEQ_FUNC if something went wrong.
static mjit_func_t
@@ -1139,7 +1203,7 @@ convert_unit_to_func(struct rb_mjit_unit *unit)
// We need to check again here because we could've waited on GC above
in_jit = (unit->iseq != NULL);
if (in_jit)
- compiling_iseq = unit->iseq;
+ in_jit &= set_compiling_iseqs(unit->iseq);
CRITICAL_SECTION_FINISH(3, "before mjit_compile to wait GC finish");
if (!in_jit) {
fclose(f);
@@ -1164,7 +1228,8 @@ convert_unit_to_func(struct rb_mjit_unit *unit)
// release blocking mjit_gc_start_hook
CRITICAL_SECTION_START(3, "after mjit_compile to wakeup client for GC");
- compiling_iseq = NULL;
+ free(compiling_iseqs);
+ compiling_iseqs = NULL;
in_jit = false;
verbose(3, "Sending wakeup signal to client in a mjit-worker for GC");
rb_native_cond_signal(&mjit_client_wakeup);
@@ -1344,6 +1409,8 @@ unload_units(void)
}
}
+static void mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info, bool worker_p);
+
// The function implementing a worker. It is executed in a separate
// thread by rb_thread_create_mjit_thread. It compiles precompiled header
// and then compiles requested ISeqs.
@@ -1380,7 +1447,7 @@ mjit_worker(void)
// Wait until a unit becomes available
CRITICAL_SECTION_START(3, "in worker dequeue");
- while ((pending_stale_p || list_empty(&unit_queue.head) || active_units.length >= mjit_opts.max_cache_size) && !stop_worker_p) {
+ while ((list_empty(&unit_queue.head) || active_units.length >= mjit_opts.max_cache_size) && !stop_worker_p) {
rb_native_cond_wait(&mjit_worker_wakeup, &mjit_engine_mutex);
verbose(3, "Getting wakeup from client");
@@ -1393,6 +1460,8 @@ mjit_worker(void)
unit->stale_p = false;
remove_from_list(unit, &active_units);
add_to_list(unit, &stale_units);
+ // Lazily put it to unit_queue as well to avoid race conditions on jit_unit with mjit_compile.
+ mjit_add_iseq_to_process(unit->iseq, &unit->iseq->body->jit_unit->compile_info, true);
}
}
}
diff --git a/numeric.c b/numeric.c
index e4bb4817bb..691ab77042 100644
--- a/numeric.c
+++ b/numeric.c
@@ -4529,6 +4529,7 @@ rb_fix_lshift(VALUE x, VALUE y)
long val, width;
val = NUM2LONG(x);
+ if (!val) return (rb_to_int(y), INT2FIX(0));
if (!FIXNUM_P(y))
return rb_big_lshift(rb_int2big(val), y);
width = FIX2LONG(y);
@@ -4575,6 +4576,7 @@ rb_fix_rshift(VALUE x, VALUE y)
long i, val;
val = FIX2LONG(x);
+ if (!val) return (rb_to_int(y), INT2FIX(0));
if (!FIXNUM_P(y))
return rb_big_rshift(rb_int2big(val), y);
i = FIX2LONG(y);
diff --git a/parse.y b/parse.y
index ca55b96ecd..3bc3748dd5 100644
--- a/parse.y
+++ b/parse.y
@@ -89,7 +89,6 @@ struct lex_context {
#define YYCALLOC(nelem, size) rb_parser_calloc(p, (nelem), (size))
#define YYFREE(ptr) rb_parser_free(p, (ptr))
#define YYFPRINTF rb_parser_printf
-#define YYPRINT(out, tok, val) parser_token_value_print(p, (tok), &(val))
#define YY_LOCATION_PRINT(File, loc) \
rb_parser_printf(p, "%d.%d-%d.%d", \
(loc).beg_pos.lineno, (loc).beg_pos.column,\
@@ -654,7 +653,6 @@ RUBY_SYMBOL_EXPORT_END
static void error_duplicate_pattern_variable(struct parser_params *p, ID id, const YYLTYPE *loc);
static void error_duplicate_pattern_key(struct parser_params *p, ID id, const YYLTYPE *loc);
-static void parser_token_value_print(struct parser_params *p, enum yytokentype type, const YYSTYPE *valp);
#ifndef RIPPER
static ID formal_argument(struct parser_params*, ID);
#else
@@ -1084,6 +1082,35 @@ static int looking_at_eol_p(struct parser_params *p);
%expect 0
%define api.pure
%define parse.error verbose
+%printer {
+#ifndef RIPPER
+ rb_parser_printf(p, "%"PRIsVALUE, rb_id2str($$));
+#else
+ rb_parser_printf(p, "%"PRIsVALUE, RNODE($$)->nd_rval);
+#endif
+} tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL tOP_ASGN
+%printer {
+#ifndef RIPPER
+ rb_parser_printf(p, "%+"PRIsVALUE, $$->nd_lit);
+#else
+ rb_parser_printf(p, "%+"PRIsVALUE, get_value($$));
+#endif
+} tINTEGER tFLOAT tRATIONAL tIMAGINARY tSTRING_CONTENT tCHAR
+%printer {
+#ifndef RIPPER
+ rb_parser_printf(p, "$%ld", $$->nd_nth);
+#else
+ rb_parser_printf(p, "%"PRIsVALUE, $$);
+#endif
+} tNTH_REF
+%printer {
+#ifndef RIPPER
+ rb_parser_printf(p, "$%c", (int)$$->nd_nth);
+#else
+ rb_parser_printf(p, "%"PRIsVALUE, $$);
+#endif
+} tBACK_REF
+
%lex-param {struct parser_params *p}
%parse-param {struct parser_params *p}
%initial-action
@@ -6763,7 +6790,11 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
goto eof;
}
if ((c = nextc(p)) == '\\') {
- if (peek(p, 'u')) goto eof;
+ switch (peekc(p)) {
+ case 'u': case 'U':
+ nextc(p);
+ goto eof;
+ }
return read_escape(p, flags|ESCAPE_META, encp) | 0x80;
}
else if (c == -1 || !ISASCII(c)) goto eof;
@@ -6788,7 +6819,11 @@ read_escape(struct parser_params *p, int flags, rb_encoding **encp)
case 'c':
if (flags & ESCAPE_CONTROL) goto eof;
if ((c = nextc(p))== '\\') {
- if (peek(p, 'u')) goto eof;
+ switch (peekc(p)) {
+ case 'u': case 'U':
+ nextc(p);
+ goto eof;
+ }
c = read_escape(p, flags|ESCAPE_CONTROL, encp);
}
else if (c == '?')
diff --git a/proc.c b/proc.c
index d3310e8621..a933fde3a8 100644
--- a/proc.c
+++ b/proc.c
@@ -3199,7 +3199,7 @@ method_super_method(VALUE method)
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
iclass = data->iclass;
if (!iclass) return Qnil;
- if (data->me->def->type == VM_METHOD_TYPE_ALIAS) {
+ if (data->me->def->type == VM_METHOD_TYPE_ALIAS && data->me->defined_class) {
super_class = RCLASS_SUPER(rb_find_defined_class_by_owner(data->me->defined_class,
data->me->def->body.alias.original_me->owner));
mid = data->me->def->body.alias.original_me->def->original_id;
diff --git a/ractor.c b/ractor.c
index 420b2051b2..bf31b545cf 100644
--- a/ractor.c
+++ b/ractor.c
@@ -34,7 +34,7 @@ ASSERT_ractor_unlocking(rb_ractor_t *r)
{
#if RACTOR_CHECK_MODE > 0
// GET_EC is NULL in an MJIT worker
- if (GET_EC() != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
+ if (rb_current_execution_context(false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
rb_bug("recursive ractor locking");
}
#endif
@@ -45,7 +45,7 @@ ASSERT_ractor_locking(rb_ractor_t *r)
{
#if RACTOR_CHECK_MODE > 0
// GET_EC is NULL in an MJIT worker
- if (GET_EC() != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
+ if (rb_current_execution_context(false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
rp(r->sync.locked_by);
rb_bug("ractor lock is not acquired.");
}
@@ -61,7 +61,7 @@ ractor_lock(rb_ractor_t *r, const char *file, int line)
rb_native_mutex_lock(&r->sync.lock);
#if RACTOR_CHECK_MODE > 0
- if (GET_EC() != NULL) { // GET_EC is NULL in an MJIT worker
+ if (rb_current_execution_context(false) != NULL) { // GET_EC is NULL in an MJIT worker
r->sync.locked_by = rb_ractor_self(GET_RACTOR());
}
#endif
@@ -917,7 +917,7 @@ static VALUE ractor_move(VALUE obj); // in this file
static VALUE ractor_copy(VALUE obj); // in this file
static void
-ractor_basket_setup(rb_execution_context_t *ec, struct rb_ractor_basket *basket, VALUE obj, VALUE move, bool exc, bool is_will)
+ractor_basket_setup(rb_execution_context_t *ec, struct rb_ractor_basket *basket, VALUE obj, VALUE move, bool exc, bool is_will, bool is_yield)
{
basket->sender = rb_ec_ractor_ptr(ec)->pub.self;
basket->exception = exc;
@@ -936,7 +936,13 @@ ractor_basket_setup(rb_execution_context_t *ec, struct rb_ractor_basket *basket,
}
else {
basket->type = basket_type_move;
- basket->v = ractor_move(obj);
+
+ if (is_yield) {
+ basket->v = obj; // call ractor_move() when yielding timing.
+ }
+ else {
+ basket->v = ractor_move(obj);
+ }
}
}
@@ -944,7 +950,7 @@ static VALUE
ractor_send(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
{
struct rb_ractor_basket basket;
- ractor_basket_setup(ec, &basket, obj, move, false, false);
+ ractor_basket_setup(ec, &basket, obj, move, false, false, false);
ractor_send_basket(ec, r, &basket);
return r->pub.self;
}
@@ -959,17 +965,23 @@ ractor_try_take(rb_execution_context_t *ec, rb_ractor_t *r)
RACTOR_LOCK(r);
{
- if (ractor_wakeup(r, wait_yielding, wakeup_by_take)) {
+ if (ractor_sleeping_by(r, wait_yielding)) {
+ MAYBE_UNUSED(bool) wakeup_result;
VM_ASSERT(r->sync.wait.yielded_basket.type != basket_type_none);
- basket = r->sync.wait.yielded_basket;
- ractor_basket_clear(&r->sync.wait.yielded_basket);
+
+ if (r->sync.wait.yielded_basket.type == basket_type_move) {
+ wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_retry);
+ }
+ else {
+ wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_take);
+ basket = r->sync.wait.yielded_basket;
+ ractor_basket_clear(&r->sync.wait.yielded_basket);
+ }
+ VM_ASSERT(wakeup_result);
}
else if (r->sync.outgoing_port_closed) {
closed = true;
}
- else {
- // not reached.
- }
}
RACTOR_UNLOCK(r);
@@ -986,6 +998,12 @@ ractor_try_take(rb_execution_context_t *ec, rb_ractor_t *r)
}
}
+static VALUE
+ractor_yield_move_body(VALUE v)
+{
+ return ractor_move(v);
+}
+
static bool
ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_basket *basket)
{
@@ -1010,8 +1028,34 @@ ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_b
RACTOR_LOCK(r);
{
- if (ractor_wakeup(r, wait_taking, wakeup_by_yield)) {
+ if (ractor_sleeping_by(r, wait_taking)) {
VM_ASSERT(r->sync.wait.taken_basket.type == basket_type_none);
+
+ if (basket->type == basket_type_move) {
+ enum ractor_wait_status prev_wait_status = r->sync.wait.status;
+ r->sync.wait.status = wait_moving;
+
+ RACTOR_UNLOCK(r);
+ {
+ int state;
+ VALUE moved_value = rb_protect(ractor_yield_move_body, basket->v, &state);
+ if (state) {
+ r->sync.wait.status = prev_wait_status;
+ rb_jump_tag(state);
+ }
+ else {
+ basket->v = moved_value;
+ }
+ }
+ RACTOR_LOCK(r);
+
+ if (!ractor_wakeup(r, wait_moving, wakeup_by_yield)) {
+ // terminating?
+ }
+ }
+ else {
+ ractor_wakeup(r, wait_taking, wakeup_by_yield);
+ }
r->sync.wait.taken_basket = *basket;
}
else {
@@ -1036,7 +1080,7 @@ ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_b
// select(r1, r2, r3, receive: true, yield: obj)
static VALUE
-ractor_select(rb_execution_context_t *ec, const VALUE *rs, int alen, VALUE yielded_value, bool move, VALUE *ret_r)
+ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VALUE yielded_value, bool move, VALUE *ret_r)
{
rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
VALUE crv = cr->pub.self;
@@ -1045,7 +1089,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, int alen, VALUE yield
bool interrupted = false;
enum ractor_wait_status wait_status = 0;
bool yield_p = (yielded_value != Qundef) ? true : false;
- const int rs_len = alen;
+ const int alen = rs_len + (yield_p ? 1 : 0);
struct ractor_select_action {
enum ractor_select_action_type {
@@ -1054,7 +1098,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, int alen, VALUE yield
ractor_select_action_yield,
} type;
VALUE v;
- } *actions = ALLOCA_N(struct ractor_select_action, alen + (yield_p ? 1 : 0));
+ } *actions = ALLOCA_N(struct ractor_select_action, alen);
VM_ASSERT(cr->sync.wait.status == wait_none);
VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
@@ -1062,7 +1106,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, int alen, VALUE yield
VM_ASSERT(cr->sync.wait.yielded_basket.type == basket_type_none);
// setup actions
- for (i=0; i<alen; i++) {
+ for (i=0; i<rs_len; i++) {
VALUE v = rs[i];
if (v == crv) {
@@ -1087,9 +1131,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, int alen, VALUE yield
actions[rs_len].type = ractor_select_action_yield;
actions[rs_len].v = Qundef;
wait_status |= wait_yielding;
- alen++;
-
- ractor_basket_setup(ec, &cr->sync.wait.yielded_basket, yielded_value, move, false, false);
+ ractor_basket_setup(ec, &cr->sync.wait.yielded_basket, yielded_value, move, false, false, true);
}
// TODO: shuffle actions
@@ -1443,6 +1485,9 @@ vm_remove_ractor(rb_vm_t *vm, rb_ractor_t *cr)
}
vm->ractor.cnt--;
+ /* Clear the cached freelist to prevent a memory leak. */
+ rb_gc_ractor_newobj_cache_clear(&cr->newobj_cache);
+
ractor_status_set(cr, ractor_terminated);
}
RB_VM_UNLOCK();
@@ -1542,11 +1587,6 @@ rb_ractor_main_setup(rb_vm_t *vm, rb_ractor_t *r, rb_thread_t *th)
rb_ractor_living_threads_insert(r, th);
}
-// io.c
-VALUE rb_io_prep_stdin(void);
-VALUE rb_io_prep_stdout(void);
-VALUE rb_io_prep_stderr(void);
-
static VALUE
ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VALUE args, VALUE block)
{
@@ -1558,10 +1598,6 @@ ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VAL
r->pub.id = ractor_next_id();
RUBY_DEBUG_LOG("r:%u", r->pub.id);
- r->r_stdin = rb_io_prep_stdin();
- r->r_stdout = rb_io_prep_stdout();
- r->r_stderr = rb_io_prep_stderr();
-
rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
r->verbose = cr->verbose;
r->debug = cr->debug;
@@ -1582,7 +1618,7 @@ ractor_yield_atexit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE v, bool e
ASSERT_ractor_unlocking(cr);
struct rb_ractor_basket basket;
- ractor_basket_setup(ec, &basket, v, Qfalse, exc, true);
+ ractor_basket_setup(ec, &basket, v, Qfalse, exc, true, true /* this flag is ignored because move is Qfalse */);
retry:
if (ractor_try_yield(ec, cr, &basket)) {
@@ -2047,6 +2083,8 @@ void
Init_Ractor(void)
{
rb_cRactor = rb_define_class("Ractor", rb_cObject);
+ rb_undef_alloc_func(rb_cRactor);
+
rb_eRactorError = rb_define_class_under(rb_cRactor, "Error", rb_eRuntimeError);
rb_eRactorIsolationError = rb_define_class_under(rb_cRactor, "IsolationError", rb_eRactorError);
rb_eRactorRemoteError = rb_define_class_under(rb_cRactor, "RemoteError", rb_eRactorError);
@@ -2329,7 +2367,11 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
.stop = false,
.data = data,
};
- rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
+ RB_VM_LOCK_ENTER_NO_BARRIER();
+ {
+ rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
+ }
+ RB_VM_LOCK_LEAVE_NO_BARRIER();
if (d.stop) return 1;
}
break;
@@ -2639,7 +2681,11 @@ static int
obj_refer_only_shareables_p(VALUE obj)
{
int cnt = 0;
- rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
+ RB_VM_LOCK_ENTER_NO_BARRIER();
+ {
+ rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
+ }
+ RB_VM_LOCK_LEAVE_NO_BARRIER();
return cnt == 0;
}
diff --git a/ractor_core.h b/ractor_core.h
index 2516277f4f..800e7ce409 100644
--- a/ractor_core.h
+++ b/ractor_core.h
@@ -61,6 +61,7 @@ struct rb_ractor_sync {
wait_receiving = 0x01,
wait_taking = 0x02,
wait_yielding = 0x04,
+ wait_moving = 0x08,
} status;
enum ractor_wakeup_status {
@@ -138,10 +139,7 @@ struct rb_ractor_struct {
VALUE verbose;
VALUE debug;
- struct {
- struct RVALUE *freelist;
- struct heap_page *using_page;
- } newobj_cache;
+ rb_ractor_newobj_cache_t newobj_cache;
// gc.c rb_objspace_reachable_objects_from
struct gc_mark_func_data_struct {
diff --git a/random.c b/random.c
index 61de6bc7aa..4d70c17116 100644
--- a/random.c
+++ b/random.c
@@ -261,7 +261,8 @@ const rb_data_type_t rb_random_data_type = {
static void
random_mt_free(void *ptr)
{
- if (ptr != default_rand())
+ rb_random_mt_t *rnd = rb_ractor_local_storage_ptr(default_rand_key);
+ if (ptr != rnd)
xfree(ptr);
}
@@ -368,15 +369,12 @@ rand_init(const rb_random_interface_t *rng, rb_random_t *rnd, VALUE seed)
int sign;
len = rb_absint_numwords(seed, 32, NULL);
+ if (len == 0) len = 1;
buf = ALLOCV_N(uint32_t, buf0, len);
sign = rb_integer_pack(seed, buf, len, sizeof(uint32_t), 0,
INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER);
if (sign < 0)
sign = -sign;
- if (len == 0) {
- buf[0] = 0;
- len = 1;
- }
if (len > 1) {
if (sign != 2 && buf[len-1] == 1) /* remove leading-zero-guard */
len--;
@@ -813,7 +811,7 @@ rand_mt_init(rb_random_t *rnd, const uint32_t *buf, size_t len)
{
struct MT *mt = &((rb_random_mt_t *)rnd)->mt;
if (len <= 1) {
- init_genrand(mt, buf[0]);
+ init_genrand(mt, len ? buf[0] : 0);
}
else {
init_by_array(mt, buf, (int)len);
diff --git a/rational.c b/rational.c
index 872bcd7e14..d621773838 100644
--- a/rational.c
+++ b/rational.c
@@ -1744,8 +1744,8 @@ nurat_rationalize(int argc, VALUE *argv, VALUE self)
}
/* :nodoc: */
-static VALUE
-nurat_hash(VALUE self)
+st_index_t
+rb_rational_hash(VALUE self)
{
st_index_t v, h[2];
VALUE n;
@@ -1756,9 +1756,16 @@ nurat_hash(VALUE self)
n = rb_hash(dat->den);
h[1] = NUM2LONG(n);
v = rb_memhash(h, sizeof(h));
- return ST2FIX(v);
+ return v;
+}
+
+static VALUE
+nurat_hash(VALUE self)
+{
+ return ST2FIX(rb_rational_hash(self));
}
+
static VALUE
f_format(VALUE self, VALUE (*func)(VALUE))
{
diff --git a/re.c b/re.c
index a56b483513..5b6f129963 100644
--- a/re.c
+++ b/re.c
@@ -1151,6 +1151,14 @@ match_size(VALUE match)
}
static int name_to_backref_number(struct re_registers *, VALUE, const char*, const char*);
+NORETURN(static void name_to_backref_error(VALUE name));
+
+static void
+name_to_backref_error(VALUE name)
+{
+ rb_raise(rb_eIndexError, "undefined group name reference: % "PRIsVALUE,
+ name);
+}
static int
match_backref_number(VALUE match, VALUE backref)
@@ -1170,10 +1178,10 @@ match_backref_number(VALUE match, VALUE backref)
}
name = StringValueCStr(backref);
- num = name_to_backref_number(regs, regexp, name, name + strlen(name));
+ num = name_to_backref_number(regs, regexp, name, name + RSTRING_LEN(backref));
if (num < 1) {
- rb_raise(rb_eIndexError, "undefined group name reference: %s", name);
+ name_to_backref_error(backref);
}
return num;
@@ -1538,8 +1546,8 @@ rb_reg_adjust_startpos(VALUE re, VALUE str, long pos, int reverse)
}
/* returns byte offset */
-long
-rb_reg_search0(VALUE re, VALUE str, long pos, int reverse, int set_backref_str)
+static long
+rb_reg_search_set_match(VALUE re, VALUE str, long pos, int reverse, int set_backref_str, VALUE *set_match)
{
long result;
VALUE match;
@@ -1561,18 +1569,7 @@ rb_reg_search0(VALUE re, VALUE str, long pos, int reverse, int set_backref_str)
tmpreg = reg != RREGEXP_PTR(re);
if (!tmpreg) RREGEXP(re)->usecnt++;
- match = rb_backref_get();
- if (!NIL_P(match)) {
- if (FL_TEST(match, MATCH_BUSY)) {
- match = Qnil;
- }
- else {
- regs = RMATCH_REGS(match);
- }
- }
- if (NIL_P(match)) {
- MEMZERO(regs, struct re_registers, 1);
- }
+ MEMZERO(regs, struct re_registers, 1);
if (!reverse) {
range += len;
}
@@ -1605,13 +1602,10 @@ rb_reg_search0(VALUE re, VALUE str, long pos, int reverse, int set_backref_str)
}
}
- if (NIL_P(match)) {
- int err;
- match = match_alloc(rb_cMatch);
- err = rb_reg_region_copy(RMATCH_REGS(match), regs);
- onig_region_free(regs, 0);
- if (err) rb_memerror();
- }
+ match = match_alloc(rb_cMatch);
+ int copy_err = rb_reg_region_copy(RMATCH_REGS(match), regs);
+ onig_region_free(regs, 0);
+ if (copy_err) rb_memerror();
if (set_backref_str) {
RMATCH(match)->str = rb_str_new4(str);
@@ -1619,11 +1613,18 @@ rb_reg_search0(VALUE re, VALUE str, long pos, int reverse, int set_backref_str)
RMATCH(match)->regexp = re;
rb_backref_set(match);
+ if (set_match) *set_match = match;
return result;
}
long
+rb_reg_search0(VALUE re, VALUE str, long pos, int reverse, int set_backref_str)
+{
+ return rb_reg_search_set_match(re, str, pos, reverse, set_backref_str, NULL);
+}
+
+long
rb_reg_search(VALUE re, VALUE str, long pos, int reverse)
{
return rb_reg_search0(re, str, pos, reverse, 1);
@@ -1928,14 +1929,6 @@ name_to_backref_number(struct re_registers *regs, VALUE regexp, const char* name
(const unsigned char *)name, (const unsigned char *)name_end, regs);
}
-NORETURN(static void name_to_backref_error(VALUE name));
-static void
-name_to_backref_error(VALUE name)
-{
- rb_raise(rb_eIndexError, "undefined group name reference: % "PRIsVALUE,
- name);
-}
-
#define NAME_TO_NUMBER(regs, re, name, name_ptr, name_end) \
(NIL_P(re) ? 0 : \
!rb_enc_compatible(RREGEXP_SRC(re), (name)) ? 0 : \
@@ -3127,7 +3120,7 @@ reg_operand(VALUE s, int check)
}
static long
-reg_match_pos(VALUE re, VALUE *strp, long pos)
+reg_match_pos(VALUE re, VALUE *strp, long pos, VALUE* set_match)
{
VALUE str = *strp;
@@ -3146,7 +3139,7 @@ reg_match_pos(VALUE re, VALUE *strp, long pos)
}
pos = rb_str_offset(str, pos);
}
- return rb_reg_search(re, str, pos, 0);
+ return rb_reg_search_set_match(re, str, pos, 0, 1, set_match);
}
/*
@@ -3200,7 +3193,7 @@ reg_match_pos(VALUE re, VALUE *strp, long pos)
VALUE
rb_reg_match(VALUE re, VALUE str)
{
- long pos = reg_match_pos(re, &str, 0);
+ long pos = reg_match_pos(re, &str, 0, NULL);
if (pos < 0) return Qnil;
pos = rb_str_sublen(str, pos);
return LONG2FIX(pos);
@@ -3311,7 +3304,7 @@ rb_reg_match2(VALUE re)
static VALUE
rb_reg_match_m(int argc, VALUE *argv, VALUE re)
{
- VALUE result, str, initpos;
+ VALUE result = Qnil, str, initpos;
long pos;
if (rb_scan_args(argc, argv, "11", &str, &initpos) == 2) {
@@ -3321,12 +3314,11 @@ rb_reg_match_m(int argc, VALUE *argv, VALUE re)
pos = 0;
}
- pos = reg_match_pos(re, &str, pos);
+ pos = reg_match_pos(re, &str, pos, &result);
if (pos < 0) {
rb_backref_set(Qnil);
return Qnil;
}
- result = rb_backref_get();
rb_match_busy(result);
if (!NIL_P(result) && rb_block_given_p()) {
return rb_yield(result);
diff --git a/regcomp.c b/regcomp.c
index 7799e1d952..bd38313875 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -142,8 +142,13 @@ bitset_on_num(BitSetRef bs)
static void
onig_reg_resize(regex_t *reg)
{
- resize:
- if (reg->alloc > reg->used) {
+ do {
+ if (!reg->used) {
+ xfree(reg->p);
+ reg->alloc = 0;
+ reg->p = 0;
+ }
+ else if (reg->alloc > reg->used) {
unsigned char *new_ptr = xrealloc(reg->p, reg->used);
// Skip the right size optimization if memory allocation fails
if (new_ptr) {
@@ -151,10 +156,7 @@ onig_reg_resize(regex_t *reg)
reg->p = new_ptr;
}
}
- if (reg->chain) {
- reg = reg->chain;
- goto resize;
- }
+ } while ((reg = reg->chain) != 0);
}
extern int
@@ -1933,7 +1935,7 @@ noname_disable_map(Node** plink, GroupNumRemap* map, int* counter)
}
static int
-renumber_node_backref(Node* node, GroupNumRemap* map)
+renumber_node_backref(Node* node, GroupNumRemap* map, const int num_mem)
{
int i, pos, n, old_num;
int *backs;
@@ -1949,6 +1951,7 @@ renumber_node_backref(Node* node, GroupNumRemap* map)
backs = bn->back_dynamic;
for (i = 0, pos = 0; i < old_num; i++) {
+ if (backs[i] > num_mem) return ONIGERR_INVALID_BACKREF;
n = map[backs[i]].new_val;
if (n > 0) {
backs[pos] = n;
@@ -1961,7 +1964,7 @@ renumber_node_backref(Node* node, GroupNumRemap* map)
}
static int
-renumber_by_map(Node* node, GroupNumRemap* map)
+renumber_by_map(Node* node, GroupNumRemap* map, const int num_mem)
{
int r = 0;
@@ -1969,28 +1972,30 @@ renumber_by_map(Node* node, GroupNumRemap* map)
case NT_LIST:
case NT_ALT:
do {
- r = renumber_by_map(NCAR(node), map);
+ r = renumber_by_map(NCAR(node), map, num_mem);
} while (r == 0 && IS_NOT_NULL(node = NCDR(node)));
break;
case NT_QTFR:
- r = renumber_by_map(NQTFR(node)->target, map);
+ r = renumber_by_map(NQTFR(node)->target, map, num_mem);
break;
case NT_ENCLOSE:
{
EncloseNode* en = NENCLOSE(node);
- if (en->type == ENCLOSE_CONDITION)
+ if (en->type == ENCLOSE_CONDITION) {
+ if (en->regnum > num_mem) return ONIGERR_INVALID_BACKREF;
en->regnum = map[en->regnum].new_val;
- r = renumber_by_map(en->target, map);
+ }
+ r = renumber_by_map(en->target, map, num_mem);
}
break;
case NT_BREF:
- r = renumber_node_backref(node, map);
+ r = renumber_node_backref(node, map, num_mem);
break;
case NT_ANCHOR:
if (NANCHOR(node)->target)
- r = renumber_by_map(NANCHOR(node)->target, map);
+ r = renumber_by_map(NANCHOR(node)->target, map, num_mem);
break;
default:
@@ -2052,7 +2057,7 @@ disable_noname_group_capture(Node** root, regex_t* reg, ScanEnv* env)
r = noname_disable_map(root, map, &counter);
if (r != 0) return r;
- r = renumber_by_map(*root, map);
+ r = renumber_by_map(*root, map, env->num_mem);
if (r != 0) return r;
for (i = 1, pos = 1; i <= env->num_mem; i++) {
diff --git a/regexec.c b/regexec.c
index 10ada4d81d..07900deeb2 100644
--- a/regexec.c
+++ b/regexec.c
@@ -2528,8 +2528,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
CASE(OP_MEMORY_END_PUSH_REC) MOP_IN(OP_MEMORY_END_PUSH_REC);
GET_MEMNUM_INC(mem, p);
STACK_GET_MEM_START(mem, stkp); /* should be before push mem-end. */
- STACK_PUSH_MEM_END(mem, s);
mem_start_stk[mem] = GET_STACK_INDEX(stkp);
+ STACK_PUSH_MEM_END(mem, s);
MOP_OUT;
JUMP;
@@ -3902,12 +3902,17 @@ forward_search_range(regex_t* reg, const UChar* str, const UChar* end, UChar* s,
UChar* range, UChar** low, UChar** high, UChar** low_prev)
{
UChar *p, *pprev = (UChar* )NULL;
+ size_t input_len = end - str;
#ifdef ONIG_DEBUG_SEARCH
fprintf(stderr, "forward_search_range: str: %"PRIuPTR" (%p), end: %"PRIuPTR" (%p), s: %"PRIuPTR" (%p), range: %"PRIuPTR" (%p)\n",
(uintptr_t )str, str, (uintptr_t )end, end, (uintptr_t )s, s, (uintptr_t )range, range);
#endif
+ if (reg->dmin > input_len) {
+ return 0;
+ }
+
p = s;
if (reg->dmin > 0) {
if (ONIGENC_IS_SINGLEBYTE(reg->enc)) {
@@ -4044,6 +4049,11 @@ backward_search_range(regex_t* reg, const UChar* str, const UChar* end,
UChar** low, UChar** high)
{
UChar *p;
+ size_t input_len = end - str;
+
+ if (reg->dmin > input_len) {
+ return 0;
+ }
range += reg->dmin;
p = s;
diff --git a/ruby.c b/ruby.c
index 1af326c8e1..f502157e97 100644
--- a/ruby.c
+++ b/ruby.c
@@ -26,7 +26,7 @@
# include <sys/pstat.h>
#endif
-#if defined(LOAD_RELATIVE) && defined(HAVE_DLADDR)
+#if (defined(LOAD_RELATIVE) || defined(__MACH__)) && defined(HAVE_DLADDR)
# include <dlfcn.h>
#endif
@@ -531,7 +531,7 @@ str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
void ruby_init_loadpath(void);
-#if defined(LOAD_RELATIVE)
+#if defined(LOAD_RELATIVE) || defined(__MACH__)
static VALUE
runtime_libruby_path(void)
{
@@ -607,6 +607,10 @@ runtime_libruby_path(void)
#define INITIAL_LOAD_PATH_MARK rb_intern_const("@gem_prelude_index")
VALUE ruby_archlibdir_path, ruby_prefix_path;
+#if defined(__MACH__)
+// A path to libruby.dylib itself or where it's statically linked to.
+VALUE rb_libruby_selfpath;
+#endif
void
ruby_init_loadpath(void)
@@ -614,6 +618,14 @@ ruby_init_loadpath(void)
VALUE load_path, archlibdir = 0;
ID id_initial_load_path_mark;
const char *paths = ruby_initial_load_paths;
+#if defined(LOAD_RELATIVE) || defined(__MACH__)
+ VALUE libruby_path = runtime_libruby_path();
+# if defined(__MACH__)
+ rb_libruby_selfpath = libruby_path;
+ rb_gc_register_address(&rb_libruby_selfpath);
+# endif
+#endif
+
#if defined LOAD_RELATIVE
#if !defined ENABLE_MULTIARCH
# define RUBY_ARCH_PATH ""
@@ -627,7 +639,7 @@ ruby_init_loadpath(void)
size_t baselen;
const char *p;
- sopath = runtime_libruby_path();
+ sopath = libruby_path;
libpath = RSTRING_PTR(sopath);
p = strrchr(libpath, '/');
@@ -1657,7 +1669,7 @@ tty_enabled(void)
DWORD m;
if (!GetConsoleMode(h, &m)) return 0;
# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
-# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x200
+# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
# endif
if (!(m & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) return 0;
return 1;
@@ -1667,6 +1679,17 @@ tty_enabled(void)
#endif
static VALUE
+copy_str(VALUE str, rb_encoding *enc, bool intern)
+{
+ if (!intern) {
+ if (rb_enc_str_coderange_scan(str, enc) == ENC_CODERANGE_BROKEN)
+ return 0;
+ return rb_enc_associate(rb_str_dup(str), enc);
+ }
+ return rb_enc_interned_str(RSTRING_PTR(str), RSTRING_LEN(str), enc);
+}
+
+static VALUE
process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
{
rb_ast_t *ast = 0;
@@ -1682,6 +1705,8 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
char fbuf[MAXPATHLEN];
int i = (int)proc_options(argc, argv, opt, 0);
unsigned int dump = opt->dump & dump_exit_bits;
+ rb_vm_t *vm = GET_VM();
+ const long loaded_before_enc = RARRAY_LEN(vm->loaded_features);
if (opt->dump & (DUMP_BIT(usage)|DUMP_BIT(help))) {
int tty = isatty(1);
@@ -1883,7 +1908,6 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
rb_obj_freeze(opt->script_name);
if (IF_UTF8_PATH(uenc != lenc, 1)) {
long i;
- rb_vm_t *vm = GET_VM();
VALUE load_path = vm->load_path;
const ID id_initial_load_path_mark = INITIAL_LOAD_PATH_MARK;
int modifiable = FALSE;
@@ -1897,7 +1921,7 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
if (newpath == path) continue;
path = newpath;
#else
- path = rb_enc_associate(rb_str_dup(path), lenc);
+ if (!(path = copy_str(path, lenc, !mark))) continue;
#endif
if (mark) rb_ivar_set(path, id_initial_load_path_mark, path);
if (!modifiable) {
@@ -1910,6 +1934,19 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
rb_ary_replace(vm->load_path_snapshot, load_path);
}
}
+ {
+ VALUE loaded_features = vm->loaded_features;
+ bool modified = false;
+ for (long i = loaded_before_enc; i < RARRAY_LEN(loaded_features); ++i) {
+ VALUE path = RARRAY_AREF(loaded_features, i);
+ if (!(path = copy_str(path, IF_UTF8_PATH(uenc, lenc), true))) continue;
+ modified = true;
+ RARRAY_ASET(loaded_features, i, path);
+ }
+ if (modified) {
+ rb_ary_replace(vm->loaded_features_snapshot, loaded_features);
+ }
+ }
if (opt->features.mask & COMPILATION_FEATURES) {
VALUE option = rb_hash_new();
diff --git a/scheduler.c b/scheduler.c
index 88db433f1e..66cbfc6f10 100644
--- a/scheduler.c
+++ b/scheduler.c
@@ -49,12 +49,36 @@ rb_scheduler_get(void)
return thread->scheduler;
}
+static void
+verify_interface(VALUE scheduler)
+{
+ if (!rb_respond_to(scheduler, id_block)) {
+ rb_raise(rb_eArgError, "Scheduler must implement #block!");
+ }
+
+ if (!rb_respond_to(scheduler, id_unblock)) {
+ rb_raise(rb_eArgError, "Scheduler must implement #unblock!");
+ }
+
+ if (!rb_respond_to(scheduler, id_kernel_sleep)) {
+ rb_raise(rb_eArgError, "Scheduler must implement #kernel_sleep!");
+ }
+
+ if (!rb_respond_to(scheduler, id_io_wait)) {
+ rb_raise(rb_eArgError, "Scheduler must implement #io_wait!");
+ }
+}
+
VALUE
rb_scheduler_set(VALUE scheduler)
{
rb_thread_t *thread = GET_THREAD();
VM_ASSERT(thread);
+ if (scheduler != Qnil) {
+ verify_interface(scheduler);
+ }
+
// We invoke Scheduler#close when setting it to something else, to ensure the previous scheduler runs to completion before changing the scheduler. That way, we do not need to consider interactions, e.g., of a Fiber from the previous scheduler with the new scheduler.
if (thread->scheduler != Qnil) {
rb_scheduler_close(thread->scheduler);
diff --git a/signal.c b/signal.c
index 6eea6aabe2..c13bc19f1c 100644
--- a/signal.c
+++ b/signal.c
@@ -557,10 +557,13 @@ static int rb_sigaltstack_size_value = 0;
void *
rb_allocate_sigaltstack(void)
{
+ void *altstack;
if (!rb_sigaltstack_size_value) {
rb_sigaltstack_size_value = rb_sigaltstack_size();
}
- return xmalloc(rb_sigaltstack_size_value);
+ altstack = malloc(rb_sigaltstack_size_value);
+ if (!altstack) rb_memerror();
+ return altstack;
}
/* alternate stack for SIGSEGV */
diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb
index 56ef4ce75a..b8191fe20f 100644
--- a/spec/bundler/bundler/bundler_spec.rb
+++ b/spec/bundler/bundler/bundler_spec.rb
@@ -176,11 +176,9 @@ RSpec.describe Bundler do
describe "configuration" do
context "disable_shared_gems" do
it "should unset GEM_PATH with empty string" do
- env = {}
expect(Bundler).to receive(:use_system_gems?).and_return(false)
- Bundler.send(:configure_gem_path, env)
- expect(env.keys).to include("GEM_PATH")
- expect(env["GEM_PATH"]).to eq ""
+ Bundler.send(:configure_gem_path)
+ expect(ENV["GEM_PATH"]).to eq ""
end
end
end
@@ -249,11 +247,8 @@ EOF
allow(Bundler.rubygems).to receive(:user_home).and_return(path)
allow(File).to receive(:directory?).with(path).and_return false
allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom"))
- message = <<EOF
-`/home/oggy` is not a directory.
-Bundler will use `/tmp/trulyrandom' as your home directory temporarily.
-EOF
- expect(Bundler.ui).to receive(:warn).with(message)
+ expect(Bundler.ui).to receive(:warn).with("`/home/oggy` is not a directory.\n")
+ expect(Bundler.ui).to receive(:warn).with("Bundler will use `/tmp/trulyrandom' as your home directory temporarily.\n")
expect(Bundler.user_home).to eq(Pathname("/tmp/trulyrandom"))
end
end
@@ -268,11 +263,8 @@ EOF
allow(File).to receive(:writable?).with(path).and_return false
allow(File).to receive(:directory?).with(dotbundle).and_return false
allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom"))
- message = <<EOF
-`/home/oggy` is not writable.
-Bundler will use `/tmp/trulyrandom' as your home directory temporarily.
-EOF
- expect(Bundler.ui).to receive(:warn).with(message)
+ expect(Bundler.ui).to receive(:warn).with("`/home/oggy` is not writable.\n")
+ expect(Bundler.ui).to receive(:warn).with("Bundler will use `/tmp/trulyrandom' as your home directory temporarily.\n")
expect(Bundler.user_home).to eq(Pathname("/tmp/trulyrandom"))
end
@@ -293,11 +285,8 @@ EOF
it "should issue warning and return a temporary user home" do
allow(Bundler.rubygems).to receive(:user_home).and_return(nil)
allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom"))
- message = <<EOF
-Your home directory is not set.
-Bundler will use `/tmp/trulyrandom' as your home directory temporarily.
-EOF
- expect(Bundler.ui).to receive(:warn).with(message)
+ expect(Bundler.ui).to receive(:warn).with("Your home directory is not set.\n")
+ expect(Bundler.ui).to receive(:warn).with("Bundler will use `/tmp/trulyrandom' as your home directory temporarily.\n")
expect(Bundler.user_home).to eq(Pathname("/tmp/trulyrandom"))
end
end
diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb
index e49066f7dc..c5de12c211 100644
--- a/spec/bundler/bundler/cli_spec.rb
+++ b/spec/bundler/bundler/cli_spec.rb
@@ -2,16 +2,6 @@
require "bundler/cli"
-using Module.new {
- # Some `man` (e.g., on macOS) always highlights the output even to
- # non-tty.
- refine Spec::Helpers do
- def out
- super.gsub(/.[\b]/, "")
- end
- end
-} if RUBY_VERSION >= "2.4"
-
RSpec.describe "bundle executable" do
it "returns non-zero exit status when passed unrecognized options" do
bundle "--invalid_argument", :raise_on_error => false
@@ -42,49 +32,57 @@ RSpec.describe "bundle executable" do
it "aliases e to exec" do
bundle "e --help"
- expect(out).to include("bundle-exec")
+ expect(out_with_macos_man_workaround).to include("bundle-exec")
end
it "aliases ex to exec" do
bundle "ex --help"
- expect(out).to include("bundle-exec")
+ expect(out_with_macos_man_workaround).to include("bundle-exec")
end
it "aliases exe to exec" do
bundle "exe --help"
- expect(out).to include("bundle-exec")
+ expect(out_with_macos_man_workaround).to include("bundle-exec")
end
it "aliases c to check" do
bundle "c --help"
- expect(out).to include("bundle-check")
+ expect(out_with_macos_man_workaround).to include("bundle-check")
end
it "aliases i to install" do
bundle "i --help"
- expect(out).to include("bundle-install")
+ expect(out_with_macos_man_workaround).to include("bundle-install")
end
it "aliases ls to list" do
bundle "ls --help"
- expect(out).to include("bundle-list")
+ expect(out_with_macos_man_workaround).to include("bundle-list")
end
it "aliases package to cache" do
bundle "package --help"
- expect(out).to include("bundle-cache")
+ expect(out_with_macos_man_workaround).to include("bundle-cache")
end
it "aliases pack to cache" do
bundle "pack --help"
- expect(out).to include("bundle-cache")
+ expect(out_with_macos_man_workaround).to include("bundle-cache")
+ end
+
+ private
+
+ # Some `man` (e.g., on macOS) always highlights the output even to
+ # non-tty.
+ def out_with_macos_man_workaround
+ out.gsub(/.[\b]/, "")
end
end
@@ -113,36 +111,20 @@ RSpec.describe "bundle executable" do
end
end
- context "when ENV['RUBYGEMS_GEMDEPS'] is set" do
- it "displays a warning" do
- gemfile bundled_app_gemfile, <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
- G
-
- bundle :install, :env => { "RUBYGEMS_GEMDEPS" => "foo" }
- expect(err).to include("RUBYGEMS_GEMDEPS")
- expect(err).to include("conflict with Bundler")
-
- bundle :install, :env => { "RUBYGEMS_GEMDEPS" => "" }
- expect(err).not_to include("RUBYGEMS_GEMDEPS")
- end
- end
-
context "with --verbose" do
it "prints the running command" do
- gemfile ""
+ gemfile "source \"#{file_uri_for(gem_repo1)}\""
bundle "info bundler", :verbose => true
expect(out).to start_with("Running `bundle info bundler --verbose` with bundler #{Bundler::VERSION}")
end
it "doesn't print defaults" do
- install_gemfile "", :verbose => true
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", :verbose => true
expect(out).to start_with("Running `bundle install --verbose` with bundler #{Bundler::VERSION}")
end
it "doesn't print defaults" do
- install_gemfile "", :verbose => true
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", :verbose => true
expect(out).to start_with("Running `bundle install --verbose` with bundler #{Bundler::VERSION}")
end
end
diff --git a/spec/bundler/bundler/compact_index_client/updater_spec.rb b/spec/bundler/bundler/compact_index_client/updater_spec.rb
index d8bb15df7e..fe417e3920 100644
--- a/spec/bundler/bundler/compact_index_client/updater_spec.rb
+++ b/spec/bundler/bundler/compact_index_client/updater_spec.rb
@@ -36,14 +36,24 @@ RSpec.describe Bundler::CompactIndexClient::Updater do
end
end
- context "when bundler doesn't have permissions on Dir.tmpdir" do
- it "Errno::EACCES is raised" do
- local_path # create local path before stubbing mktmpdir
- allow(Bundler::Dir).to receive(:mktmpdir) { raise Errno::EACCES }
+ context "when receiving non UTF-8 data and default internal encoding set to ASCII" do
+ let(:response) { double(:response, :body => "\x8B".b) }
+
+ it "works just fine" do
+ old_verbose = $VERBOSE
+ previous_internal_encoding = Encoding.default_internal
+
+ begin
+ $VERBOSE = false
+ Encoding.default_internal = "ASCII"
+ expect(response).to receive(:[]).with("ETag") { nil }
+ expect(fetcher).to receive(:call) { response }
- expect do
updater.update(local_path, remote_path)
- end.to raise_error(Bundler::PermissionError)
+ ensure
+ Encoding.default_internal = previous_internal_encoding
+ $VERBOSE = old_verbose
+ end
end
end
end
diff --git a/spec/bundler/bundler/definition_spec.rb b/spec/bundler/bundler/definition_spec.rb
index dcdde75315..cd70d77cba 100644
--- a/spec/bundler/bundler/definition_spec.rb
+++ b/spec/bundler/bundler/definition_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe Bundler::Definition do
bundle :install, :env => { "DEBUG" => "1" }
expect(out).to match(/re-resolving dependencies/)
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
specs:
@@ -86,7 +86,7 @@ RSpec.describe Bundler::Definition do
bundle :check, :env => { "DEBUG" => "1" }
expect(out).to match(/using resolution from the lockfile/)
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
specs:
@@ -119,15 +119,14 @@ RSpec.describe Bundler::Definition do
bundle :check, :env => { "DEBUG" => "1" }
expect(out).to match(/using resolution from the lockfile/)
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
only_java (1.1-java)
PLATFORMS
- java
- #{lockfile_platforms}
+ #{lockfile_platforms_for(["java"] + local_platforms)}
DEPENDENCIES
only_java
@@ -146,7 +145,7 @@ RSpec.describe Bundler::Definition do
bundle :check, :env => { "DEBUG" => "1" }
expect(out).to match(/using resolution from the lockfile/)
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
@@ -194,7 +193,7 @@ RSpec.describe Bundler::Definition do
context "eager unlock" do
let(:source_list) do
Bundler::SourceList.new.tap do |source_list|
- source_list.global_rubygems_source = file_uri_for(gem_repo4)
+ source_list.add_global_rubygems_remote(file_uri_for(gem_repo4))
end
end
@@ -258,7 +257,7 @@ RSpec.describe Bundler::Definition do
bundled_app_lock,
updated_deps_in_gemfile,
source_list,
- :gems => ["shared_owner_a"], :lock_shared_dependencies => true
+ :gems => ["shared_owner_a"], :conservative => true
)
locked = definition.send(:converge_locked_specs).map(&:name)
expect(locked).to eq %w[isolated_dep isolated_owner shared_dep shared_owner_b]
@@ -268,33 +267,6 @@ RSpec.describe Bundler::Definition do
end
end
- describe "find_resolved_spec" do
- it "with no platform set in SpecSet" do
- ss = Bundler::SpecSet.new([build_stub_spec("a", "1.0"), build_stub_spec("b", "1.0")])
- dfn = Bundler::Definition.new(nil, [], mock_source_list, true)
- dfn.instance_variable_set("@specs", ss)
- found = dfn.find_resolved_spec(build_spec("a", "0.9", "ruby").first)
- expect(found.name).to eq "a"
- expect(found.version.to_s).to eq "1.0"
- end
- end
-
- describe "find_indexed_specs" do
- it "with no platform set in indexed specs" do
- index = Bundler::Index.new
- %w[1.0.0 1.0.1 1.1.0].each {|v| index << build_stub_spec("foo", v) }
-
- dfn = Bundler::Definition.new(nil, [], mock_source_list, true)
- dfn.instance_variable_set("@index", index)
- found = dfn.find_indexed_specs(build_spec("foo", "0.9", "ruby").first)
- expect(found.length).to eq 3
- end
- end
-
- def build_stub_spec(name, version)
- Bundler::StubSpecification.new(name, version, nil, nil)
- end
-
def mock_source_list
Class.new do
def all_sources
diff --git a/spec/bundler/bundler/dep_proxy_spec.rb b/spec/bundler/bundler/dep_proxy_spec.rb
index 84243d2ee2..8d02a33725 100644
--- a/spec/bundler/bundler/dep_proxy_spec.rb
+++ b/spec/bundler/bundler/dep_proxy_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe Bundler::DepProxy do
end
describe "frozen" do
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.5.0")
+ if Gem.ruby_version >= Gem::Version.new("2.5.0")
error = Object.const_get("FrozenError")
else
error = RuntimeError
diff --git a/spec/bundler/bundler/digest_spec.rb b/spec/bundler/bundler/digest_spec.rb
new file mode 100644
index 0000000000..d6bb043fd0
--- /dev/null
+++ b/spec/bundler/bundler/digest_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require "digest"
+require "bundler/digest"
+
+RSpec.describe Bundler::Digest do
+ context "SHA1" do
+ subject { Bundler::Digest }
+ let(:stdlib) { ::Digest::SHA1 }
+
+ it "is compatible with stdlib" do
+ ["foo", "skfjsdlkfjsdf", "3924m", "ldskfj"].each do |payload|
+ expect(subject.sha1(payload)).to be == stdlib.hexdigest(payload)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb
index ff87b57886..a44a12924c 100644
--- a/spec/bundler/bundler/dsl_spec.rb
+++ b/spec/bundler/bundler/dsl_spec.rb
@@ -25,7 +25,46 @@ RSpec.describe Bundler::Dsl do
expect { subject.git_source(:example) }.to raise_error(Bundler::InvalidOption)
end
- context "default hosts", :bundler => "2" do
+ it "converts :github PR to URI using https" do
+ subject.gem("sparks", :github => "https://github.com/indirect/sparks/pull/5")
+ github_uri = "https://github.com/indirect/sparks.git"
+ expect(subject.dependencies.first.source.uri).to eq(github_uri)
+ expect(subject.dependencies.first.source.branch).to eq("refs/pull/5/head")
+ end
+
+ it "rejects :github PR URI with a branch, ref or tag" do
+ expect do
+ subject.gem("sparks", :github => "https://github.com/indirect/sparks/pull/5", :branch => "foo")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :branch option can't be used with `github: "https://github.com/indirect/sparks/pull/5"`),
+ )
+
+ expect do
+ subject.gem("sparks", :github => "https://github.com/indirect/sparks/pull/5", :ref => "foo")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :ref option can't be used with `github: "https://github.com/indirect/sparks/pull/5"`),
+ )
+
+ expect do
+ subject.gem("sparks", :github => "https://github.com/indirect/sparks/pull/5", :tag => "foo")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :tag option can't be used with `github: "https://github.com/indirect/sparks/pull/5"`),
+ )
+ end
+
+ it "rejects :github with :git" do
+ expect do
+ subject.gem("sparks", :github => "indirect/sparks", :git => "https://github.com/indirect/sparks.git")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :git option can't be used with `github: "indirect/sparks"`),
+ )
+ end
+
+ context "default hosts", :bundler => "< 3" do
it "converts :github to URI using https" do
subject.gem("sparks", :github => "indirect/sparks")
github_uri = "https://github.com/indirect/sparks.git"
@@ -195,19 +234,6 @@ RSpec.describe Bundler::Dsl do
# gem 'spree_api'
# gem 'spree_backend'
# end
- describe "#github", :bundler => "< 3" do
- it "from github" do
- spree_gems = %w[spree_core spree_api spree_backend]
- subject.github "spree" do
- spree_gems.each {|spree_gem| subject.send :gem, spree_gem }
- end
-
- subject.dependencies.each do |d|
- expect(d.source.uri).to eq("https://github.com/spree/spree.git")
- end
- end
- end
-
describe "#github" do
it "from github" do
spree_gems = %w[spree_core spree_api spree_backend]
@@ -254,4 +280,21 @@ RSpec.describe Bundler::Dsl do
end
end
end
+
+ describe "#check_primary_source_safety" do
+ context "when a global source is not defined implicitly" do
+ it "will raise a major deprecation warning" do
+ not_a_global_source = double("not-a-global-source", :no_remotes? => true)
+ allow(Bundler::Source::Rubygems).to receive(:new).and_return(not_a_global_source)
+
+ warning = "This Gemfile does not include an explicit global source. " \
+ "Not using an explicit global source may result in a different lockfile being generated depending on " \
+ "the gems you have installed locally before bundler is run. " \
+ "Instead, define a global source in your Gemfile like this: source \"https://rubygems.org\"."
+ expect(Bundler::SharedHelpers).to receive(:major_deprecation).with(2, warning)
+
+ subject.check_primary_source_safety
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/env_spec.rb b/spec/bundler/bundler/env_spec.rb
index ac65c34b0d..8cd59311b2 100644
--- a/spec/bundler/bundler/env_spec.rb
+++ b/spec/bundler/bundler/env_spec.rb
@@ -73,7 +73,7 @@ RSpec.describe Bundler::Env do
context "when there is a Gemfile and a lockfile and print_gemfile is true" do
before do
- gemfile "gem 'rack', '1.0.0'"
+ gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem 'rack', '1.0.0'"
lockfile <<-L
GEM
@@ -112,6 +112,34 @@ RSpec.describe Bundler::Env do
end
end
+ context "when there's bundler config with credentials" do
+ before do
+ bundle "config set https://localgemserver.test/ user:pass"
+ end
+
+ let(:output) { described_class.report(:print_gemfile => true) }
+
+ it "prints the config with redacted values" do
+ expect(output).to include("https://localgemserver.test")
+ expect(output).to include("user:[REDACTED]")
+ expect(output).to_not include("user:pass")
+ end
+ end
+
+ context "when there's bundler config with OAuth token credentials" do
+ before do
+ bundle "config set https://localgemserver.test/ api_token:x-oauth-basic"
+ end
+
+ let(:output) { described_class.report(:print_gemfile => true) }
+
+ it "prints the config with redacted values" do
+ expect(output).to include("https://localgemserver.test")
+ expect(output).to include("[REDACTED]:x-oauth-basic")
+ expect(output).to_not include("api_token:x-oauth-basic")
+ end
+ end
+
context "when Gemfile contains a gemspec and print_gemspecs is true" do
let(:gemspec) do
strip_whitespace(<<-GEMSPEC)
@@ -123,7 +151,7 @@ RSpec.describe Bundler::Env do
end
before do
- gemfile("gemspec")
+ gemfile("source \"#{file_uri_for(gem_repo1)}\"; gemspec")
File.open(bundled_app.join("foo.gemspec"), "wb") do |f|
f.write(gemspec)
diff --git a/spec/bundler/bundler/fetcher/downloader_spec.rb b/spec/bundler/bundler/fetcher/downloader_spec.rb
index ba8451d9fa..94a0993a54 100644
--- a/spec/bundler/bundler/fetcher/downloader_spec.rb
+++ b/spec/bundler/bundler/fetcher/downloader_spec.rb
@@ -83,6 +83,11 @@ RSpec.describe Bundler::Fetcher::Downloader do
/Authentication is required for www.uri-to-fetch.com/)
end
+ it "should raise a Bundler::Fetcher::AuthenticationRequiredError with advices" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
+ /`bundle config set --global www\.uri-to-fetch\.com username:password`.*`BUNDLE_WWW__URI___TO___FETCH__COM`/m)
+ end
+
context "when the there are credentials provided in the request" do
let(:uri) { Bundler::URI("http://user:password@www.uri-to-fetch.com") }
@@ -188,7 +193,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
let(:message) { "undefined method 'undefined_method_call'" }
it "should raise the original NoMethodError" do
- expect { subject.request(uri, options) }.to raise_error(NoMethodError, "undefined method 'undefined_method_call'")
+ expect { subject.request(uri, options) }.to raise_error(NoMethodError, /undefined method 'undefined_method_call'/)
end
end
end
@@ -226,16 +231,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
- context "when error message is about getaddrinfo issues" do
- let(:message) { "getaddrinfo: nodename nor servname provided for http://www.uri-to-fetch.com" }
-
- it "should raise a Bundler::Fetcher::NetworkDownError" do
- expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::NetworkDownError,
- /Could not reach host www.uri-to-fetch.com/)
- end
- end
-
- context "when error message is about neither host down or getaddrinfo" do
+ context "when error message is not about host down" do
let(:message) { "other error about network" }
it "should raise a Bundler::HTTPError" do
diff --git a/spec/bundler/bundler/fetcher/index_spec.rb b/spec/bundler/bundler/fetcher/index_spec.rb
index b8ce46321e..f0db07583c 100644
--- a/spec/bundler/bundler/fetcher/index_spec.rb
+++ b/spec/bundler/bundler/fetcher/index_spec.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "rubygems/remote_fetcher"
+
RSpec.describe Bundler::Fetcher::Index do
let(:downloader) { nil }
let(:remote) { nil }
diff --git a/spec/bundler/bundler/gem_helper_spec.rb b/spec/bundler/bundler/gem_helper_spec.rb
index d718615ad2..2c43719aa1 100644
--- a/spec/bundler/bundler/gem_helper_spec.rb
+++ b/spec/bundler/bundler/gem_helper_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Bundler::GemHelper do
let(:app_gemspec_path) { app_path.join("#{app_name}.gemspec") }
before(:each) do
- global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__RUBOCOP" => "false",
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__LINTER" => "false",
"BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__CHANGELOG" => "false"
bundle "gem #{app_name}"
prepare_gemspec(app_gemspec_path)
@@ -61,10 +61,16 @@ RSpec.describe Bundler::GemHelper do
mock_confirm_message message
end
+ def mock_checksum_message(name, version)
+ message = "#{name} #{version} checksum written to checksums/#{name}-#{version}.gem.sha512."
+ mock_confirm_message message
+ end
+
subject! { Bundler::GemHelper.new(app_path) }
let(:app_version) { "0.1.0" }
let(:app_gem_dir) { app_path.join("pkg") }
let(:app_gem_path) { app_gem_dir.join("#{app_name}-#{app_version}.gem") }
+ let(:app_sha_path) { app_path.join("checksums", "#{app_name}-#{app_version}.gem.sha512") }
let(:app_gemspec_content) { File.read(app_gemspec_path) }
before(:each) do
@@ -162,6 +168,37 @@ RSpec.describe Bundler::GemHelper do
end
end
+ describe "#build_checksum" do
+ context "when build was successful" do
+ it "creates .sha512 file" do
+ mock_build_message app_name, app_version
+ mock_checksum_message app_name, app_version
+ subject.build_checksum
+ expect(app_sha_path).to exist
+ end
+ end
+ context "when building in the current working directory" do
+ it "creates a .sha512 file" do
+ mock_build_message app_name, app_version
+ mock_checksum_message app_name, app_version
+ Dir.chdir app_path do
+ Bundler::GemHelper.new.build_checksum
+ end
+ expect(app_sha_path).to exist
+ end
+ end
+ context "when building in a location relative to the current working directory" do
+ it "creates a .sha512 file" do
+ mock_build_message app_name, app_version
+ mock_checksum_message app_name, app_version
+ Dir.chdir File.dirname(app_path) do
+ Bundler::GemHelper.new(File.basename(app_path)).build_checksum
+ end
+ expect(app_sha_path).to exist
+ end
+ end
+ end
+
describe "#install_gem" do
context "when installation was successful" do
it "gem is installed" do
@@ -182,7 +219,7 @@ RSpec.describe Bundler::GemHelper do
FileUtils.touch app_gem_path
app_gem_path
end
- expect { subject.install_gem }.to raise_error(/Couldn't install gem/)
+ expect { subject.install_gem }.to raise_error(/Running `#{gem_bin} install #{app_gem_path}` failed/)
end
end
end
diff --git a/spec/bundler/bundler/installer/parallel_installer_spec.rb b/spec/bundler/bundler/installer/parallel_installer_spec.rb
index ace5c1a23a..e680633862 100644
--- a/spec/bundler/bundler/installer/parallel_installer_spec.rb
+++ b/spec/bundler/bundler/installer/parallel_installer_spec.rb
@@ -44,4 +44,37 @@ The missing gems are:
end
end
end
+
+ context "when the spec set is not a valid resolution" do
+ let(:all_specs) do
+ [
+ build_spec("cucumber", "4.1.0") {|s| s.runtime "diff-lcs", "< 1.4" },
+ build_spec("diff-lcs", "1.4.4"),
+ ].flatten
+ end
+
+ it "prints a warning" do
+ expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
+Your lockfile doesn't include a valid resolution.
+You can fix this by regenerating your lockfile or trying to manually editing the bad locked gems to a version that satisfies all dependencies.
+The unmet dependencies are:
+* diff-lcs (< 1.4), depended upon cucumber-4.1.0, unsatisfied by diff-lcs-1.4.4
+ W
+ subject.check_for_unmet_dependencies
+ end
+ end
+
+ context "when the spec set is a valid resolution" do
+ let(:all_specs) do
+ [
+ build_spec("cucumber", "4.1.0") {|s| s.runtime "diff-lcs", "< 1.4" },
+ build_spec("diff-lcs", "1.3"),
+ ].flatten
+ end
+
+ it "doesn't print a warning" do
+ expect(Bundler.ui).not_to receive(:warn)
+ subject.check_for_unmet_dependencies
+ end
+ end
end
diff --git a/spec/bundler/bundler/installer/spec_installation_spec.rb b/spec/bundler/bundler/installer/spec_installation_spec.rb
index a9cf09a372..e63ef26cb3 100644
--- a/spec/bundler/bundler/installer/spec_installation_spec.rb
+++ b/spec/bundler/bundler/installer/spec_installation_spec.rb
@@ -8,6 +8,10 @@ RSpec.describe Bundler::ParallelInstaller::SpecInstallation do
def a_spec.name
"I like tests"
end
+
+ def a_spec.full_name
+ "I really like tests"
+ end
a_spec
end
diff --git a/spec/bundler/bundler/plugin/dsl_spec.rb b/spec/bundler/bundler/plugin/dsl_spec.rb
index be23db3bba..00e39dca69 100644
--- a/spec/bundler/bundler/plugin/dsl_spec.rb
+++ b/spec/bundler/bundler/plugin/dsl_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Bundler::Plugin::DSL do
expect(dsl.inferred_plugins).to eq(["bundler-source-news"])
end
- it "registers a source type plugin only once for multiple declataions" do
+ it "registers a source type plugin only once for multiple declarations" do
expect(dsl).to receive(:plugin).with("bundler-source-news").and_call_original.once
dsl.source("some_random_url", :type => "news") {}
diff --git a/spec/bundler/bundler/plugin/index_spec.rb b/spec/bundler/bundler/plugin/index_spec.rb
index 925dc558ac..d34b0de342 100644
--- a/spec/bundler/bundler/plugin/index_spec.rb
+++ b/spec/bundler/bundler/plugin/index_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe Bundler::Plugin::Index do
before do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- gemfile ""
+ gemfile "source \"#{file_uri_for(gem_repo1)}\""
path = lib_path(plugin_name)
index.register_plugin("new-plugin", path.to_s, [path.join("lib").to_s], commands, sources, hooks)
end
@@ -22,7 +22,7 @@ RSpec.describe Bundler::Plugin::Index do
expect(index.plugin_path(plugin_name)).to eq(lib_path(plugin_name))
end
- it "load_paths is available for retrival" do
+ it "load_paths is available for retrieval" do
expect(index.load_paths(plugin_name)).to eq([lib_path(plugin_name).join("lib").to_s])
end
@@ -98,7 +98,13 @@ RSpec.describe Bundler::Plugin::Index do
expect(index.hook_plugins("after-bar")).to eq([plugin_name])
end
- context "that are not registered", :focused do
+ it "is gone after unregistration" do
+ expect(index.index_file.read).to include("after-bar:\n - \"new-plugin\"\n")
+ index.unregister_plugin(plugin_name)
+ expect(index.index_file.read).to_not include("after-bar:\n - \n")
+ end
+
+ context "that are not registered" do
let(:file) { double("index-file") }
before do
diff --git a/spec/bundler/bundler/plugin_spec.rb b/spec/bundler/bundler/plugin_spec.rb
index 8c95723bcc..25cae014dd 100644
--- a/spec/bundler/bundler/plugin_spec.rb
+++ b/spec/bundler/bundler/plugin_spec.rb
@@ -67,6 +67,8 @@ RSpec.describe Bundler::Plugin do
end
it "passes the name and options to installer" do
+ allow(index).to receive(:installed?).
+ with("new-plugin")
allow(installer).to receive(:install).with(["new-plugin"], opts) do
{ "new-plugin" => spec }
end.once
@@ -75,6 +77,8 @@ RSpec.describe Bundler::Plugin do
end
it "validates the installed plugin" do
+ allow(index).to receive(:installed?).
+ with("new-plugin")
allow(subject).
to receive(:validate_plugin!).with(lib_path("new-plugin")).once
@@ -82,6 +86,8 @@ RSpec.describe Bundler::Plugin do
end
it "registers the plugin with index" do
+ allow(index).to receive(:installed?).
+ with("new-plugin")
allow(index).to receive(:register_plugin).
with("new-plugin", lib_path("new-plugin").to_s, [lib_path("new-plugin").join("lib").to_s], []).once
subject.install ["new-plugin"], opts
@@ -98,6 +104,7 @@ RSpec.describe Bundler::Plugin do
end.once
allow(subject).to receive(:validate_plugin!).twice
+ allow(index).to receive(:installed?).twice
allow(index).to receive(:register_plugin).twice
subject.install ["new-plugin", "another-plugin"], opts
end
@@ -112,6 +119,7 @@ RSpec.describe Bundler::Plugin do
before do
allow(Plugin::DSL).to receive(:new) { builder }
allow(builder).to receive(:eval_gemfile).with(gemfile)
+ allow(builder).to receive(:check_primary_source_safety)
allow(builder).to receive(:to_definition) { definition }
allow(builder).to receive(:inferred_plugins) { [] }
end
@@ -278,7 +286,7 @@ RSpec.describe Bundler::Plugin do
Bundler::Plugin::Events.send(:define, :EVENT_2, "event-2")
allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_1).
- and_return(["foo-plugin"])
+ and_return(["foo-plugin", "", nil])
allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_2).
and_return(["foo-plugin"])
allow(index).to receive(:plugin_path).with("foo-plugin").and_return(path)
diff --git a/spec/bundler/bundler/rubygems_integration_spec.rb b/spec/bundler/bundler/rubygems_integration_spec.rb
index 11fa2f4e0d..7557c806fe 100644
--- a/spec/bundler/bundler/rubygems_integration_spec.rb
+++ b/spec/bundler/bundler/rubygems_integration_spec.rb
@@ -43,12 +43,10 @@ RSpec.describe Bundler::RubygemsIntegration do
describe "#download_gem" do
let(:bundler_retry) { double(Bundler::Retry) }
- let(:retry) { double("Bundler::Retry") }
- let(:uri) { Bundler::URI.parse("https://foo.bar") }
- let(:path) { Gem.path.first }
+ let(:uri) { Bundler::URI.parse("https://foo.bar") }
+ let(:cache_dir) { "#{Gem.path.first}/cache" }
let(:spec) do
- spec = Bundler::RemoteSpecification.new("Foo", Gem::Version.new("2.5.2"),
- Gem::Platform::RUBY, nil)
+ spec = Gem::Specification.new("Foo", Gem::Version.new("2.5.2"))
spec.remote = Bundler::Source::Rubygems::Remote.new(uri.to_s)
spec
end
@@ -56,13 +54,13 @@ RSpec.describe Bundler::RubygemsIntegration do
it "successfully downloads gem with retries" do
expect(Bundler.rubygems).to receive(:gem_remote_fetcher).and_return(fetcher)
- expect(fetcher).to receive(:headers=).with("X-Gemfile-Source" => "https://foo.bar")
+ expect(fetcher).to receive(:headers=).with({ "X-Gemfile-Source" => "https://foo.bar" })
expect(Bundler::Retry).to receive(:new).with("download gem from #{uri}/").
and_return(bundler_retry)
expect(bundler_retry).to receive(:attempts).and_yield
- expect(fetcher).to receive(:download).with(spec, uri, path)
+ expect(fetcher).to receive(:cache_update_path)
- Bundler.rubygems.download_gem(spec, uri, path)
+ Bundler.rubygems.download_gem(spec, uri, cache_dir)
end
end
@@ -78,7 +76,7 @@ RSpec.describe Bundler::RubygemsIntegration do
it "sets the 'X-Gemfile-Source' header containing the original source" do
expect(Bundler.rubygems).to receive(:gem_remote_fetcher).twice.and_return(fetcher)
- expect(fetcher).to receive(:headers=).with("X-Gemfile-Source" => "http://zombo.com").twice
+ expect(fetcher).to receive(:headers=).with({ "X-Gemfile-Source" => "http://zombo.com" }).twice
expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(specs_response)
expect(fetcher).to receive(:fetch_path).with(uri + "prerelease_specs.4.8.gz").and_return(prerelease_specs_response)
result = Bundler.rubygems.fetch_all_remote_specs(remote_with_mirror)
diff --git a/spec/bundler/bundler/settings_spec.rb b/spec/bundler/bundler/settings_spec.rb
index 116a038445..24e3de7ba8 100644
--- a/spec/bundler/bundler/settings_spec.rb
+++ b/spec/bundler/bundler/settings_spec.rb
@@ -64,13 +64,10 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
describe "#global_config_file" do
context "when $HOME is not accessible" do
- context "when $TMPDIR is not writable" do
- it "does not raise" do
- expect(Bundler.rubygems).to receive(:user_home).twice.and_return(nil)
- expect(Bundler).to receive(:tmp).twice.and_raise(Errno::EROFS, "Read-only file system @ dir_s_mkdir - /tmp/bundler")
+ it "does not raise" do
+ expect(Bundler.rubygems).to receive(:user_home).twice.and_return(nil)
- expect(subject.send(:global_config_file)).to be_nil
- end
+ expect(subject.send(:global_config_file)).to be_nil
end
end
end
@@ -312,16 +309,26 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
describe "BUNDLE_ keys format" do
let(:settings) { described_class.new(bundled_app(".bundle")) }
- it "converts older keys without double dashes" do
+ it "converts older keys without double underscore" do
config("BUNDLE_MY__PERSONAL.RACK" => "~/Work/git/rack")
expect(settings["my.personal.rack"]).to eq("~/Work/git/rack")
end
- it "converts older keys without trailing slashes and double dashes" do
+ it "converts older keys without trailing slashes and double underscore" do
config("BUNDLE_MIRROR__HTTPS://RUBYGEMS.ORG" => "http://rubygems-mirror.org")
expect(settings["mirror.https://rubygems.org/"]).to eq("http://rubygems-mirror.org")
end
+ it "converts older keys with dashes" do
+ config("BUNDLE_MY-PERSONAL-SERVER__ORG" => "my-personal-server.org")
+ expect(Bundler.ui).to receive(:warn).with(
+ "Your #{bundled_app(".bundle/config")} config includes `BUNDLE_MY-PERSONAL-SERVER__ORG`, which contains the dash character (`-`).\n" \
+ "This is deprecated, because configuration through `ENV` should be possible, but `ENV` keys cannot include dashes.\n" \
+ "Please edit #{bundled_app(".bundle/config")} and replace any dashes in configuration keys with a triple underscore (`___`)."
+ )
+ expect(settings["my-personal-server.org"]).to eq("my-personal-server.org")
+ end
+
it "reads newer keys format properly" do
config("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://rubygems-mirror.org")
expect(settings["mirror.https://rubygems.org/"]).to eq("http://rubygems-mirror.org")
diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb
index 97f06973cb..cffd72cc3f 100644
--- a/spec/bundler/bundler/source/git/git_proxy_spec.rb
+++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb
@@ -11,21 +11,21 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with configured credentials" do
it "adds username and password to URI" do
Bundler.settings.temporary(uri => "u:p") do
- expect(subject).to receive(:git_retry).with("clone", "https://u:p@github.com/rubygems/rubygems.git", any_args)
+ expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s)
subject.checkout
end
end
it "adds username and password to URI for host" do
Bundler.settings.temporary("github.com" => "u:p") do
- expect(subject).to receive(:git_retry).with("clone", "https://u:p@github.com/rubygems/rubygems.git", any_args)
+ expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s)
subject.checkout
end
end
it "does not add username and password to mismatched URI" do
Bundler.settings.temporary("https://u:p@github.com/rubygems/rubygems-mismatch.git" => "u:p") do
- expect(subject).to receive(:git_retry).with("clone", uri, any_args)
+ expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", uri, path.to_s)
subject.checkout
end
end
@@ -34,7 +34,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
Bundler.settings.temporary("github.com" => "u:p") do
original = "https://orig:info@github.com/rubygems/rubygems.git"
subject = described_class.new(Pathname("path"), original, "HEAD")
- expect(subject).to receive(:git_retry).with("clone", original, any_args)
+ expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--", original, path.to_s)
subject.checkout
end
end
@@ -148,4 +148,24 @@ RSpec.describe Bundler::Source::Git::GitProxy do
end
end
end
+
+ it "doesn't allow arbitrary code execution through Gemfile uris with a leading dash" do
+ gemfile <<~G
+ gem "poc", git: "-u./pay:load.sh"
+ G
+
+ file = bundled_app("pay:load.sh")
+
+ create_file file, <<~RUBY
+ #!/bin/sh
+
+ touch #{bundled_app("canary")}
+ RUBY
+
+ FileUtils.chmod("+x", file)
+
+ bundle :lock, :raise_on_error => false
+
+ expect(Pathname.new(bundled_app("canary"))).not_to exist
+ end
end
diff --git a/spec/bundler/bundler/source/git_spec.rb b/spec/bundler/bundler/source/git_spec.rb
index 6668b6e69a..ed6dc3cd29 100644
--- a/spec/bundler/bundler/source/git_spec.rb
+++ b/spec/bundler/bundler/source/git_spec.rb
@@ -24,5 +24,50 @@ RSpec.describe Bundler::Source::Git do
expect(subject.to_s).to eq "https://x-oauth-basic@github.com/foo/bar.git"
end
end
+
+ context "when the source has a glob specifier" do
+ let(:glob) { "bar/baz/*.gemspec" }
+ let(:options) do
+ { "uri" => uri, "glob" => glob }
+ end
+
+ it "includes it" do
+ expect(subject.to_s).to eq "https://github.com/foo/bar.git (glob: bar/baz/*.gemspec)"
+ end
+ end
+
+ context "when the source has a reference" do
+ let(:git_proxy_stub) do
+ instance_double(Bundler::Source::Git::GitProxy, :revision => "123abc", :branch => "v1.0.0")
+ end
+ let(:options) do
+ { "uri" => uri, "ref" => "v1.0.0" }
+ end
+
+ before do
+ allow(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub)
+ end
+
+ it "includes it" do
+ expect(subject.to_s).to eq "https://github.com/foo/bar.git (at v1.0.0@123abc)"
+ end
+ end
+
+ context "when the source has both reference and glob specifiers" do
+ let(:git_proxy_stub) do
+ instance_double(Bundler::Source::Git::GitProxy, :revision => "123abc", :branch => "v1.0.0")
+ end
+ let(:options) do
+ { "uri" => uri, "ref" => "v1.0.0", "glob" => "gems/foo/*.gemspec" }
+ end
+
+ before do
+ allow(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub)
+ end
+
+ it "includes both" do
+ expect(subject.to_s).to eq "https://github.com/foo/bar.git (at v1.0.0@123abc, glob: gems/foo/*.gemspec)"
+ end
+ end
end
end
diff --git a/spec/bundler/bundler/source/rubygems_spec.rb b/spec/bundler/bundler/source/rubygems_spec.rb
index 7c457a7265..884fa81046 100644
--- a/spec/bundler/bundler/source/rubygems_spec.rb
+++ b/spec/bundler/bundler/source/rubygems_spec.rb
@@ -30,4 +30,18 @@ RSpec.describe Bundler::Source::Rubygems do
end
end
end
+
+ describe "#no_remotes?" do
+ context "when no remote provided" do
+ it "returns a truthy value" do
+ expect(described_class.new("remotes" => []).no_remotes?).to be_truthy
+ end
+ end
+
+ context "when a remote provided" do
+ it "returns a falsey value" do
+ expect(described_class.new("remotes" => ["https://rubygems.org"]).no_remotes?).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/source_list_spec.rb b/spec/bundler/bundler/source_list_spec.rb
index 3a0691b959..f860e9ff58 100644
--- a/spec/bundler/bundler/source_list_spec.rb
+++ b/spec/bundler/bundler/source_list_spec.rb
@@ -115,15 +115,15 @@ RSpec.describe Bundler::SourceList do
end
end
- describe "#add_rubygems_remote", :bundler => "< 3" do
- let!(:returned_source) { source_list.add_rubygems_remote("https://rubygems.org/") }
+ describe "#add_global_rubygems_remote" do
+ let!(:returned_source) { source_list.add_global_rubygems_remote("https://rubygems.org/") }
it "returns the aggregate rubygems source" do
expect(returned_source).to be_instance_of(Bundler::Source::Rubygems)
end
it "adds the provided remote to the beginning of the aggregate source" do
- source_list.add_rubygems_remote("https://othersource.org")
+ source_list.add_global_rubygems_remote("https://othersource.org")
expect(returned_source.remotes).to eq [
Bundler::URI("https://othersource.org/"),
Bundler::URI("https://rubygems.org/"),
@@ -212,22 +212,22 @@ RSpec.describe Bundler::SourceList do
describe "#path_sources" do
it "returns an empty array when no path sources have been added" do
- source_list.add_rubygems_remote("https://rubygems.org")
+ source_list.add_global_rubygems_remote("https://rubygems.org")
source_list.add_git_source("uri" => "git://host/path.git")
expect(source_list.path_sources).to be_empty
end
it "returns path sources in the reverse order that they were added" do
source_list.add_git_source("uri" => "git://third-git.org/path.git")
- source_list.add_rubygems_remote("https://fifth-rubygems.org")
+ source_list.add_global_rubygems_remote("https://fifth-rubygems.org")
source_list.add_path_source("path" => "/third/path/to/gem")
- source_list.add_rubygems_remote("https://fourth-rubygems.org")
+ source_list.add_global_rubygems_remote("https://fourth-rubygems.org")
source_list.add_path_source("path" => "/second/path/to/gem")
- source_list.add_rubygems_remote("https://third-rubygems.org")
+ source_list.add_global_rubygems_remote("https://third-rubygems.org")
source_list.add_git_source("uri" => "git://second-git.org/path.git")
- source_list.add_rubygems_remote("https://second-rubygems.org")
+ source_list.add_global_rubygems_remote("https://second-rubygems.org")
source_list.add_path_source("path" => "/first/path/to/gem")
- source_list.add_rubygems_remote("https://first-rubygems.org")
+ source_list.add_global_rubygems_remote("https://first-rubygems.org")
source_list.add_git_source("uri" => "git://first-git.org/path.git")
expect(source_list.path_sources).to eq [
@@ -240,7 +240,7 @@ RSpec.describe Bundler::SourceList do
describe "#git_sources" do
it "returns an empty array when no git sources have been added" do
- source_list.add_rubygems_remote("https://rubygems.org")
+ source_list.add_global_rubygems_remote("https://rubygems.org")
source_list.add_path_source("path" => "/path/to/gem")
expect(source_list.git_sources).to be_empty
@@ -248,15 +248,15 @@ RSpec.describe Bundler::SourceList do
it "returns git sources in the reverse order that they were added" do
source_list.add_git_source("uri" => "git://third-git.org/path.git")
- source_list.add_rubygems_remote("https://fifth-rubygems.org")
+ source_list.add_global_rubygems_remote("https://fifth-rubygems.org")
source_list.add_path_source("path" => "/third/path/to/gem")
- source_list.add_rubygems_remote("https://fourth-rubygems.org")
+ source_list.add_global_rubygems_remote("https://fourth-rubygems.org")
source_list.add_path_source("path" => "/second/path/to/gem")
- source_list.add_rubygems_remote("https://third-rubygems.org")
+ source_list.add_global_rubygems_remote("https://third-rubygems.org")
source_list.add_git_source("uri" => "git://second-git.org/path.git")
- source_list.add_rubygems_remote("https://second-rubygems.org")
+ source_list.add_global_rubygems_remote("https://second-rubygems.org")
source_list.add_path_source("path" => "/first/path/to/gem")
- source_list.add_rubygems_remote("https://first-rubygems.org")
+ source_list.add_global_rubygems_remote("https://first-rubygems.org")
source_list.add_git_source("uri" => "git://first-git.org/path.git")
expect(source_list.git_sources).to eq [
@@ -269,7 +269,7 @@ RSpec.describe Bundler::SourceList do
describe "#plugin_sources" do
it "returns an empty array when no plugin sources have been added" do
- source_list.add_rubygems_remote("https://rubygems.org")
+ source_list.add_global_rubygems_remote("https://rubygems.org")
source_list.add_path_source("path" => "/path/to/gem")
expect(source_list.plugin_sources).to be_empty
@@ -279,13 +279,13 @@ RSpec.describe Bundler::SourceList do
source_list.add_plugin_source("new_source", "uri" => "https://third-git.org/path.git")
source_list.add_git_source("https://new-git.org")
source_list.add_path_source("path" => "/third/path/to/gem")
- source_list.add_rubygems_remote("https://fourth-rubygems.org")
+ source_list.add_global_rubygems_remote("https://fourth-rubygems.org")
source_list.add_path_source("path" => "/second/path/to/gem")
- source_list.add_rubygems_remote("https://third-rubygems.org")
+ source_list.add_global_rubygems_remote("https://third-rubygems.org")
source_list.add_plugin_source("new_source", "uri" => "git://second-git.org/path.git")
- source_list.add_rubygems_remote("https://second-rubygems.org")
+ source_list.add_global_rubygems_remote("https://second-rubygems.org")
source_list.add_path_source("path" => "/first/path/to/gem")
- source_list.add_rubygems_remote("https://first-rubygems.org")
+ source_list.add_global_rubygems_remote("https://first-rubygems.org")
source_list.add_plugin_source("new_source", "uri" => "git://first-git.org/path.git")
expect(source_list.plugin_sources).to eq [
@@ -339,7 +339,7 @@ RSpec.describe Bundler::SourceList do
describe "#get" do
context "when it includes an equal source" do
let(:rubygems_source) { Bundler::Source::Rubygems.new("remotes" => ["https://rubygems.org"]) }
- before { @equal_source = source_list.add_rubygems_remote("https://rubygems.org") }
+ before { @equal_source = source_list.add_global_rubygems_remote("https://rubygems.org") }
it "returns the equal source" do
expect(source_list.get(rubygems_source)).to be @equal_source
@@ -372,26 +372,7 @@ RSpec.describe Bundler::SourceList do
source_list.add_git_source("uri" => "git://first-git.org/path.git")
end
- it "combines the rubygems sources into a single instance, removing duplicate remotes from the end", :bundler => "< 3" do
- expect(source_list.lock_sources).to eq [
- Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"),
- Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"),
- Bundler::Source::Git.new("uri" => "git://third-git.org/path.git"),
- ASourcePlugin.new("uri" => "https://second-plugin.org/random"),
- ASourcePlugin.new("uri" => "https://third-bar.org/foo"),
- Bundler::Source::Path.new("path" => "/first/path/to/gem"),
- Bundler::Source::Path.new("path" => "/second/path/to/gem"),
- Bundler::Source::Path.new("path" => "/third/path/to/gem"),
- Bundler::Source::Rubygems.new("remotes" => [
- "https://duplicate-rubygems.org",
- "https://first-rubygems.org",
- "https://second-rubygems.org",
- "https://third-rubygems.org",
- ]),
- ]
- end
-
- it "returns all sources, without combining rubygems sources", :bundler => "3" do
+ it "returns all sources, without combining rubygems sources" do
expect(source_list.lock_sources).to eq [
Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"),
Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"),
@@ -460,4 +441,19 @@ RSpec.describe Bundler::SourceList do
source_list.remote!
end
end
+
+ describe "implicit_global_source?" do
+ context "when a global rubygem source provided" do
+ it "returns a falsy value" do
+ source_list.add_global_rubygems_remote("https://rubygems.org")
+
+ expect(source_list.implicit_global_source?).to be_falsey
+ end
+ end
+ context "when no global rubygem source provided" do
+ it "returns a truthy value" do
+ expect(source_list.implicit_global_source?).to be_truthy
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/stub_specification_spec.rb b/spec/bundler/bundler/stub_specification_spec.rb
index 0a54f9d7b5..fb612813c2 100644
--- a/spec/bundler/bundler/stub_specification_spec.rb
+++ b/spec/bundler/bundler/stub_specification_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Bundler::StubSpecification do
s.name = "gemname"
s.version = "1.0.0"
s.loaded_from = __FILE__
+ s.extensions = "ext/gemname"
end
described_class.from_stub(gemspec)
@@ -17,4 +18,30 @@ RSpec.describe Bundler::StubSpecification do
expect(stub).to be(with_bundler_stub_spec)
end
end
+
+ describe "#manually_installed?" do
+ it "returns true if installed_by_version is nil or 0" do
+ stub = described_class.from_stub(with_bundler_stub_spec)
+ expect(stub.manually_installed?).to be true
+ end
+
+ it "returns false if installed_by_version is greater than 0" do
+ stub = described_class.from_stub(with_bundler_stub_spec)
+ stub.installed_by_version = Gem::Version.new(1)
+ expect(stub.manually_installed?).to be false
+ end
+ end
+
+ describe "#missing_extensions?" do
+ it "returns false if manually_installed?" do
+ stub = described_class.from_stub(with_bundler_stub_spec)
+ expect(stub.missing_extensions?).to be false
+ end
+
+ it "returns true if not manually_installed?" do
+ stub = described_class.from_stub(with_bundler_stub_spec)
+ stub.installed_by_version = Gem::Version.new(1)
+ expect(stub.missing_extensions?).to be true
+ end
+ end
end
diff --git a/spec/bundler/bundler/worker_spec.rb b/spec/bundler/bundler/worker_spec.rb
index 2e5642709d..e4ebbd2932 100644
--- a/spec/bundler/bundler/worker_spec.rb
+++ b/spec/bundler/bundler/worker_spec.rb
@@ -19,4 +19,51 @@ RSpec.describe Bundler::Worker do
end
end
end
+
+ describe "handling interrupts" do
+ let(:status) do
+ pid = Process.fork do
+ $stderr.reopen File.new("/dev/null", "w")
+ Signal.trap "INT", previous_interrupt_handler
+ subject.enq "a"
+ subject.stop unless interrupt_before_stopping
+ Process.kill "INT", Process.pid
+ end
+
+ Process.wait2(pid).last
+ end
+
+ before do
+ skip "requires Process.fork" unless Process.respond_to?(:fork)
+ end
+
+ context "when interrupted before stopping" do
+ let(:interrupt_before_stopping) { true }
+ let(:previous_interrupt_handler) { ->(*) { exit 0 } }
+
+ it "aborts" do
+ expect(status.exitstatus).to eq(1)
+ end
+ end
+
+ context "when interrupted after stopping" do
+ let(:interrupt_before_stopping) { false }
+
+ context "when the previous interrupt handler was the default" do
+ let(:previous_interrupt_handler) { "DEFAULT" }
+
+ it "uses the default interrupt handler" do
+ expect(status).to be_signaled
+ end
+ end
+
+ context "when the previous interrupt handler was customized" do
+ let(:previous_interrupt_handler) { ->(*) { exit 42 } }
+
+ it "restores the custom interrupt handler after stopping" do
+ expect(status.exitstatus).to eq(42)
+ end
+ end
+ end
+ end
end
diff --git a/spec/bundler/cache/gems_spec.rb b/spec/bundler/cache/gems_spec.rb
index 161ec64218..a8382a5d8c 100644
--- a/spec/bundler/cache/gems_spec.rb
+++ b/spec/bundler/cache/gems_spec.rb
@@ -4,6 +4,7 @@ RSpec.describe "bundle cache" do
shared_examples_for "when there are only gemsources" do
before :each do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack'
G
@@ -39,6 +40,7 @@ RSpec.describe "bundle cache" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -49,6 +51,7 @@ RSpec.describe "bundle cache" do
system_gems "rack-1.0.0", :path => default_bundle_path
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -64,6 +67,7 @@ RSpec.describe "bundle cache" do
cache_gems "rack-1.0.0"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -85,35 +89,33 @@ RSpec.describe "bundle cache" do
it_behaves_like "when there are only gemsources"
end
- describe "when there is a built-in gem" do
+ describe "when there is a built-in gem", :ruby_repo do
+ let(:default_json_version) { ruby "gem 'json'; require 'json'; puts JSON::VERSION" }
+
before :each do
build_repo2 do
- build_gem "builtin_gem", "1.0.2"
+ build_gem "json", default_json_version
end
- build_gem "builtin_gem", "1.0.2", :to_system => true do |s|
- s.summary = "This builtin_gem is bundled with Ruby"
- end
-
- FileUtils.rm("#{system_gem_path}/cache/builtin_gem-1.0.2.gem")
+ build_gem "json", default_json_version, :to_system => true, :default => true
end
it "uses builtin gems when installing to system gems" do
bundle "config set path.system true"
- install_gemfile %(gem 'builtin_gem', '1.0.2')
- expect(the_bundle).to include_gems("builtin_gem 1.0.2")
+ install_gemfile %(source "#{file_uri_for(gem_repo1)}"; gem 'json', '#{default_json_version}'), :verbose => true
+ expect(out).to include("Using json #{default_json_version}")
end
it "caches remote and builtin gems" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
- gem 'builtin_gem', '1.0.2'
+ gem 'json', '#{default_json_version}'
gem 'rack', '1.0.0'
G
bundle :cache
expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
- expect(bundled_app("vendor/cache/builtin_gem-1.0.2.gem")).to exist
+ expect(bundled_app("vendor/cache/json-#{default_json_version}.gem")).to exist
end
it "doesn't make remote request after caching the gem" do
@@ -134,12 +136,13 @@ RSpec.describe "bundle cache" do
bundle "config set path.system true"
install_gemfile <<-G
- gem 'builtin_gem', '1.0.2'
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'json', '#{default_json_version}'
G
bundle :cache, :raise_on_error => false
expect(exitstatus).to_not eq(0)
- expect(err).to include("builtin_gem-1.0.2 is built in to Ruby, and can't be cached")
+ expect(err).to include("json-#{default_json_version} is built in to Ruby, and can't be cached")
end
end
@@ -302,6 +305,7 @@ RSpec.describe "bundle cache" do
:path => bundled_app("vendor/cache")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo-bundler"
G
diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb
index 25f12a9e87..10e44656b8 100644
--- a/spec/bundler/cache/git_spec.rb
+++ b/spec/bundler/cache/git_spec.rb
@@ -18,6 +18,7 @@ RSpec.describe "bundle cache with git" do
ref = git.ref_for("master", 11)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
@@ -36,6 +37,7 @@ RSpec.describe "bundle cache with git" do
ref = git.ref_for("master", 11)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
@@ -55,6 +57,7 @@ RSpec.describe "bundle cache with git" do
build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
@@ -72,6 +75,7 @@ RSpec.describe "bundle cache with git" do
old_ref = git.ref_for("master", 11)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
@@ -102,6 +106,7 @@ RSpec.describe "bundle cache with git" do
old_ref = git.ref_for("master", 11)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
@@ -130,6 +135,7 @@ RSpec.describe "bundle cache with git" do
ref = git.ref_for("master", 11)
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => '#{lib_path("foo-invalid")}', :branch => :master
G
@@ -150,6 +156,9 @@ RSpec.describe "bundle cache with git" do
end
it "copies repository to vendor cache, including submodules" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
git = build_git "has_submodule", "1.0" do |s|
@@ -160,6 +169,7 @@ RSpec.describe "bundle cache with git" do
sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("has_submodule-1.0")}", :submodules => true do
gem "has_submodule"
end
@@ -183,6 +193,7 @@ RSpec.describe "bundle cache with git" do
update_git("foo") {|s| s.write "foo.gemspec", spec_lines.join("\n") }
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
bundle "config set cache_all true"
@@ -197,6 +208,7 @@ RSpec.describe "bundle cache with git" do
build_git "foo"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
bundle "config set cache_all true"
diff --git a/spec/bundler/cache/path_spec.rb b/spec/bundler/cache/path_spec.rb
index c81dda7405..2ad136a008 100644
--- a/spec/bundler/cache/path_spec.rb
+++ b/spec/bundler/cache/path_spec.rb
@@ -5,6 +5,7 @@ RSpec.describe "bundle cache with path" do
build_lib "foo", :path => bundled_app("lib/foo")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => '#{bundled_app("lib/foo")}'
G
@@ -18,6 +19,7 @@ RSpec.describe "bundle cache with path" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
@@ -36,6 +38,7 @@ RSpec.describe "bundle cache with path" do
build_lib libname, :path => libpath
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "#{libname}", :path => '#{libpath}'
G
@@ -51,6 +54,7 @@ RSpec.describe "bundle cache with path" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
@@ -73,6 +77,7 @@ RSpec.describe "bundle cache with path" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
@@ -84,6 +89,7 @@ RSpec.describe "bundle cache with path" do
build_lib "bar"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "bar", :path => '#{lib_path("bar-1.0")}'
G
@@ -95,6 +101,7 @@ RSpec.describe "bundle cache with path" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
@@ -107,6 +114,7 @@ RSpec.describe "bundle cache with path" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
@@ -119,6 +127,7 @@ RSpec.describe "bundle cache with path" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
@@ -127,6 +136,7 @@ RSpec.describe "bundle cache with path" do
build_lib "bar"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => '#{lib_path("foo-1.0")}'
gem "bar", :path => '#{lib_path("bar-1.0")}'
G
@@ -139,6 +149,7 @@ RSpec.describe "bundle cache with path" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
@@ -147,6 +158,7 @@ RSpec.describe "bundle cache with path" do
build_lib "baz"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => '#{lib_path("foo-1.0")}'
gem "baz", :path => '#{lib_path("baz-1.0")}'
G
diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb
index 3b177b32ea..1cd0e16d95 100644
--- a/spec/bundler/commands/binstubs_spec.rb
+++ b/spec/bundler/commands/binstubs_spec.rb
@@ -116,7 +116,7 @@ RSpec.describe "bundle binstubs <gem>" do
s.executables = "print_loaded_gems"
s.bindir = "exe"
s.write "exe/print_loaded_gems", <<-R
- specs = Gem.loaded_specs.values.reject {|s| Bundler.rubygems.spec_default_gem?(s) }
+ specs = Gem.loaded_specs.values.reject {|s| s.default_gem? }
puts specs.map(&:full_name).sort.inspect
R
end
@@ -140,8 +140,15 @@ RSpec.describe "bundle binstubs <gem>" do
it "runs the correct version of bundler" do
sys_exec "bin/bundle install", :env => { "BUNDLER_VERSION" => "999.999.999" }, :raise_on_error => false
expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (~> 999.999) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`")
+ expect(err).to include("Activating bundler (999.999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
+ end
+
+ it "runs the correct version of bundler even if a higher version is installed" do
+ system_gems "bundler-999.999.998", "bundler-999.999.999"
+
+ sys_exec "bin/bundle install", :env => { "BUNDLER_VERSION" => "999.999.998", "DEBUG" => "1" }, :raise_on_error => false
+ expect(out).to include %(Using bundler 999.999.998\n)
end
end
@@ -215,8 +222,8 @@ RSpec.describe "bundle binstubs <gem>" do
it "calls through to the explicit bundler version" do
sys_exec "bin/bundle update --bundler=999.999.999", :raise_on_error => false
expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (~> 999.999) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`")
+ expect(err).to include("Activating bundler (999.999.999) failed:").
+ and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
end
end
@@ -254,6 +261,7 @@ RSpec.describe "bundle binstubs <gem>" do
s.executables = %w[foo]
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo")}"
G
@@ -269,6 +277,7 @@ RSpec.describe "bundle binstubs <gem>" do
s.executables = %w[foo]
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo")}"
G
@@ -278,8 +287,6 @@ RSpec.describe "bundle binstubs <gem>" do
end
it "sets correct permissions for binstubs" do
- skip "https://github.com/rubygems/rubygems/issues/3352" if Gem.win_platform?
-
with_umask(0o002) do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -288,7 +295,7 @@ RSpec.describe "bundle binstubs <gem>" do
bundle "binstubs rack"
binary = bundled_app("bin/rackup")
- expect(File.stat(binary).mode.to_s(8)).to eq("100775")
+ expect(File.stat(binary).mode.to_s(8)).to eq(Gem.win_platform? ? "100644" : "100775")
end
end
diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb
index df5f01bc45..9bb85c6587 100644
--- a/spec/bundler/commands/cache_spec.rb
+++ b/spec/bundler/commands/cache_spec.rb
@@ -234,7 +234,7 @@ RSpec.describe "bundle cache" do
end
end
- bundle "config --local without wo"
+ bundle "config set --local without wo"
install_gemfile <<-G
source "file:#{gem_repo1}"
gem "rack"
@@ -250,11 +250,26 @@ RSpec.describe "bundle cache" do
expect(the_bundle).to include_gem "rack 1.0"
expect(the_bundle).not_to include_gems "weakling", "uninstallable"
- bundle "config --local without wo"
+ bundle "config set --local without wo"
bundle :install
expect(the_bundle).to include_gem "rack 1.0"
expect(the_bundle).not_to include_gems "weakling", "uninstallable"
end
+
+ it "does not fail to cache gems in excluded groups when there's a lockfile but gems not previously installed" do
+ bundle "config set --local without wo"
+ gemfile <<-G
+ source "https://my.gem.repo.1"
+ gem "rack"
+ group :wo do
+ gem "weakling"
+ end
+ G
+
+ bundle :lock, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ bundle :cache, "all-platforms" => true, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ expect(bundled_app("vendor/cache/weakling-0.0.3.gem")).to exist
+ end
end
context "with frozen configured" do
@@ -267,7 +282,7 @@ RSpec.describe "bundle cache" do
end
subject do
- bundle "config --local frozen true"
+ bundle "config set --local frozen true"
bundle :cache, :raise_on_error => false
end
@@ -287,6 +302,30 @@ RSpec.describe "bundle cache" do
expect(out).to include("frozen").or include("deployment")
end
end
+
+ context "with gems with extensions" do
+ before do
+ build_repo2 do
+ build_gem "racc", "2.0" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", "task(:default) { puts 'INSTALLING rack' }"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo2)}"
+
+ gem "racc"
+ G
+ end
+
+ it "installs them properly from cache to a different path" do
+ bundle "cache"
+ bundle "config set --local path vendor/bundle"
+ bundle "install --local"
+ end
+ end
end
RSpec.describe "bundle install with gem sources" do
@@ -306,7 +345,7 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems "rack 1.0.0"
end
- it "does not hit the remote at all" do
+ it "does not hit the remote at all in frozen mode" do
build_repo2
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -317,12 +356,30 @@ RSpec.describe "bundle install with gem sources" do
simulate_new_machine
FileUtils.rm_rf gem_repo2
- bundle "config --local deployment true"
- bundle "config --local path vendor/bundle"
+ bundle "config set --local deployment true"
+ bundle "config set --local path vendor/bundle"
bundle :install
expect(the_bundle).to include_gems "rack 1.0.0"
end
+ it "does not hit the remote at all when cache_all_platforms configured" do
+ build_repo2
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "rack"
+ G
+
+ bundle :cache
+ simulate_new_machine
+ FileUtils.rm_rf gem_repo2
+
+ bundle "config set --local cache_all_platforms true"
+ bundle "config set --local path vendor/bundle"
+ bundle "install --local"
+ expect(out).not_to include("Fetching gem metadata")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
it "does not reinstall already-installed gems" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb
index fdf3bc7d51..1fa35136eb 100644
--- a/spec/bundler/commands/check_spec.rb
+++ b/spec/bundler/commands/check_spec.rb
@@ -124,7 +124,7 @@ RSpec.describe "bundle check" do
gem "rack", :group => :foo
G
- bundle "config --local without foo"
+ bundle "config set --local without foo"
bundle :install
gemfile <<-G
@@ -137,6 +137,22 @@ RSpec.describe "bundle check" do
expect(exitstatus).to eq(1)
end
+ it "ensures that gems are actually installed and not just cached in applications' cache" do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ G
+
+ bundle "config set --local path vendor/bundle"
+ bundle :cache
+
+ gem_command "uninstall rack", :env => { "GEM_HOME" => vendored_gems.to_s }
+
+ bundle "check", :raise_on_error => false
+ expect(err).to include("* rack (1.0.0)")
+ expect(exitstatus).to eq(1)
+ end
+
it "ignores missing gems restricted to other platforms" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -217,7 +233,7 @@ RSpec.describe "bundle check" do
gem "foo"
G
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle "install"
FileUtils.rm(bundled_app_lock)
@@ -288,9 +304,136 @@ RSpec.describe "bundle check" do
end
end
+ describe "when locked with multiple dependents with different requirements" do
+ before :each do
+ build_repo4 do
+ build_gem "depends_on_rack" do |s|
+ s.add_dependency "rack", ">= 1.0"
+ end
+ build_gem "also_depends_on_rack" do |s|
+ s.add_dependency "rack", "~> 1.0"
+ end
+ build_gem "rack"
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "depends_on_rack"
+ gem "also_depends_on_rack"
+ G
+
+ bundle "lock"
+ end
+
+ it "shows what is missing with the current Gemfile without duplications" do
+ bundle :check, :raise_on_error => false
+ expect(err).to match(/The following gems are missing/)
+ expect(err).to include("* rack (1.0").once
+ end
+ end
+
+ describe "when locked under multiple platforms" do
+ before :each do
+ build_repo4 do
+ build_gem "rack"
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "rack"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ rack (1.0)
+
+ PLATFORMS
+ ruby
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "shows what is missing with the current Gemfile without duplications" do
+ bundle :check, :raise_on_error => false
+ expect(err).to match(/The following gems are missing/)
+ expect(err).to include("* rack (1.0").once
+ end
+ end
+
+ describe "when using only scoped rubygems sources" do
+ before do
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo2)}"
+ source "#{file_uri_for(gem_repo1)}" do
+ gem "rack"
+ end
+ G
+ end
+
+ it "returns success when the Gemfile is satisfied" do
+ system_gems "rack-1.0.0", :path => default_bundle_path
+ bundle :check
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+ end
+
+ describe "when using only scoped rubygems sources with indirect dependencies" do
+ before do
+ build_repo4 do
+ build_gem "depends_on_rack" do |s|
+ s.add_dependency "rack"
+ end
+
+ build_gem "rack"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo1)}"
+ source "#{file_uri_for(gem_repo4)}" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ it "returns success when the Gemfile is satisfied and generates a correct lockfile" do
+ system_gems "depends_on_rack-1.0", "rack-1.0", :gem_repo => gem_repo4, :path => default_bundle_path
+ bundle :check
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ depends_on_rack (1.0)
+ rack
+ rack (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ depends_on_rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
describe "BUNDLED WITH" do
def lock_with(bundler_version = nil)
- lock = <<-L
+ lock = <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
@@ -304,7 +447,7 @@ RSpec.describe "bundle check" do
L
if bundler_version
- lock += "\n BUNDLED WITH\n #{bundler_version}\n"
+ lock += "\nBUNDLED WITH\n #{bundler_version}\n"
end
lock
@@ -323,7 +466,7 @@ RSpec.describe "bundle check" do
it "does not change the lock" do
lockfile lock_with(nil)
bundle :check
- lockfile_should_be lock_with(nil)
+ expect(lockfile).to eq lock_with(nil)
end
end
@@ -332,7 +475,7 @@ RSpec.describe "bundle check" do
lockfile lock_with(Bundler::VERSION.succ)
bundle :check
expect(err).to include("the running version of Bundler (#{Bundler::VERSION}) is older than the version that created the lockfile (#{Bundler::VERSION.succ})")
- lockfile_should_be lock_with(Bundler::VERSION.succ)
+ expect(lockfile).to eq lock_with(Bundler::VERSION.succ)
end
end
@@ -341,7 +484,7 @@ RSpec.describe "bundle check" do
system_gems "bundler-1.18.0"
lockfile lock_with("1.18.0")
bundle :check
- lockfile_should_be lock_with("1.18.0")
+ expect(lockfile).to eq lock_with("1.18.0")
end
end
end
diff --git a/spec/bundler/commands/clean_spec.rb b/spec/bundler/commands/clean_spec.rb
index 7c43aaabc4..9cce998416 100644
--- a/spec/bundler/commands/clean_spec.rb
+++ b/spec/bundler/commands/clean_spec.rb
@@ -236,7 +236,7 @@ RSpec.describe "bundle clean" do
bundle "config set path vendor/bundle"
bundle "install"
- update_git "foo", :path => lib_path("foo-bar")
+ update_git "foo-bar", :path => lib_path("foo-bar")
revision2 = revision_for(lib_path("foo-bar"))
bundle "update", :all => true
@@ -261,6 +261,7 @@ RSpec.describe "bundle clean" do
revision = revision_for(lib_path("rails"))
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "activesupport", :git => "#{lib_path("rails")}", :ref => '#{revision}'
G
@@ -625,21 +626,19 @@ RSpec.describe "bundle clean" do
end
it "when using --force, it doesn't remove default gem binaries" do
- skip "does not work on ruby 3.0 because it changes the path to look for default gems, tsort is a default gem there, and we can't install it either like we do with fiddle because it doesn't yet exist" unless RUBY_VERSION < "3.0.0"
+ skip "does not work on old rubies because the realworld gems that need to be installed don't support them" if RUBY_VERSION < "2.7.0"
skip "does not work on rubygems versions where `--install_dir` doesn't respect --default" unless Gem::Installer.for_spec(loaded_gemspec, :install_dir => "/foo").default_spec_file == "/foo/specifications/default/bundler-#{Bundler::VERSION}.gemspec" # Since rubygems 3.2.0.rc.2
default_irb_version = ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", :raise_on_error => false
skip "irb isn't a default gem" if default_irb_version.empty?
- build_repo2 do
- # simulate executable for default gem
- build_gem "irb", default_irb_version, :to_system => true, :default => true do |s|
- s.executables = "irb"
- end
+ # simulate executable for default gem
+ build_gem "irb", default_irb_version, :to_system => true, :default => true do |s|
+ s.executables = "irb"
end
- realworld_system_gems "fiddle --version 1.0.0"
+ realworld_system_gems "fiddle --version 1.0.6", "tsort --version 0.1.0", "pathname --version 0.1.0", "set --version 1.0.1"
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -869,6 +868,7 @@ RSpec.describe "bundle clean" do
expect(very_simple_binary_extensions_dir).to exist
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}", :ref => "#{revision}"
G
@@ -878,6 +878,7 @@ RSpec.describe "bundle clean" do
expect(very_simple_binary_extensions_dir).to exist
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
G
bundle "install"
diff --git a/spec/bundler/commands/config_spec.rb b/spec/bundler/commands/config_spec.rb
index 70e3feff00..2d0a7dc989 100644
--- a/spec/bundler/commands/config_spec.rb
+++ b/spec/bundler/commands/config_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe ".bundle/config" do
it "can be moved with an environment variable" do
ENV["BUNDLE_APP_CONFIG"] = tmp("foo/bar").to_s
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle "install"
expect(bundled_app(".bundle")).not_to exist
@@ -57,7 +57,7 @@ RSpec.describe ".bundle/config" do
FileUtils.mkdir_p bundled_app("omg")
ENV["BUNDLE_APP_CONFIG"] = "../foo"
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle "install", :dir => bundled_app("omg")
expect(bundled_app(".bundle")).not_to exist
@@ -76,6 +76,30 @@ RSpec.describe ".bundle/config" do
end
end
+ describe "config location" do
+ let(:bundle_user_config) { File.join(Dir.home, ".config/bundler") }
+
+ before do
+ Dir.mkdir File.dirname(bundle_user_config)
+ end
+
+ it "can be configured through BUNDLE_USER_CONFIG" do
+ bundle "config set path vendor", :env => { "BUNDLE_USER_CONFIG" => bundle_user_config }
+ bundle "config get path", :env => { "BUNDLE_USER_CONFIG" => bundle_user_config }
+ expect(out).to include("Set for the current user (#{bundle_user_config}): \"vendor\"")
+ end
+
+ context "when not explicitly configured, but BUNDLE_USER_HOME set" do
+ let(:bundle_user_home) { bundled_app(".bundle").to_s }
+
+ it "uses the right location" do
+ bundle "config set path vendor", :env => { "BUNDLE_USER_HOME" => bundle_user_home }
+ bundle "config get path", :env => { "BUNDLE_USER_HOME" => bundle_user_home }
+ expect(out).to include("Set for the current user (#{bundle_user_home}/config): \"vendor\"")
+ end
+ end
+ end
+
describe "global" do
before(:each) do
install_gemfile <<-G
@@ -321,7 +345,7 @@ E
end
describe "quoting" do
- before(:each) { gemfile "# no gems" }
+ before(:each) { gemfile "source \"#{file_uri_for(gem_repo1)}\"" }
let(:long_string) do
"--with-xml2-include=/usr/pkg/include/libxml2 --with-xml2-lib=/usr/pkg/lib " \
"--with-xslt-dir=/usr/pkg"
@@ -408,6 +432,22 @@ E
expect(out).to eq "spec_run=true"
end
+ it "list with credentials" do
+ bundle "config list", :env => { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" }
+ expect(out).to eq "Settings are listed in order of priority. The top value will be used.\ngems.myserver.com\nSet via BUNDLE_GEMS__MYSERVER__COM: \"user:[REDACTED]\"\n\nspec_run\nSet via BUNDLE_SPEC_RUN: \"true\""
+
+ bundle "config list", :parseable => true, :env => { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" }
+ expect(out).to eq "gems.myserver.com=user:password\nspec_run=true"
+ end
+
+ it "list with API token credentials" do
+ bundle "config list", :env => { "BUNDLE_GEMS__MYSERVER__COM" => "api_token:x-oauth-basic" }
+ expect(out).to eq "Settings are listed in order of priority. The top value will be used.\ngems.myserver.com\nSet via BUNDLE_GEMS__MYSERVER__COM: \"[REDACTED]:x-oauth-basic\"\n\nspec_run\nSet via BUNDLE_SPEC_RUN: \"true\""
+
+ bundle "config list", :parseable => true, :env => { "BUNDLE_GEMS__MYSERVER__COM" => "api_token:x-oauth-basic" }
+ expect(out).to eq "gems.myserver.com=api_token:x-oauth-basic\nspec_run=true"
+ end
+
it "get" do
ENV["BUNDLE_BAR"] = "bar_val"
diff --git a/spec/bundler/commands/doctor_spec.rb b/spec/bundler/commands/doctor_spec.rb
index d8e9674a24..860b638f06 100644
--- a/spec/bundler/commands/doctor_spec.rb
+++ b/spec/bundler/commands/doctor_spec.rb
@@ -32,6 +32,8 @@ RSpec.describe "bundle doctor" do
unwritable_file = double("file")
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [unwritable_file] }
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with(unwritable_file).and_return(true)
allow(File).to receive(:stat).with(unwritable_file) { stat }
allow(stat).to receive(:uid) { Process.uid }
allow(File).to receive(:writable?).with(unwritable_file) { true }
@@ -47,7 +49,6 @@ RSpec.describe "bundle doctor" do
doctor = Bundler::CLI::Doctor.new({})
expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"]
- allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with("/usr/lib/libSystem.dylib").and_return(true)
expect { doctor.run }.not_to(raise_error, @stdout.string)
expect(@stdout.string).to be_empty
@@ -57,7 +58,6 @@ RSpec.describe "bundle doctor" do
doctor = Bundler::CLI::Doctor.new({})
expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"]
- allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(false)
expect { doctor.run }.to raise_error(Bundler::ProductionError, strip_whitespace(<<-E).strip), @stdout.string
The following gems are missing OS dependencies:
@@ -67,12 +67,32 @@ RSpec.describe "bundle doctor" do
end
end
+ context "when home contains broken symlinks" do
+ before(:each) do
+ @broken_symlink = double("file")
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [@broken_symlink] }
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with(@broken_symlink) { false }
+ end
+
+ it "exits with an error if home contains files that are not readable/writable" do
+ expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect(@stdout.string).to include(
+ "Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{@broken_symlink}"
+ )
+ expect(@stdout.string).not_to include("No issues")
+ end
+ end
+
context "when home contains files that are not readable/writable" do
before(:each) do
@stat = double("stat")
@unwritable_file = double("file")
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [@unwritable_file] }
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with(@unwritable_file) { true }
allow(File).to receive(:stat).with(@unwritable_file) { @stat }
end
@@ -113,4 +133,14 @@ RSpec.describe "bundle doctor" do
end
end
end
+
+ context "when home contains filesname with special characters" do
+ it "escape filename before command execute" do
+ doctor = Bundler::CLI::Doctor.new({})
+ expect(doctor).to receive(:`).with("/usr/bin/otool -L \\$\\(date\\)\\ \\\"\\'\\\\.bundle").and_return("dummy string")
+ doctor.dylibs_darwin('$(date) "\'\.bundle')
+ expect(doctor).to receive(:`).with("/usr/bin/ldd \\$\\(date\\)\\ \\\"\\'\\\\.bundle").and_return("dummy string")
+ doctor.dylibs_ldd('$(date) "\'\.bundle')
+ end
+ end
end
diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb
index 5d43586116..e67e5b96ef 100644
--- a/spec/bundler/commands/exec_spec.rb
+++ b/spec/bundler/commands/exec_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe "bundle exec" do
it "works with --gemfile flag" do
create_file "CustomGemfile", <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack", "1.0.0"
G
@@ -17,6 +18,7 @@ RSpec.describe "bundle exec" do
it "activates the correct gem" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
G
@@ -24,8 +26,20 @@ RSpec.describe "bundle exec" do
expect(out).to eq("0.9.1")
end
+ it "works and prints no warnings when HOME is not writable" do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack", "0.9.1"
+ G
+
+ bundle "exec rackup", :env => { "HOME" => "/" }
+ expect(out).to eq("0.9.1")
+ expect(err).to be_empty
+ end
+
it "works when the bins are in ~/.bundle" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -35,6 +49,7 @@ RSpec.describe "bundle exec" do
it "works when running from a random directory" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -44,37 +59,39 @@ RSpec.describe "bundle exec" do
end
it "works when exec'ing something else" do
- install_gemfile 'gem "rack"'
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\""
bundle "exec echo exec"
expect(out).to eq("exec")
end
it "works when exec'ing to ruby" do
- install_gemfile 'gem "rack"'
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\""
bundle "exec ruby -e 'puts %{hi}'"
expect(out).to eq("hi")
end
it "works when exec'ing to rubygems" do
- install_gemfile 'gem "rack"'
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\""
bundle "exec #{gem_cmd} --version"
expect(out).to eq(Gem::VERSION)
end
it "works when exec'ing to rubygems through sh -c" do
- install_gemfile 'gem "rack"'
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\""
bundle "exec sh -c '#{gem_cmd} --version'"
expect(out).to eq(Gem::VERSION)
end
it "works when exec'ing back to bundler with a lockfile that doesn't include the current platform" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
G
# simulate lockfile generated with old version not including specific platform
lockfile <<-L
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
rack (0.9.1)
@@ -100,20 +117,20 @@ RSpec.describe "bundle exec" do
Process.setproctitle("1-2-3-4-5-6-7")
puts `ps -ocommand= -p#{$$}`
RUBY
- create_file "Gemfile"
+ create_file "Gemfile", "source \"#{file_uri_for(gem_repo1)}\""
create_file "a.rb", script_that_changes_its_own_title_and_checks_if_picked_up_by_ps_unix_utility
bundle "exec ruby a.rb"
expect(out).to eq("1-2-3-4-5-6-7")
end
it "accepts --verbose" do
- install_gemfile 'gem "rack"'
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\""
bundle "exec --verbose echo foobar"
expect(out).to eq("foobar")
end
it "passes --verbose to command if it is given after the command" do
- install_gemfile 'gem "rack"'
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\""
bundle "exec echo --verbose"
expect(out).to eq("--verbose")
end
@@ -137,7 +154,7 @@ RSpec.describe "bundle exec" do
end
G
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
sys_exec "#{Gem.ruby} #{command.path}"
expect(out).to be_empty
@@ -145,7 +162,7 @@ RSpec.describe "bundle exec" do
end
it "accepts --keep-file-descriptors" do
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
bundle "exec --keep-file-descriptors echo foobar"
expect(err).to be_empty
@@ -154,7 +171,7 @@ RSpec.describe "bundle exec" do
it "can run a command named --verbose" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
- install_gemfile 'gem "rack"'
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\""
File.open(bundled_app("--verbose"), "w") do |f|
f.puts "#!/bin/sh"
f.puts "echo foobar"
@@ -200,7 +217,7 @@ RSpec.describe "bundle exec" do
before do
skip "irb isn't a default gem" if default_irb_version.empty?
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
end
it "uses version provided by ruby" do
@@ -295,7 +312,7 @@ RSpec.describe "bundle exec" do
end
it "handles gems installed with --without" do
- bundle "config --local without middleware"
+ bundle "config set --local without middleware"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack" # rack 0.9.1 and 1.0 exist
@@ -315,6 +332,7 @@ RSpec.describe "bundle exec" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -333,6 +351,7 @@ RSpec.describe "bundle exec" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -349,6 +368,7 @@ RSpec.describe "bundle exec" do
it "errors nicely when the argument doesn't exist" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -360,6 +380,7 @@ RSpec.describe "bundle exec" do
it "errors nicely when the argument is not executable" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -371,6 +392,7 @@ RSpec.describe "bundle exec" do
it "errors nicely when no arguments are passed" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -402,6 +424,7 @@ RSpec.describe "bundle exec" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
@@ -486,6 +509,7 @@ RSpec.describe "bundle exec" do
describe "run from a random directory" do
before(:each) do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
end
@@ -509,6 +533,7 @@ RSpec.describe "bundle exec" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "fizz", :path => "#{File.expand_path(home("fizz"))}"
G
end
@@ -533,6 +558,7 @@ RSpec.describe "bundle exec" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "fizz_git", :git => "#{lib_path("fizz_git-1.0")}"
G
end
@@ -556,6 +582,7 @@ RSpec.describe "bundle exec" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "fizz_no_gemspec", "1.0", :git => "#{lib_path("fizz_no_gemspec-1.0")}"
G
end
@@ -585,6 +612,36 @@ RSpec.describe "bundle exec" do
expect(out).to include("Installing foo 1.0")
end
+ it "loads the correct optparse when `auto_install` is set, and optparse is a dependency" do
+ if Gem.ruby_version >= Gem::Version.new("3.0.0") && Gem.rubygems_version < Gem::Version.new("3.3.0.a")
+ skip "optparse is a default gem, and rubygems loads it during install"
+ end
+
+ build_repo4 do
+ build_gem "fastlane", "2.192.0" do |s|
+ s.executables = "fastlane"
+ s.add_dependency "optparse", "~> 999.999.999"
+ end
+
+ build_gem "optparse", "999.999.998"
+ build_gem "optparse", "999.999.999"
+ end
+
+ system_gems "optparse-999.999.998", :gem_repo => gem_repo4
+
+ bundle "config set auto_install 1"
+ bundle "config set --local path vendor/bundle"
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "fastlane"
+ G
+
+ bundle "exec fastlane"
+ expect(out).to include("Installing optparse 999.999.999")
+ expect(out).to include("2.192.0")
+ end
+
describe "with gems bundled via :path with invalid gemspecs" do
it "outputs the gemspec validation errors" do
build_lib "foo"
@@ -602,6 +659,7 @@ RSpec.describe "bundle exec" do
end
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
@@ -617,6 +675,8 @@ RSpec.describe "bundle exec" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+
module Monkey
def bin_path(a,b,c)
raise Gem::GemNotFoundException.new('Fail')
@@ -650,6 +710,7 @@ RSpec.describe "bundle exec" do
bundled_app(path).chmod(0o755)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
end
@@ -668,29 +729,40 @@ RSpec.describe "bundle exec" do
subject { bundle "exec #{path} arg1 arg2", :raise_on_error => false }
- shared_examples_for "it runs" do
- it "like a normally executed executable" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
- subject
- expect(exitstatus).to eq(exit_code)
- expect(err).to eq(expected_err)
- expect(out).to eq(expected)
- end
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
end
- it_behaves_like "it runs"
-
context "the executable exits explicitly" do
let(:executable) { super() << "\nexit #{exit_code}\nputs 'POST_EXIT'\n" }
context "with exit 0" do
- it_behaves_like "it runs"
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
context "with exit 99" do
let(:exit_code) { 99 }
- it_behaves_like "it runs"
+
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
end
@@ -707,7 +779,15 @@ RSpec.describe "bundle exec" do
# this is specified by C99
128 + 15
end
- it_behaves_like "it runs"
+
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
context "the executable is empty" do
@@ -716,7 +796,15 @@ RSpec.describe "bundle exec" do
let(:exit_code) { 0 }
let(:expected_err) { "#{path} is empty" }
let(:expected) { "" }
- it_behaves_like "it runs"
+
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
context "the executable raises" do
@@ -743,17 +831,33 @@ RSpec.describe "bundle exec" do
let(:expected_err) { "bundler: failed to load command: #{path} (#{path})\n#{system_gem_path("bin/bundle")}: Err (Err)" }
let(:expected) { super() }
- it_behaves_like "it runs"
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
context "when the file uses the current ruby shebang" do
let(:shebang) { "#!#{Gem.ruby}" }
- it_behaves_like "it runs"
+
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
context "when Bundler.setup fails", :bundler => "< 3" do
before do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack', '2'
G
ENV["BUNDLER_FORCE_TTY"] = "true"
@@ -762,16 +866,25 @@ RSpec.describe "bundle exec" do
let(:exit_code) { Bundler::GemNotFound.new.status_code }
let(:expected) { "" }
let(:expected_err) { <<-EOS.strip }
-\e[31mCould not find gem 'rack (= 2)' in any of the gem sources listed in your Gemfile.\e[0m
-\e[33mRun `bundle install` to install missing gems.\e[0m
+Could not find gem 'rack (= 2)' in locally installed gems.
+The source contains the following versions of 'rack': 0.9.1, 1.0.0
+Run `bundle install` to install missing gems.
EOS
- it_behaves_like "it runs"
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
context "when Bundler.setup fails", :bundler => "3" do
before do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack', '2'
G
ENV["BUNDLER_FORCE_TTY"] = "true"
@@ -780,19 +893,33 @@ RSpec.describe "bundle exec" do
let(:exit_code) { Bundler::GemNotFound.new.status_code }
let(:expected) { "" }
let(:expected_err) { <<-EOS.strip }
-\e[31mCould not find gem 'rack (= 2)' in locally installed gems.
-The source contains the following versions of 'rack': 1.0.0\e[0m
-\e[33mRun `bundle install` to install missing gems.\e[0m
+Could not find gem 'rack (= 2)' in locally installed gems.
+The source contains the following versions of 'rack': 1.0.0
+Run `bundle install` to install missing gems.
EOS
- it_behaves_like "it runs"
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
context "when the executable exits non-zero via at_exit" do
let(:executable) { super() + "\n\nat_exit { $! ? raise($!) : exit(1) }" }
let(:exit_code) { 1 }
- it_behaves_like "it runs"
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
context "when disable_exec_load is set" do
@@ -803,7 +930,14 @@ The source contains the following versions of 'rack': 1.0.0\e[0m
bundle "config set disable_exec_load true"
end
- it_behaves_like "it runs"
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
context "regarding $0 and __FILE__" do
@@ -819,12 +953,26 @@ $0: #{path.to_s.inspect}
__FILE__: #{path.to_s.inspect}
EOS
- it_behaves_like "it runs"
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
context "when the path is relative" do
let(:path) { super().relative_path_from(bundled_app) }
- it_behaves_like "it runs"
+ it "runs" do
+ skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
context "when the path is relative with a leading ./" do
@@ -925,7 +1073,7 @@ __FILE__: #{path.to_s.inspect}
before do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
end
it "does not undo the monkeypatches" do
@@ -984,7 +1132,7 @@ __FILE__: #{path.to_s.inspect}
RUBY
# A Gemfile needs to be in the root to trick bundler's root resolution
- create_file(bundled_app("Gemfile"))
+ create_file(bundled_app("Gemfile"), "source \"#{file_uri_for(gem_repo1)}\"")
bundle "install"
end
@@ -1015,7 +1163,7 @@ __FILE__: #{path.to_s.inspect}
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
skip "openssl isn't a default gem" if expected.empty?
- install_gemfile "" # must happen before installing the broken system gem
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" # must happen before installing the broken system gem
build_repo4 do
build_gem "openssl", openssl_version do |s|
@@ -1056,6 +1204,7 @@ __FILE__: #{path.to_s.inspect}
build_git "simple_git_binary", &:add_c_extension
bundle "config set --local path .bundle"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "simple_git_binary", :git => '#{lib_path("simple_git_binary-1.0")}'
G
end
diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb
index 7702959306..7f618b5f6c 100644
--- a/spec/bundler/commands/info_spec.rb
+++ b/spec/bundler/commands/info_spec.rb
@@ -50,6 +50,19 @@ RSpec.describe "bundle info" do
expect(out).to eq(root.to_s)
end
+ it "prints gem version if exists in bundle" do
+ bundle "info rails --version"
+ expect(out).to eq("2.3.2")
+ end
+
+ it "doesn't claim that bundler has been deleted, even if using a custom path without bundler there" do
+ bundle "config set --local path vendor/bundle"
+ bundle "install"
+ bundle "info bundler"
+ expect(out).to include("\tPath: #{root}")
+ expect(err).not_to match(/The gem bundler has been deleted/i)
+ end
+
it "complains if gem not in bundle" do
bundle "info missing", :raise_on_error => false
expect(err).to eq("Could not find gem 'missing'.")
@@ -60,7 +73,15 @@ RSpec.describe "bundle info" do
bundle "info rails --path"
- expect(err).to match(/has been deleted/i)
+ expect(err).to match(/The gem rails has been deleted/i)
+ expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
+
+ bundle "info rail --path"
+ expect(err).to match(/The gem rails has been deleted/i)
+ expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
+
+ bundle "info rails"
+ expect(err).to match(/The gem rails has been deleted/i)
expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
end
@@ -111,6 +132,7 @@ RSpec.describe "bundle info" do
it "prints out git info" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
expect(the_bundle).to include_gems "foo 1.0"
@@ -126,6 +148,7 @@ RSpec.describe "bundle info" do
@revision = revision_for(lib_path("foo-1.0"))[0...6]
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "omg"
G
expect(the_bundle).to include_gems "foo 1.0.omg"
@@ -137,6 +160,7 @@ RSpec.describe "bundle info" do
it "doesn't print the branch when tied to a ref" do
sha = revision_for(lib_path("foo-1.0"))
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{sha}"
G
@@ -147,6 +171,7 @@ RSpec.describe "bundle info" do
it "handles when a version is a '-' prerelease" do
@git = build_git("foo", "1.0.0-beta.1", :path => lib_path("foo"))
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.0.0-beta.1", :git => "#{lib_path("foo")}"
G
expect(the_bundle).to include_gems "foo 1.0.0.pre.beta.1"
@@ -182,4 +207,18 @@ RSpec.describe "bundle info" do
expect(err).to include("Could not find gem '#{invalid_regexp}'.")
end
end
+
+ context "with without configured" do
+ it "does not find the gem, but gives a helpful error" do
+ bundle "config without test"
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rails", group: :test
+ G
+
+ bundle "info rails", :raise_on_error => false
+ expect(err).to include("Could not find gem 'rails', because it's in the group 'test', configured to be ignored.")
+ end
+ end
end
diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb
index f12d32d6a8..e00caa5315 100644
--- a/spec/bundler/commands/install_spec.rb
+++ b/spec/bundler/commands/install_spec.rb
@@ -94,6 +94,21 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems("rack 1.0.0")
end
+ it "auto-heals missing gems" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'rack'
+ G
+
+ FileUtils.rm_rf(default_bundle_path("gems/rack-1.0.0"))
+
+ bundle "install --verbose"
+
+ expect(out).to include("Installing rack 1.0.0")
+ expect(default_bundle_path("gems/rack-1.0.0")).to exist
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+
it "fetches gems when multiple versions are specified" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -291,7 +306,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "works" do
- bundle "config --local path vendor"
+ bundle "config set --local path vendor"
bundle "install"
expect(the_bundle).to include_gems "rack 1.0"
end
@@ -334,64 +349,87 @@ RSpec.describe "bundle install with gem sources" do
gem "rack"
G
- expect(err).to include("Your Gemfile has no gem server sources")
+ expect(err).to include("This Gemfile does not include an explicit global source. " \
+ "Not using an explicit global source may result in a different lockfile being generated depending on " \
+ "the gems you have installed locally before bundler is run. " \
+ "Instead, define a global source in your Gemfile like this: source \"https://rubygems.org\".")
end
it "creates a Gemfile.lock on a blank Gemfile" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
G
expect(File.exist?(bundled_app_lock)).to eq(true)
end
- context "throws a warning if a gem is added twice in Gemfile" do
- it "without version requirements" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
- gem "rack"
- G
+ it "throws a warning if a gem is added twice in Gemfile without version requirements" do
+ install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo2)}"
+ gem "rack"
+ gem "rack"
+ G
- expect(err).to include("Your Gemfile lists the gem rack (>= 0) more than once.")
- expect(err).to include("Remove any duplicate entries and specify the gem only once.")
- expect(err).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.")
- end
+ expect(err).to include("Your Gemfile lists the gem rack (>= 0) more than once.")
+ expect(err).to include("Remove any duplicate entries and specify the gem only once.")
+ expect(err).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.")
+ end
- it "with same versions" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
- gem "rack", "1.0"
- gem "rack", "1.0"
- G
+ it "throws a warning if a gem is added twice in Gemfile with same versions" do
+ install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo2)}"
+ gem "rack", "1.0"
+ gem "rack", "1.0"
+ G
- expect(err).to include("Your Gemfile lists the gem rack (= 1.0) more than once.")
- expect(err).to include("Remove any duplicate entries and specify the gem only once.")
- expect(err).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.")
- end
+ expect(err).to include("Your Gemfile lists the gem rack (= 1.0) more than once.")
+ expect(err).to include("Remove any duplicate entries and specify the gem only once.")
+ expect(err).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.")
end
- context "throws an error if a gem is added twice in Gemfile" do
- it "when version of one dependency is not specified" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
- gem "rack", "1.0"
- G
+ it "does not throw a warning if a gem is added once in Gemfile and also inside a gemspec as a development dependency" do
+ build_lib "my-gem", :path => bundled_app do |s|
+ s.add_development_dependency "my-private-gem"
+ end
- expect(err).to include("You cannot specify the same gem twice with different version requirements")
- expect(err).to include("You specified: rack (>= 0) and rack (= 1.0).")
+ build_repo2 do
+ build_gem "my-private-gem"
end
- it "when different versions of both dependencies are specified" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
- gem "rack", "1.0"
- gem "rack", "1.1"
- G
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo2)}"
- expect(err).to include("You cannot specify the same gem twice with different version requirements")
- expect(err).to include("You specified: rack (= 1.0) and rack (= 1.1).")
- end
+ gemspec
+
+ gem "my-private-gem", :group => :development
+ G
+
+ bundle :install
+
+ expect(err).to be_empty
+ expect(the_bundle).to include_gems("my-private-gem 1.0")
+ end
+
+ it "throws an error if a gem is added twice in Gemfile when version of one dependency is not specified" do
+ install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo2)}"
+ gem "rack"
+ gem "rack", "1.0"
+ G
+
+ expect(err).to include("You cannot specify the same gem twice with different version requirements")
+ expect(err).to include("You specified: rack (>= 0) and rack (= 1.0).")
+ end
+
+ it "throws an error if a gem is added twice in Gemfile when different versions of both dependencies are specified" do
+ install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo2)}"
+ gem "rack", "1.0"
+ gem "rack", "1.1"
+ G
+
+ expect(err).to include("You cannot specify the same gem twice with different version requirements")
+ expect(err).to include("You specified: rack (= 1.0) and rack (= 1.1).")
end
it "gracefully handles error when rubygems server is unavailable" do
@@ -429,7 +467,7 @@ RSpec.describe "bundle install with gem sources" do
expect(last_command.stdboth).not_to match(/Error Report/i)
expect(err).to include("An error occurred while installing ajp-rails (0.0.0), and Bundler cannot continue.").
- and include("Make sure that `gem install ajp-rails -v '0.0.0' --source '#{file_uri_for(gem_repo2)}/'` succeeds before bundling.")
+ and include("Bundler::APIResponseInvalidDependenciesError")
end
it "doesn't blow up when the local .bundle/config is empty" do
@@ -463,6 +501,7 @@ RSpec.describe "bundle install with gem sources" do
install_gemfile <<-G, :raise_on_error => false
::RUBY_VERSION = '2.0.1'
ruby '~> 2.2'
+ source "#{file_uri_for(gem_repo1)}"
G
expect(err).to include("Your Ruby version is 2.0.1, but your Gemfile specified ~> 2.2")
end
@@ -474,12 +513,14 @@ RSpec.describe "bundle install with gem sources" do
::RUBY_VERSION = '2.1.3'
::RUBY_PATCHLEVEL = 100
ruby '~> 2.1.0'
+ source "#{file_uri_for(gem_repo1)}"
G
end
it "writes current Ruby version to Gemfile.lock" do
- lockfile_should_be <<-L
+ expect(lockfile).to eq <<~L
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -500,10 +541,12 @@ RSpec.describe "bundle install with gem sources" do
::RUBY_VERSION = '2.2.3'
::RUBY_PATCHLEVEL = 100
ruby '~> 2.2.0'
+ source "#{file_uri_for(gem_repo1)}"
G
- lockfile_should_be <<-L
+ expect(lockfile).to eq <<~L
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -521,6 +564,7 @@ RSpec.describe "bundle install with gem sources" do
it "does not crash when unlocking" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
ruby '>= 2.1.0'
G
@@ -539,6 +583,7 @@ RSpec.describe "bundle install with gem sources" do
build_lib "foo"
gemfile = <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :path => "#{lib_path("foo-1.0")}"
G
File.open("#{root_dir}/Gemfile", "w") do |file|
@@ -555,6 +600,7 @@ RSpec.describe "bundle install with gem sources" do
build_lib "foo", :path => root_dir
gemfile = <<-G
+ source "#{file_uri_for(gem_repo1)}"
gemspec
G
File.open("#{root_dir}/Gemfile", "w") do |file|
@@ -566,16 +612,48 @@ RSpec.describe "bundle install with gem sources" do
end
describe "when requesting a quiet install via --quiet" do
- it "should be quiet" do
+ it "should be quiet if there are no warnings" do
bundle "config set force_ruby_platform true"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack'
G
- bundle :install, :quiet => true, :raise_on_error => false
- expect(err).to include("Could not find gem 'rack'")
- expect(err).to_not include("Your Gemfile has no gem server sources")
+ bundle :install, :quiet => true
+ expect(out).to be_empty
+ expect(err).to be_empty
+ end
+
+ it "should still display warnings and errors" do
+ bundle "config set force_ruby_platform true"
+
+ create_file("install_with_warning.rb", <<~RUBY)
+ require "#{lib_dir}/bundler"
+ require "#{lib_dir}/bundler/cli"
+ require "#{lib_dir}/bundler/cli/install"
+
+ module RunWithWarning
+ def run
+ super
+ rescue
+ Bundler.ui.warn "BOOOOO"
+ raise
+ end
+ end
+
+ Bundler::CLI::Install.prepend(RunWithWarning)
+ RUBY
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'non-existing-gem'
+ G
+
+ bundle :install, :quiet => true, :raise_on_error => false, :env => { "RUBYOPT" => "-r#{bundled_app("install_with_warning.rb")}" }
+ expect(out).to be_empty
+ expect(err).to include("Could not find gem 'non-existing-gem'")
+ expect(err).to include("BOOOOO")
end
end
@@ -600,6 +678,77 @@ RSpec.describe "bundle install with gem sources" do
end
end
+ describe "when bundle gems path does not have write access", :permissions do
+ let(:gems_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems") }
+
+ before do
+ FileUtils.mkdir_p(gems_path)
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem 'rack'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ FileUtils.chmod("-x", gems_path)
+ bundle "config set --local path vendor"
+
+ begin
+ bundle :install, :raise_on_error => false
+ ensure
+ FileUtils.chmod("+x", gems_path)
+ end
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+
+ expect(err).to include(
+ "There was an error while trying to create `#{gems_path.join("rack-1.0.0")}`. " \
+ "It is likely that you need to grant executable permissions for all parent directories and write permissions for `#{gems_path}`."
+ )
+ end
+ end
+
+ describe "when the path of a specific gem is not writable", :permissions do
+ let(:gems_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems") }
+ let(:foo_path) { gems_path.join("foo-1.0.0") }
+
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0.0" do |s|
+ s.write "CHANGELOG.md", "foo"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'foo'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ bundle "config set --local path vendor"
+ bundle :install
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+
+ FileUtils.chmod("-x", foo_path)
+
+ begin
+ bundle "install --redownload", :raise_on_error => false
+ ensure
+ FileUtils.chmod("+x", foo_path)
+ end
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+
+ expect(err).to include(
+ "There was an error while trying to delete `#{foo_path}`. " \
+ "It is likely that you need to grant executable permissions for all parent directories " \
+ "and write permissions for `#{gems_path}`, and the same thing for all subdirectories inside #{foo_path}."
+ )
+ end
+ end
+
describe "when bundle cache path does not have write access", :permissions do
let(:cache_path) { bundled_app("vendor/#{Bundler.ruby_scope}/cache") }
@@ -614,7 +763,7 @@ RSpec.describe "bundle install with gem sources" do
it "should display a proper message to explain the problem" do
FileUtils.chmod(0o500, cache_path)
- bundle "config --local path vendor"
+ bundle "config set --local path vendor"
bundle :install, :raise_on_error => false
expect(err).to include(cache_path.to_s)
expect(err).to include("grant write permissions")
@@ -627,7 +776,7 @@ RSpec.describe "bundle install with gem sources" do
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
- bundle "config --local path bundle"
+ bundle "config set --local path bundle"
bundle "install", :standalone => true
end
@@ -696,4 +845,117 @@ RSpec.describe "bundle install with gem sources" do
)
end
end
+
+ context "with missing platform specific gems in lockfile" do
+ before do
+ build_repo4 do
+ build_gem "racc", "1.5.2"
+
+ build_gem "nokogiri", "1.12.4" do |s|
+ s.platform = "x86_64-darwin"
+ s.add_runtime_dependency "racc", "~> 1.4"
+ end
+
+ build_gem "nokogiri", "1.12.4" do |s|
+ s.platform = "x86_64-linux"
+ s.add_runtime_dependency "racc", "~> 1.4"
+ end
+
+ build_gem "crass", "1.0.6"
+
+ build_gem "loofah", "2.12.0" do |s|
+ s.add_runtime_dependency "crass", "~> 1.0.2"
+ s.add_runtime_dependency "nokogiri", ">= 1.5.9"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ ruby "#{RUBY_VERSION}"
+
+ gem "loofah", "~> 2.12.0"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ crass (1.0.6)
+ loofah (2.12.0)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ nokogiri (1.12.4-x86_64-darwin)
+ racc (~> 1.4)
+ racc (1.5.2)
+
+ PLATFORMS
+ x86_64-darwin-20
+ x86_64-linux
+
+ DEPENDENCIES
+ loofah (~> 2.12.0)
+
+ RUBY VERSION
+ #{Bundler::RubyVersion.system}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile" do
+ bundle "config set --local path vendor/bundle"
+
+ simulate_platform "x86_64-linux" do
+ bundle "install", :artifice => "compact_index"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ crass (1.0.6)
+ loofah (2.12.0)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ nokogiri (1.12.4-x86_64-darwin)
+ racc (~> 1.4)
+ nokogiri (1.12.4-x86_64-linux)
+ racc (~> 1.4)
+ racc (1.5.2)
+
+ PLATFORMS
+ x86_64-darwin-20
+ x86_64-linux
+
+ DEPENDENCIES
+ loofah (~> 2.12.0)
+
+ RUBY VERSION
+ #{Bundler::RubyVersion.system}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with --local flag" do
+ before do
+ system_gems "rack-1.0.0", :path => default_bundle_path
+ end
+
+ it "respects installed gems without fetching any remote sources" do
+ install_gemfile <<-G, :local => true
+ source "#{file_uri_for(gem_repo1)}"
+
+ source "https://not-existing-source" do
+ gem "rack"
+ end
+ G
+
+ expect(last_command).to be_success
+ end
+ end
end
diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb
index 44bde4a9b8..22709f4528 100644
--- a/spec/bundler/commands/lock_spec.rb
+++ b/spec/bundler/commands/lock_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe "bundle lock" do
it "does not fetch remote specs when using the --local option" do
bundle "lock --update --local", :raise_on_error => false
- expect(err).to match(/sources listed in your Gemfile|installed locally/)
+ expect(err).to match(/locally installed gems/)
end
it "works with --gemfile flag" do
@@ -314,7 +314,7 @@ RSpec.describe "bundle lock" do
simulate_platform(mingw) { bundle :lock }
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
@@ -339,7 +339,7 @@ RSpec.describe "bundle lock" do
simulate_platform(rb) { bundle :lock }
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
@@ -426,7 +426,7 @@ RSpec.describe "bundle lock" do
simulate_platform(Gem::Platform.new("x86_64-darwin")) { bundle "lock" }
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
@@ -491,6 +491,51 @@ RSpec.describe "bundle lock" do
end
end
+ it "does not conflict on ruby requirements when adding new platforms" do
+ next_minor = Gem.ruby_version.segments[0..1].map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".")
+
+ build_repo4 do
+ build_gem "raygun-apm", "1.0.78" do |s|
+ s.platform = "x86_64-linux"
+ s.required_ruby_version = "< #{next_minor}.dev"
+ end
+
+ build_gem "raygun-apm", "1.0.78" do |s|
+ s.platform = "universal-darwin"
+ s.required_ruby_version = "< #{next_minor}.dev"
+ end
+
+ build_gem "raygun-apm", "1.0.78" do |s|
+ s.platform = "x64-mingw32"
+ s.required_ruby_version = "< #{next_minor}.dev"
+ end
+ end
+
+ gemfile <<-G
+ source "https://localgemserver.test"
+
+ gem "raygun-apm"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://localgemserver.test/
+ specs:
+ raygun-apm (1.0.78-universal-darwin)
+
+ PLATFORMS
+ x86_64-darwin-19
+
+ DEPENDENCIES
+ raygun-apm
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --add-platform x86_64-linux", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ end
+
context "when an update is available" do
let(:repo) { gem_repo2 }
diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb
index c5294a6b4d..db5228ebc2 100644
--- a/spec/bundler/commands/newgem_spec.rb
+++ b/spec/bundler/commands/newgem_spec.rb
@@ -12,15 +12,16 @@ RSpec.describe "bundle gem" do
def bundle_exec_rubocop
prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec"))
- rubocop_version = RUBY_VERSION > "2.4" ? "1.7.0" : "0.81.0"
- gems = ["minitest", "rake", "rake-compiler", "rspec", "rubocop -v #{rubocop_version}", "test-unit"]
- gems.unshift "parallel -v 1.19.2" if RUBY_VERSION < "2.5"
- gems += ["rubocop-ast -v 1.4.0"] if rubocop_version == "1.7.0"
- path = Bundler.feature_flag.default_install_uses_path? ? local_gem_path(:base => bundled_app(gem_name)) : system_gem_path
- realworld_system_gems gems, :path => path
+ bundle "config set path #{rubocop_gems}", :dir => bundled_app(gem_name)
bundle "exec rubocop --debug --config .rubocop.yml", :dir => bundled_app(gem_name)
end
+ def bundle_exec_standardrb
+ prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec"))
+ bundle "config set path #{standard_gems}", :dir => bundled_app(gem_name)
+ bundle "exec standardrb --debug", :dir => bundled_app(gem_name)
+ end
+
let(:generated_gemspec) { Bundler.load_gemspec_uncached(bundled_app(gem_name).join("#{gem_name}.gemspec")) }
let(:gem_name) { "mygem" }
@@ -28,77 +29,38 @@ RSpec.describe "bundle gem" do
let(:require_path) { "mygem" }
before do
- git_config_content = <<-EOF
- [user]
- name = "Bundler User"
- email = user@example.com
- [github]
- user = bundleuser
- EOF
- @git_config_location = ENV["GIT_CONFIG"]
- path = "#{tmp}/test_git_config.txt"
- File.open(path, "w") {|f| f.write(git_config_content) }
- ENV["GIT_CONFIG"] = path
- end
-
- after do
- FileUtils.rm(ENV["GIT_CONFIG"]) if File.exist?(ENV["GIT_CONFIG"])
- ENV["GIT_CONFIG"] = @git_config_location
- end
-
- shared_examples_for "git config is present" do
- context "git config user.{name,email} present" do
- it "sets gemspec author to git user.name if available" do
- expect(generated_gemspec.authors.first).to eq("Bundler User")
- end
-
- it "sets gemspec email to git user.email if available" do
- expect(generated_gemspec.email.first).to eq("user@example.com")
- end
- end
- end
-
- shared_examples_for "git config is absent" do
- it "sets gemspec author to default message if git user.name is not set or empty" do
- expect(generated_gemspec.authors.first).to eq("TODO: Write your name")
- end
-
- it "sets gemspec email to default message if git user.email is not set or empty" do
- expect(generated_gemspec.email.first).to eq("TODO: Write your email address")
- end
+ sys_exec("git config --global user.name 'Bundler User'")
+ sys_exec("git config --global user.email user@example.com")
+ sys_exec("git config --global github.user bundleuser")
end
describe "git repo initialization" do
- shared_examples_for "a gem with an initial git repo" do
- before do
- bundle "gem #{gem_name} #{flags}"
- end
-
- it "generates a gem skeleton with a .git folder", :readline do
- gem_skeleton_assertions
- expect(bundled_app("#{gem_name}/.git")).to exist
- end
+ it "generates a gem skeleton with a .git folder", :readline do
+ bundle "gem #{gem_name}"
+ gem_skeleton_assertions
+ expect(bundled_app("#{gem_name}/.git")).to exist
end
- context "when using the default" do
- it_behaves_like "a gem with an initial git repo" do
- let(:flags) { "" }
- end
+ it "generates a gem skeleton with a .git folder when passing --git", :readline do
+ bundle "gem #{gem_name} --git"
+ gem_skeleton_assertions
+ expect(bundled_app("#{gem_name}/.git")).to exist
end
- context "when explicitly passing --git" do
- it_behaves_like "a gem with an initial git repo" do
- let(:flags) { "--git" }
- end
+ it "generates a gem skeleton without a .git folder when passing --no-git", :readline do
+ bundle "gem #{gem_name} --no-git"
+ gem_skeleton_assertions
+ expect(bundled_app("#{gem_name}/.git")).not_to exist
end
- context "when passing --no-git", :readline do
+ context "on a path with spaces" do
before do
- bundle "gem #{gem_name} --no-git"
+ Dir.mkdir(bundled_app("path with spaces"))
end
- it "generates a gem skeleton without a .git folder" do
- gem_skeleton_assertions
- expect(bundled_app("#{gem_name}/.git")).not_to exist
+
+ it "properly initializes git repo", :readline do
+ bundle "gem #{gem_name}", :dir => bundled_app("path with spaces")
+ expect(bundled_app("path with spaces/#{gem_name}/.git")).to exist
end
end
end
@@ -125,19 +87,24 @@ RSpec.describe "bundle gem" do
end
shared_examples_for "--coc flag" do
- before do
- bundle "gem #{gem_name} --coc"
- end
it "generates a gem skeleton with MIT license" do
+ bundle "gem #{gem_name} --coc"
gem_skeleton_assertions
expect(bundled_app("#{gem_name}/CODE_OF_CONDUCT.md")).to exist
end
- describe "README additions" do
- it "generates the README with a section for the Code of Conduct" do
- expect(bundled_app("#{gem_name}/README.md").read).to include("## Code of Conduct")
- expect(bundled_app("#{gem_name}/README.md").read).to include("https://github.com/bundleuser/#{gem_name}/blob/master/CODE_OF_CONDUCT.md")
- end
+ it "generates the README with a section for the Code of Conduct" do
+ bundle "gem #{gem_name} --coc"
+ expect(bundled_app("#{gem_name}/README.md").read).to include("## Code of Conduct")
+ expect(bundled_app("#{gem_name}/README.md").read).to match(%r{https://github\.com/bundleuser/#{gem_name}/blob/.*/CODE_OF_CONDUCT.md})
+ end
+
+ it "generates the README with a section for the Code of Conduct, respecting the configured git default branch", :git => ">= 2.28.0" do
+ sys_exec("git config --global init.defaultBranch main")
+ bundle "gem #{gem_name} --coc"
+
+ expect(bundled_app("#{gem_name}/README.md").read).to include("## Code of Conduct")
+ expect(bundled_app("#{gem_name}/README.md").read).to include("https://github.com/bundleuser/#{gem_name}/blob/main/CODE_OF_CONDUCT.md")
end
end
@@ -150,11 +117,9 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/CODE_OF_CONDUCT.md")).to_not exist
end
- describe "README additions" do
- it "generates the README without a section for the Code of Conduct" do
- expect(bundled_app("#{gem_name}/README.md").read).not_to include("## Code of Conduct")
- expect(bundled_app("#{gem_name}/README.md").read).not_to include("https://github.com/bundleuser/#{gem_name}/blob/master/CODE_OF_CONDUCT.md")
- end
+ it "generates the README without a section for the Code of Conduct" do
+ expect(bundled_app("#{gem_name}/README.md").read).not_to include("## Code of Conduct")
+ expect(bundled_app("#{gem_name}/README.md").read).not_to match(%r{https://github\.com/bundleuser/#{gem_name}/blob/.*/CODE_OF_CONDUCT.md})
end
end
@@ -179,8 +144,68 @@ RSpec.describe "bundle gem" do
end
shared_examples_for "--rubocop flag" do
+ context "is deprecated", :bundler => "< 3" do
+ before do
+ bundle "gem #{gem_name} --rubocop"
+ end
+
+ it "generates a gem skeleton with rubocop" do
+ gem_skeleton_assertions
+ expect(bundled_app("test-gem/Rakefile")).to read_as(
+ include("# frozen_string_literal: true").
+ and(include('require "rubocop/rake_task"').
+ and(include("RuboCop::RakeTask.new").
+ and(match(/default:.+:rubocop/))))
+ )
+ end
+
+ it "includes rubocop in generated Gemfile" do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ builder = Bundler::Dsl.new
+ builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
+ builder.dependencies
+ rubocop_dep = builder.dependencies.find {|d| d.name == "rubocop" }
+ expect(rubocop_dep).not_to be_nil
+ end
+
+ it "generates a default .rubocop.yml" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ end
+ end
+ end
+
+ shared_examples_for "--no-rubocop flag" do
+ context "is deprecated", :bundler => "< 3" do
+ define_negated_matcher :exclude, :include
+
+ before do
+ bundle "gem #{gem_name} --no-rubocop"
+ end
+
+ it "generates a gem skeleton without rubocop" do
+ gem_skeleton_assertions
+ expect(bundled_app("test-gem/Rakefile")).to read_as(exclude("rubocop"))
+ expect(bundled_app("test-gem/#{gem_name}.gemspec")).to read_as(exclude("rubocop"))
+ end
+
+ it "does not include rubocop in generated Gemfile" do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ builder = Bundler::Dsl.new
+ builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
+ builder.dependencies
+ rubocop_dep = builder.dependencies.find {|d| d.name == "rubocop" }
+ expect(rubocop_dep).to be_nil
+ end
+
+ it "doesn't generate a default .rubocop.yml" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ end
+ end
+ end
+
+ shared_examples_for "--linter=rubocop flag" do
before do
- bundle "gem #{gem_name} --rubocop"
+ bundle "gem #{gem_name} --linter=rubocop"
end
it "generates a gem skeleton with rubocop" do
@@ -207,11 +232,38 @@ RSpec.describe "bundle gem" do
end
end
- shared_examples_for "--no-rubocop flag" do
+ shared_examples_for "--linter=standard flag" do
+ before do
+ bundle "gem #{gem_name} --linter=standard"
+ end
+
+ it "generates a gem skeleton with standard" do
+ gem_skeleton_assertions
+ expect(bundled_app("test-gem/Rakefile")).to read_as(
+ include('require "standard/rake"').
+ and(match(/default:.+:standard/))
+ )
+ end
+
+ it "includes standard in generated Gemfile" do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ builder = Bundler::Dsl.new
+ builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
+ builder.dependencies
+ standard_dep = builder.dependencies.find {|d| d.name == "standard" }
+ expect(standard_dep).not_to be_nil
+ end
+
+ it "generates a default .standard.yml" do
+ expect(bundled_app("#{gem_name}/.standard.yml")).to exist
+ end
+ end
+
+ shared_examples_for "--linter=none flag" do
define_negated_matcher :exclude, :include
before do
- bundle "gem #{gem_name} --no-rubocop"
+ bundle "gem #{gem_name} --linter=none"
end
it "generates a gem skeleton without rubocop" do
@@ -229,43 +281,63 @@ RSpec.describe "bundle gem" do
expect(rubocop_dep).to be_nil
end
+ it "does not include standard in generated Gemfile" do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ builder = Bundler::Dsl.new
+ builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
+ builder.dependencies
+ standard_dep = builder.dependencies.find {|d| d.name == "standard" }
+ expect(standard_dep).to be_nil
+ end
+
it "doesn't generate a default .rubocop.yml" do
expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
end
+
+ it "doesn't generate a default .standard.yml" do
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ end
end
- it "has no rubocop offenses when using --rubocop flag", :readline do
+ it "has no rubocop offenses when using --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --rubocop"
+ bundle "gem #{gem_name} --linter=rubocop"
bundle_exec_rubocop
- expect(err).to be_empty
+ expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext and --rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --rubocop"
+ bundle "gem #{gem_name} --ext --linter=rubocop"
bundle_exec_rubocop
- expect(err).to be_empty
+ expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext, --test=minitest, and --rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext, --test=minitest, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --test=minitest --rubocop"
+ bundle "gem #{gem_name} --ext --test=minitest --linter=rubocop"
bundle_exec_rubocop
- expect(err).to be_empty
+ expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext, --test=rspec, and --rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext, --test=rspec, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --test=rspec --rubocop"
+ bundle "gem #{gem_name} --ext --test=rspec --linter=rubocop"
bundle_exec_rubocop
- expect(err).to be_empty
+ expect(last_command).to be_success
end
- it "has no rubocop offenses when using --ext, --ext=test-unit, and --rubocop flag", :readline do
+ it "has no rubocop offenses when using --ext, --ext=test-unit, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- bundle "gem #{gem_name} --ext --test=test-unit --rubocop"
+ bundle "gem #{gem_name} --ext --test=test-unit --linter=rubocop"
bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
+ it "has no standard offenses when using --linter=standard flag", :readline do
+ skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
+ bundle "gem #{gem_name} --linter=standard"
+ bundle_exec_standardrb
expect(err).to be_empty
end
@@ -302,7 +374,7 @@ RSpec.describe "bundle gem" do
context "git config github.user is absent" do
before do
- sys_exec("git config --unset github.user")
+ sys_exec("git config --global --unset github.user")
bundle "gem #{gem_name}"
end
@@ -383,6 +455,55 @@ RSpec.describe "bundle gem" do
end
end
+ shared_examples_for "--github-username option" do |github_username|
+ before do
+ bundle "gem #{gem_name} --github-username=#{github_username}"
+ end
+
+ it "generates a gem skeleton" do
+ gem_skeleton_assertions
+ end
+
+ it "contribute URL set to given github username" do
+ expect(bundled_app("#{gem_name}/README.md").read).not_to include("[USERNAME]")
+ expect(bundled_app("#{gem_name}/README.md").read).to include("github.com/#{github_username}")
+ end
+ end
+
+ shared_examples_for "github_username configuration" do
+ context "with github_username setting set to some value" do
+ before do
+ global_config "BUNDLE_GEM__GITHUB_USERNAME" => "different_username"
+ bundle "gem #{gem_name}"
+ end
+
+ it "generates a gem skeleton" do
+ gem_skeleton_assertions
+ end
+
+ it "contribute URL set to bundle config setting" do
+ expect(bundled_app("#{gem_name}/README.md").read).not_to include("[USERNAME]")
+ expect(bundled_app("#{gem_name}/README.md").read).to include("github.com/different_username")
+ end
+ end
+
+ context "with github_username setting set to false" do
+ before do
+ global_config "BUNDLE_GEM__GITHUB_USERNAME" => "false"
+ bundle "gem #{gem_name}"
+ end
+
+ it "generates a gem skeleton" do
+ gem_skeleton_assertions
+ end
+
+ it "contribute URL set to [USERNAME]" do
+ expect(bundled_app("#{gem_name}/README.md").read).to include("[USERNAME]")
+ expect(bundled_app("#{gem_name}/README.md").read).not_to include("github.com/bundleuser")
+ end
+ end
+ end
+
shared_examples_for "generating a gem" do
it "generates a gem skeleton" do
bundle "gem #{gem_name}"
@@ -392,6 +513,7 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/Rakefile")).to exist
expect(bundled_app("#{gem_name}/lib/#{require_path}.rb")).to exist
expect(bundled_app("#{gem_name}/lib/#{require_path}/version.rb")).to exist
+ expect(bundled_app("#{gem_name}/sig/#{require_path}.rbs")).to exist
expect(bundled_app("#{gem_name}/.gitignore")).to exist
expect(bundled_app("#{gem_name}/bin/setup")).to exist
@@ -408,35 +530,53 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/lib/#{require_path}/version.rb").read).to match(/VERSION = "0.1.0"/)
end
+ it "declare String type for VERSION constant" do
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/sig/#{require_path}.rbs").read).to match(/VERSION: String/)
+ end
+
context "git config user.{name,email} is set" do
before do
bundle "gem #{gem_name}"
end
- it_should_behave_like "git config is present"
+ it "sets gemspec author to git user.name if available" do
+ expect(generated_gemspec.authors.first).to eq("Bundler User")
+ end
+
+ it "sets gemspec email to git user.email if available" do
+ expect(generated_gemspec.email.first).to eq("user@example.com")
+ end
end
context "git config user.{name,email} is not set" do
before do
- sys_exec("git config --unset user.name", :dir => bundled_app)
- sys_exec("git config --unset user.email", :dir => bundled_app)
+ sys_exec("git config --global --unset user.name")
+ sys_exec("git config --global --unset user.email")
bundle "gem #{gem_name}"
end
- it_should_behave_like "git config is absent"
+ it "sets gemspec author to default message if git user.name is not set or empty" do
+ expect(generated_gemspec.authors.first).to eq("TODO: Write your name")
+ end
+
+ it "sets gemspec email to default message if git user.email is not set or empty" do
+ expect(generated_gemspec.email.first).to eq("TODO: Write your email address")
+ end
end
it "sets gemspec metadata['allowed_push_host']" do
bundle "gem #{gem_name}"
expect(generated_gemspec.metadata["allowed_push_host"]).
- to match(/mygemserver\.com/)
+ to match(/example\.com/)
end
it "sets a minimum ruby version" do
bundle "gem #{gem_name}"
- expect(generated_gemspec.required_ruby_version).to eq(Gem::Requirement.new(Gem.ruby_version < Gem::Version.new("2.4.a") ? ">= 2.3.0" : ">= 2.4.0"))
+ expect(generated_gemspec.required_ruby_version.to_s).to start_with(">=")
end
it "requires the version file" do
@@ -451,6 +591,14 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(/class Error < StandardError; end$/)
end
+ it "does not include the gemspec file in files" do
+ bundle "gem #{gem_name}"
+
+ bundler_gemspec = Bundler::GemHelper.new(gemspec_dir).gemspec
+
+ expect(bundler_gemspec.files).not_to include("#{gem_name}.gemspec")
+ end
+
it "runs rake without problems" do
bundle "gem #{gem_name}"
@@ -861,6 +1009,127 @@ RSpec.describe "bundle gem" do
end
end
+ context "--linter with no argument" do
+ it "does not generate any linter config" do
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ end
+ end
+
+ context "--linter set to rubocop" do
+ it "generates a RuboCop config" do
+ bundle "gem #{gem_name} --linter=rubocop"
+
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ end
+ end
+
+ context "--linter set to standard" do
+ it "generates a Standard config" do
+ bundle "gem #{gem_name} --linter=standard"
+
+ expect(bundled_app("#{gem_name}/.standard.yml")).to exist
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ end
+ end
+
+ context "gem.linter setting set to none" do
+ it "doesn't generate any linter config" do
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ end
+ end
+
+ context "gem.linter setting set to rubocop" do
+ it "generates a RuboCop config file" do
+ bundle "config set gem.linter rubocop"
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ end
+ end
+
+ context "gem.linter setting set to standard" do
+ it "generates a Standard config file" do
+ bundle "config set gem.linter standard"
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/.standard.yml")).to exist
+ end
+ end
+
+ context "gem.rubocop setting set to true", :bundler => "< 3" do
+ before do
+ bundle "config set gem.rubocop true"
+ bundle "gem #{gem_name}"
+ end
+
+ it "generates rubocop config" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ end
+
+ it "unsets gem.rubocop" do
+ bundle "config gem.rubocop"
+ expect(out).to include("You have not configured a value for `gem.rubocop`")
+ end
+
+ it "sets gem.linter=rubocop instead" do
+ bundle "config gem.linter"
+ expect(out).to match(/Set for the current user .*: "rubocop"/)
+ end
+ end
+
+ context "gem.linter set to rubocop and --linter with no arguments", :hint_text do
+ before do
+ bundle "config set gem.linter rubocop"
+ bundle "gem #{gem_name} --linter"
+ end
+
+ it "generates a RuboCop config file" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ end
+
+ it "hints that --linter is already configured" do
+ expect(out).to match("rubocop is already configured, ignoring --linter flag.")
+ end
+ end
+
+ context "gem.linter setting set to false and --linter with no arguments", :hint_text do
+ before do
+ bundle "config set gem.linter false"
+ bundle "gem #{gem_name} --linter"
+ end
+
+ it "asks to setup a linter" do
+ expect(out).to match("Do you want to add a code linter and formatter to your gem?")
+ end
+
+ it "hints that the choice will only be applied to the current gem" do
+ expect(out).to match("Your choice will only be applied to this gem.")
+ end
+ end
+
+ context "gem.linter setting not set and --linter with no arguments", :hint_text do
+ before do
+ bundle "gem #{gem_name} --linter"
+ end
+
+ it "asks to setup a linter" do
+ expect(out).to match("Do you want to add a code linter and formatter to your gem?")
+ end
+
+ it "hints that the choice will be applied to future bundle gem calls" do
+ hint = "Future `bundle gem` calls will use your choice. " \
+ "This setting can be changed anytime with `bundle config gem.linter`."
+ expect(out).to match(hint)
+ end
+ end
+
context "--edit option" do
it "opens the generated gemspec in the user's text editor" do
output = bundle "gem #{gem_name} --edit=echo"
@@ -911,6 +1180,9 @@ RSpec.describe "bundle gem" do
before do
global_config "BUNDLE_GEM__RUBOCOP" => "true"
end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--linter=none flag"
it_behaves_like "--rubocop flag"
it_behaves_like "--no-rubocop flag"
end
@@ -919,10 +1191,40 @@ RSpec.describe "bundle gem" do
before do
global_config "BUNDLE_GEM__RUBOCOP" => "false"
end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--linter=none flag"
it_behaves_like "--rubocop flag"
it_behaves_like "--no-rubocop flag"
end
+ context "with linter option in bundle config settings set to rubocop" do
+ before do
+ global_config "BUNDLE_GEM__LINTER" => "rubocop"
+ end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--linter=none flag"
+ end
+
+ context "with linter option in bundle config settings set to standard" do
+ before do
+ global_config "BUNDLE_GEM__LINTER" => "standard"
+ end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--linter=none flag"
+ end
+
+ context "with linter option in bundle config settings set to false" do
+ before do
+ global_config "BUNDLE_GEM__LINTER" => "false"
+ end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--linter=none flag"
+ end
+
context "with changelog option in bundle config settings set to true" do
before do
global_config "BUNDLE_GEM__CHANGELOG" => "true"
@@ -940,6 +1242,57 @@ RSpec.describe "bundle gem" do
end
end
+ context "testing --github-username option against git and bundle config settings", :readline do
+ context "without git config set" do
+ before do
+ sys_exec("git config --global --unset github.user")
+ end
+ context "with github-username option in bundle config settings set to some value" do
+ before do
+ global_config "BUNDLE_GEM__GITHUB_USERNAME" => "different_username"
+ end
+ it_behaves_like "--github-username option", "gh_user"
+ end
+
+ context "with github-username option in bundle config settings set to false" do
+ before do
+ global_config "BUNDLE_GEM__GITHUB_USERNAME" => "false"
+ end
+ it_behaves_like "--github-username option", "gh_user"
+ end
+ end
+
+ context "with git config set" do
+ context "with github-username option in bundle config settings set to some value" do
+ before do
+ global_config "BUNDLE_GEM__GITHUB_USERNAME" => "different_username"
+ end
+ it_behaves_like "--github-username option", "gh_user"
+ end
+
+ context "with github-username option in bundle config settings set to false" do
+ before do
+ global_config "BUNDLE_GEM__GITHUB_USERNAME" => "false"
+ end
+ it_behaves_like "--github-username option", "gh_user"
+ end
+ end
+ end
+
+ context "testing github_username bundle config against git config settings", :readline do
+ context "without git config set" do
+ before do
+ sys_exec("git config --global --unset github.user")
+ end
+
+ it_behaves_like "github_username configuration"
+ end
+
+ context "with git config set" do
+ it_behaves_like "github_username configuration"
+ end
+ end
+
context "gem naming with underscore", :readline do
let(:gem_name) { "test_gem" }
@@ -1093,7 +1446,7 @@ Usage: "bundle gem NAME [OPTIONS]"
end
it "asks about CI service" do
- global_config "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__RUBOCOP" => "false"
+ global_config "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__LINTER" => "false"
bundle "gem foobar" do |input, _, _|
input.puts "github"
@@ -1103,7 +1456,7 @@ Usage: "bundle gem NAME [OPTIONS]"
end
it "asks about MIT license" do
- global_config "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__RUBOCOP" => "false"
+ global_config "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__LINTER" => "false"
bundle "config list"
@@ -1115,7 +1468,7 @@ Usage: "bundle gem NAME [OPTIONS]"
end
it "asks about CoC" do
- global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__RUBOCOP" => "false"
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__LINTER" => "false"
bundle "gem foobar" do |input, _, _|
input.puts "yes"
@@ -1125,7 +1478,7 @@ Usage: "bundle gem NAME [OPTIONS]"
end
it "asks about CHANGELOG" do
- global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__RUBOCOP" => "false",
+ global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__LINTER" => "false",
"BUNDLE_GEM__COC" => "false"
bundle "gem foobar" do |input, _, _|
@@ -1140,7 +1493,7 @@ Usage: "bundle gem NAME [OPTIONS]"
it "should fail gracefully" do
FileUtils.touch(bundled_app("conflict-foobar"))
bundle "gem conflict-foobar", :raise_on_error => false
- expect(err).to include("Errno::ENOTDIR")
+ expect(err).to eq("Couldn't create a new gem named `conflict-foobar` because there's an existing file named `conflict-foobar`.")
expect(exitstatus).to eql(32)
end
end
diff --git a/spec/bundler/commands/open_spec.rb b/spec/bundler/commands/open_spec.rb
index d18e620783..53dc35c2c7 100644
--- a/spec/bundler/commands/open_spec.rb
+++ b/spec/bundler/commands/open_spec.rb
@@ -105,6 +105,7 @@ RSpec.describe "bundle open" do
skip "No default gems available on this test run" if default_gems.empty?
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "json"
G
end
diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb
index 0ee8bd425a..731d67af1b 100644
--- a/spec/bundler/commands/outdated_spec.rb
+++ b/spec/bundler/commands/outdated_spec.rb
@@ -1,24 +1,24 @@
# frozen_string_literal: true
RSpec.describe "bundle outdated" do
- before :each do
- build_repo2 do
- build_git "foo", :path => lib_path("foo")
- build_git "zebra", :path => lib_path("zebra")
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "zebra", :git => "#{lib_path("zebra")}"
- gem "foo", :git => "#{lib_path("foo")}"
- gem "activesupport", "2.3.5"
- gem "weakling", "~> 0.0.1"
- gem "duradura", '7.0'
- gem "terranova", '8'
- G
- end
-
describe "with no arguments" do
+ before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+ end
+
it "returns a sorted list of outdated gems" do
update_repo2 do
build_gem "activesupport", "3.0"
@@ -102,6 +102,23 @@ RSpec.describe "bundle outdated" do
end
describe "with --verbose option" do
+ before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+ end
+
it "shows the location of the latest version's gemspec if installed" do
bundle "config set clean false"
@@ -134,8 +151,79 @@ RSpec.describe "bundle outdated" do
end
end
+ describe "with multiple, duplicated sources, with lockfile in old format", :bundler => "< 3" do
+ before do
+ build_repo2 do
+ build_gem "dotenv", "2.7.6"
+
+ build_gem "oj", "3.11.3"
+ build_gem "oj", "3.11.5"
+
+ build_gem "vcr", "6.0.0"
+ end
+
+ build_repo gem_repo3 do
+ build_gem "pkg-gem-flowbyte-with-dep", "1.0.0" do |s|
+ s.add_dependency "oj"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "dotenv"
+
+ source "https://gem.repo3" do
+ gem 'pkg-gem-flowbyte-with-dep'
+ end
+
+ gem "vcr",source: "https://gem.repo2"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ remote: https://gem.repo3/
+ specs:
+ dotenv (2.7.6)
+ oj (3.11.3)
+ pkg-gem-flowbyte-with-dep (1.0.0)
+ oj
+ vcr (6.0.0)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ dotenv
+ pkg-gem-flowbyte-with-dep!
+ vcr!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "works" do
+ bundle :install, :artifice => "compact_index"
+ bundle :outdated, :artifice => "compact_index", :raise_on_error => false
+
+ expected_output = <<~TABLE
+ Gem Current Latest Requested Groups
+ oj 3.11.3 3.11.5
+ TABLE
+
+ expect(out).to include(expected_output.strip)
+ end
+ end
+
describe "with --group option" do
before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -201,7 +289,10 @@ RSpec.describe "bundle outdated" do
describe "with --groups option and outdated transitive dependencies" do
before do
- update_repo2 do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+
build_gem "bar", %w[2.0.0]
build_gem "bar_dependant", "7.0" do |s|
@@ -234,6 +325,11 @@ RSpec.describe "bundle outdated" do
describe "with --groups option" do
before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -272,6 +368,24 @@ RSpec.describe "bundle outdated" do
end
describe "with --local option" do
+ before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+
+ gem "weakling", "~> 0.0.1"
+ gem "terranova", '8'
+ group :development, :test do
+ gem 'activesupport', '2.3.5'
+ gem "duradura", '7.0'
+ end
+ G
+ end
+
it "uses local cache to return a list of outdated gems" do
update_repo2 do
build_gem "activesupport", "2.3.4"
@@ -305,10 +419,23 @@ RSpec.describe "bundle outdated" do
shared_examples_for "a minimal output is desired" do
context "and gems are outdated" do
before do
- update_repo2 do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+
build_gem "activesupport", "3.0"
build_gem "weakling", "0.2"
end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
end
it "outputs a sorted list of outdated gems with a more minimal format" do
@@ -341,6 +468,21 @@ RSpec.describe "bundle outdated" do
describe "with specified gems" do
it "returns list of outdated gems" do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+
update_repo2 do
build_gem "activesupport", "3.0"
update_git "foo", :path => lib_path("foo")
@@ -358,6 +500,23 @@ RSpec.describe "bundle outdated" do
end
describe "pre-release gems" do
+ before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+ end
+
context "without the --pre option" do
it "ignores pre-release versions" do
update_repo2 do
@@ -413,6 +572,23 @@ RSpec.describe "bundle outdated" do
filter_strict_option = Bundler.feature_flag.bundler_2_mode? ? :"filter-strict" : :strict
describe "with --#{filter_strict_option} option" do
+ before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+ end
+
it "only reports gems that have a newer version that matches the specified dependency version requirements" do
update_repo2 do
build_gem "activesupport", "3.0"
@@ -521,6 +697,23 @@ RSpec.describe "bundle outdated" do
end
describe "with invalid gem name" do
+ before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+ end
+
it "returns could not find gem name" do
bundle "outdated invalid_gem_name", :raise_on_error => false
expect(err).to include("Could not find gem 'invalid_gem_name'.")
@@ -546,12 +739,16 @@ RSpec.describe "bundle outdated" do
context "after bundle install --deployment", :bundler => "< 3" do
before do
- install_gemfile <<-G, :deployment => true, :raise_on_error => false
+ build_repo2
+
+ gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "rack"
gem "foo"
G
+ bundle :lock
+ bundle :install, :deployment => true
end
it "outputs a helpful message about being in deployment mode" do
@@ -568,6 +765,11 @@ RSpec.describe "bundle outdated" do
context "after bundle config set --local deployment true" do
before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -591,6 +793,8 @@ RSpec.describe "bundle outdated" do
context "update available for a gem on a different platform" do
before do
+ build_repo2
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "laduradura", '= 5.15.2'
@@ -604,6 +808,10 @@ RSpec.describe "bundle outdated" do
end
context "update available for a gem on the same platform while multiple platforms used for gem" do
+ before do
+ build_repo2
+ end
+
it "reports that updates are available if the Ruby platform is used" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -643,6 +851,21 @@ RSpec.describe "bundle outdated" do
shared_examples_for "major version updates are detected" do
before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+
update_repo2 do
build_gem "activesupport", "3.3.5"
build_gem "weakling", "0.8.0"
@@ -654,6 +877,21 @@ RSpec.describe "bundle outdated" do
context "when on a new machine" do
before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+
simulate_new_machine
update_git "foo", :path => lib_path("foo")
@@ -669,6 +907,21 @@ RSpec.describe "bundle outdated" do
shared_examples_for "minor version updates are detected" do
before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+
update_repo2 do
build_gem "activesupport", "2.7.5"
build_gem "weakling", "2.0.1"
@@ -680,6 +933,21 @@ RSpec.describe "bundle outdated" do
shared_examples_for "patch version updates are detected" do
before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+
update_repo2 do
build_gem "activesupport", "2.3.7"
build_gem "weakling", "0.3.1"
@@ -698,6 +966,21 @@ RSpec.describe "bundle outdated" do
shared_examples_for "major version is ignored" do
before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+
update_repo2 do
build_gem "activesupport", "3.3.5"
build_gem "weakling", "1.0.1"
@@ -709,6 +992,21 @@ RSpec.describe "bundle outdated" do
shared_examples_for "minor version is ignored" do
before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+
update_repo2 do
build_gem "activesupport", "2.4.5"
build_gem "weakling", "0.3.1"
@@ -720,6 +1018,21 @@ RSpec.describe "bundle outdated" do
shared_examples_for "patch version is ignored" do
before do
+ build_repo2 do
+ build_git "foo", :path => lib_path("foo")
+ build_git "zebra", :path => lib_path("zebra")
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "zebra", :git => "#{lib_path("zebra")}"
+ gem "foo", :git => "#{lib_path("foo")}"
+ gem "activesupport", "2.3.5"
+ gem "weakling", "~> 0.0.1"
+ gem "duradura", '7.0'
+ gem "terranova", '8'
+ G
+
update_repo2 do
build_gem "activesupport", "2.3.6"
build_gem "weakling", "0.0.4"
@@ -929,4 +1242,103 @@ RSpec.describe "bundle outdated" do
expect(out).to end_with(expected_output)
end
end
+
+ describe "with a multiplatform lockfile" do
+ before do
+ build_repo4 do
+ build_gem "nokogiri", "1.11.1"
+ build_gem "nokogiri", "1.11.1" do |s|
+ s.platform = Bundler.local_platform
+ end
+
+ build_gem "nokogiri", "1.11.2"
+ build_gem "nokogiri", "1.11.2" do |s|
+ s.platform = Bundler.local_platform
+ end
+ end
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.11.1)
+ nokogiri (1.11.1-#{Bundler.local_platform})
+
+ PLATFORMS
+ ruby
+ #{Bundler.local_platform}
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "nokogiri"
+ G
+ end
+
+ it "reports a single entry per gem" do
+ bundle "outdated", :raise_on_error => false
+
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups
+ nokogiri 1.11.1 1.11.2 >= 0 default
+ TABLE
+
+ expect(out).to end_with(expected_output)
+ end
+ end
+
+ context "when a gem is no longer a dependency after a full update" do
+ before do
+ build_repo4 do
+ build_gem "mini_portile2", "2.5.2" do |s|
+ s.add_dependency "net-ftp", "~> 0.1"
+ end
+
+ build_gem "mini_portile2", "2.5.3"
+
+ build_gem "net-ftp", "0.1.2"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "mini_portile2"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ mini_portile2 (2.5.2)
+ net-ftp (~> 0.1)
+ net-ftp (0.1.2)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ mini_portile2
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "works" do
+ bundle "outdated", :raise_on_error => false
+
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups
+ mini_portile2 2.5.2 2.5.3 >= 0 default
+ TABLE
+
+ expect(out).to end_with(expected_output)
+ end
+ end
end
diff --git a/spec/bundler/commands/post_bundle_message_spec.rb b/spec/bundler/commands/post_bundle_message_spec.rb
index 2c965f0ddd..3050b87754 100644
--- a/spec/bundler/commands/post_bundle_message_spec.rb
+++ b/spec/bundler/commands/post_bundle_message_spec.rb
@@ -29,24 +29,24 @@ RSpec.describe "post bundle message" do
expect(out).to include(bundle_complete_message)
expect(out).to include(installed_gems_stats)
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle :install
expect(out).to include(bundle_show_message)
- expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include("Gems in the group 'emo' were not installed")
expect(out).to include(bundle_complete_message)
expect(out).to include(installed_gems_stats)
- bundle "config --local without emo test"
+ bundle "config set --local without emo test"
bundle :install
expect(out).to include(bundle_show_message)
- expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include("Gems in the groups 'emo' and 'test' were not installed")
expect(out).to include(bundle_complete_message)
expect(out).to include("4 Gemfile dependencies, 3 gems now installed.")
- bundle "config --local without emo obama test"
+ bundle "config set --local without emo obama test"
bundle :install
expect(out).to include(bundle_show_message)
- expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not installed")
expect(out).to include(bundle_complete_message)
expect(out).to include("4 Gemfile dependencies, 2 gems now installed.")
end
@@ -55,31 +55,31 @@ RSpec.describe "post bundle message" do
let(:bundle_path) { "./vendor" }
it "shows proper messages according to the configured groups" do
- bundle "config --local path vendor"
+ bundle "config set --local path vendor"
bundle :install
expect(out).to include(bundle_show_path_message)
expect(out).to_not include("Gems in the group")
expect(out).to include(bundle_complete_message)
- bundle "config --local path vendor"
- bundle "config --local without emo"
+ bundle "config set --local path vendor"
+ bundle "config set --local without emo"
bundle :install
expect(out).to include(bundle_show_path_message)
- expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include("Gems in the group 'emo' were not installed")
expect(out).to include(bundle_complete_message)
- bundle "config --local path vendor"
- bundle "config --local without emo test"
+ bundle "config set --local path vendor"
+ bundle "config set --local without emo test"
bundle :install
expect(out).to include(bundle_show_path_message)
- expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include("Gems in the groups 'emo' and 'test' were not installed")
expect(out).to include(bundle_complete_message)
- bundle "config --local path vendor"
- bundle "config --local without emo obama test"
+ bundle "config set --local path vendor"
+ bundle "config set --local without emo obama test"
bundle :install
expect(out).to include(bundle_show_path_message)
- expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not installed")
expect(out).to include(bundle_complete_message)
end
end
@@ -88,7 +88,7 @@ RSpec.describe "post bundle message" do
let(:bundle_path) { bundled_app("cache") }
it "shows proper messages according to the configured groups" do
- bundle "config --local path #{bundle_path}"
+ bundle "config set --local path #{bundle_path}"
bundle :install
expect(out).to include("Bundled gems are installed into `./cache`")
expect(out).to_not include("Gems in the group")
@@ -100,7 +100,7 @@ RSpec.describe "post bundle message" do
let(:bundle_path) { tmp("not_bundled_app") }
it "shows proper messages according to the configured groups" do
- bundle "config --local path #{bundle_path}"
+ bundle "config set --local path #{bundle_path}"
bundle :install
expect(out).to include("Bundled gems are installed into `#{tmp("not_bundled_app")}`")
expect(out).to_not include("Gems in the group")
@@ -113,16 +113,7 @@ RSpec.describe "post bundle message" do
bundle "config set force_ruby_platform true"
end
- it "should report a helpful error message", :bundler => "< 3" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "not-a-gem", :group => :development
- G
- expect(err).to include("Could not find gem 'not-a-gem' in any of the gem sources listed in your Gemfile.")
- end
-
- it "should report a helpful error message", :bundler => "3" do
+ it "should report a helpful error message" do
install_gemfile <<-G, :raise_on_error => false
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -130,7 +121,6 @@ RSpec.describe "post bundle message" do
G
expect(err).to include <<-EOS.strip
Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)}/ or installed locally.
-The source does not contain any versions of 'not-a-gem'
EOS
end
@@ -165,7 +155,7 @@ The source does not contain any versions of 'not-a-gem'
bundle "install --without emo"
bundle :install
expect(out).to include(bundle_show_message)
- expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include("Gems in the group 'emo' were not installed")
expect(out).to include(bundle_complete_message)
expect(out).to include(installed_gems_stats)
end
@@ -174,7 +164,7 @@ The source does not contain any versions of 'not-a-gem'
bundle "install --without emo test"
bundle :install
expect(out).to include(bundle_show_message)
- expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include("Gems in the groups 'emo' and 'test' were not installed")
expect(out).to include(bundle_complete_message)
end
@@ -182,7 +172,7 @@ The source does not contain any versions of 'not-a-gem'
bundle "install --without emo obama test"
bundle :install
expect(out).to include(bundle_show_message)
- expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not installed")
expect(out).to include(bundle_complete_message)
end
end
@@ -193,22 +183,22 @@ The source does not contain any versions of 'not-a-gem'
expect(out).not_to include("Gems in the groups")
expect(out).to include(bundle_updated_message)
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle :install
bundle :update, :all => true
- expect(out).to include("Gems in the group emo were not updated")
+ expect(out).to include("Gems in the group 'emo' were not updated")
expect(out).to include(bundle_updated_message)
- bundle "config --local without emo test"
+ bundle "config set --local without emo test"
bundle :install
bundle :update, :all => true
- expect(out).to include("Gems in the groups emo and test were not updated")
+ expect(out).to include("Gems in the groups 'emo' and 'test' were not updated")
expect(out).to include(bundle_updated_message)
- bundle "config --local without emo obama test"
+ bundle "config set --local without emo obama test"
bundle :install
bundle :update, :all => true
- expect(out).to include("Gems in the groups emo, obama and test were not updated")
+ expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not updated")
expect(out).to include(bundle_updated_message)
end
end
diff --git a/spec/bundler/commands/pristine_spec.rb b/spec/bundler/commands/pristine_spec.rb
index 6978f302c1..2f730bd4e2 100644
--- a/spec/bundler/commands/pristine_spec.rb
+++ b/spec/bundler/commands/pristine_spec.rb
@@ -203,6 +203,16 @@ RSpec.describe "bundle pristine", :ruby_repo do
end
end
+ context "when BUNDLE_GEMFILE doesn't exist" do
+ before do
+ bundle "pristine", :env => { "BUNDLE_GEMFILE" => "does/not/exist" }, :raise_on_error => false
+ end
+
+ it "shows a meaningful error" do
+ expect(err).to eq("#{bundled_app("does/not/exist")} not found")
+ end
+ end
+
def find_spec(name)
without_env_side_effects do
Bundler.definition.specs[name].first
diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb
index 46c42fea10..95d6e75e9f 100644
--- a/spec/bundler/commands/remove_spec.rb
+++ b/spec/bundler/commands/remove_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe "bundle remove" do
end
end
- context "when --install flag is specified" do
+ context "when --install flag is specified", :bundler => "< 3" do
it "removes gems from .bundle" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -40,19 +40,22 @@ RSpec.describe "bundle remove" do
bundle "remove rack"
expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
+ expect(the_bundle).to_not include_gems "rack"
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
context "when gem is specified in multiple lines" do
it "shows success for removed gem" do
+ build_git "rack"
+
gemfile <<-G
source '#{file_uri_for(gem_repo1)}'
gem 'git'
gem 'rack',
- git: 'https://github.com/rack/rack',
+ git: "#{lib_path("rack-1.0")}",
branch: 'master'
gem 'nokogiri'
G
@@ -60,7 +63,7 @@ RSpec.describe "bundle remove" do
bundle "remove rack"
expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source '#{file_uri_for(gem_repo1)}'
gem 'git'
@@ -97,7 +100,7 @@ RSpec.describe "bundle remove" do
expect(out).to include("rack was removed.")
expect(out).to include("rails was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -116,7 +119,7 @@ RSpec.describe "bundle remove" do
bundle "remove rails rack minitest", :raise_on_error => false
expect(err).to include("`rack` is not specified in #{bundled_app_gemfile} so it could not be removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
gem "rails"
@@ -138,7 +141,7 @@ RSpec.describe "bundle remove" do
bundle "remove rack"
expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -158,7 +161,7 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -178,7 +181,7 @@ RSpec.describe "bundle remove" do
bundle "remove rack"
expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
group :test do
@@ -204,7 +207,7 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -223,13 +226,13 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
end
- context "when the gem is present in mutiple groups" do
+ context "when the gem is present in multiple groups" do
it "removes all empty blocks" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -246,7 +249,7 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -269,7 +272,7 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -292,7 +295,7 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
group :test do
@@ -319,7 +322,7 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
group :test do
@@ -333,7 +336,7 @@ RSpec.describe "bundle remove" do
end
describe "arbitrary gemfile" do
- context "when mutiple gems are present in same line" do
+ context "when multiple gems are present in same line" do
it "shows warning for gems not removed" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -343,7 +346,7 @@ RSpec.describe "bundle remove" do
bundle "remove rails", :raise_on_error => false
expect(err).to include("Gems could not be removed. rack (>= 0) would also have been removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
gem "rack"; gem "rails"
G
@@ -365,7 +368,7 @@ RSpec.describe "bundle remove" do
expect(out).to include("rails was removed.")
expect(out).to include("minitest was removed.")
expect(out).to include("rack, rspec could not be removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
gem"rack"
gem"rspec"
@@ -397,7 +400,7 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -481,7 +484,7 @@ RSpec.describe "bundle remove" do
expect(out).to include("rack was removed.")
expect(err).to include("`rack` is not specified in #{bundled_app("Gemfile-other")} so it could not be removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
eval_gemfile "Gemfile-other"
@@ -506,7 +509,7 @@ RSpec.describe "bundle remove" do
expect(out).to include("rack was removed.")
expect(err).to include("Gems could not be removed. rails (>= 0) would also have been removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
eval_gemfile "Gemfile-other"
@@ -531,7 +534,7 @@ RSpec.describe "bundle remove" do
expect(err).to include("Gems could not be removed. rails (>= 0) would also have been removed.")
expect(bundled_app("Gemfile-other").read).to include("gem \"rack\"")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
eval_gemfile "Gemfile-other"
@@ -574,7 +577,7 @@ RSpec.describe "bundle remove" do
bundle "remove rack"
expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -593,7 +596,7 @@ RSpec.describe "bundle remove" do
bundle "remove rack"
expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -630,7 +633,7 @@ RSpec.describe "bundle remove" do
bundle "remove rack"
expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
# gem "rack" might be used in the future
@@ -649,7 +652,7 @@ RSpec.describe "bundle remove" do
bundle "remove rack"
expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
G
end
@@ -668,7 +671,7 @@ RSpec.describe "bundle remove" do
expect(out).to_not include("puma was removed.")
expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
gem "puma" # implements interface provided by gem "rack"
G
@@ -688,7 +691,7 @@ RSpec.describe "bundle remove" do
expect(out).to include("puma was removed.")
expect(out).to_not include("rack was removed.")
- gemfile_should_be <<-G
+ expect(gemfile).to eq <<~G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
index 521c175711..403a48a508 100644
--- a/spec/bundler/commands/update_spec.rb
+++ b/spec/bundler/commands/update_spec.rb
@@ -1,18 +1,18 @@
# frozen_string_literal: true
RSpec.describe "bundle update" do
- before :each do
- build_repo2
+ describe "with no arguments" do
+ before do
+ build_repo2
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport"
- gem "rack-obama"
- gem "platform_specific"
- G
- end
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "activesupport"
+ gem "rack-obama"
+ gem "platform_specific"
+ G
+ end
- describe "with no arguments", :bundler => "< 3" do
it "updates the entire bundle" do
update_repo2 do
build_gem "rack", "1.2" do |s|
@@ -39,7 +39,18 @@ RSpec.describe "bundle update" do
end
end
- describe "with --all", :bundler => "3" do
+ describe "with --all" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "activesupport"
+ gem "rack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "updates the entire bundle" do
update_repo2 do
build_gem "rack", "1.2" do |s|
@@ -55,6 +66,8 @@ RSpec.describe "bundle update" do
end
it "doesn't delete the Gemfile.lock file if something goes wrong" do
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
+
gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "activesupport"
@@ -83,25 +96,36 @@ RSpec.describe "bundle update" do
before { bundle "config set update_requires_all_flag true" }
it "errors when passed nothing" do
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
bundle :update, :raise_on_error => false
expect(err).to eq("To update everything, pass the `--all` flag.")
end
it "errors when passed --all and another option" do
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
bundle "update --all foo", :raise_on_error => false
expect(err).to eq("Cannot specify --all along with specific options.")
end
it "updates everything when passed --all" do
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
bundle "update --all"
expect(out).to include("Bundle updated!")
end
end
describe "--quiet argument" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "activesupport"
+ gem "rack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "hides UI messages" do
bundle "update --quiet"
expect(out).not_to include("Bundle updated!")
@@ -109,6 +133,17 @@ RSpec.describe "bundle update" do
end
describe "with a top level dependency" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "activesupport"
+ gem "rack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "unlocks all child dependencies that are unrelated to other locked dependencies" do
update_repo2 do
build_gem "rack", "1.2" do |s|
@@ -124,6 +159,17 @@ RSpec.describe "bundle update" do
end
describe "with an unknown dependency" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "activesupport"
+ gem "rack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "should inform the user" do
bundle "update halting-problem-solver", :raise_on_error => false
expect(err).to include "Could not find gem 'halting-problem-solver'"
@@ -135,6 +181,17 @@ RSpec.describe "bundle update" do
end
describe "with a child dependency" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "activesupport"
+ gem "rack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "should update the child dependency" do
update_repo2 do
build_gem "rack", "1.2" do |s|
@@ -148,76 +205,230 @@ RSpec.describe "bundle update" do
end
describe "when a possible resolve requires an older version of a locked gem" do
- context "and only_update_to_newer_versions is set" do
- before do
- bundle "config set only_update_to_newer_versions true"
+ it "does not go to an older version" do
+ build_repo4 do
+ build_gem "tilt", "2.0.8"
+ build_gem "slim", "3.0.9" do |s|
+ s.add_dependency "tilt", [">= 1.3.3", "< 2.1"]
+ end
+ build_gem "slim_lint", "0.16.1" do |s|
+ s.add_dependency "slim", [">= 3.0", "< 5.0"]
+ end
+ build_gem "slim-rails", "0.2.1" do |s|
+ s.add_dependency "slim", ">= 0.9.2"
+ end
+ build_gem "slim-rails", "3.1.3" do |s|
+ s.add_dependency "slim", "~> 3.0"
+ end
end
- it "does not go to an older version" do
- build_repo4 do
- build_gem "tilt", "2.0.8"
- build_gem "slim", "3.0.9" do |s|
- s.add_dependency "tilt", [">= 1.3.3", "< 2.1"]
- end
- build_gem "slim_lint", "0.16.1" do |s|
- s.add_dependency "slim", [">= 3.0", "< 5.0"]
- end
- build_gem "slim-rails", "0.2.1" do |s|
- s.add_dependency "slim", ">= 0.9.2"
- end
- build_gem "slim-rails", "3.1.3" do |s|
- s.add_dependency "slim", "~> 3.0"
- end
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "slim-rails"
+ gem "slim_lint"
+ G
+
+ expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
+
+ update_repo4 do
+ build_gem "slim", "4.0.0" do |s|
+ s.add_dependency "tilt", [">= 2.0.6", "< 2.1"]
end
+ end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "slim-rails"
- gem "slim_lint"
- G
+ bundle "update", :all => true
- expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
+ expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
+ end
- update_repo4 do
- build_gem "slim", "4.0.0" do |s|
- s.add_dependency "tilt", [">= 2.0.6", "< 2.1"]
- end
+ it "does not go to an older version, even if the version upgrade that could cause another gem to downgrade is activated first" do
+ build_repo4 do
+ # countries is processed before country_select by the resolver due to having less spec groups (groups of versions with the same dependencies) (2 vs 3)
+
+ build_gem "countries", "2.1.4"
+ build_gem "countries", "3.1.0"
+
+ build_gem "countries", "4.0.0" do |s|
+ s.add_dependency "sixarm_ruby_unaccent", "~> 1.1"
+ end
+
+ build_gem "country_select", "1.2.0"
+
+ build_gem "country_select", "2.1.4" do |s|
+ s.add_dependency "countries", "~> 2.0"
+ end
+ build_gem "country_select", "3.1.1" do |s|
+ s.add_dependency "countries", "~> 2.0"
end
- bundle "update", :all => true
+ build_gem "country_select", "5.1.0" do |s|
+ s.add_dependency "countries", "~> 3.0"
+ end
- expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
+ build_gem "sixarm_ruby_unaccent", "1.1.0"
end
- it "should still downgrade if forced by the Gemfile" do
- build_repo4 do
- build_gem "a"
- build_gem "b", "1.0"
- build_gem "b", "2.0"
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "country_select"
+ gem "countries"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ countries (3.1.0)
+ country_select (5.1.0)
+ countries (~> 3.0)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ countries
+ country_select
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ previous_lockfile = lockfile
+
+ bundle "lock --update"
+
+ expect(lockfile).to eq(previous_lockfile)
+ end
+
+ it "does not downgrade indirect dependencies unnecessarily" do
+ build_repo4 do
+ build_gem "a" do |s|
+ s.add_dependency "b"
+ s.add_dependency "c"
end
+ build_gem "b"
+ build_gem "c"
+ build_gem "c", "2.0"
+ end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "a"
- gem "b"
- G
+ install_gemfile <<-G, :verbose => true
+ source "#{file_uri_for(gem_repo4)}"
+ gem "a"
+ G
- expect(the_bundle).to include_gems("a 1.0", "b 2.0")
+ expect(the_bundle).to include_gems("a 1.0", "b 1.0", "c 2.0")
- gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "a"
- gem "b", "1.0"
- G
+ update_repo4 do
+ build_gem "b", "2.0" do |s|
+ s.add_dependency "c", "< 2"
+ end
+ end
- bundle "update b"
+ bundle "update", :all => true, :verbose => true
+ expect(the_bundle).to include_gems("a 1.0", "b 1.0", "c 2.0")
+ end
- expect(the_bundle).to include_gems("a 1.0", "b 1.0")
+ it "should still downgrade if forced by the Gemfile" do
+ build_repo4 do
+ build_gem "a"
+ build_gem "b", "1.0"
+ build_gem "b", "2.0"
end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "a"
+ gem "b"
+ G
+
+ expect(the_bundle).to include_gems("a 1.0", "b 2.0")
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "a"
+ gem "b", "1.0"
+ G
+
+ bundle "update b"
+
+ expect(the_bundle).to include_gems("a 1.0", "b 1.0")
+ end
+
+ it "should still downgrade if forced by the Gemfile, when transitive dependencies also need downgrade" do
+ build_repo4 do
+ build_gem "activesupport", "6.1.4.1" do |s|
+ s.add_dependency "tzinfo", "~> 2.0"
+ end
+
+ build_gem "activesupport", "6.0.4.1" do |s|
+ s.add_dependency "tzinfo", "~> 1.1"
+ end
+
+ build_gem "tzinfo", "2.0.4"
+ build_gem "tzinfo", "1.2.9"
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "activesupport", "~> 6.1.0"
+ G
+
+ expect(the_bundle).to include_gems("activesupport 6.1.4.1", "tzinfo 2.0.4")
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "activesupport", "~> 6.0.0"
+ G
+
+ original_lockfile = lockfile
+
+ expected_lockfile = <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ activesupport (6.0.4.1)
+ tzinfo (~> 1.1)
+ tzinfo (1.2.9)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ activesupport (~> 6.0.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update activesupport"
+ expect(the_bundle).to include_gems("activesupport 6.0.4.1", "tzinfo 1.2.9")
+ expect(lockfile).to eq(expected_lockfile)
+
+ lockfile original_lockfile
+ bundle "update"
+ expect(the_bundle).to include_gems("activesupport 6.0.4.1", "tzinfo 1.2.9")
+ expect(lockfile).to eq(expected_lockfile)
+
+ lockfile original_lockfile
+ bundle "lock --update"
+ expect(the_bundle).to include_gems("activesupport 6.0.4.1", "tzinfo 1.2.9")
+ expect(lockfile).to eq(expected_lockfile)
end
end
describe "with --local option" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "activesupport"
+ gem "rack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "doesn't hit repo2" do
FileUtils.rm_rf(gem_repo2)
@@ -227,6 +438,10 @@ RSpec.describe "bundle update" do
end
describe "with --group option" do
+ before do
+ build_repo2
+ end
+
it "should update only specified group gems" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -263,7 +478,7 @@ RSpec.describe "bundle update" do
end
context "when there is a source with the same name as a gem in a group" do
- before :each do
+ before do
build_git "foo", :path => lib_path("activesupport")
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
@@ -305,6 +520,17 @@ RSpec.describe "bundle update" do
end
describe "in a frozen bundle" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "activesupport"
+ gem "rack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "should fail loudly", :bundler => "< 3" do
bundle "install --deployment"
bundle "update", :all => true, :raise_on_error => false
@@ -330,7 +556,11 @@ RSpec.describe "bundle update" do
end
describe "with --source option" do
- it "should not update gems not included in the source that happen to have the same name", :bundler => "< 3" do
+ before do
+ build_repo2
+ end
+
+ it "should not update gems not included in the source that happen to have the same name" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "activesupport"
@@ -338,10 +568,10 @@ RSpec.describe "bundle update" do
update_repo2 { build_gem "activesupport", "3.0" }
bundle "update --source activesupport"
- expect(the_bundle).to include_gem "activesupport 3.0"
+ expect(the_bundle).not_to include_gem "activesupport 3.0"
end
- it "should not update gems not included in the source that happen to have the same name", :bundler => "3" do
+ it "should not update gems not included in the source that happen to have the same name" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "activesupport"
@@ -349,22 +579,7 @@ RSpec.describe "bundle update" do
update_repo2 { build_gem "activesupport", "3.0" }
bundle "update --source activesupport"
- expect(the_bundle).not_to include_gem "activesupport 3.0"
- end
-
- context "with unlock_source_unlocks_spec set to false" do
- before { bundle "config set unlock_source_unlocks_spec false" }
-
- it "should not update gems not included in the source that happen to have the same name" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport"
- G
- update_repo2 { build_gem "activesupport", "3.0" }
-
- bundle "update --source activesupport"
- expect(the_bundle).not_to include_gems "activesupport 3.0"
- end
+ expect(the_bundle).not_to include_gems "activesupport 3.0"
end
end
@@ -384,20 +599,7 @@ RSpec.describe "bundle update" do
G
end
- it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "< 3" do
- update_repo2 do
- build_gem "fred", "2.0"
- build_gem "harry", "2.0" do |s|
- s.add_dependency "fred"
- end
- end
-
- bundle "update --source harry"
- expect(the_bundle).to include_gems "harry 2.0"
- expect(the_bundle).to include_gems "fred 1.0"
- end
-
- it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "3" do
+ it "should not update the child dependencies of a gem that has the same name as the source" do
update_repo2 do
build_gem "fred", "2.0"
build_gem "harry", "2.0" do |s|
@@ -429,7 +631,7 @@ RSpec.describe "bundle update" do
G
end
- it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "< 3" do
+ it "should not update the child dependencies of a gem that has the same name as the source" do
update_repo2 do
build_gem "george", "2.0"
build_gem "harry", "2.0" do |s|
@@ -438,27 +640,133 @@ RSpec.describe "bundle update" do
end
bundle "update --source harry"
- expect(the_bundle).to include_gems "harry 2.0"
- expect(the_bundle).to include_gems "fred 1.0"
- expect(the_bundle).to include_gems "george 1.0"
+ expect(the_bundle).to include_gems "harry 1.0", "fred 1.0", "george 1.0"
end
+ end
- it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "3" do
- update_repo2 do
- build_gem "george", "2.0"
- build_gem "harry", "2.0" do |s|
- s.add_dependency "george"
+ it "shows the previous version of the gem when updated from rubygems source", :bundler => "< 3" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "activesupport"
+ G
+
+ bundle "update", :all => true
+ expect(out).to include("Using activesupport 2.3.5")
+
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update", :all => true
+ expect(out).to include("Installing activesupport 3.0 (was 2.3.5)")
+ end
+
+ context "with suppress_install_using_messages set" do
+ before { bundle "config set suppress_install_using_messages true" }
+
+ it "only prints `Using` for versions that have changed" do
+ build_repo4 do
+ build_gem "bar"
+ build_gem "foo"
+ end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "bar"
+ gem "foo"
+ G
+
+ bundle "update", :all => true
+ expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/)
+
+ update_repo4 do
+ build_gem "foo", "2.0"
+ end
+
+ bundle "update", :all => true
+ out.sub!("Removing foo (1.0)\n", "")
+ expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/)
+ end
+ end
+
+ it "shows error message when Gemfile.lock is not preset and gem is specified" do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "activesupport"
+ G
+
+ bundle "update nonexisting", :raise_on_error => false
+ expect(err).to include("This Bundle hasn't been installed yet. Run `bundle install` to update and install the bundled gems.")
+ expect(exitstatus).to eq(22)
+ end
+
+ context "with multiple, duplicated sources, with lockfile in old format", :bundler => "< 3" do
+ before do
+ build_repo2 do
+ build_gem "dotenv", "2.7.6"
+
+ build_gem "oj", "3.11.3"
+ build_gem "oj", "3.11.5"
+
+ build_gem "vcr", "6.0.0"
+ end
+
+ build_repo gem_repo3 do
+ build_gem "pkg-gem-flowbyte-with-dep", "1.0.0" do |s|
+ s.add_dependency "oj"
end
end
- bundle "update --source harry"
- expect(the_bundle).to include_gems "harry 1.0", "fred 1.0", "george 1.0"
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "dotenv"
+
+ source "https://gem.repo3" do
+ gem 'pkg-gem-flowbyte-with-dep'
+ end
+
+ gem "vcr",source: "https://gem.repo2"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ remote: https://gem.repo3/
+ specs:
+ dotenv (2.7.6)
+ oj (3.11.3)
+ pkg-gem-flowbyte-with-dep (1.0.0)
+ oj
+ vcr (6.0.0)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ dotenv
+ pkg-gem-flowbyte-with-dep!
+ vcr!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "works" do
+ bundle :install, :artifice => "compact_index"
+ bundle "update oj", :artifice => "compact_index"
+
+ expect(out).to include("Bundle updated!")
+ expect(the_bundle).to include_gems "oj 3.11.5"
end
end
end
RSpec.describe "bundle update in more complicated situations" do
- before :each do
+ before do
build_repo2
end
@@ -506,6 +814,7 @@ RSpec.describe "bundle update in more complicated situations" do
build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
@@ -522,6 +831,7 @@ RSpec.describe "bundle update in more complicated situations" do
build_git "rack"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
gem "rack", :git => '#{lib_path("rack-1.0")}'
G
@@ -646,7 +956,7 @@ RSpec.describe "bundle update without a Gemfile.lock" do
end
RSpec.describe "bundle update when a gem depends on a newer version of bundler" do
- before(:each) do
+ before do
build_repo2 do
build_gem "rails", "3.0.1" do |s|
s.add_dependency "bundler", Bundler::VERSION.succ
@@ -669,72 +979,13 @@ RSpec.describe "bundle update when a gem depends on a newer version of bundler"
end
end
-RSpec.describe "bundle update" do
- it "shows the previous version of the gem when updated from rubygems source", :bundler => "< 3" do
- build_repo2
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport"
- G
-
- bundle "update", :all => true
- expect(out).to include("Using activesupport 2.3.5")
-
- update_repo2 do
- build_gem "activesupport", "3.0"
- end
-
- bundle "update", :all => true
- expect(out).to include("Installing activesupport 3.0 (was 2.3.5)")
- end
-
- context "with suppress_install_using_messages set" do
- before { bundle "config set suppress_install_using_messages true" }
-
- it "only prints `Using` for versions that have changed" do
- build_repo4 do
- build_gem "bar"
- build_gem "foo"
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "bar"
- gem "foo"
- G
-
- bundle "update", :all => true
- expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/)
-
- update_repo4 do
- build_gem "foo", "2.0"
- end
-
- bundle "update", :all => true
- out.sub!("Removing foo (1.0)\n", "")
- expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/)
- end
- end
-
- it "shows error message when Gemfile.lock is not preset and gem is specified" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport"
- G
-
- bundle "update nonexisting", :raise_on_error => false
- expect(err).to include("This Bundle hasn't been installed yet. Run `bundle install` to update and install the bundled gems.")
- expect(exitstatus).to eq(22)
- end
-end
-
RSpec.describe "bundle update --ruby" do
before do
install_gemfile <<-G
::RUBY_VERSION = '2.1.3'
::RUBY_PATCHLEVEL = 100
ruby '~> 2.1.0'
+ source "#{file_uri_for(gem_repo1)}"
G
end
@@ -743,13 +994,15 @@ RSpec.describe "bundle update --ruby" do
gemfile <<-G
::RUBY_VERSION = '2.1.4'
::RUBY_PATCHLEVEL = 222
+ source "#{file_uri_for(gem_repo1)}"
G
end
it "removes the Ruby from the Gemfile.lock" do
bundle "update --ruby"
- lockfile_should_be <<-L
+ expect(lockfile).to eq <<~L
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -769,13 +1022,15 @@ RSpec.describe "bundle update --ruby" do
::RUBY_VERSION = '2.1.4'
::RUBY_PATCHLEVEL = 222
ruby '~> 2.1.0'
+ source "#{file_uri_for(gem_repo1)}"
G
end
it "updates the Gemfile.lock with the latest version" do
bundle "update --ruby"
- lockfile_should_be <<-L
+ expect(lockfile).to eq <<~L
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -798,6 +1053,7 @@ RSpec.describe "bundle update --ruby" do
::RUBY_VERSION = '2.2.2'
::RUBY_PATCHLEVEL = 505
ruby '~> 2.1.0'
+ source "#{file_uri_for(gem_repo1)}"
G
end
it "shows a helpful error message" do
@@ -813,13 +1069,15 @@ RSpec.describe "bundle update --ruby" do
::RUBY_VERSION = '1.8.3'
::RUBY_PATCHLEVEL = 55
ruby '~> 1.8.0'
+ source "#{file_uri_for(gem_repo1)}"
G
end
it "updates the Gemfile.lock with the latest version" do
bundle "update --ruby"
- lockfile_should_be <<-L
+ expect(lockfile).to eq <<~L
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -965,9 +1223,9 @@ RSpec.describe "bundle update conservative" do
gem 'shared_owner_b'
G
- lockfile <<-L
+ lockfile <<~L
GEM
- remote: #{file_uri_for(gem_repo4)}
+ remote: #{file_uri_for(gem_repo4)}/
specs:
isolated_dep (2.0.1)
isolated_owner (1.0.1)
@@ -979,12 +1237,12 @@ RSpec.describe "bundle update conservative" do
shared_dep (~> 5.0)
PLATFORMS
- ruby
+ #{specific_local_platform}
DEPENDENCIES
+ isolated_owner
shared_owner_a
shared_owner_b
- isolated_owner
BUNDLED WITH
#{Bundler::VERSION}
@@ -1006,7 +1264,42 @@ RSpec.describe "bundle update conservative" do
it "should not eagerly unlock with --conservative" do
bundle "update --conservative shared_owner_a isolated_owner"
- expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.2", "shared_dep 5.0.1", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.1", "shared_dep 5.0.1", "shared_owner_a 3.0.2", "shared_owner_b 4.0.1"
+ end
+
+ it "should only update direct dependencies when fully updating with --conservative" do
+ bundle "update --conservative"
+
+ expect(the_bundle).to include_gems "isolated_owner 1.0.2", "isolated_dep 2.0.1", "shared_dep 5.0.1", "shared_owner_a 3.0.2", "shared_owner_b 4.0.2"
+ end
+
+ it "should only change direct dependencies when updating the lockfile with --conservative" do
+ bundle "lock --update --conservative"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ isolated_dep (2.0.1)
+ isolated_owner (1.0.2)
+ isolated_dep (~> 2.0)
+ shared_dep (5.0.1)
+ shared_owner_a (3.0.2)
+ shared_dep (~> 5.0)
+ shared_owner_b (4.0.2)
+ shared_dep (~> 5.0)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ isolated_owner
+ shared_owner_a
+ shared_owner_b
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
it "should match bundle install conservative update behavior when not eagerly unlocking" do
@@ -1026,7 +1319,7 @@ RSpec.describe "bundle update conservative" do
context "error handling" do
before do
- gemfile ""
+ gemfile "source \"#{file_uri_for(gem_repo1)}\""
end
it "raises if too many flags are provided" do
diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb
index d0aa4e4d9e..524363fde5 100644
--- a/spec/bundler/install/allow_offline_install_spec.rb
+++ b/spec/bundler/install/allow_offline_install_spec.rb
@@ -75,6 +75,7 @@ RSpec.describe "bundle install with :allow_offline_install" do
git = build_git "a", "1.0.0", :path => lib_path("a")
update_git("a", :path => git.path, :branch => "new_branch")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "a", :git => #{git.path.to_s.dump}
G
@@ -84,6 +85,7 @@ RSpec.describe "bundle install with :allow_offline_install" do
break_git_remote_ops! do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "a", :git => #{git.path.to_s.dump}, :branch => "new_branch"
G
end
diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb
index 9445be66f1..963ce82db8 100644
--- a/spec/bundler/install/bundler_spec.rb
+++ b/spec/bundler/install/bundler_spec.rb
@@ -21,12 +21,12 @@ RSpec.describe "bundle install" do
expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}"
end
- it "are not added if not already present" do
+ it "are forced to the current bundler version even if not already present" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
- expect(the_bundle).not_to include_gems "bundler #{Bundler::VERSION}"
+ expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}"
end
it "causes a conflict if explicitly requesting a different version of bundler" do
diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb
index 73a0c18f4a..54fc6371cb 100644
--- a/spec/bundler/install/deploy_spec.rb
+++ b/spec/bundler/install/deploy_spec.rb
@@ -44,8 +44,8 @@ RSpec.describe "install in deployment or frozen mode" do
it "still works if you are not in the app directory and specify --gemfile" do
bundle "install"
simulate_new_machine
- bundle "config --local deployment true"
- bundle "config --local path vendor/bundle"
+ bundle "config set --local deployment true"
+ bundle "config set --local path vendor/bundle"
bundle "install --gemfile #{tmp}/bundled_app/Gemfile", :dir => tmp
expect(the_bundle).to include_gems "rack 1.0"
end
@@ -53,13 +53,14 @@ RSpec.describe "install in deployment or frozen mode" do
it "works if you exclude a group with a git gem" do
build_git "foo"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
group :test do
gem "foo", :git => "#{lib_path("foo-1.0")}"
end
G
bundle :install
- bundle "config --local deployment true"
- bundle "config --local without test"
+ bundle "config set --local deployment true"
+ bundle "config set --local without test"
bundle :install
end
@@ -67,7 +68,7 @@ RSpec.describe "install in deployment or frozen mode" do
skip "doesn't find bundle" if Gem.win_platform?
bundle :install
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install
bundle "exec bundle check", :env => { "PATH" => path }
end
@@ -76,12 +77,26 @@ RSpec.describe "install in deployment or frozen mode" do
build_lib "foo", :path => lib_path("nested/foo")
build_lib "bar", :path => lib_path("nested/bar")
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.0", :path => "#{lib_path("nested")}"
gem "bar", :path => "#{lib_path("nested")}"
G
bundle :install
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
+ bundle :install
+ end
+
+ it "works when path gems are specified twice" do
+ build_lib "foo", :path => lib_path("nested/foo")
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :path => "#{lib_path("nested/foo")}"
+ gem "foo", :path => "#{lib_path("nested/foo")}"
+ G
+
+ bundle :install
+ bundle "config set --local deployment true"
bundle :install
end
@@ -92,18 +107,19 @@ RSpec.describe "install in deployment or frozen mode" do
gem "rack-obama", ">= 1.0"
G
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :artifice => "endpoint_strict_basic_authentication"
end
it "works with sources given by a block" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
source "#{file_uri_for(gem_repo1)}" do
gem "rack"
end
G
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install
expect(the_bundle).to include_gems "rack 1.0"
@@ -111,24 +127,24 @@ RSpec.describe "install in deployment or frozen mode" do
context "when replacing a host with the same host with credentials" do
before do
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle "install"
gemfile <<-G
- source "http://user_name:password@localgemserver.test/"
- gem "rack"
+ source "http://user_name:password@localgemserver.test/"
+ gem "rack"
G
lockfile <<-G
- GEM
- remote: http://localgemserver.test/
- specs:
- rack (1.0.0)
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ rack (1.0.0)
- PLATFORMS
- #{local}
+ PLATFORMS
+ #{local}
- DEPENDENCIES
- rack
+ DEPENDENCIES
+ rack
G
bundle "config set --local deployment true"
@@ -215,7 +231,7 @@ RSpec.describe "install in deployment or frozen mode" do
gem "rack-obama"
G
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
expect(err).to include("deployment mode")
expect(err).to include("You have added to the Gemfile")
@@ -234,9 +250,9 @@ RSpec.describe "install in deployment or frozen mode" do
expect(the_bundle).to include_gems "path_gem 1.0"
FileUtils.rm_r lib_path("path_gem-1.0")
- bundle "config --local path .bundle"
- bundle "config --local without development"
- bundle "config --local deployment true"
+ bundle "config set --local path .bundle"
+ bundle "config set --local without development"
+ bundle "config set --local deployment true"
bundle :install, :env => { "DEBUG" => "1" }
run "puts :WIN"
expect(out).to eq("WIN")
@@ -252,8 +268,8 @@ RSpec.describe "install in deployment or frozen mode" do
expect(the_bundle).to include_gems "path_gem 1.0"
FileUtils.rm_r lib_path("path_gem-1.0")
- bundle "config --local path .bundle"
- bundle "config --local deployment true"
+ bundle "config set --local path .bundle"
+ bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
expect(err).to include("The path `#{lib_path("path_gem-1.0")}` does not exist.")
end
@@ -324,7 +340,7 @@ RSpec.describe "install in deployment or frozen mode" do
gem "activesupport"
G
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
expect(err).to include("deployment mode")
expect(err).to include("You have added to the Gemfile:\n* activesupport\n\n")
@@ -338,14 +354,14 @@ RSpec.describe "install in deployment or frozen mode" do
gem "rack", :git => "git://hubz.com"
G
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
expect(err).to include("deployment mode")
- expect(err).to include("You have added to the Gemfile:\n* source: git://hubz.com")
- expect(err).not_to include("You have changed in the Gemfile")
+ expect(err).not_to include("You have added to the Gemfile")
+ expect(err).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `git://hubz.com`")
end
- it "explodes if you unpin a source" do
+ it "explodes if you change a source" do
build_git "rack"
install_gemfile <<-G
@@ -358,15 +374,15 @@ RSpec.describe "install in deployment or frozen mode" do
gem "rack"
G
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
expect(err).to include("deployment mode")
- expect(err).to include("You have deleted from the Gemfile:\n* source: #{lib_path("rack-1.0")}")
+ expect(err).not_to include("You have deleted from the Gemfile")
expect(err).not_to include("You have added to the Gemfile")
- expect(err).not_to include("You have changed in the Gemfile")
+ expect(err).to include("You have changed in the Gemfile:\n* rack from `#{lib_path("rack-1.0")}` to `no specified source`")
end
- it "explodes if you unpin a source, leaving it pinned somewhere else" do
+ it "explodes if you change a source" do
build_lib "foo", :path => lib_path("rack/foo")
build_git "rack", :path => lib_path("rack")
@@ -382,10 +398,10 @@ RSpec.describe "install in deployment or frozen mode" do
gem "foo", :git => "#{lib_path("rack")}"
G
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
expect(err).to include("deployment mode")
- expect(err).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `#{lib_path("rack")}`")
+ expect(err).to include("You have changed in the Gemfile:\n* rack from `#{lib_path("rack")}` to `no specified source`")
expect(err).not_to include("You have added to the Gemfile")
expect(err).not_to include("You have deleted from the Gemfile")
end
@@ -401,7 +417,7 @@ RSpec.describe "install in deployment or frozen mode" do
gem "rack-obama"
G
- expect(the_bundle).not_to include_gems "rack 1.0.0"
+ run "require 'rack'", :raise_on_error => false
expect(err).to include strip_whitespace(<<-E).strip
The dependencies in your gemfile changed
@@ -419,6 +435,7 @@ You have deleted from the Gemfile:
it "works fine after bundle package and bundle install --local" do
build_lib "foo", :path => lib_path("foo")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo")}"
G
diff --git a/spec/bundler/install/failure_spec.rb b/spec/bundler/install/failure_spec.rb
index d265fafa63..4a9c33754f 100644
--- a/spec/bundler/install/failure_spec.rb
+++ b/spec/bundler/install/failure_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe "bundle install" do
context "installing a gem fails" do
- it "prints out why that gem was being installed" do
+ it "prints out why that gem was being installed and the underlying error" do
build_repo2 do
build_gem "activesupport", "2.3.2" do |s|
s.extensions << "Rakefile"
@@ -18,102 +18,9 @@ RSpec.describe "bundle install" do
source "#{file_uri_for(gem_repo2)}"
gem "rails"
G
+ expect(err).to start_with("Gem::Ext::BuildError: ERROR: Failed to build gem native extension.")
expect(err).to end_with(<<-M.strip)
An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
-Make sure that `gem install activesupport -v '2.3.2' --source '#{file_uri_for(gem_repo2)}/'` succeeds before bundling.
-
-In Gemfile:
- rails was resolved to 2.3.2, which depends on
- actionmailer was resolved to 2.3.2, which depends on
- activesupport
- M
- end
-
- context "when installing a git gem" do
- it "does not tell the user to run 'gem install'" do
- build_git "activesupport", "2.3.2", :path => lib_path("activesupport") do |s|
- s.extensions << "Rakefile"
- s.write "Rakefile", <<-RUBY
- task :default do
- abort "make installing activesupport-2.3.2 fail"
- end
- RUBY
- end
-
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
- gem "activesupport", :git => "#{lib_path("activesupport")}"
- G
-
- expect(err).to end_with(<<-M.strip)
-An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
-
-In Gemfile:
- rails was resolved to 2.3.2, which depends on
- actionmailer was resolved to 2.3.2, which depends on
- activesupport
- M
- end
- end
-
- context "when installing a gem using a git block" do
- it "does not tell the user to run 'gem install'" do
- build_git "activesupport", "2.3.2", :path => lib_path("activesupport") do |s|
- s.extensions << "Rakefile"
- s.write "Rakefile", <<-RUBY
- task :default do
- abort "make installing activesupport-2.3.2 fail"
- end
- RUBY
- end
-
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
-
- git "#{lib_path("activesupport")}" do
- gem "activesupport"
- end
- G
-
- expect(err).to end_with(<<-M.strip)
-An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
-
-
-In Gemfile:
- rails was resolved to 2.3.2, which depends on
- actionmailer was resolved to 2.3.2, which depends on
- activesupport
- M
- end
- end
-
- it "prints out the hint for the remote source when available" do
- build_repo2 do
- build_gem "activesupport", "2.3.2" do |s|
- s.extensions << "Rakefile"
- s.write "Rakefile", <<-RUBY
- task :default do
- abort "make installing activesupport-2.3.2 fail"
- end
- RUBY
- end
- end
-
- build_repo4 do
- build_gem "a"
- end
-
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo4)}"
- source "#{file_uri_for(gem_repo2)}" do
- gem "rails"
- end
- G
- expect(err).to end_with(<<-M.strip)
-An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
-Make sure that `gem install activesupport -v '2.3.2' --source '#{file_uri_for(gem_repo2)}/'` succeeds before bundling.
In Gemfile:
rails was resolved to 2.3.2, which depends on
diff --git a/spec/bundler/install/gemfile/eval_gemfile_spec.rb b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
index c42ae7ef79..02283291b4 100644
--- a/spec/bundler/install/gemfile/eval_gemfile_spec.rb
+++ b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
@@ -11,12 +11,14 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
context "eval-ed Gemfile points to an internal gemspec" do
before do
create_file "Gemfile-other", <<-G
+ source "#{file_uri_for(gem_repo1)}"
gemspec :path => 'gems/gunks'
G
end
it "installs the gemspec specified gem" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
eval_gemfile 'Gemfile-other'
G
expect(out).to include("Resolving dependencies")
@@ -26,14 +28,50 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
end
end
+ context "eval-ed Gemfile points to an internal gemspec and uses a scoped source that duplicates the main Gemfile global source" do
+ before do
+ build_repo2 do
+ build_gem "rails", "6.1.3.2"
+
+ build_gem "zip-zip", "0.3"
+ end
+
+ create_file bundled_app("gems/Gemfile"), <<-G
+ source "#{file_uri_for(gem_repo2)}"
+
+ gemspec :path => "\#{__dir__}/gunks"
+
+ source "#{file_uri_for(gem_repo2)}" do
+ gem "zip-zip"
+ end
+ G
+ end
+
+ it "installs and finds gems correctly" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+
+ gem "rails"
+
+ eval_gemfile File.join(__dir__, "gems/Gemfile")
+ G
+ expect(out).to include("Resolving dependencies")
+ expect(out).to include("Bundle complete")
+
+ expect(the_bundle).to include_gem "rails 6.1.3.2"
+ end
+ end
+
context "eval-ed Gemfile has relative-path gems" do
before do
build_lib("a", :path => bundled_app("gems/a"))
create_file bundled_app("nested/Gemfile-nested"), <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "a", :path => "../gems/a"
G
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
eval_gemfile "nested/Gemfile-nested"
G
end
@@ -47,7 +85,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
# parsed lockfile and the evaluated gemfile.
it "bundles with deployment mode configured" do
bundle :install
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install
end
end
@@ -57,6 +95,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
it "installs the gemspec specified gem" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
eval_gemfile 'other/Gemfile-other'
gemspec :path => 'gems/gunks'
G
diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb
index 7a95a8abde..6d9cd2daff 100644
--- a/spec/bundler/install/gemfile/gemspec_spec.rb
+++ b/spec/bundler/install/gemfile/gemspec_spec.rb
@@ -210,6 +210,7 @@ RSpec.describe "bundle install from an existing gemspec" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gemspec
G
@@ -259,16 +260,17 @@ RSpec.describe "bundle install from an existing gemspec" do
expect(out).to eq("WIN")
end
- it "works with only_update_to_newer_versions" do
+ it "handles downgrades" do
build_lib "omg", "2.0", :path => lib_path("omg")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gemspec :path => "#{lib_path("omg")}"
G
build_lib "omg", "1.0", :path => lib_path("omg")
- bundle :install, :env => { "BUNDLE_BUNDLE_ONLY_UPDATE_TO_NEWER_VERSIONS" => "true" }
+ bundle :install
expect(the_bundle).to include_gems "omg 1.0"
end
@@ -291,7 +293,7 @@ RSpec.describe "bundle install from an existing gemspec" do
s.add_dependency "activesupport", ">= 1.0.1"
end
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :raise_on_error => false
expect(err).to include("changed")
@@ -422,14 +424,13 @@ RSpec.describe "bundle install from an existing gemspec" do
end
end
- %w[ruby jruby].each do |platform|
- simulate_platform(platform) do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gemspec
- G
- end
- end
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gemspec
+ G
+
+ simulate_platform("ruby") { bundle "install" }
+ simulate_platform("jruby") { bundle "install" }
end
context "on ruby" do
@@ -558,7 +559,7 @@ RSpec.describe "bundle install from an existing gemspec" do
it "installs the ruby platform gemspec and skips dev deps with `without development` configured" do
simulate_platform "ruby"
- bundle "config --local without development"
+ bundle "config set --local without development"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
@@ -568,4 +569,57 @@ RSpec.describe "bundle install from an existing gemspec" do
expect(the_bundle).not_to include_gem "rack"
end
end
+
+ context "with multiple platforms and resolving for more specific platforms" do
+ before do
+ build_lib("chef", :path => tmp.join("chef")) do |s|
+ s.version = "17.1.17"
+ s.write "chef-universal-mingw32.gemspec", build_spec("chef", "17.1.17", "universal-mingw32") {|sw| sw.runtime "win32-api", "~> 1.5.3" }.first.to_ruby
+ end
+ end
+
+ it "does not remove the platform specific specs from the lockfile when updating" do
+ build_repo4 do
+ build_gem "win32-api", "1.5.3" do |s|
+ s.platform = "universal-mingw32"
+ end
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gemspec :path => "../chef"
+ G
+
+ initial_lockfile = <<~L
+ PATH
+ remote: ../chef
+ specs:
+ chef (17.1.17)
+ chef (17.1.17-universal-mingw32)
+ win32-api (~> 1.5.3)
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ win32-api (1.5.3-universal-mingw32)
+
+ PLATFORMS
+ ruby
+ x64-mingw32
+ x86-mingw32
+
+ DEPENDENCIES
+ chef!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile initial_lockfile
+
+ bundle "update"
+
+ expect(lockfile).to eq initial_lockfile
+ end
+ end
end
diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb
index c0181a8788..fd3afe366a 100644
--- a/spec/bundler/install/gemfile/git_spec.rb
+++ b/spec/bundler/install/gemfile/git_spec.rb
@@ -62,6 +62,7 @@ RSpec.describe "bundle install with git sources" do
update_git "foo"
install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
@@ -84,6 +85,7 @@ RSpec.describe "bundle install with git sources" do
build_git "foo"
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.1", :git => "#{lib_path("foo-1.0")}"
G
@@ -98,6 +100,7 @@ RSpec.describe "bundle install with git sources" do
end
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
platforms :jruby do
gem "only_java", "1.2", :git => "#{lib_path("only_java-1.0-java")}"
end
@@ -119,6 +122,7 @@ RSpec.describe "bundle install with git sources" do
end
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
platforms :jruby do
gem "only_java", "1.2", :git => "#{lib_path("only_java-1.1-java")}"
end
@@ -128,7 +132,7 @@ RSpec.describe "bundle install with git sources" do
end
it "still works after moving the application directory" do
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle "install"
FileUtils.mv bundled_app, tmp("bundled_app.bck")
@@ -137,7 +141,7 @@ RSpec.describe "bundle install with git sources" do
end
it "can still install after moving the application directory" do
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle "install"
FileUtils.mv bundled_app, tmp("bundled_app.bck")
@@ -187,6 +191,7 @@ RSpec.describe "bundle install with git sources" do
it "works" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}", :ref => "#{@revision}" do
gem "foo"
end
@@ -202,6 +207,7 @@ RSpec.describe "bundle install with git sources" do
it "works when the revision is a symbol" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}", :ref => #{@revision.to_sym.inspect} do
gem "foo"
end
@@ -226,10 +232,11 @@ RSpec.describe "bundle install with git sources" do
# want to ensure we don't fallback to HEAD
update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s|
- s.write("lib/foo.rb", "raise 'FAIL'")
+ s.write("lib/foo.rb", "raise 'FAIL_FROM_RANDO'")
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}", :ref => "refs/bundler/1" do
gem "foo"
end
@@ -246,6 +253,7 @@ RSpec.describe "bundle install with git sources" do
it "works when the revision is a non-head ref and it was previously downloaded" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}" do
gem "foo"
end
@@ -260,10 +268,11 @@ RSpec.describe "bundle install with git sources" do
# want to ensure we don't fallback to HEAD
update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s|
- s.write("lib/foo.rb", "raise 'FAIL'")
+ s.write("lib/foo.rb", "raise 'FAIL_FROM_RANDO'")
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}", :ref => "refs/bundler/1" do
gem "foo"
end
@@ -284,6 +293,7 @@ RSpec.describe "bundle install with git sources" do
bundle "config set global_gem_cache true"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}" do
gem "foo"
end
@@ -306,6 +316,7 @@ RSpec.describe "bundle install with git sources" do
update_git("foo", :path => repo, :branch => branch)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{repo}", :branch => #{branch.dump} do
gem "foo"
end
@@ -322,6 +333,7 @@ RSpec.describe "bundle install with git sources" do
update_git("foo", :path => repo, :branch => branch)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{repo}", :branch => #{branch.dump} do
gem "foo"
end
@@ -339,6 +351,7 @@ RSpec.describe "bundle install with git sources" do
update_git("foo", :path => repo, :branch => branch)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{repo}", :branch => #{branch.dump} do
gem "foo"
end
@@ -357,6 +370,7 @@ RSpec.describe "bundle install with git sources" do
update_git("foo", :path => repo, :tag => tag)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{repo}", :tag => #{tag.dump} do
gem "foo"
end
@@ -373,6 +387,7 @@ RSpec.describe "bundle install with git sources" do
update_git("foo", :path => repo, :tag => tag)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{repo}", :tag => #{tag.dump} do
gem "foo"
end
@@ -390,6 +405,7 @@ RSpec.describe "bundle install with git sources" do
update_git("foo", :path => repo, :tag => tag)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{repo}", :tag => #{tag.dump} do
gem "foo"
end
@@ -647,8 +663,6 @@ RSpec.describe "bundle install with git sources" do
end
it "installs dependencies from git even if a newer gem is available elsewhere" do
- skip "override is not winning" if Gem.win_platform?
-
system_gems "rack-1.0.0"
build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
@@ -707,6 +721,7 @@ RSpec.describe "bundle install with git sources" do
build_lib "hi2u", :path => lib_path("hi2u")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path("hi2u")}" do
gem "omg"
gem "hi2u"
@@ -723,6 +738,7 @@ RSpec.describe "bundle install with git sources" do
update_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{@revision}"
G
@@ -787,6 +803,7 @@ RSpec.describe "bundle install with git sources" do
build_git "foo", "1.0"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.0", :git => "#{lib_path("foo-1.0")}"
G
@@ -808,6 +825,7 @@ RSpec.describe "bundle install with git sources" do
it "catches git errors and spits out useful output" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.0", :git => "omgomg"
G
@@ -822,6 +840,7 @@ RSpec.describe "bundle install with git sources" do
build_git "foo", :path => lib_path("foo space-1.0")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo space-1.0")}"
G
@@ -832,6 +851,7 @@ RSpec.describe "bundle install with git sources" do
build_git "forced", "1.0"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("forced-1.0")}" do
gem 'forced'
end
@@ -852,6 +872,9 @@ RSpec.describe "bundle install with git sources" do
end
it "ignores submodules if :submodule is not passed" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
build_git "has_submodule", "1.0" do |s|
s.add_dependency "submodule"
@@ -860,6 +883,7 @@ RSpec.describe "bundle install with git sources" do
sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("has_submodule-1.0")}" do
gem "has_submodule"
end
@@ -870,6 +894,9 @@ RSpec.describe "bundle install with git sources" do
end
it "handles repos with submodules" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
build_git "has_submodule", "1.0" do |s|
s.add_dependency "submodule"
@@ -878,6 +905,7 @@ RSpec.describe "bundle install with git sources" do
sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("has_submodule-1.0")}", :submodules => true do
gem "has_submodule"
end
@@ -887,6 +915,9 @@ RSpec.describe "bundle install with git sources" do
end
it "does not warn when deiniting submodules" do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_git "submodule", "1.0"
build_git "has_submodule", "1.0"
@@ -894,6 +925,7 @@ RSpec.describe "bundle install with git sources" do
sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("has_submodule-1.0")}" do
gem "has_submodule"
end
@@ -908,6 +940,7 @@ RSpec.describe "bundle install with git sources" do
git = build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}" do
gem "foo"
end
@@ -917,6 +950,7 @@ RSpec.describe "bundle install with git sources" do
update_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}", :ref => "#{git.ref_for("HEAD^")}" do
gem "foo"
end
@@ -934,6 +968,7 @@ RSpec.describe "bundle install with git sources" do
build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -947,6 +982,7 @@ RSpec.describe "bundle install with git sources" do
build_git "foo"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -961,6 +997,7 @@ RSpec.describe "bundle install with git sources" do
FileUtils.touch(default_bundle_path("bundler"))
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -978,6 +1015,7 @@ RSpec.describe "bundle install with git sources" do
build_git "bar", :path => lib_path("nested")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("nested")}"
gem "bar", :git => "#{lib_path("nested")}"
G
@@ -1035,6 +1073,7 @@ RSpec.describe "bundle install with git sources" do
build_git "valim"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "valim", :git => "#{file_uri_for(lib_path("valim-1.0"))}"
G
@@ -1060,11 +1099,13 @@ RSpec.describe "bundle install with git sources" do
revision = revision_for(lib_path("foo-1.0"))
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :ref => "#{revision}"
G
expect(out).to_not match(/Revision.*does not exist/)
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :ref => "deadbeef"
G
expect(err).to include("Revision deadbeef does not exist in the repository")
@@ -1082,7 +1123,7 @@ RSpec.describe "bundle install with git sources" do
simulate_new_machine
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install
end
end
@@ -1091,6 +1132,7 @@ RSpec.describe "bundle install with git sources" do
it "runs pre-install hooks" do
build_git "foo"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1110,6 +1152,7 @@ RSpec.describe "bundle install with git sources" do
it "runs post-install hooks" do
build_git "foo"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1129,6 +1172,7 @@ RSpec.describe "bundle install with git sources" do
it "complains if the install hook fails" do
build_git "foo"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1401,10 +1445,48 @@ In Gemfile:
end
describe "without git installed" do
- it "prints a better error message" do
+ it "prints a better error message when installing" do
+ build_git "foo"
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+
+ gem "rake", git: "https://github.com/ruby/rake"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: https://github.com/ruby/rake
+ revision: 5c60da8644a9e4f655e819252e3b6ca77f42b7af
+ specs:
+ rake (13.0.6)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rake!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ with_path_as("") do
+ bundle "install", :raise_on_error => false
+ end
+ expect(err).
+ to include("You need to install git to be able to use gems from git repositories. For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git")
+ end
+
+ it "prints a better error message when updating" do
build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
@@ -1421,6 +1503,7 @@ In Gemfile:
build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
@@ -1450,6 +1533,7 @@ In Gemfile:
build_git "foo", "1.0", :path => lib_path("foo")
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo")}", :branch => "master"
G
@@ -1465,6 +1549,7 @@ In Gemfile:
it "does not display the password" do
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
git "https://#{credentials}@github.com/company/private-repo" do
gem "foo"
end
@@ -1480,6 +1565,7 @@ In Gemfile:
it "displays the oauth scheme but not the oauth token" do
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
git "https://#{credentials}:x-oauth-basic@github.com/company/private-repo" do
gem "foo"
end
diff --git a/spec/bundler/install/gemfile/groups_spec.rb b/spec/bundler/install/gemfile/groups_spec.rb
index 567a9b1172..c92b5dcc57 100644
--- a/spec/bundler/install/gemfile/groups_spec.rb
+++ b/spec/bundler/install/gemfile/groups_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems in the default group" do
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle :install
expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
end
@@ -117,7 +117,7 @@ RSpec.describe "bundle install with groups" do
end
it "does not install gems from the excluded group" do
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle :install
expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
end
@@ -130,13 +130,13 @@ RSpec.describe "bundle install with groups" do
end
it "does not say it installed gems from the excluded group" do
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle :install
expect(out).not_to include("activesupport")
end
it "allows Bundler.setup for specific groups" do
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle :install
run("require 'rack'; puts RACK", :default)
expect(out).to eq("1.0.0")
@@ -151,7 +151,7 @@ RSpec.describe "bundle install with groups" do
end
G
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle :install
expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => [:default]
end
@@ -188,7 +188,7 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems from the optional group when requested" do
- bundle "config --local with debugging"
+ bundle "config set --local with debugging"
bundle :install
expect(the_bundle).to include_gems "thin 1.0"
end
@@ -214,13 +214,13 @@ RSpec.describe "bundle install with groups" do
end
it "removes groups from without when passed at --with", :bundler => "< 3" do
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle "install --with emo"
expect(the_bundle).to include_gems "activesupport 2.3.5"
end
it "removes groups from with when passed at --without", :bundler => "< 3" do
- bundle "config --local with debugging"
+ bundle "config set --local with debugging"
bundle "install --without debugging", :raise_on_error => false
expect(the_bundle).not_to include_gem "thin 1.0"
end
@@ -251,13 +251,13 @@ RSpec.describe "bundle install with groups" do
end
it "has no effect when listing a not optional group in with" do
- bundle "config --local with emo"
+ bundle "config set --local with emo"
bundle :install
expect(the_bundle).to include_gems "activesupport 2.3.5"
end
it "has no effect when listing an optional group in without" do
- bundle "config --local without debugging"
+ bundle "config set --local without debugging"
bundle :install
expect(the_bundle).not_to include_gems "thin 1.0"
end
@@ -275,13 +275,13 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems in the default group" do
- bundle "config --local without emo lolercoaster"
+ bundle "config set --local without emo lolercoaster"
bundle :install
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "installs the gem if any of its groups are installed" do
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle :install
expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
end
@@ -303,19 +303,19 @@ RSpec.describe "bundle install with groups" do
end
it "installs the gem unless all groups are excluded" do
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle :install
expect(the_bundle).to include_gems "activesupport 2.3.5"
- bundle "config --local without lolercoaster"
+ bundle "config set --local without lolercoaster"
bundle :install
expect(the_bundle).to include_gems "activesupport 2.3.5"
- bundle "config --local without emo lolercoaster"
+ bundle "config set --local without emo lolercoaster"
bundle :install
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
- bundle "config --local without 'emo lolercoaster'"
+ bundle "config set --local without 'emo lolercoaster'"
bundle :install
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
end
@@ -336,13 +336,13 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems in the default group" do
- bundle "config --local without emo lolercoaster"
+ bundle "config set --local without emo lolercoaster"
bundle :install
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "installs the gem if any of its groups are installed" do
- bundle "config --local without emo"
+ bundle "config set --local without emo"
bundle :install
expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
end
@@ -358,7 +358,7 @@ RSpec.describe "bundle install with groups" do
G
ruby <<-R
- require "#{lib_dir}/bundler"
+ require "#{entrypoint}"
Bundler.setup :default
Bundler.require :default
puts RACK
@@ -380,7 +380,7 @@ RSpec.describe "bundle install with groups" do
system_gems "rack-0.9.1"
- bundle "config --local without rack"
+ bundle "config set --local without rack"
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "rack"
@@ -404,7 +404,7 @@ RSpec.describe "bundle install with groups" do
it "does not hit the remote a second time" do
FileUtils.rm_rf gem_repo2
- bundle "config --local without rack"
+ bundle "config set --local without rack"
bundle :install, :verbose => true
expect(last_command.stdboth).not_to match(/fetching/i)
end
diff --git a/spec/bundler/install/gemfile/install_if_spec.rb b/spec/bundler/install/gemfile/install_if_spec.rb
index 786e0e9258..3d2d15a698 100644
--- a/spec/bundler/install/gemfile/install_if_spec.rb
+++ b/spec/bundler/install/gemfile/install_if_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe "bundle install with install_if conditionals" do
expect(the_bundle).not_to include_gems("thin")
expect(the_bundle).not_to include_gems("foo")
- lockfile_should_be <<-L
+ expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb
index 6cd981e3d3..bea7c11dec 100644
--- a/spec/bundler/install/gemfile/path_spec.rb
+++ b/spec/bundler/install/gemfile/path_spec.rb
@@ -16,6 +16,7 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path("foo-1.0")}" do
gem 'foo'
end
@@ -28,6 +29,7 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :path => "#{lib_path("foo-1.0")}"
G
@@ -40,6 +42,7 @@ RSpec.describe "bundle install with explicit source paths" do
relative_path = lib_path("foo-1.0").relative_path_from(bundled_app)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :path => "#{relative_path}"
G
@@ -52,6 +55,7 @@ RSpec.describe "bundle install with explicit source paths" do
relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("~").expand_path)
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :path => "~/#{relative_path}"
G
@@ -66,6 +70,7 @@ RSpec.describe "bundle install with explicit source paths" do
relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("/home/#{username}").expand_path)
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :path => "~#{username}/#{relative_path}"
G
expect(err).to match("There was an error while trying to use the path `~#{username}/#{relative_path}`.")
@@ -76,6 +81,7 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo", :path => bundled_app("foo-1.0")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :path => "./foo-1.0"
G
@@ -87,6 +93,7 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "aaa", :path => lib_path("demo/aaa")
gemfile = <<-G
+ source "#{file_uri_for(gem_repo1)}"
gemspec
gem "aaa", :path => "./aaa"
G
@@ -105,6 +112,7 @@ RSpec.describe "bundle install with explicit source paths" do
aaa (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -119,25 +127,24 @@ RSpec.describe "bundle install with explicit source paths" do
L
bundle :install, :dir => lib_path("demo")
- expect(lib_path("demo/Gemfile.lock")).to have_lockfile(lockfile)
+ expect(lib_path("demo/Gemfile.lock")).to read_as(lockfile)
bundle :update, :all => true, :dir => lib_path("demo")
- expect(lib_path("demo/Gemfile.lock")).to have_lockfile(lockfile)
+ expect(lib_path("demo/Gemfile.lock")).to read_as(lockfile)
end
it "expands paths when comparing locked paths to Gemfile paths" do
build_lib "foo", :path => bundled_app("foo-1.0")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :path => File.expand_path("../foo-1.0", __FILE__)
G
- bundle "config --local frozen true"
+ bundle "config set --local frozen true"
bundle :install
end
it "installs dependencies from the path even if a newer gem is available elsewhere" do
- skip "override is not winning" if Gem.win_platform?
-
system_gems "rack-1.0.0"
build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
@@ -169,22 +176,90 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo", "1.0.0", :path => lib_path("omg/foo")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "omg", :path => "#{lib_path("omg")}"
G
expect(the_bundle).to include_gems "foo 1.0"
end
- it "works with only_update_to_newer_versions" do
+ it "works when using prereleases of 0.0.0" do
+ build_lib "foo", "0.0.0.dev", :path => lib_path("foo")
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ lockfile <<~L
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (0.0.0.dev)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install
+
+ expect(the_bundle).to include_gems "foo 0.0.0.dev"
+ end
+
+ it "works when using uppercase prereleases of 0.0.0" do
+ build_lib "foo", "0.0.0.SNAPSHOT", :path => lib_path("foo")
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ lockfile <<~L
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (0.0.0.SNAPSHOT)
+
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install
+
+ expect(the_bundle).to include_gems "foo 0.0.0.SNAPSHOT"
+ end
+
+ it "handles downgrades" do
build_lib "omg", "2.0", :path => lib_path("omg")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "omg", :path => "#{lib_path("omg")}"
G
build_lib "omg", "1.0", :path => lib_path("omg")
- bundle :install, :env => { "BUNDLE_BUNDLE_ONLY_UPDATE_TO_NEWER_VERSIONS" => "true" }
+ bundle :install
expect(the_bundle).to include_gems "omg 1.0"
end
@@ -202,6 +277,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "premailer", :path => "#{lib_path("premailer")}"
G
@@ -223,6 +299,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
@@ -306,6 +383,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
gemspec :path => "#{lib_path("foo")}"
G
@@ -319,6 +397,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gemspec :path => "#{lib_path("foo")}", :name => "foo"
G
@@ -330,11 +409,13 @@ RSpec.describe "bundle install with explicit source paths" do
s.executables = "foobar"
end
- install_gemfile <<-G
+ install_gemfile <<-G, :verbose => true
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path("foo-1.0")}" do
gem 'foo'
end
G
+ expect(out).to include("Using foo 1.0 from source at `#{lib_path("foo-1.0")}` and installing its executables")
expect(the_bundle).to include_gems "foo 1.0"
bundle "exec foobar"
@@ -347,6 +428,7 @@ RSpec.describe "bundle install with explicit source paths" do
lib_path("foo-1.0").join("bin/performance").mkpath
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', '1.0', :path => "#{lib_path("foo-1.0")}"
G
expect(err).to be_empty
@@ -356,6 +438,7 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :path => "#{lib_path("foo-1.0")}"
G
@@ -368,6 +451,7 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "hi2u"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "omg"
gem "hi2u"
@@ -386,6 +470,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo")}"
gem "omg", :path => "#{lib_path("omg")}"
G
@@ -397,6 +482,7 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo", :gemspec => false
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.0", :path => "#{lib_path("foo-1.0")}"
G
@@ -412,12 +498,13 @@ RSpec.describe "bundle install with explicit source paths" do
specs:
GEM
- remote: http://rubygems.org
+ remote: http://rubygems.org/
L
FileUtils.mkdir_p(bundled_app("vendor/bar"))
install_gemfile <<-G
+ source "http://rubygems.org"
gem "bar", "1.0.0", path: "vendor/bar", require: "bar/nyard"
G
end
@@ -462,6 +549,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
@@ -477,6 +565,7 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "bar", "1.0", :path => lib_path("foo/bar")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo")}"
G
end
@@ -529,7 +618,7 @@ RSpec.describe "bundle install with explicit source paths" do
expect(the_bundle).to include_gems "rack 0.9.1"
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
specs:
@@ -557,7 +646,7 @@ RSpec.describe "bundle install with explicit source paths" do
bundle "install"
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
specs:
@@ -591,7 +680,7 @@ RSpec.describe "bundle install with explicit source paths" do
expect(the_bundle).to include_gems "rack 0.9.1"
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
specs:
@@ -620,7 +709,7 @@ RSpec.describe "bundle install with explicit source paths" do
bundle "install"
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
specs:
@@ -701,8 +790,6 @@ RSpec.describe "bundle install with explicit source paths" do
describe "when there are both a gemspec and remote gems" do
it "doesn't query rubygems for local gemspec name" do
- skip "platform issues" if Gem.win_platform?
-
build_lib "private_lib", "2.2", :path => lib_path("private_lib")
gemfile = <<-G
source "http://localgemserver.test"
@@ -723,6 +810,7 @@ RSpec.describe "bundle install with explicit source paths" do
it "runs pre-install hooks" do
build_git "foo"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -742,6 +830,7 @@ RSpec.describe "bundle install with explicit source paths" do
it "runs post-install hooks" do
build_git "foo"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -761,6 +850,7 @@ RSpec.describe "bundle install with explicit source paths" do
it "complains if the install hook fails" do
build_git "foo"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -791,6 +881,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo-1.0")}"
gem "bar", :path => "#{lib_path("bar-1.0")}"
G
diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb
index 8ab59abeeb..35a3872c03 100644
--- a/spec/bundler/install/gemfile/platform_spec.rb
+++ b/spec/bundler/install/gemfile/platform_spec.rb
@@ -88,14 +88,15 @@ RSpec.describe "bundle install across platforms" do
simulate_new_machine
simulate_platform "ruby"
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
-
- gem "nokogiri"
- G
+ bundle "install"
expect(the_bundle).to include_gems "nokogiri 1.4.2"
expect(the_bundle).not_to include_gems "weakling"
+
+ simulate_platform "java"
+ bundle "install"
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3"
end
it "does not keep unneeded platforms for gems that are used" do
@@ -127,7 +128,7 @@ RSpec.describe "bundle install across platforms" do
gem "pry"
G
- lockfile_should_be <<-L
+ expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
@@ -155,7 +156,7 @@ RSpec.describe "bundle install across platforms" do
bundle "lock --add-platform ruby"
- good_lockfile = strip_whitespace(<<-L)
+ good_lockfile = <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
@@ -185,9 +186,9 @@ RSpec.describe "bundle install across platforms" do
#{Bundler::VERSION}
L
- lockfile_should_be good_lockfile
+ expect(lockfile).to eq good_lockfile
- bad_lockfile = strip_whitespace <<-L
+ bad_lockfile = <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
@@ -221,40 +222,26 @@ RSpec.describe "bundle install across platforms" do
aggregate_failures do
lockfile bad_lockfile
bundle :install
- lockfile_should_be good_lockfile
+ expect(lockfile).to eq good_lockfile
lockfile bad_lockfile
bundle :update, :all => true
- lockfile_should_be good_lockfile
+ expect(lockfile).to eq good_lockfile
lockfile bad_lockfile
bundle "update ffi"
- lockfile_should_be good_lockfile
+ expect(lockfile).to eq good_lockfile
lockfile bad_lockfile
bundle "update empyrean"
- lockfile_should_be good_lockfile
+ expect(lockfile).to eq good_lockfile
lockfile bad_lockfile
bundle :lock
- lockfile_should_be good_lockfile
+ expect(lockfile).to eq good_lockfile
end
end
- it "works the other way with gems that have different dependencies" do
- simulate_platform "ruby"
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
-
- gem "nokogiri"
- G
-
- simulate_platform "java"
- bundle "install"
-
- expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3"
- end
-
it "works with gems with platform-specific dependency having different requirements order" do
simulate_platform x64_mac
@@ -291,7 +278,7 @@ RSpec.describe "bundle install across platforms" do
gem "rack", "1.0.0"
G
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle :install
FileUtils.mv(vendored_gems, bundled_app("vendor/bundle", Gem.ruby_engine, "1.8"))
@@ -323,7 +310,7 @@ RSpec.describe "bundle install across platforms" do
expect(the_bundle).to include_gem "platform_specific 1.0 RUBY"
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
@@ -371,6 +358,49 @@ RSpec.describe "bundle install with platform conditionals" do
expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
end
+ it "installs gems tagged w/ another platform but also dependent on the current one transitively" do
+ build_repo4 do
+ build_gem "activesupport", "6.1.4.1" do |s|
+ s.add_dependency "tzinfo", "~> 2.0"
+ end
+
+ build_gem "tzinfo", "2.0.4"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "activesupport"
+
+ platforms :#{not_local_tag} do
+ gem "tzinfo", "~> 1.2"
+ end
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ activesupport (6.1.4.1)
+ tzinfo (~> 2.0)
+ tzinfo (2.0.4)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ activesupport
+ tzinfo (~> 1.2)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose"
+
+ expect(the_bundle).to include_gems "tzinfo 2.0.4"
+ end
+
it "installs gems tagged w/ the current platforms inline" do
skip "platform issues" if Gem.win_platform?
@@ -413,6 +443,7 @@ RSpec.describe "bundle install with platform conditionals" do
build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
platform :#{not_local_tag} do
gem "foo", :git => "#{lib_path("foo-1.0")}"
end
@@ -459,7 +490,7 @@ RSpec.describe "bundle install with platform conditionals" do
expect(err).to be_empty
- lockfile_should_be <<-L
+ expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
index 655f91dd69..5456e95f33 100644
--- a/spec/bundler/install/gemfile/sources_spec.rb
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -20,23 +20,23 @@ RSpec.describe "bundle install with gems on multiple sources" do
before do
gemfile <<-G
- source "#{file_uri_for(gem_repo3)}"
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo3"
+ source "https://gem.repo1"
gem "rack-obama"
gem "rack"
G
end
- it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "2" do
- bundle :install
+ it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "< 3" do
+ bundle :install, :artifice => "compact_index"
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(err).to include("Installed from: #{file_uri_for(gem_repo1)}")
+ expect(err).to include("Installed from: https://gem.repo1")
expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
end
it "fails", :bundler => "3" do
- bundle :instal, :raise_on_error => false
+ bundle :instal, :artifice => "compact_index", :raise_on_error => false
expect(err).to include("Each source after the first must include a block")
expect(exitstatus).to eq(4)
end
@@ -47,22 +47,22 @@ RSpec.describe "bundle install with gems on multiple sources" do
before do
gemfile <<-G
- source "#{file_uri_for(gem_repo3)}"
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo3"
+ source "https://gem.repo1"
gem "rack-obama"
gem "rack", "1.0.0" # force it to install the working version in repo1
G
end
- it "warns about ambiguous gems, but installs anyway", :bundler => "2" do
- bundle :install
+ it "warns about ambiguous gems, but installs anyway", :bundler => "< 3" do
+ bundle :install, :artifice => "compact_index"
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(err).to include("Installed from: #{file_uri_for(gem_repo1)}")
+ expect(err).to include("Installed from: https://gem.repo1")
expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
end
it "fails", :bundler => "3" do
- bundle :install, :raise_on_error => false
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
expect(err).to include("Each source after the first must include a block")
expect(exitstatus).to eq(4)
end
@@ -85,30 +85,30 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
gemfile <<-G
- source "#{file_uri_for(gem_repo3)}"
- source "#{file_uri_for(gem_repo1)}" do
+ source "https://gem.repo3"
+ source "https://gem.repo1" do
gem "thin" # comes first to test name sorting
gem "rack"
end
- gem "rack-obama" # shoud come from repo3!
+ gem "rack-obama" # should come from repo3!
G
end
it "installs the gems without any warning" do
- bundle :install
- expect(out).not_to include("Warning")
+ bundle :install, :artifice => "compact_index"
+ expect(err).not_to include("Warning")
expect(the_bundle).to include_gems("rack-obama 1.0.0")
expect(the_bundle).to include_gems("rack 1.0.0", :source => "remote1")
end
it "can cache and deploy" do
- bundle :cache
+ bundle :cache, :artifice => "compact_index"
expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/rack-obama-1.0.gem")).to exist
- bundle "config --local deployment true"
- bundle :install
+ bundle "config set --local deployment true"
+ bundle :install, :artifice => "compact_index"
expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
end
@@ -128,258 +128,813 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
end
- gemfile <<-G
- source "#{file_uri_for(gem_repo3)}"
+ install_gemfile <<-G, :artifice => "compact_index"
+ source "https://gem.repo3"
gem "rack-obama" # should come from repo3!
- gem "rack", :source => "#{file_uri_for(gem_repo1)}"
+ gem "rack", :source => "https://gem.repo1"
G
end
it "installs the gems without any warning" do
- bundle :install
- expect(out).not_to include("Warning")
+ expect(err).not_to include("Warning")
expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
end
end
- context "when a pinned gem has an indirect dependency" do
+ context "when a pinned gem has an indirect dependency in the pinned source" do
before do
build_repo gem_repo3 do
build_gem "depends_on_rack", "1.0.1" do |s|
s.add_dependency "rack"
end
end
+
+ # we need a working rack gem in repo3
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ source "https://gem.repo3" do
+ gem "depends_on_rack"
+ end
+ G
end
- context "when the indirect dependency is in the pinned source" do
+ context "and not in any other sources" do
before do
- # we need a working rack gem in repo3
- update_repo gem_repo3 do
- build_gem "rack", "1.0.0"
+ build_repo(gem_repo2) {}
+ end
+
+ it "installs from the same source without any warning" do
+ bundle :install, :artifice => "compact_index"
+ expect(err).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3")
+ end
+ end
+
+ context "and in another source" do
+ before do
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
end
+ end
+
+ it "installs from the same source without any warning" do
+ bundle :install, :artifice => "compact_index"
+
+ expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3")
+ # In https://github.com/bundler/bundler/issues/3585 this failed
+ # when there is already a lock file, and the gems are missing, so try again
+ system_gems []
+ bundle :install, :artifice => "compact_index"
+
+ expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3")
+ end
+ end
+ end
+
+ context "when a pinned gem has an indirect dependency in a different source" do
+ before do
+ # In these tests, we need a working rack gem in repo2 and not repo3
+
+ build_repo gem_repo3 do
+ build_gem "depends_on_rack", "1.0.1" do |s|
+ s.add_dependency "rack"
+ end
+ end
+
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
+ end
+ end
+
+ context "and not in any other sources" do
+ before do
+ install_gemfile <<-G, :artifice => "compact_index"
+ source "https://gem.repo2"
+ source "https://gem.repo3" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ it "installs from the other source without any warning" do
+ expect(err).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+
+ context "and in yet another source" do
+ before do
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- source "#{file_uri_for(gem_repo3)}" do
+ source "https://gem.repo1"
+ source "https://gem.repo2"
+ source "https://gem.repo3" do
gem "depends_on_rack"
end
G
end
- context "and not in any other sources" do
- before do
- build_repo(gem_repo2) {}
- end
+ it "installs from the other source and warns about ambiguous gems", :bundler => "< 3" do
+ bundle :install, :artifice => "compact_index"
+ expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(err).to include("Installed from: https://gem.repo2")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo1/
+ remote: https://gem.repo2/
+ specs:
+ rack (1.0.0)
+
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ depends_on_rack (1.0.1)
+ rack
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ depends_on_rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ previous_lockfile = lockfile
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ expect(lockfile).to eq(previous_lockfile)
+ end
- it "installs from the same source without any warning" do
- bundle :install
- expect(out).not_to include("Warning")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
- end
+ it "fails", :bundler => "3" do
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
+ expect(err).to include("Each source after the first must include a block")
+ expect(exitstatus).to eq(4)
end
+ end
- context "and in another source" do
- before do
- # need this to be broken to check for correct source ordering
- build_repo gem_repo2 do
- build_gem "rack", "1.0.0" do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
- end
+ context "and only the dependency is pinned" do
+ before do
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
end
end
- context "when disable_multisource is set" do
- before do
- bundle "config set disable_multisource true"
- end
+ gemfile <<-G
+ source "https://gem.repo3" # contains depends_on_rack
+ source "https://gem.repo2" # contains broken rack
- it "installs from the same source without any warning" do
- bundle :install
+ gem "depends_on_rack" # installed from gem_repo3
+ gem "rack", :source => "https://gem.repo1"
+ G
+ end
- expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ it "installs the dependency from the pinned source without warning", :bundler => "< 3" do
+ bundle :install, :artifice => "compact_index"
- # when there is already a lock file, and the gems are missing, so try again
- system_gems []
- bundle :install
+ expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
- expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
- end
- end
+ # In https://github.com/rubygems/bundler/issues/3585 this failed
+ # when there is already a lock file, and the gems are missing, so try again
+ system_gems []
+ bundle :install, :artifice => "compact_index"
+
+ expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+
+ it "fails", :bundler => "3" do
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
+ expect(err).to include("Each source after the first must include a block")
+ expect(exitstatus).to eq(4)
end
end
+ end
- context "when the indirect dependency is in a different source" do
- before do
- # In these tests, we need a working rack gem in repo2 and not repo3
- build_repo gem_repo2 do
- build_gem "rack", "1.0.0"
- end
+ context "when a top-level gem can only be found in an scoped source" do
+ before do
+ build_repo2
+
+ build_repo gem_repo3 do
+ build_gem "private_gem_1", "1.0.0"
+ build_gem "private_gem_2", "1.0.0"
end
- context "and not in any other sources" do
- before do
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- source "#{file_uri_for(gem_repo3)}" do
- gem "depends_on_rack"
- end
- G
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ gem "private_gem_1"
+
+ source "https://gem.repo3" do
+ gem "private_gem_2"
end
+ G
+ end
+
+ it "fails" do
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
+ expect(err).to include("Could not find gem 'private_gem_1' in rubygems repository https://gem.repo2/ or installed locally.")
+ end
+ end
+
+ context "when an indirect dependency can't be found in the aggregate rubygems source", :bundler => "< 3" do
+ before do
+ build_repo2
- it "installs from the other source without any warning" do
- bundle :install
- expect(out).not_to include("Warning")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ build_repo gem_repo3 do
+ build_gem "depends_on_missing", "1.0.1" do |s|
+ s.add_dependency "missing"
end
end
- context "and in yet another source" do
- before do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- source "#{file_uri_for(gem_repo2)}"
- source "#{file_uri_for(gem_repo3)}" do
- gem "depends_on_rack"
- end
- G
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ source "https://gem.repo3"
+
+ gem "depends_on_missing"
+ G
+ end
+
+ it "fails" do
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
+ expect(err).to include("Could not find gem 'missing', which is required by gem 'depends_on_missing', in any of the sources.")
+ end
+ end
+
+ context "when a top-level gem has an indirect dependency" do
+ before do
+ build_repo gem_repo2 do
+ build_gem "depends_on_rack", "1.0.1" do |s|
+ s.add_dependency "rack"
end
+ end
- it "installs from the other source and warns about ambiguous gems", :bundler => "2" do
- bundle :install
- expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(err).to include("Installed from: #{file_uri_for(gem_repo2)}")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ build_repo gem_repo3 do
+ build_gem "unrelated_gem", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ gem "depends_on_rack"
+
+ source "https://gem.repo3" do
+ gem "unrelated_gem"
end
+ G
+ end
- it "fails", :bundler => "3" do
- bundle :install, :raise_on_error => false
- expect(err).to include("Each source after the first must include a block")
- expect(exitstatus).to eq(4)
+ context "and the dependency is only in the top-level source" do
+ before do
+ update_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
end
end
- context "and only the dependency is pinned" do
- before do
- # need this to be broken to check for correct source ordering
- build_repo gem_repo2 do
- build_gem "rack", "1.0.0" do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
- end
+ it "installs the dependency from the top-level source without warning" do
+ bundle :install, :artifice => "compact_index"
+ expect(err).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote2")
+ expect(the_bundle).to include_gems("unrelated_gem 1.0.0", :source => "remote3")
+ end
+ end
+
+ context "and the dependency is only in a pinned source" do
+ before do
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
end
+ end
+ end
+
+ it "does not find the dependency" do
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
+ expect(err).to include(
+ "Could not find gem 'rack', which is required by gem 'depends_on_rack', in rubygems repository https://gem.repo2/ or installed locally."
+ )
+ end
+ end
- gemfile <<-G
- source "#{file_uri_for(gem_repo3)}" # contains depends_on_rack
- source "#{file_uri_for(gem_repo2)}" # contains broken rack
+ context "and the dependency is in both the top-level and a pinned source" do
+ before do
+ update_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
+ end
- gem "depends_on_rack" # installed from gem_repo3
- gem "rack", :source => "#{file_uri_for(gem_repo1)}"
- G
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
end
+ end
- it "installs the dependency from the pinned source without warning", :bundler => "2" do
- bundle :install
+ it "installs the dependency from the top-level source without warning" do
+ bundle :install, :artifice => "compact_index"
+ expect(err).not_to include("Warning")
+ expect(run("require 'rack'; puts RACK")).to eq("1.0.0")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote2")
+ expect(the_bundle).to include_gems("unrelated_gem 1.0.0", :source => "remote3")
+ end
+ end
+ end
+
+ context "when a scoped gem has a deeply nested indirect dependency" do
+ before do
+ build_repo gem_repo3 do
+ build_gem "depends_on_depends_on_rack", "1.0.1" do |s|
+ s.add_dependency "depends_on_rack"
+ end
- expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ build_gem "depends_on_rack", "1.0.1" do |s|
+ s.add_dependency "rack"
+ end
+ end
- # In https://github.com/rubygems/bundler/issues/3585 this failed
- # when there is already a lock file, and the gems are missing, so try again
- system_gems []
- bundle :install
+ gemfile <<-G
+ source "https://gem.repo2"
- expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ source "https://gem.repo3" do
+ gem "depends_on_depends_on_rack"
end
+ G
+ end
- it "fails", :bundler => "3" do
- bundle :install, :raise_on_error => false
- expect(err).to include("Each source after the first must include a block")
- expect(exitstatus).to eq(4)
+ context "and the dependency is only in the top-level source" do
+ before do
+ update_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
end
end
+
+ it "installs the dependency from the top-level source" do
+ bundle :install, :artifice => "compact_index"
+ expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0")
+ expect(the_bundle).to include_gems("rack 1.0.0", :source => "remote2")
+ expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", :source => "remote3")
+ end
end
- end
- context "when a top-level gem has an indirect dependency" do
- context "when disable_multisource is set" do
+ context "and the dependency is only in a pinned source" do
before do
- bundle "config set disable_multisource true"
+ build_repo2
+
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0"
+ end
end
+ it "installs the dependency from the pinned source" do
+ bundle :install, :artifice => "compact_index"
+ expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3")
+ end
+ end
+
+ context "and the dependency is in both the top-level and a pinned source" do
before do
- build_repo gem_repo2 do
- build_gem "depends_on_rack", "1.0.1" do |s|
- s.add_dependency "rack"
+ update_repo gem_repo2 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
end
end
- build_repo gem_repo3 do
- build_gem "unrelated_gem", "1.0.0"
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0"
end
+ end
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ it "installs the dependency from the pinned source without warning" do
+ bundle :install, :artifice => "compact_index"
+ expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3")
+ end
+ end
+ end
- gem "depends_on_rack"
+ context "when the lockfile has aggregated rubygems sources and newer versions of dependencies are available" do
+ before do
+ build_repo gem_repo2 do
+ build_gem "activesupport", "6.0.3.4" do |s|
+ s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
+ s.add_dependency "i18n", ">= 0.7", "< 2"
+ s.add_dependency "minitest", "~> 5.1"
+ s.add_dependency "tzinfo", "~> 1.1"
+ s.add_dependency "zeitwerk", "~> 2.2", ">= 2.2.2"
+ end
- source "#{file_uri_for(gem_repo3)}" do
- gem "unrelated_gem"
- end
- G
- end
+ build_gem "activesupport", "6.1.2.1" do |s|
+ s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
+ s.add_dependency "i18n", ">= 1.6", "< 2"
+ s.add_dependency "minitest", ">= 5.1"
+ s.add_dependency "tzinfo", "~> 2.0"
+ s.add_dependency "zeitwerk", "~> 2.3"
+ end
- context "and the dependency is only in the top-level source" do
- before do
- update_repo gem_repo2 do
- build_gem "rack", "1.0.0"
- end
+ build_gem "concurrent-ruby", "1.1.8"
+ build_gem "concurrent-ruby", "1.1.9"
+ build_gem "connection_pool", "2.2.3"
+
+ build_gem "i18n", "1.8.9" do |s|
+ s.add_dependency "concurrent-ruby", "~> 1.0"
+ end
+
+ build_gem "minitest", "5.14.3"
+ build_gem "rack", "2.2.3"
+ build_gem "redis", "4.2.5"
+
+ build_gem "sidekiq", "6.1.3" do |s|
+ s.add_dependency "connection_pool", ">= 2.2.2"
+ s.add_dependency "rack", "~> 2.0"
+ s.add_dependency "redis", ">= 4.2.0"
+ end
+
+ build_gem "thread_safe", "0.3.6"
+
+ build_gem "tzinfo", "1.2.9" do |s|
+ s.add_dependency "thread_safe", "~> 0.1"
end
- it "installs all gems without warning" do
- bundle :install
- expect(err).not_to include("Warning")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
+ build_gem "tzinfo", "2.0.4" do |s|
+ s.add_dependency "concurrent-ruby", "~> 1.0"
end
+
+ build_gem "zeitwerk", "2.4.2"
end
- context "and the dependency is only in a pinned source" do
- before do
- update_repo gem_repo3 do
- build_gem "rack", "1.0.0" do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
- end
- end
+ build_repo gem_repo3 do
+ build_gem "sidekiq-pro", "5.2.1" do |s|
+ s.add_dependency "connection_pool", ">= 2.2.3"
+ s.add_dependency "sidekiq", ">= 6.1.0"
+ end
+ end
+
+ gemfile <<-G
+ # frozen_string_literal: true
+
+ source "https://gem.repo2"
+
+ gem "activesupport"
+
+ source "https://gem.repo3" do
+ gem "sidekiq-pro"
end
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ remote: https://gem.repo3/
+ specs:
+ activesupport (6.0.3.4)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ zeitwerk (~> 2.2, >= 2.2.2)
+ concurrent-ruby (1.1.8)
+ connection_pool (2.2.3)
+ i18n (1.8.9)
+ concurrent-ruby (~> 1.0)
+ minitest (5.14.3)
+ rack (2.2.3)
+ redis (4.2.5)
+ sidekiq (6.1.3)
+ connection_pool (>= 2.2.2)
+ rack (~> 2.0)
+ redis (>= 4.2.0)
+ sidekiq-pro (5.2.1)
+ connection_pool (>= 2.2.3)
+ sidekiq (>= 6.1.0)
+ thread_safe (0.3.6)
+ tzinfo (1.2.9)
+ thread_safe (~> 0.1)
+ zeitwerk (2.4.2)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ activesupport
+ sidekiq-pro!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "does not install newer versions but updates the lockfile format when running bundle install in non frozen mode, and doesn't warn" do
+ bundle :install, :artifice => "compact_index"
+ expect(err).to be_empty
+
+ expect(the_bundle).to include_gems("activesupport 6.0.3.4")
+ expect(the_bundle).not_to include_gems("activesupport 6.1.2.1")
+ expect(the_bundle).to include_gems("tzinfo 1.2.9")
+ expect(the_bundle).not_to include_gems("tzinfo 2.0.4")
+ expect(the_bundle).to include_gems("concurrent-ruby 1.1.8")
+ expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.9")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ activesupport (6.0.3.4)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ zeitwerk (~> 2.2, >= 2.2.2)
+ concurrent-ruby (1.1.8)
+ connection_pool (2.2.3)
+ i18n (1.8.9)
+ concurrent-ruby (~> 1.0)
+ minitest (5.14.3)
+ rack (2.2.3)
+ redis (4.2.5)
+ sidekiq (6.1.3)
+ connection_pool (>= 2.2.2)
+ rack (~> 2.0)
+ redis (>= 4.2.0)
+ thread_safe (0.3.6)
+ tzinfo (1.2.9)
+ thread_safe (~> 0.1)
+ zeitwerk (2.4.2)
+
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ sidekiq-pro (5.2.1)
+ connection_pool (>= 2.2.3)
+ sidekiq (>= 6.1.0)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ activesupport
+ sidekiq-pro!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "does not install newer versions or generate lockfile changes when running bundle install in frozen mode, and warns", :bundler => "< 3" do
+ initial_lockfile = lockfile
+
+ bundle "config set --local frozen true"
+ bundle :install, :artifice => "compact_index"
+
+ expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
+
+ expect(the_bundle).to include_gems("activesupport 6.0.3.4")
+ expect(the_bundle).not_to include_gems("activesupport 6.1.2.1")
+ expect(the_bundle).to include_gems("tzinfo 1.2.9")
+ expect(the_bundle).not_to include_gems("tzinfo 2.0.4")
+ expect(the_bundle).to include_gems("concurrent-ruby 1.1.8")
+ expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.9")
+
+ expect(lockfile).to eq(initial_lockfile)
+ end
+
+ it "fails when running bundle install in frozen mode", :bundler => "3" do
+ initial_lockfile = lockfile
+
+ bundle "config set --local frozen true"
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
+
+ expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
+
+ expect(lockfile).to eq(initial_lockfile)
+ end
+
+ it "splits sections and upgrades gems when running bundle update, and doesn't warn" do
+ bundle "update --all", :artifice => "compact_index"
+ expect(err).to be_empty
+
+ expect(the_bundle).not_to include_gems("activesupport 6.0.3.4")
+ expect(the_bundle).to include_gems("activesupport 6.1.2.1")
+ expect(the_bundle).not_to include_gems("tzinfo 1.2.9")
+ expect(the_bundle).to include_gems("tzinfo 2.0.4")
+ expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.8")
+ expect(the_bundle).to include_gems("concurrent-ruby 1.1.9")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ activesupport (6.1.2.1)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 1.6, < 2)
+ minitest (>= 5.1)
+ tzinfo (~> 2.0)
+ zeitwerk (~> 2.3)
+ concurrent-ruby (1.1.9)
+ connection_pool (2.2.3)
+ i18n (1.8.9)
+ concurrent-ruby (~> 1.0)
+ minitest (5.14.3)
+ rack (2.2.3)
+ redis (4.2.5)
+ sidekiq (6.1.3)
+ connection_pool (>= 2.2.2)
+ rack (~> 2.0)
+ redis (>= 4.2.0)
+ tzinfo (2.0.4)
+ concurrent-ruby (~> 1.0)
+ zeitwerk (2.4.2)
+
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ sidekiq-pro (5.2.1)
+ connection_pool (>= 2.2.3)
+ sidekiq (>= 6.1.0)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ activesupport
+ sidekiq-pro!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
- it "does not find the dependency" do
- bundle :install, :raise_on_error => false
- expect(err).to include("Could not find gem 'rack', which is required by gem 'depends_on_rack', in any of the relevant sources")
+ it "upgrades the lockfile format and upgrades the requested gem when running bundle update with an argument" do
+ bundle "update concurrent-ruby", :artifice => "compact_index"
+ expect(err).to be_empty
+
+ expect(the_bundle).to include_gems("activesupport 6.0.3.4")
+ expect(the_bundle).not_to include_gems("activesupport 6.1.2.1")
+ expect(the_bundle).to include_gems("tzinfo 1.2.9")
+ expect(the_bundle).not_to include_gems("tzinfo 2.0.4")
+ expect(the_bundle).to include_gems("concurrent-ruby 1.1.9")
+ expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.8")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ activesupport (6.0.3.4)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ zeitwerk (~> 2.2, >= 2.2.2)
+ concurrent-ruby (1.1.9)
+ connection_pool (2.2.3)
+ i18n (1.8.9)
+ concurrent-ruby (~> 1.0)
+ minitest (5.14.3)
+ rack (2.2.3)
+ redis (4.2.5)
+ sidekiq (6.1.3)
+ connection_pool (>= 2.2.2)
+ rack (~> 2.0)
+ redis (>= 4.2.0)
+ thread_safe (0.3.6)
+ tzinfo (1.2.9)
+ thread_safe (~> 0.1)
+ zeitwerk (2.4.2)
+
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ sidekiq-pro (5.2.1)
+ connection_pool (>= 2.2.3)
+ sidekiq (>= 6.1.0)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ activesupport
+ sidekiq-pro!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when a top-level gem has an indirect dependency present in the default source, but with a different version from the one resolved" do
+ before do
+ build_lib "activesupport", "7.0.0.alpha", :path => lib_path("rails/activesupport")
+ build_lib "rails", "7.0.0.alpha", :path => lib_path("rails") do |s|
+ s.add_dependency "activesupport", "= 7.0.0.alpha"
+ end
+
+ build_repo gem_repo2 do
+ build_gem "activesupport", "6.1.2"
+
+ build_gem "webpacker", "5.2.1" do |s|
+ s.add_dependency "activesupport", ">= 5.2"
end
end
- context "and the dependency is in both the top-level and a pinned source" do
- before do
- update_repo gem_repo2 do
- build_gem "rack", "1.0.0"
- end
+ gemfile <<-G
+ source "https://gem.repo2"
- update_repo gem_repo3 do
- build_gem "rack", "1.0.0" do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
- end
- end
+ gemspec :path => "#{lib_path("rails")}"
+
+ gem "webpacker", "~> 5.0"
+ G
+ end
+
+ it "installs all gems without warning" do
+ bundle :install, :artifice => "compact_index"
+ expect(err).not_to include("Warning")
+ expect(the_bundle).to include_gems("activesupport 7.0.0.alpha", "rails 7.0.0.alpha")
+ expect(the_bundle).to include_gems("activesupport 7.0.0.alpha", :source => "path@#{lib_path("rails/activesupport")}")
+ expect(the_bundle).to include_gems("rails 7.0.0.alpha", :source => "path@#{lib_path("rails")}")
+ end
+ end
+
+ context "when a pinned gem has an indirect dependency with more than one level of indirection in the default source " do
+ before do
+ build_repo gem_repo3 do
+ build_gem "handsoap", "0.2.5.5" do |s|
+ s.add_dependency "nokogiri", ">= 1.2.3"
end
+ end
- it "installs the dependency from the top-level source without warning" do
- bundle :install
- expect(err).not_to include("Warning")
- expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
+ update_repo gem_repo2 do
+ build_gem "nokogiri", "1.11.1" do |s|
+ s.add_dependency "racca", "~> 1.4"
end
+
+ build_gem "racca", "1.5.2"
end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ source "https://gem.repo3" do
+ gem "handsoap"
+ end
+
+ gem "nokogiri"
+ G
+ end
+
+ it "installs from the default source without any warnings or errors and generates a proper lockfile" do
+ expected_lockfile = <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ nokogiri (1.11.1)
+ racca (~> 1.4)
+ racca (1.5.2)
+
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ handsoap (0.2.5.5)
+ nokogiri (>= 1.2.3)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ handsoap!
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose", :artifice => "compact_index"
+ expect(err).not_to include("Warning")
+ expect(the_bundle).to include_gems("handsoap 0.2.5.5", "nokogiri 1.11.1", "racca 1.5.2")
+ expect(the_bundle).to include_gems("handsoap 0.2.5.5", :source => "remote3")
+ expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", :source => "remote2")
+ expect(lockfile).to eq(expected_lockfile)
+
+ # Even if the gems are already installed
+ FileUtils.rm bundled_app_lock
+ bundle "install --verbose", :artifice => "compact_index"
+ expect(err).not_to include("Warning")
+ expect(the_bundle).to include_gems("handsoap 0.2.5.5", "nokogiri 1.11.1", "racca 1.5.2")
+ expect(the_bundle).to include_gems("handsoap 0.2.5.5", :source => "remote3")
+ expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", :source => "remote2")
+ expect(lockfile).to eq(expected_lockfile)
end
end
@@ -389,14 +944,13 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_gem "not_in_repo1", "1.0.0"
end
- gemfile <<-G
- source "#{file_uri_for(gem_repo3)}"
- gem "not_in_repo1", :source => "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, :artifice => "compact_index", :raise_on_error => false
+ source "https://gem.repo3"
+ gem "not_in_repo1", :source => "https://gem.repo1"
G
end
it "does not install the gem" do
- bundle :install, :raise_on_error => false
expect(err).to include("Could not find gem 'not_in_repo1'")
end
end
@@ -407,21 +961,24 @@ RSpec.describe "bundle install with gems on multiple sources" do
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo1)}
- remote: #{file_uri_for(gem_repo3)}
+ remote: https://gem.repo1
+ specs:
+
+ GEM
+ remote: https://gem.repo3
specs:
rack (0.9.1)
PLATFORMS
- ruby
+ #{specific_local_platform}
DEPENDENCIES
rack!
L
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- source "#{file_uri_for(gem_repo3)}" do
+ source "https://gem.repo1"
+ source "https://gem.repo3" do
gem 'rack'
end
G
@@ -433,18 +990,97 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
end
+ context "with a lockfile with aggregated rubygems sources" do
+ let(:aggregate_gem_section_lockfile) do
+ <<~L
+ GEM
+ remote: https://gem.repo1/
+ remote: https://gem.repo3/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ let(:split_gem_section_lockfile) do
+ <<~L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ before do
+ build_repo gem_repo3 do
+ build_gem "rack", "0.9.1"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ source "https://gem.repo3" do
+ gem 'rack'
+ end
+ G
+
+ lockfile aggregate_gem_section_lockfile
+ end
+
+ it "installs the existing lockfile but prints a warning", :bundler => "< 3" do
+ bundle "config set --local deployment true"
+
+ bundle "install", :artifice => "compact_index"
+
+ expect(lockfile).to eq(aggregate_gem_section_lockfile)
+ expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
+ expect(the_bundle).to include_gems("rack 0.9.1", :source => "remote3")
+ end
+
+ it "refuses to install the existing lockfile and prints an error", :bundler => "3" do
+ bundle "config set --local deployment true"
+
+ bundle "install", :artifice => "compact_index", :raise_on_error =>false
+
+ expect(lockfile).to eq(aggregate_gem_section_lockfile)
+ expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
+ expect(out).to be_empty
+ end
+ end
+
context "with a path gem in the same Gemfile" do
before do
build_lib "foo"
gemfile <<-G
- gem "rack", :source => "#{file_uri_for(gem_repo1)}"
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack", :source => "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
end
it "does not unlock the non-path gem after install" do
- bundle :install
+ bundle :install, :artifice => "compact_index"
bundle %(exec ruby -e 'puts "OK"')
@@ -457,14 +1093,13 @@ RSpec.describe "bundle install with gems on multiple sources" do
before do
system_gems "rack-0.9.1"
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack" # shoud come from repo1!
+ install_gemfile <<-G, :artifice => "compact_index"
+ source "https://gem.repo1"
+ gem "rack" # should come from repo1!
G
end
it "installs the gems without any warning" do
- bundle :install
expect(err).not_to include("Warning")
expect(the_bundle).to include_gems("rack 1.0.0")
end
@@ -480,14 +1115,14 @@ RSpec.describe "bundle install with gems on multiple sources" do
# Installing this gemfile...
gemfile <<-G
- source '#{file_uri_for(gem_repo1)}'
+ source 'https://gem.repo1'
gem 'rack'
- gem 'foo', '~> 0.1', :source => '#{file_uri_for(gem_repo4)}'
- gem 'bar', '~> 0.1', :source => '#{file_uri_for(gem_repo4)}'
+ gem 'foo', '~> 0.1', :source => 'https://gem.repo4'
+ gem 'bar', '~> 0.1', :source => 'https://gem.repo4'
G
- bundle "config --local path ../gems/system"
- bundle :install
+ bundle "config set --local path ../gems/system"
+ bundle :install, :artifice => "compact_index"
# And then we add some new versions...
update_repo4 do
@@ -498,11 +1133,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
it "allows them to be unlocked separately" do
# And install this gemfile, updating only foo.
- install_gemfile <<-G
- source '#{file_uri_for(gem_repo1)}'
+ install_gemfile <<-G, :artifice => "compact_index"
+ source 'https://gem.repo1'
gem 'rack'
- gem 'foo', '~> 0.2', :source => '#{file_uri_for(gem_repo4)}'
- gem 'bar', '~> 0.1', :source => '#{file_uri_for(gem_repo4)}'
+ gem 'foo', '~> 0.2', :source => 'https://gem.repo4'
+ gem 'bar', '~> 0.1', :source => 'https://gem.repo4'
G
# It should update foo to 0.2, but not the (locked) bar 0.1
@@ -513,17 +1148,20 @@ RSpec.describe "bundle install with gems on multiple sources" do
context "re-resolving" do
context "when there is a mix of sources in the gemfile" do
before do
- build_repo3
+ build_repo gem_repo3 do
+ build_gem "rack"
+ end
+
build_lib "path1"
build_lib "path2"
build_git "git1"
build_git "git2"
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, :artifice => "compact_index"
+ source "https://gem.repo1"
gem "rails"
- source "#{file_uri_for(gem_repo3)}" do
+ source "https://gem.repo3" do
gem "rack"
end
@@ -535,7 +1173,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "does not re-resolve" do
- bundle :install, :verbose => true
+ bundle :install, :artifice => "compact_index", :verbose => true
expect(out).to include("using resolution from the lockfile")
expect(out).not_to include("re-resolving dependencies")
end
@@ -544,27 +1182,24 @@ RSpec.describe "bundle install with gems on multiple sources" do
context "when a gem is installed to system gems" do
before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, :artifice => "compact_index"
+ source "https://gem.repo1"
gem "rack"
G
end
context "and the gemfile changes" do
it "is still able to find that gem from remote sources" do
- source_uri = file_uri_for(gem_repo1)
- second_uri = file_uri_for(gem_repo4)
-
build_repo4 do
build_gem "rack", "2.0.1.1.forked"
build_gem "thor", "0.19.1.1.forked"
end
# When this gemfile is installed...
- install_gemfile <<-G
- source "#{source_uri}"
+ install_gemfile <<-G, :artifice => "compact_index"
+ source "https://gem.repo1"
- source "#{second_uri}" do
+ source "https://gem.repo4" do
gem "rack", "2.0.1.1.forked"
gem "thor"
end
@@ -573,9 +1208,9 @@ RSpec.describe "bundle install with gems on multiple sources" do
# Then we change the Gemfile by adding a version to thor
gemfile <<-G
- source "#{source_uri}"
+ source "https://gem.repo1"
- source "#{second_uri}" do
+ source "https://gem.repo4" do
gem "rack", "2.0.1.1.forked"
gem "thor", "0.19.1.1.forked"
end
@@ -583,15 +1218,15 @@ RSpec.describe "bundle install with gems on multiple sources" do
G
# But we should still be able to find rack 2.0.1.1.forked and install it
- bundle :install
+ bundle :install, :artifice => "compact_index"
end
end
end
describe "source changed to one containing a higher version of a dependency" do
before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, :artifice => "compact_index"
+ source "https://gem.repo1"
gem "rack"
G
@@ -608,23 +1243,109 @@ RSpec.describe "bundle install with gems on multiple sources" do
s.add_dependency "bar", "=1.0.0"
end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ install_gemfile <<-G, :artifice => "compact_index"
+ source "https://gem.repo2"
gem "rack"
gemspec :path => "#{tmp.join("gemspec_test")}"
G
end
- it "keeps the old version", :bundler => "2" do
+ it "conservatively installs the existing locked version" do
expect(the_bundle).to include_gems("rack 1.0.0")
end
+ end
+
+ it "doesn't update version when a gem uses a source block but a higher version from another source is already installed locally" do
+ build_repo2 do
+ build_gem "example", "0.1.0"
+ end
+
+ build_repo4 do
+ build_gem "example", "1.0.2"
+ end
+
+ install_gemfile <<-G, :artifice => "compact_index"
+ source "https://gem.repo4"
+
+ gem "example", :source => "https://gem.repo2"
+ G
+
+ bundle "info example"
+ expect(out).to include("example (0.1.0)")
+
+ system_gems "example-1.0.2", :path => default_bundle_path, :gem_repo => gem_repo4
+
+ bundle "update example --verbose", :artifice => "compact_index"
+ expect(out).not_to include("Using example 1.0.2")
+ expect(out).to include("Using example 0.1.0")
+ end
+
+ it "fails inmmediately with a helpful error when a rubygems source does not exist and bundler/setup is required" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ source "https://gem.repo4" do
+ gem "example"
+ end
+ G
+
+ simulate_bundler_version_when_missing_prerelease_default_gem_activation do
+ ruby <<~R, :raise_on_error => false
+ require 'bundler/setup'
+ R
+ end
+
+ expect(last_command).to be_failure
+ expect(err).to include("Could not find gem 'example' in locally installed gems.")
+ end
+
+ it "fails inmmediately with a helpful error when a non retriable network error happens while resolving sources" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ source "https://gem.repo4" do
+ gem "example"
+ end
+ G
+
+ bundle "install", :artifice => nil, :raise_on_error => false
+
+ expect(last_command).to be_failure
+ expect(err).to include("Could not reach host gem.repo4. Check your network connection and try again.")
+ end
+
+ context "when an indirect dependency is available from multiple ambiguous sources", :bundler => "< 3" do
+ it "succeeds but warns, suggesting a source block" do
+ build_repo4 do
+ build_gem "depends_on_rack" do |s|
+ s.add_dependency "rack"
+ end
+ build_gem "rack"
+ end
- it "installs the higher version in the new repo", :bundler => "3" do
- expect(the_bundle).to include_gems("rack 1.2")
+ install_gemfile <<-G, :artifice => "compact_index", :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
+
+ source "https://gem.repo4" do
+ gem "depends_on_rack"
+ end
+
+ source "https://gem.repo1" do
+ gem "thin"
+ end
+ G
+ expect(err).to eq strip_whitespace(<<-EOS).strip
+ Warning: The gem 'rack' was found in multiple relevant sources.
+ * rubygems repository https://gem.repo1/
+ * rubygems repository https://gem.repo4/
+ You should add this gem to the source block for the source you wish it to be installed from.
+ EOS
+ expect(last_command).to be_success
+ expect(the_bundle).to be_locked
end
end
- context "when a gem is available from multiple ambiguous sources", :bundler => "3" do
+ context "when an indirect dependency is available from multiple ambiguous sources", :bundler => "3" do
it "raises, suggesting a source block" do
build_repo4 do
build_gem "depends_on_rack" do |s|
@@ -633,21 +1354,93 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_gem "rack"
end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo4)}"
- source "#{file_uri_for(gem_repo1)}" do
+ install_gemfile <<-G, :artifice => "compact_index", :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo4" do
+ gem "depends_on_rack"
+ end
+ source "https://gem.repo1" do
gem "thin"
end
- gem "depends_on_rack"
G
expect(last_command).to be_failure
expect(err).to eq strip_whitespace(<<-EOS).strip
The gem 'rack' was found in multiple relevant sources.
- * rubygems repository #{file_uri_for(gem_repo1)}/ or installed locally
- * rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally
+ * rubygems repository https://gem.repo1/
+ * rubygems repository https://gem.repo4/
You must add this gem to the source block for the source you wish it to be installed from.
EOS
expect(the_bundle).not_to be_locked
end
end
+
+ context "when upgrading a lockfile suffering from dependency confusion" do
+ before do
+ build_repo4 do
+ build_gem "mime-types", "3.0.0"
+ end
+
+ build_repo2 do
+ build_gem "capybara", "2.5.0" do |s|
+ s.add_dependency "mime-types", ">= 1.16"
+ end
+
+ build_gem "mime-types", "3.3.1"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "capybara", "~> 2.5.0"
+
+ source "https://gem.repo4" do
+ gem "mime-types", "~> 3.0"
+ end
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ remote: https://gem.repo4/
+ specs:
+ capybara (2.5.0)
+ mime-types (>= 1.16)
+ mime-types (3.3.1)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ capybara (~> 2.5.0)
+ mime-types (~> 3.0)!
+ L
+ end
+
+ it "upgrades the lockfile correctly" do
+ bundle "lock --update", :artifice => "compact_index"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ capybara (2.5.0)
+ mime-types (>= 1.16)
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ mime-types (3.0.0)
+
+ PLATFORMS
+ #{specific_local_platform}
+
+ DEPENDENCIES
+ capybara (~> 2.5.0)
+ mime-types (~> 3.0)!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
end
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
index 9e30fc4fd4..ce2823ce9a 100644
--- a/spec/bundler/install/gemfile/specific_platform_spec.rb
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe "bundle install with specific platforms" do
])
end
- it "understands that a non-plaform specific gem in a old lockfile doesn't necessarily mean installing the non-specific variant" do
+ it "understands that a non-platform specific gem in a old lockfile doesn't necessarily mean installing the non-specific variant" do
setup_multiplatform_gem
system_gems "bundler-2.1.4"
@@ -54,7 +54,7 @@ RSpec.describe "bundle install with specific platforms" do
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
end
- it "understands that a non-plaform specific gem in a new lockfile locked only to RUBY doesn't necessarily mean installing the non-specific variant" do
+ it "understands that a non-platform specific gem in a new lockfile locked only to RUBY doesn't necessarily mean installing the non-specific variant" do
setup_multiplatform_gem
system_gems "bundler-2.1.4"
@@ -87,7 +87,7 @@ RSpec.describe "bundle install with specific platforms" do
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
# make sure we're still only locked to ruby
- lockfile_should_be <<-L
+ expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -104,6 +104,50 @@ RSpec.describe "bundle install with specific platforms" do
L
end
+ it "doesn't discard previously installed platform specific gem and fall back to ruby on subsequent bundles" do
+ build_repo2 do
+ build_gem("libv8", "8.4.255.0")
+ build_gem("libv8", "8.4.255.0") {|s| s.platform = "universal-darwin" }
+
+ build_gem("mini_racer", "1.0.0") do |s|
+ s.add_runtime_dependency "libv8"
+ end
+ end
+
+ system_gems "bundler-2.1.4"
+
+ # Consistent location to install and look for gems
+ bundle "config set --local path vendor/bundle", :env => { "BUNDLER_VERSION" => "2.1.4" }
+
+ gemfile <<-G
+ source "https://localgemserver.test"
+ gem "libv8"
+ G
+
+ # simulate lockfile created with old bundler, which only locks for ruby platform
+ lockfile <<-L
+ GEM
+ remote: https://localgemserver.test/
+ specs:
+ libv8 (8.4.255.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ libv8
+
+ BUNDLED WITH
+ 2.1.4
+ L
+
+ bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_VERSION" => "2.1.4", "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ expect(out).to include("Installing libv8 8.4.255.0 (universal-darwin)")
+
+ bundle "add mini_racer --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ expect(out).to include("Using libv8 8.4.255.0 (universal-darwin)")
+ end
+
it "caches the universal-darwin gem when --all-platforms is passed and properly picks it up on further bundler invocations" do
setup_multiplatform_gem
gemfile(google_protobuf)
@@ -129,6 +173,7 @@ RSpec.describe "bundle install with specific platforms" do
git = build_git "pg_array_parser", "1.0"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "pg_array_parser", :git => "#{lib_path("pg_array_parser-1.0")}"
G
@@ -205,6 +250,38 @@ RSpec.describe "bundle install with specific platforms" do
end
end
+ it "installs sorbet-static, which does not provide a pure ruby variant, just fine on truffleruby", :truffleruby do
+ build_repo2 do
+ build_gem("sorbet-static", "0.5.6403") {|s| s.platform = "x86_64-linux" }
+ build_gem("sorbet-static", "0.5.6403") {|s| s.platform = "universal-darwin-20" }
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo2)}"
+
+ gem "sorbet-static", "0.5.6403"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ sorbet-static (0.5.6403-universal-darwin-20)
+ sorbet-static (0.5.6403-x86_64-linux)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ sorbet-static (= 0.5.6403)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose"
+ end
+
private
def setup_multiplatform_gem
diff --git a/spec/bundler/install/gemfile_spec.rb b/spec/bundler/install/gemfile_spec.rb
index ffbb2e3a61..0f8f1ecfa8 100644
--- a/spec/bundler/install/gemfile_spec.rb
+++ b/spec/bundler/install/gemfile_spec.rb
@@ -4,6 +4,8 @@ RSpec.describe "bundle install" do
context "with duplicated gems" do
it "will display a warning" do
install_gemfile <<-G, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
+
gem 'rails', '~> 4.0.0'
gem 'rails', '~> 4.0.0'
G
@@ -54,6 +56,8 @@ RSpec.describe "bundle install" do
context "with deprecated features" do
it "reports that lib is an invalid option" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+
gem "rack", :lib => "rack"
G
@@ -86,6 +90,8 @@ RSpec.describe "bundle install" do
context "with a Gemfile containing non-US-ASCII characters" do
it "reads the Gemfile with the UTF-8 encoding by default" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+
str = "Il était une fois ..."
puts "The source encoding is: " + str.encoding.name
G
@@ -99,6 +105,8 @@ RSpec.describe "bundle install" do
# NOTE: This works thanks to #eval interpreting the magic encoding comment
install_gemfile <<-G
# encoding: iso-8859-1
+ source "#{file_uri_for(gem_repo1)}"
+
str = "Il #{"\xE9".dup.force_encoding("binary")}tait une fois ..."
puts "The source encoding is: " + str.encoding.name
G
diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb
index b5fca9c6ad..0cee69f702 100644
--- a/spec/bundler/install/gems/compact_index_spec.rb
+++ b/spec/bundler/install/gems/compact_index_spec.rb
@@ -80,8 +80,8 @@ RSpec.describe "compact index api" do
G
bundle :install, :artifice => "compact_index"
- bundle "config --local deployment true"
- bundle "config --local path vendor/bundle"
+ bundle "config set --local deployment true"
+ bundle "config set --local path vendor/bundle"
bundle :install, :artifice => "compact_index"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -118,7 +118,7 @@ RSpec.describe "compact index api" do
bundle :install, :artifice => "compact_index"
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :artifice => "compact_index"
expect(the_bundle).to include_gems("rails 2.3.2")
@@ -132,7 +132,7 @@ RSpec.describe "compact index api" do
G
bundle "install", :artifice => "compact_index"
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :artifice => "compact_index"
expect(the_bundle).to include_gems("foo 1.0")
@@ -366,31 +366,6 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(the_bundle).to include_gems "activesupport 1.2.3"
end
- it "considers all possible versions of dependencies from all api gem sources when using blocks", :bundler => "< 3" do
- # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
- # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
- # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
- # repo and installs it.
- build_repo4 do
- build_gem "activesupport", "1.2.0"
- build_gem "somegem", "1.0.0" do |s|
- s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
- end
- end
-
- gemfile <<-G
- source "#{source_uri}"
- source "#{source_uri}/extra" do
- gem 'somegem', '1.0.0'
- end
- G
-
- bundle :install, :artifice => "compact_index_extra_api"
-
- expect(the_bundle).to include_gems "somegem 1.0.0"
- expect(the_bundle).to include_gems "activesupport 1.2.3"
- end
-
it "prints API output properly with back deps" do
build_repo2 do
build_gem "back_deps" do |s|
@@ -424,7 +399,7 @@ The checksum of /versions does not match the checksum provided by the server! So
api_request_limit = low_api_request_limit_for(gem_repo2)
- install_gemfile <<-G, :artifice => "compact_index_extra_missing", :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "compact_index_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -446,7 +421,7 @@ The checksum of /versions does not match the checksum provided by the server! So
api_request_limit = low_api_request_limit_for(gem_repo4)
- install_gemfile <<-G, :artifice => "compact_index_extra_api_missing", :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "compact_index_extra_api_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -467,7 +442,7 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(the_bundle).to include_gems "foo 1.0"
end
- it "fetches again when more dependencies are found in subsequent sources using --deployment", :bundler => "< 3" do
+ it "fetches again when more dependencies are found in subsequent sources using deployment mode", :bundler => "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -482,8 +457,8 @@ The checksum of /versions does not match the checksum provided by the server! So
G
bundle :install, :artifice => "compact_index_extra"
-
- bundle "install --deployment", :artifice => "compact_index_extra"
+ bundle "config --set local deployment true"
+ bundle :install, :artifice => "compact_index_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
@@ -503,7 +478,7 @@ The checksum of /versions does not match the checksum provided by the server! So
G
bundle :install, :artifice => "compact_index_extra"
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :artifice => "compact_index_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
@@ -614,6 +589,17 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(the_bundle).to include_gems "rack 1.0.0"
end
+ it "passes basic authentication details and strips out creds also in verbose mode" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :verbose => true, :artifice => "compact_index_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 3" do
gemfile <<-G
source "#{basic_auth_source_uri}"
@@ -815,6 +801,28 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(the_bundle).to include_gems "rack 1.0.0"
end
+ it "performs full update if server endpoints serve partial content responses but don't have incremental content and provide no Etag" do
+ build_repo4 do
+ build_gem "rack", "0.9.1"
+ end
+
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'rack', '0.9.1'
+ G
+
+ update_repo4 do
+ build_gem "rack", "1.0.0"
+ end
+
+ install_gemfile <<-G, :artifice => "compact_index_partial_update_no_etag_not_incremental", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'rack', '1.0.0'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
it "performs full update of compact index info cache if range is not satisfiable" do
gemfile <<-G
source "#{source_uri}"
@@ -922,6 +930,7 @@ Either installing with `--full-index` or running `bundle update rails` should fi
it "does not duplicate specs in the lockfile when updating and a dependency is not installed" do
install_gemfile <<-G, :artifice => "compact_index"
+ source "#{file_uri_for(gem_repo1)}"
source "#{source_uri}" do
gem "rails"
gem "activemerchant"
diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb
index 5e0be89995..9738a75474 100644
--- a/spec/bundler/install/gems/dependency_api_spec.rb
+++ b/spec/bundler/install/gems/dependency_api_spec.rb
@@ -60,8 +60,8 @@ RSpec.describe "gemcutter's dependency API" do
G
bundle :install, :artifice => "endpoint"
- bundle "config --local deployment true"
- bundle "config --local path vendor/bundle"
+ bundle "config set --local deployment true"
+ bundle "config set --local path vendor/bundle"
bundle :install, :artifice => "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "rack 1.0.0"
@@ -98,7 +98,7 @@ RSpec.describe "gemcutter's dependency API" do
bundle :install, :artifice => "endpoint"
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :artifice => "endpoint"
expect(the_bundle).to include_gems("rails 2.3.2")
@@ -112,7 +112,7 @@ RSpec.describe "gemcutter's dependency API" do
G
bundle "install", :artifice => "endpoint"
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle :install, :artifice => "endpoint"
expect(the_bundle).to include_gems("foo 1.0")
@@ -338,31 +338,6 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "activesupport 1.2.3"
end
- it "considers all possible versions of dependencies from all api gem sources using blocks" do
- # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
- # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
- # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
- # repo and installs it.
- build_repo4 do
- build_gem "activesupport", "1.2.0"
- build_gem "somegem", "1.0.0" do |s|
- s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
- end
- end
-
- gemfile <<-G
- source "#{source_uri}"
- source "#{source_uri}/extra" do
- gem 'somegem', '1.0.0'
- end
- G
-
- bundle :install, :artifice => "endpoint_extra_api"
-
- expect(the_bundle).to include_gems "somegem 1.0.0"
- expect(the_bundle).to include_gems "activesupport 1.2.3"
- end
-
it "prints API output properly with back deps" do
build_repo2 do
build_gem "back_deps" do |s|
@@ -396,7 +371,7 @@ RSpec.describe "gemcutter's dependency API" do
api_request_limit = low_api_request_limit_for(gem_repo2)
- install_gemfile <<-G, :artifice => "endpoint_extra_missing", :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
source "#{source_uri}"
source "#{source_uri}/extra"
gem "back_deps"
@@ -417,7 +392,7 @@ RSpec.describe "gemcutter's dependency API" do
api_request_limit = low_api_request_limit_for(gem_repo2)
- install_gemfile <<-G, :artifice => "endpoint_extra_missing", :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
+ install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -438,7 +413,7 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "foo 1.0"
end
- it "fetches again when more dependencies are found in subsequent sources using --deployment", :bundler => "< 3" do
+ it "fetches again when more dependencies are found in subsequent sources using deployment mode", :bundler => "< 3" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
@@ -453,8 +428,8 @@ RSpec.describe "gemcutter's dependency API" do
G
bundle :install, :artifice => "endpoint_extra"
-
- bundle "install --deployment", :artifice => "endpoint_extra"
+ bundle "config set --local deployment true"
+ bundle :install, :artifice => "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
@@ -474,8 +449,7 @@ RSpec.describe "gemcutter's dependency API" do
G
bundle :install, :artifice => "endpoint_extra"
-
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
bundle "install", :artifice => "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
@@ -586,6 +560,17 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "rack 1.0.0"
end
+ it "passes basic authentication details and strips out creds also in verbose mode" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :verbose => true, :artifice => "endpoint_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
it "strips http basic authentication creds for modern index" do
gemfile <<-G
source "#{basic_auth_source_uri}"
@@ -630,6 +615,22 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "rack 1.0.0"
end
+ describe "with host including dashes" do
+ before do
+ gemfile <<-G
+ source "http://local-gemserver.test"
+ gem "rack"
+ G
+ end
+
+ it "reads authentication details from a valid ENV variable" do
+ bundle :install, :artifice => "endpoint_strict_basic_authentication", :env => { "BUNDLE_LOCAL___GEMSERVER__TEST" => "#{user}:#{password}" }
+
+ expect(out).to include("Fetching gem metadata from http://local-gemserver.test")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
describe "with authentication details in bundle config" do
before do
gemfile <<-G
diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb
index 7ab0ded26d..f9b374cf01 100644
--- a/spec/bundler/install/gems/flex_spec.rb
+++ b/spec/bundler/install/gems/flex_spec.rb
@@ -229,14 +229,27 @@ RSpec.describe "bundle flex_install" do
G
end
- it "does something" do
- expect do
- bundle "install", :raise_on_error => false
- end.not_to change { File.read(bundled_app_lock) }
-
- expect(err).to include("rack = 0.9.1")
- expect(err).to include("locked at 1.0.0")
- expect(err).to include("bundle update rack")
+ it "should work when you install" do
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+ rack (0.9.1)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack (= 0.9.1)
+ rack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
it "should work when you update" do
@@ -245,37 +258,7 @@ RSpec.describe "bundle flex_install" do
end
describe "when adding a new source" do
- it "updates the lockfile", :bundler => "< 3" do
- build_repo2
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
- G
-
- lockfile_should_be <<-L
- GEM
- remote: #{file_uri_for(gem_repo1)}/
- remote: #{file_uri_for(gem_repo2)}/
- specs:
- rack (1.0.0)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- rack
-
- BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
-
- it "updates the lockfile", :bundler => "3" do
+ it "updates the lockfile" do
build_repo2
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
@@ -289,24 +272,24 @@ RSpec.describe "bundle flex_install" do
gem "rack"
G
- lockfile_should_be <<-L
- GEM
- remote: #{file_uri_for(gem_repo1)}/
- specs:
- rack (1.0.0)
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+ rack (1.0.0)
- GEM
- remote: #{file_uri_for(gem_repo2)}/
- specs:
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
- PLATFORMS
- #{lockfile_platforms}
+ PLATFORMS
+ #{lockfile_platforms}
- DEPENDENCIES
- rack
+ DEPENDENCIES
+ rack
- BUNDLED WITH
- #{Bundler::VERSION}
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
end
diff --git a/spec/bundler/install/gems/native_extensions_spec.rb b/spec/bundler/install/gems/native_extensions_spec.rb
index 8a4de3cf92..d5cafcfc2c 100644
--- a/spec/bundler/install/gems/native_extensions_spec.rb
+++ b/spec/bundler/install/gems/native_extensions_spec.rb
@@ -78,6 +78,7 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
bundle "config set build.c_extension --with-c_extension=hello"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "c_extension", :git => #{lib_path("c_extension-1.0").to_s.dump}
G
@@ -126,11 +127,13 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
# 1st time, require only one gem -- only one of the extensions gets built.
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "c_extension_one", :git => #{lib_path("gems").to_s.dump}
G
# 2nd time, require both gems -- we need both extensions to be built now.
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "c_extension_one", :git => #{lib_path("gems").to_s.dump}
gem "c_extension_two", :git => #{lib_path("gems").to_s.dump}
G
@@ -171,6 +174,7 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
bundle "config set build.c_extension --with-c_extension=hello --with-c_extension_bundle-dir=hola"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "c_extension", :git => #{lib_path("c_extension-1.0").to_s.dump}
G
diff --git a/spec/bundler/install/gems/post_install_spec.rb b/spec/bundler/install/gems/post_install_spec.rb
index 3f6d7ce42c..7426f54877 100644
--- a/spec/bundler/install/gems/post_install_spec.rb
+++ b/spec/bundler/install/gems/post_install_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe "bundle install" do
end
end
- context "when a dependecy includes a post install message" do
+ context "when a dependency includes a post install message" do
it "should display the post install message" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb
index 2d0ac5a805..94fac0052c 100644
--- a/spec/bundler/install/gems/resolving_spec.rb
+++ b/spec/bundler/install/gems/resolving_spec.rb
@@ -3,6 +3,32 @@
RSpec.describe "bundle install with install-time dependencies" do
before do
build_repo2 do
+ build_gem "with_implicit_rake_dep" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/implicit_rake_dep.rb", "w") do |f|
+ f.puts "IMPLICIT_RAKE_DEP = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ build_gem "another_implicit_rake_dep" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/another_implicit_rake_dep.rb", "w") do |f|
+ f.puts "ANOTHER_IMPLICIT_RAKE_DEP = 'YES'"
+ end
+ end
+ RUBY
+ end
+
# Test complicated gem dependencies for install
build_gem "net_a" do |s|
s.add_dependency "net_b"
@@ -55,6 +81,25 @@ RSpec.describe "bundle install with install-time dependencies" do
expect(out).to eq("YES\nYES")
end
+ it "installs gems with implicit rake dependencies without rake previously installed" do
+ with_path_as("") do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "with_implicit_rake_dep"
+ gem "another_implicit_rake_dep"
+ gem "rake"
+ G
+ end
+
+ run <<-R
+ require 'implicit_rake_dep'
+ require 'another_implicit_rake_dep'
+ puts IMPLICIT_RAKE_DEP
+ puts ANOTHER_IMPLICIT_RAKE_DEP
+ R
+ expect(out).to eq("YES\nYES")
+ end
+
it "installs gems with a dependency with no type" do
skip "incorrect data check error" if Gem.win_platform?
@@ -160,10 +205,6 @@ RSpec.describe "bundle install with install-time dependencies" do
describe "when a required ruby version" do
context "allows only an older version" do
- before do
- skip "gem not found" if Gem.win_platform?
- end
-
it "installs the older version" do
build_repo2 do
build_gem "rack", "1.2" do |s|
diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb
index 0a0f992704..db16a1b0e1 100644
--- a/spec/bundler/install/gems/standalone_spec.rb
+++ b/spec/bundler/install/gems/standalone_spec.rb
@@ -94,7 +94,7 @@ RSpec.shared_examples "bundle install --standalone" do
source "#{file_uri_for(gem_repo1)}"
gem "rails"
G
- bundle "config --local path #{bundled_app("bundle")}"
+ bundle "config set --local path #{bundled_app("bundle")}"
bundle :install, :standalone => true, :dir => cwd
end
@@ -108,9 +108,78 @@ RSpec.shared_examples "bundle install --standalone" do
include_examples "common functionality"
end
+ describe "with default gems and a lockfile", :ruby_repo do
+ before do
+ skip "does not work on rubygems versions where `--install_dir` doesn't respect --default" unless Gem::Installer.for_spec(loaded_gemspec, :install_dir => "/foo").default_spec_file == "/foo/specifications/default/bundler-#{Bundler::VERSION}.gemspec" # Since rubygems 3.2.0.rc.2
+ skip "does not work on old rubies because the realworld gems that need to be installed don't support them" if RUBY_VERSION < "2.7.0"
+
+ realworld_system_gems "fiddle --version 1.0.6", "tsort --version 0.1.0"
+
+ necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "yaml --version 0.1.1", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.0.0"]
+ necessary_system_gems += ["shellwords --version 0.1.0", "base64 --version 0.1.0", "resolv --version 0.2.1"] if Gem.rubygems_version < Gem::Version.new("3.3.3.a")
+ realworld_system_gems(*necessary_system_gems, :path => scoped_gem_path(bundled_app("bundle")))
+
+ build_gem "foo", "1.0.0", :to_system => true, :default => true do |s|
+ s.add_dependency "bar"
+ end
+
+ build_gem "bar", "1.0.0", :to_system => true, :default => true
+
+ build_repo4 do
+ build_gem "foo", "1.0.0" do |s|
+ s.add_dependency "bar"
+ end
+
+ build_gem "bar", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "foo"
+ G
+
+ bundle "lock", :dir => cwd, :artifice => "compact_index"
+ end
+
+ it "works" do
+ bundle "config set --local path #{bundled_app("bundle")}"
+ bundle :install, :standalone => true, :dir => cwd, :artifice => "compact_index", :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
+ end
+ end
+
+ describe "with Gemfiles using path sources and resulting bundle moved to a folder hierarchy with different nesting" do
+ before do
+ build_lib "minitest", "1.0.0", :path => lib_path("minitest")
+
+ Dir.mkdir bundled_app("app")
+
+ gemfile bundled_app("app/Gemfile"), <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "minitest", :path => "#{lib_path("minitest")}"
+ G
+
+ bundle "install", :standalone => true, :dir => bundled_app("app")
+
+ Dir.mkdir tmp("one_more_level")
+ FileUtils.mv bundled_app, tmp("one_more_level")
+ end
+
+ it "also works" do
+ ruby <<-RUBY, :dir => tmp("one_more_level/bundled_app/app")
+ require "./bundle/bundler/setup"
+
+ require "minitest"
+ puts MINITEST
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ end
+ end
+
describe "with gems with native extension", :ruby_repo do
before do
- bundle "config --local path #{bundled_app("bundle")}"
+ bundle "config set --local path #{bundled_app("bundle")}"
install_gemfile <<-G, :standalone => true, :dir => cwd
source "#{file_uri_for(gem_repo1)}"
gem "very_simple_binary"
@@ -120,7 +189,7 @@ RSpec.shared_examples "bundle install --standalone" do
it "generates a bundle/bundler/setup.rb with the proper paths" do
expected_path = bundled_app("bundle/bundler/setup.rb")
extension_line = File.read(expected_path).each_line.find {|line| line.include? "/extensions/" }.strip
- expect(extension_line).to start_with '$:.unshift File.expand_path("#{path}/../#{ruby_engine}/#{ruby_version}/extensions/'
+ expect(extension_line).to start_with '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{RbConfig::CONFIG["ruby_version"]}/extensions/'
expect(extension_line).to end_with '/very_simple_binary-1.0")'
end
end
@@ -144,8 +213,9 @@ RSpec.shared_examples "bundle install --standalone" do
end
G
end
- bundle "config --local path #{bundled_app("bundle")}"
+ bundle "config set --local path #{bundled_app("bundle")}"
install_gemfile <<-G, :standalone => true, :dir => cwd, :raise_on_error => false
+ source "#{file_uri_for(gem_repo1)}"
gem "bar", :git => "#{lib_path("bar-1.0")}"
G
end
@@ -165,7 +235,7 @@ RSpec.shared_examples "bundle install --standalone" do
gem "rails"
gem "devise", :git => "#{lib_path("devise-1.0")}"
G
- bundle "config --local path #{bundled_app("bundle")}"
+ bundle "config set --local path #{bundled_app("bundle")}"
bundle :install, :standalone => true, :dir => cwd
end
@@ -193,7 +263,7 @@ RSpec.shared_examples "bundle install --standalone" do
gem "rack-test"
end
G
- bundle "config --local path #{bundled_app("bundle")}"
+ bundle "config set --local path #{bundled_app("bundle")}"
bundle :install, :standalone => true, :dir => cwd
end
@@ -207,7 +277,7 @@ RSpec.shared_examples "bundle install --standalone" do
include_examples "common functionality"
it "allows creating a standalone file with limited groups" do
- bundle "config --local path #{bundled_app("bundle")}"
+ bundle "config set --local path #{bundled_app("bundle")}"
bundle :install, :standalone => "default", :dir => cwd
load_error_ruby <<-RUBY, "spec"
@@ -224,8 +294,8 @@ RSpec.shared_examples "bundle install --standalone" do
end
it "allows `without` configuration to limit the groups used in a standalone" do
- bundle "config --local path #{bundled_app("bundle")}"
- bundle "config --local without test"
+ bundle "config set --local path #{bundled_app("bundle")}"
+ bundle "config set --local without test"
bundle :install, :standalone => true, :dir => cwd
load_error_ruby <<-RUBY, "spec"
@@ -242,7 +312,7 @@ RSpec.shared_examples "bundle install --standalone" do
end
it "allows `path` configuration to change the location of the standalone bundle" do
- bundle "config --local path path/to/bundle"
+ bundle "config set --local path path/to/bundle"
bundle "install", :standalone => true, :dir => cwd
ruby <<-RUBY
@@ -257,9 +327,9 @@ RSpec.shared_examples "bundle install --standalone" do
end
it "allows `without` to limit the groups used in a standalone" do
- bundle "config --local without test"
+ bundle "config set --local without test"
bundle :install, :dir => cwd
- bundle "config --local path #{bundled_app("bundle")}"
+ bundle "config set --local path #{bundled_app("bundle")}"
bundle :install, :standalone => true, :dir => cwd
load_error_ruby <<-RUBY, "spec"
@@ -285,7 +355,7 @@ RSpec.shared_examples "bundle install --standalone" do
source "#{source_uri}"
gem "rails"
G
- bundle "config --local path #{bundled_app("bundle")}"
+ bundle "config set --local path #{bundled_app("bundle")}"
bundle :install, :standalone => true, :artifice => "endpoint", :dir => cwd
end
@@ -306,7 +376,7 @@ RSpec.shared_examples "bundle install --standalone" do
source "#{file_uri_for(gem_repo1)}"
gem "rails"
G
- bundle "config --local path #{bundled_app("bundle")}"
+ bundle "config set --local path #{bundled_app("bundle")}"
bundle :install, :standalone => true, :binstubs => true, :dir => cwd
end
diff --git a/spec/bundler/install/gems/sudo_spec.rb b/spec/bundler/install/gems/sudo_spec.rb
index ff73b4a1fa..3e5d38ea4c 100644
--- a/spec/bundler/install/gems/sudo_spec.rb
+++ b/spec/bundler/install/gems/sudo_spec.rb
@@ -49,8 +49,23 @@ RSpec.describe "when using sudo", :sudo => true do
end
it "installs rake and a gem dependent on rake in the same session" do
+ build_repo2 do
+ build_gem "another_implicit_rake_dep" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/another_implicit_rake_dep.rb", "w") do |f|
+ f.puts "ANOTHER_IMPLICIT_RAKE_DEP = 'YES'"
+ end
+ end
+ RUBY
+ end
+ end
+
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "#{file_uri_for(gem_repo2)}"
gem "rake"
gem "another_implicit_rake_dep"
G
diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb
index 0db1f1985b..3684d8749d 100644
--- a/spec/bundler/install/gemspecs_spec.rb
+++ b/spec/bundler/install/gemspecs_spec.rb
@@ -21,6 +21,7 @@ RSpec.describe "bundle install" do
build_lib "yaml_spec", :gemspec => :yaml
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'yaml_spec', :path => "#{lib_path("yaml_spec-1.0")}"
G
expect(err).to be_empty
@@ -28,13 +29,13 @@ RSpec.describe "bundle install" do
end
it "should use gemspecs in the system cache when available" do
- skip "weird incompatible marshal file format error" if Gem.win_platform?
-
gemfile <<-G
source "http://localtestserver.gem"
gem 'rack'
G
+ system_gems "rack-1.0.0", :path => default_bundle_path
+
FileUtils.mkdir_p "#{default_bundle_path}/specifications"
File.open("#{default_bundle_path}/specifications/rack-1.0.0.gemspec", "w+") do |f|
spec = Gem::Specification.new do |s|
@@ -45,7 +46,7 @@ RSpec.describe "bundle install" do
f.write spec.to_ruby
end
bundle :install, :artifice => "endpoint_marshal_fail" # force gemspec load
- expect(the_bundle).to include_gems "activesupport 2.3.2"
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.2"
end
it "does not hang when gemspec has incompatible encoding" do
@@ -59,6 +60,7 @@ RSpec.describe "bundle install" do
G
install_gemfile <<-G, :env => { "LANG" => "C" }
+ source "#{file_uri_for(gem_repo1)}"
gemspec
G
@@ -84,6 +86,7 @@ RSpec.describe "bundle install" do
G
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gemspec
G
@@ -98,6 +101,7 @@ RSpec.describe "bundle install" do
install_gemfile <<-G
ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby'
+ source "#{file_uri_for(gem_repo1)}"
gemspec
G
expect(the_bundle).to include_gems "foo 1.0"
@@ -111,6 +115,7 @@ RSpec.describe "bundle install" do
install_gemfile <<-G, :raise_on_error => false
ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{RUBY_PATCHLEVEL}'
+ source "#{file_uri_for(gem_repo1)}"
gemspec
G
expect(the_bundle).to include_gems "foo 1.0"
@@ -125,6 +130,7 @@ RSpec.describe "bundle install" do
install_gemfile <<-G, :raise_on_error => false
ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{patchlevel}'
+ source "#{file_uri_for(gem_repo1)}"
gemspec
G
@@ -142,6 +148,7 @@ RSpec.describe "bundle install" do
install_gemfile <<-G, :raise_on_error => false
ruby '#{version}', :engine_version => '#{version}', :engine => 'ruby'
+ source "#{file_uri_for(gem_repo1)}"
gemspec
G
diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb
index 61eaee4a84..d43aacee7e 100644
--- a/spec/bundler/install/git_spec.rb
+++ b/spec/bundler/install/git_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe "bundle install" do
build_git "foo", "1.0", :path => lib_path("foo")
install_gemfile <<-G, :verbose => true
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo"))}"
G
@@ -13,10 +14,11 @@ RSpec.describe "bundle install" do
expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
end
- it "displays the correct default branch" do
+ it "displays the correct default branch", :git => ">= 2.28.0" do
build_git "foo", "1.0", :path => lib_path("foo"), :default_branch => "main"
install_gemfile <<-G, :verbose => true
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo"))}"
G
@@ -34,6 +36,7 @@ RSpec.describe "bundle install" do
update_git "foo", "3.0", :path => lib_path("foo"), :gemspec => true
install_gemfile <<-G, :verbose => true
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo"))}", :ref => "master~2"
G
@@ -51,6 +54,7 @@ RSpec.describe "bundle install" do
revision = build_git("foo").ref_for("HEAD")
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :group => :development
G
@@ -68,8 +72,8 @@ RSpec.describe "bundle install" do
foo!
L
- bundle "config --local path vendor/bundle"
- bundle "config --local without development"
+ bundle "config set --local path vendor/bundle"
+ bundle "config set --local without development"
bundle :install
expect(out).to include("Bundle complete!")
diff --git a/spec/bundler/install/global_cache_spec.rb b/spec/bundler/install/global_cache_spec.rb
index f3609715fb..afa0ff76c1 100644
--- a/spec/bundler/install/global_cache_spec.rb
+++ b/spec/bundler/install/global_cache_spec.rb
@@ -37,6 +37,18 @@ RSpec.describe "global gem caching" do
expect(the_bundle).to include_gems "rack 1.0.0"
end
+ it "shows a proper error message if a cached gem is corrupted" do
+ source_global_cache.mkpath
+ FileUtils.touch(source_global_cache("rack-1.0.0.gem"))
+
+ install_gemfile <<-G, :artifice => "compact_index_no_gem", :raise_on_error => false
+ source "#{source}"
+ gem "rack"
+ G
+
+ expect(err).to include("Gem::Package::FormatError: package metadata is missing in #{source_global_cache("rack-1.0.0.gem")}")
+ end
+
describe "when the same gem from different sources is installed" do
it "should use the appropriate one from the global cache" do
install_gemfile <<-G, :artifice => "compact_index"
@@ -113,6 +125,8 @@ RSpec.describe "global gem caching" do
expect(source2_global_cache("rack-0.9.1.gem")).to exist
bundle :install, :artifice => "compact_index_no_gem", :raise_on_error => false
expect(err).to include("Internal Server Error 500")
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+
# rack 1.0.0 is not installed and rack 0.9.1 is not
expect(the_bundle).not_to include_gems "rack 1.0.0"
expect(the_bundle).not_to include_gems "rack 0.9.1"
@@ -126,6 +140,8 @@ RSpec.describe "global gem caching" do
expect(source2_global_cache("rack-0.9.1.gem")).to exist
bundle :install, :artifice => "compact_index_no_gem", :raise_on_error => false
expect(err).to include("Internal Server Error 500")
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+
# rack 0.9.1 is not installed and rack 1.0.0 is not
expect(the_bundle).not_to include_gems "rack 0.9.1"
expect(the_bundle).not_to include_gems "rack 1.0.0"
@@ -177,8 +193,11 @@ RSpec.describe "global gem caching" do
bundle :install, :artifice => "compact_index_no_gem", :dir => bundled_app2
# activesupport is installed and both are in the global cache
- expect(the_bundle).not_to include_gems "rack 1.0.0", :dir => bundled_app2
- expect(the_bundle).to include_gems "activesupport 2.3.5", :dir => bundled_app2
+ simulate_bundler_version_when_missing_prerelease_default_gem_activation do
+ expect(the_bundle).not_to include_gems "rack 1.0.0", :dir => bundled_app2
+ expect(the_bundle).to include_gems "activesupport 2.3.5", :dir => bundled_app2
+ end
+
expect(source_global_cache("rack-1.0.0.gem")).to exist
expect(source_global_cache("activesupport-2.3.5.gem")).to exist
end
diff --git a/spec/bundler/install/path_spec.rb b/spec/bundler/install/path_spec.rb
index a05467db12..b0392c4ed2 100644
--- a/spec/bundler/install/path_spec.rb
+++ b/spec/bundler/install/path_spec.rb
@@ -14,14 +14,14 @@ RSpec.describe "bundle install" do
end
it "does not use available system gems with `vendor/bundle" do
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle :install
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "uses system gems with `path.system` configured with more priority than `path`" do
- bundle "config --local path.system true"
- bundle "config --global path vendor/bundle"
+ bundle "config set --local path.system true"
+ bundle "config set --global path vendor/bundle"
bundle :install
run "require 'rack'", :raise_on_error => false
expect(out).to include("FAIL")
@@ -31,7 +31,7 @@ RSpec.describe "bundle install" do
dir = bundled_app("bun++dle")
dir.mkpath
- bundle "config --local path #{dir.join("vendor/bundle")}"
+ bundle "config set --local path #{dir.join("vendor/bundle")}"
bundle :install, :dir => dir
expect(out).to include("installed into `./vendor/bundle`")
@@ -39,7 +39,7 @@ RSpec.describe "bundle install" do
end
it "prints a message to let the user know where gems where installed" do
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle :install
expect(out).to include("gems are installed into `./vendor/bundle`")
end
@@ -109,7 +109,7 @@ RSpec.describe "bundle install" do
context "when set via #{type}" do
it "installs gems to a path if one is specified" do
set_bundle_path(type, bundled_app("vendor2").to_s)
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle :install
expect(vendored_gems("gems/rack-1.0.0")).to be_directory
@@ -159,7 +159,7 @@ RSpec.describe "bundle install" do
end
it "sets BUNDLE_PATH as the first argument to bundle install" do
- bundle "config --local path ./vendor/bundle"
+ bundle "config set --local path ./vendor/bundle"
bundle :install
expect(vendored_gems("gems/rack-1.0.0")).to be_directory
@@ -169,7 +169,7 @@ RSpec.describe "bundle install" do
it "disables system gems when passing a path to install" do
# This is so that vendored gems can be distributed to others
build_gem "rack", "1.1.0", :to_system => true
- bundle "config --local path ./vendor/bundle"
+ bundle "config set --local path ./vendor/bundle"
bundle :install
expect(vendored_gems("gems/rack-1.0.0")).to be_directory
@@ -186,7 +186,7 @@ RSpec.describe "bundle install" do
gem "very_simple_binary"
G
- bundle "config --local path ./vendor/bundle"
+ bundle "config set --local path ./vendor/bundle"
bundle :install
expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
@@ -198,7 +198,7 @@ RSpec.describe "bundle install" do
run "require 'very_simple_binary_c'", :raise_on_error => false
expect(err).to include("Bundler::GemNotFound")
- bundle "config --local path ./vendor/bundle"
+ bundle "config set --local path ./vendor/bundle"
bundle :install
expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
@@ -218,7 +218,7 @@ RSpec.describe "bundle install" do
gem "rack"
G
- bundle "config --local path bundle"
+ bundle "config set --local path bundle"
bundle :install, :raise_on_error => false
expect(err).to include("file already exists")
end
diff --git a/spec/bundler/install/prereleases_spec.rb b/spec/bundler/install/prereleases_spec.rb
index c3f968ad70..629eb89dac 100644
--- a/spec/bundler/install/prereleases_spec.rb
+++ b/spec/bundler/install/prereleases_spec.rb
@@ -38,7 +38,11 @@ RSpec.describe "bundle install" do
describe "when prerelease gems are not available" do
it "still works" do
- build_repo3
+ build_repo gem_repo3 do
+ build_gem "rack"
+ end
+ FileUtils.rm_rf Dir[gem_repo3("prerelease*")]
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo3)}"
gem "rack"
diff --git a/spec/bundler/install/redownload_spec.rb b/spec/bundler/install/redownload_spec.rb
index 0eae615c59..a936b2b536 100644
--- a/spec/bundler/install/redownload_spec.rb
+++ b/spec/bundler/install/redownload_spec.rb
@@ -33,6 +33,7 @@ RSpec.describe "bundle install" do
before do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
end
diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb
index 6d3065a836..c5f3d788ba 100644
--- a/spec/bundler/install/yanked_spec.rb
+++ b/spec/bundler/install/yanked_spec.rb
@@ -70,4 +70,35 @@ RSpec.context "when using gem before installing" do
expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
expect(err).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.")
end
+
+ it "does not suggest the author has yanked the gem when using more than one gem, but shows all gems that couldn't be found in the source" do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack", "0.9.1"
+ gem "rack_middleware", "1.0"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo1)}
+ specs:
+ rack (0.9.1)
+ rack_middleware (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 0.9.1)
+ rack_middleware (1.0)
+ L
+
+ bundle :list, :raise_on_error => false
+
+ expect(err).to include("Could not find rack-0.9.1, rack_middleware-1.0 in any of the sources")
+ expect(err).to include("Install missing gems with `bundle install`.")
+ expect(err).to_not include("Your bundle is locked to rack (0.9.1), but that version could not be found in any of the sources listed in your Gemfile.")
+ expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
+ expect(err).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.")
+ end
end
diff --git a/spec/bundler/lock/git_spec.rb b/spec/bundler/lock/git_spec.rb
index 14b80483ee..56db5d8305 100644
--- a/spec/bundler/lock/git_spec.rb
+++ b/spec/bundler/lock/git_spec.rb
@@ -5,6 +5,7 @@ RSpec.describe "bundle lock with git gems" do
build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :git => "#{lib_path("foo-1.0")}"
G
end
diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb
index d3591cd62a..8befb0d400 100644
--- a/spec/bundler/lock/lockfile_spec.rb
+++ b/spec/bundler/lock/lockfile_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe "the lockfile format" do
gem "rack"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -70,7 +70,7 @@ RSpec.describe "the lockfile format" do
gem "rack"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -112,7 +112,7 @@ RSpec.describe "the lockfile format" do
gem "rack"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -149,7 +149,7 @@ RSpec.describe "the lockfile format" do
gem "rack", "> 0"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -166,9 +166,10 @@ RSpec.describe "the lockfile format" do
G
end
- it "warns if the current is older than lockfile's bundler version" do
- current_version = Bundler::VERSION
- newer_minor = bump_minor(current_version)
+ it "warns if the current version is older than lockfile's bundler version, and locked version is a final release" do
+ current_version = "999.998.999"
+ system_gems "bundler-#{current_version}"
+ newer_minor = "999.999.0"
lockfile <<-L
GEM
@@ -186,20 +187,115 @@ RSpec.describe "the lockfile format" do
#{newer_minor}
L
- install_gemfile <<-G
+ install_gemfile <<-G, :env => { "BUNDLER_VERSION" => current_version }
source "#{file_uri_for(gem_repo2)}"
gem "rack"
G
- pre_flag = prerelease?(newer_minor) ? " --pre" : ""
warning_message = "the running version of Bundler (#{current_version}) is older " \
"than the version that created the lockfile (#{newer_minor}). " \
"We suggest you to upgrade to the version that created the " \
- "lockfile by running `gem install bundler:#{newer_minor}#{pre_flag}`."
+ "lockfile by running `gem install bundler:#{newer_minor}`."
expect(err).to include warning_message
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{newer_minor}
+ G
+ end
+
+ it "warns if the current version is older than lockfile's bundler version, and locked version is a prerelease" do
+ current_version = "999.998.999"
+ system_gems "bundler-#{current_version}"
+ newer_minor = "999.999.0.pre1"
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{newer_minor}
+ L
+
+ install_gemfile <<-G, :env => { "BUNDLER_VERSION" => current_version }
+ source "#{file_uri_for(gem_repo2)}"
+
+ gem "rack"
+ G
+
+ warning_message = "the running version of Bundler (#{current_version}) is older " \
+ "than the version that created the lockfile (#{newer_minor}). " \
+ "We suggest you to upgrade to the version that created the " \
+ "lockfile by running `gem install bundler:#{newer_minor} --pre`."
+ expect(err).to include warning_message
+
+ expect(lockfile).to eq <<~G
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{newer_minor}
+ G
+ end
+
+ it "doesn't warn if the current version is older than lockfile's bundler version, and locked version is a dev version" do
+ current_version = "999.998.999"
+ system_gems "bundler-#{current_version}"
+ newer_minor = "999.999.0.dev"
+
+ lockfile <<-L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{newer_minor}
+ L
+
+ install_gemfile <<-G, :env => { "BUNDLER_VERSION" => current_version }
+ source "#{file_uri_for(gem_repo2)}"
+
+ gem "rack"
+ G
+
+ expect(err).to be_empty
+
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -249,7 +345,7 @@ RSpec.describe "the lockfile format" do
"#{current_version.split(".").first}, after which you will be unable to return to Bundler #{older_major.split(".").first}."
)
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -273,7 +369,7 @@ RSpec.describe "the lockfile format" do
gem "rack-obama"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -299,7 +395,7 @@ RSpec.describe "the lockfile format" do
gem "rack-obama", ">= 1.0"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -318,43 +414,12 @@ RSpec.describe "the lockfile format" do
G
end
- it "generates a lockfile without credentials for a configured source", :bundler => "< 3" do
+ it "generates a lockfile without credentials for a configured source" do
bundle "config set http://localgemserver.test/ user:pass"
install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
- source "http://localgemserver.test/" do
-
- end
-
- source "http://user:pass@othergemserver.test/" do
- gem "rack-obama", ">= 1.0"
- end
- G
-
- lockfile_should_be <<-G
- GEM
- remote: http://localgemserver.test/
- remote: http://user:pass@othergemserver.test/
- specs:
- rack (1.0.0)
- rack-obama (1.0)
- rack
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- rack-obama (>= 1.0)!
-
- BUNDLED WITH
- #{Bundler::VERSION}
- G
- end
+ source "#{file_uri_for(gem_repo1)}"
- it "generates a lockfile without credentials for a configured source", :bundler => "3" do
- bundle "config set http://localgemserver.test/ user:pass"
-
- install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
source "http://localgemserver.test/" do
end
@@ -364,8 +429,9 @@ RSpec.describe "the lockfile format" do
end
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
GEM
@@ -396,7 +462,7 @@ RSpec.describe "the lockfile format" do
gem "net-sftp"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -421,10 +487,11 @@ RSpec.describe "the lockfile format" do
git = build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GIT
remote: #{lib_path("foo-1.0")}
revision: #{git.ref_for("master")}
@@ -432,6 +499,7 @@ RSpec.describe "the lockfile format" do
foo (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -488,12 +556,13 @@ RSpec.describe "the lockfile format" do
git = build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}" do
gem "foo"
end
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GIT
remote: #{lib_path("foo-1.0")}
revision: #{git.ref_for("master")}
@@ -501,6 +570,7 @@ RSpec.describe "the lockfile format" do
foo (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -519,10 +589,11 @@ RSpec.describe "the lockfile format" do
update_git "foo", :branch => "omg"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "omg"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GIT
remote: #{lib_path("foo-1.0")}
revision: #{git.ref_for("omg")}
@@ -531,6 +602,7 @@ RSpec.describe "the lockfile format" do
foo (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -549,10 +621,11 @@ RSpec.describe "the lockfile format" do
update_git "foo", :tag => "omg"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}", :tag => "omg"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GIT
remote: #{lib_path("foo-1.0")}
revision: #{git.ref_for("omg")}
@@ -561,6 +634,7 @@ RSpec.describe "the lockfile format" do
foo (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -578,16 +652,18 @@ RSpec.describe "the lockfile format" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo-1.0")}
specs:
foo (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -605,6 +681,7 @@ RSpec.describe "the lockfile format" do
build_lib "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
@@ -612,13 +689,14 @@ RSpec.describe "the lockfile format" do
bundle :cache
bundle :install, :local => true
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo-1.0")}
specs:
foo (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -644,7 +722,7 @@ RSpec.describe "the lockfile format" do
gem "bar", :git => "#{lib_path("bar-1.0")}"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GIT
remote: #{lib_path("bar-1.0")}
revision: #{bar.ref_for("master")}
@@ -674,6 +752,30 @@ RSpec.describe "the lockfile format" do
G
end
+ it "removes redundant sources" do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}/"
+
+ gem "rack", :source => "#{file_uri_for(gem_repo2)}/"
+ G
+
+ expect(lockfile).to eq <<~G
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
it "lists gems alphabetically" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
@@ -683,7 +785,7 @@ RSpec.describe "the lockfile format" do
gem "rack-obama"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -716,7 +818,7 @@ RSpec.describe "the lockfile format" do
gem "rails"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -754,7 +856,7 @@ RSpec.describe "the lockfile format" do
gem 'double_deps'
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -781,7 +883,7 @@ RSpec.describe "the lockfile format" do
gem "rack-obama", ">= 1.0", :require => "rack/obama"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -807,7 +909,7 @@ RSpec.describe "the lockfile format" do
gem "rack-obama", ">= 1.0", :group => :test
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -830,18 +932,20 @@ RSpec.describe "the lockfile format" do
build_lib "foo", :path => bundled_app("foo")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "foo" do
gem "foo"
end
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: foo
specs:
foo (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -859,18 +963,20 @@ RSpec.describe "the lockfile format" do
build_lib "foo", :path => bundled_app(File.join("..", "foo"))
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "../foo" do
gem "foo"
end
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: ../foo
specs:
foo (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -888,18 +994,20 @@ RSpec.describe "the lockfile format" do
build_lib "foo", :path => bundled_app("foo")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path File.expand_path("../foo", __FILE__) do
gem "foo"
end
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: foo
specs:
foo (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -917,16 +1025,18 @@ RSpec.describe "the lockfile format" do
build_lib("foo", :path => tmp.join("foo"))
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gemspec :path => "../foo"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: ../foo
specs:
foo (1.0)
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -963,15 +1073,14 @@ RSpec.describe "the lockfile format" do
gem "rack"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
rack (1.0.0)
PLATFORMS
- java
- #{lockfile_platforms}
+ #{lockfile_platforms_for(["java"] + local_platforms)}
DEPENDENCIES
rack
@@ -995,7 +1104,7 @@ RSpec.describe "the lockfile format" do
gem "platform_specific"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -1024,7 +1133,7 @@ RSpec.describe "the lockfile format" do
gem "activesupport"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -1050,7 +1159,7 @@ RSpec.describe "the lockfile format" do
gem "rack"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -1074,7 +1183,7 @@ RSpec.describe "the lockfile format" do
gem "rack", "1.0"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -1098,7 +1207,7 @@ RSpec.describe "the lockfile format" do
gem "rack", "1.0", :group => :two
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -1143,7 +1252,7 @@ RSpec.describe "the lockfile format" do
gem "rack", "> 0.9", "< 1.0"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -1167,7 +1276,7 @@ RSpec.describe "the lockfile format" do
gem "rack", "> 0.9", "< 1.0"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
@@ -1187,83 +1296,6 @@ RSpec.describe "the lockfile format" do
G
end
- # Some versions of the Bundler 1.1 RC series introduced corrupted
- # lockfiles. There were two major problems:
- #
- # * multiple copies of the same GIT section appeared in the lockfile
- # * when this happened, those sections got multiple copies of gems
- # in those sections.
- it "fixes corrupted lockfiles" do
- build_git "omg", :path => lib_path("omg")
- revision = revision_for(lib_path("omg"))
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}/"
- gem "omg", :git => "#{lib_path("omg")}", :branch => 'master'
- G
-
- bundle "config --local path vendor"
- bundle :install
- expect(the_bundle).to include_gems "omg 1.0"
-
- # Create a Gemfile.lock that has duplicate GIT sections
- lockfile <<-L
- GIT
- remote: #{lib_path("omg")}
- revision: #{revision}
- branch: master
- specs:
- omg (1.0)
-
- GIT
- remote: #{lib_path("omg")}
- revision: #{revision}
- branch: master
- specs:
- omg (1.0)
-
- GEM
- remote: #{file_uri_for(gem_repo2)}/
- specs:
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- omg!
-
- BUNDLED WITH
- #{Bundler::VERSION}
- L
-
- FileUtils.rm_rf(bundled_app("vendor"))
- bundle "install"
- expect(the_bundle).to include_gems "omg 1.0"
-
- # Confirm that duplicate specs do not appear
- lockfile_should_be(<<-L)
- GIT
- remote: #{lib_path("omg")}
- revision: #{revision}
- branch: master
- specs:
- omg (1.0)
-
- GEM
- remote: #{file_uri_for(gem_repo2)}/
- specs:
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- omg!
-
- BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
-
it "raises a helpful error message when the lockfile is missing deps" do
lockfile <<-L
GEM
@@ -1335,7 +1367,10 @@ RSpec.describe "the lockfile format" do
expect { bundle "update", :all => true }.to change { File.mtime(bundled_app_lock) }
expect(File.read(bundled_app_lock)).to match("\r\n")
- expect(the_bundle).to include_gems "rack 1.2"
+
+ simulate_bundler_version_when_missing_prerelease_default_gem_activation do
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
end
end
@@ -1356,7 +1391,7 @@ RSpec.describe "the lockfile format" do
expect do
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require '#{entrypoint}'
Bundler.setup
RUBY
end.not_to change { File.mtime(bundled_app_lock) }
diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb
index d061ca7064..b228027c03 100644
--- a/spec/bundler/other/major_deprecation_spec.rb
+++ b/spec/bundler/other/major_deprecation_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe "major deprecations" do
bundle "exec ruby -e #{source.dump}"
end
- it "is deprecated in favor of .unbundled_env", :bundler => "2" do
+ it "is deprecated in favor of .unbundled_env", :bundler => "< 3" do
expect(deprecations).to include \
"`Bundler.clean_env` has been deprecated in favor of `Bundler.unbundled_env`. " \
"If you instead want the environment before bundler was originally loaded, use `Bundler.original_env` " \
@@ -33,7 +33,7 @@ RSpec.describe "major deprecations" do
bundle "exec ruby -e #{source.dump}"
end
- it "is deprecated in favor of .unbundled_env", :bundler => "2" do
+ it "is deprecated in favor of .unbundled_env", :bundler => "< 3" do
expect(deprecations).to include(
"`Bundler.with_clean_env` has been deprecated in favor of `Bundler.with_unbundled_env`. " \
"If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env` " \
@@ -50,7 +50,7 @@ RSpec.describe "major deprecations" do
bundle "exec ruby -e #{source.dump}"
end
- it "is deprecated in favor of .unbundled_system", :bundler => "2" do
+ it "is deprecated in favor of .unbundled_system", :bundler => "< 3" do
expect(deprecations).to include(
"`Bundler.clean_system` has been deprecated in favor of `Bundler.unbundled_system`. " \
"If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system` " \
@@ -67,7 +67,7 @@ RSpec.describe "major deprecations" do
bundle "exec ruby -e #{source.dump}"
end
- it "is deprecated in favor of .unbundled_exec", :bundler => "2" do
+ it "is deprecated in favor of .unbundled_exec", :bundler => "< 3" do
expect(deprecations).to include(
"`Bundler.clean_exec` has been deprecated in favor of `Bundler.unbundled_exec`. " \
"If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec` " \
@@ -84,7 +84,7 @@ RSpec.describe "major deprecations" do
bundle "exec ruby -e #{source.dump}"
end
- it "is deprecated in favor of .load", :bundler => "2" do
+ it "is deprecated in favor of .load", :bundler => "< 3" do
expect(deprecations).to include "Bundler.environment has been removed in favor of Bundler.load (called at -e:1)"
end
@@ -92,6 +92,18 @@ RSpec.describe "major deprecations" do
end
end
+ describe "bundle exec --no-keep-file-descriptors" do
+ before do
+ bundle "exec --no-keep-file-descriptors -e 1", :raise_on_error => false
+ end
+
+ it "is deprecated", :bundler => "< 3" do
+ expect(deprecations).to include "The `--no-keep-file-descriptors` has been deprecated. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to"
+ end
+
+ pending "is removed and shows a helpful error message about it", :bundler => "3"
+ end
+
describe "bundle update --quiet" do
it "does not print any deprecations" do
bundle :update, :quiet => true, :raise_on_error => false
@@ -109,7 +121,7 @@ RSpec.describe "major deprecations" do
bundle "check --path vendor/bundle", :raise_on_error => false
end
- it "should print a deprecation warning", :bundler => "2" do
+ it "should print a deprecation warning", :bundler => "< 3" do
expect(deprecations).to include(
"The `--path` flag is deprecated because it relies on being " \
"remembered across bundler invocations, which bundler will no " \
@@ -118,7 +130,7 @@ RSpec.describe "major deprecations" do
)
end
- pending "should fail with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", :bundler => "3"
end
context "bundle check --path=" do
@@ -131,7 +143,7 @@ RSpec.describe "major deprecations" do
bundle "check --path=vendor/bundle", :raise_on_error => false
end
- it "should print a deprecation warning", :bundler => "2" do
+ it "should print a deprecation warning", :bundler => "< 3" do
expect(deprecations).to include(
"The `--path` flag is deprecated because it relies on being " \
"remembered across bundler invocations, which bundler will no " \
@@ -140,7 +152,7 @@ RSpec.describe "major deprecations" do
)
end
- pending "should fail with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", :bundler => "3"
end
context "bundle cache --all" do
@@ -153,7 +165,7 @@ RSpec.describe "major deprecations" do
bundle "cache --all", :raise_on_error => false
end
- it "should print a deprecation warning", :bundler => "2" do
+ it "should print a deprecation warning", :bundler => "< 3" do
expect(deprecations).to include(
"The `--all` flag is deprecated because it relies on being " \
"remembered across bundler invocations, which bundler will no " \
@@ -162,7 +174,29 @@ RSpec.describe "major deprecations" do
)
end
- pending "should fail with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", :bundler => "3"
+ end
+
+ context "bundle cache --path" do
+ before do
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ G
+
+ bundle "cache --path foo", :raise_on_error => false
+ end
+
+ it "should print a deprecation warning", :bundler => "< 3" do
+ expect(deprecations).to include(
+ "The `--path` flag is deprecated because its semantics are unclear. " \
+ "Use `bundle config cache_path` to configure the path of your cache of gems, " \
+ "and `bundle config path` to configure the path where your gems are installed, " \
+ "and stop using this flag"
+ )
+ end
+
+ pending "fails with a helpful error", :bundler => "3"
end
describe "bundle config" do
@@ -292,7 +326,7 @@ RSpec.describe "major deprecations" do
G
end
- it "should output a deprecation warning", :bundler => "2" do
+ it "should output a deprecation warning", :bundler => "< 3" do
expect(deprecations).to include("The --binstubs option will be removed in favor of `bundle binstubs --all`")
end
@@ -311,7 +345,7 @@ RSpec.describe "major deprecations" do
end
it "should print a proper warning, and use gems.rb" do
- create_file "gems.rb"
+ create_file "gems.rb", "source \"#{file_uri_for(gem_repo1)}\""
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
@@ -356,7 +390,7 @@ RSpec.describe "major deprecations" do
bundle "install #{flag_name} #{value}"
end
- it "should print a deprecation warning", :bundler => "2" do
+ it "should print a deprecation warning", :bundler => "< 3" do
expect(deprecations).to include(
"The `#{flag_name}` flag is deprecated because it relies on " \
"being remembered across bundler invocations, which bundler " \
@@ -365,7 +399,7 @@ RSpec.describe "major deprecations" do
)
end
- pending "should fail with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", :bundler => "3"
end
end
end
@@ -378,30 +412,98 @@ RSpec.describe "major deprecations" do
G
end
- it "shows a deprecation", :bundler => "2" do
+ it "shows a deprecation", :bundler => "< 3" do
+ expect(deprecations).to include(
+ "Your Gemfile contains multiple primary sources. " \
+ "Using `source` more than once without a block is a security risk, and " \
+ "may result in installing unexpected gems. To resolve this warning, use " \
+ "a block to indicate which gems should come from the secondary source."
+ )
+ end
+
+ it "doesn't show lockfile deprecations if there's a lockfile", :bundler => "< 3" do
+ bundle "install"
+
+ expect(deprecations).to include(
+ "Your Gemfile contains multiple primary sources. " \
+ "Using `source` more than once without a block is a security risk, and " \
+ "may result in installing unexpected gems. To resolve this warning, use " \
+ "a block to indicate which gems should come from the secondary source."
+ )
+ expect(deprecations).not_to include(
+ "Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. " \
+ "Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure."
+ )
+ bundle "config set --local frozen true"
+ bundle "install"
+
expect(deprecations).to include(
"Your Gemfile contains multiple primary sources. " \
"Using `source` more than once without a block is a security risk, and " \
"may result in installing unexpected gems. To resolve this warning, use " \
- "a block to indicate which gems should come from the secondary source. " \
- "To upgrade this warning to an error, run `bundle config set --local " \
- "disable_multisource true`."
+ "a block to indicate which gems should come from the secondary source."
)
+ expect(deprecations).not_to include(
+ "Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. " \
+ "Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure."
+ )
+ end
+
+ pending "fails with a helpful error", :bundler => "3"
+ end
+
+ context "bundle install in frozen mode with a lockfile with a single rubygems section with multiple remotes" do
+ before do
+ build_repo gem_repo3 do
+ build_gem "rack", "0.9.1"
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ source "#{file_uri_for(gem_repo3)}" do
+ gem 'rack'
+ end
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ remote: #{file_uri_for(gem_repo3)}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "config set --local frozen true"
+ end
+
+ it "shows a deprecation", :bundler => "< 3" do
+ bundle "install"
+
+ expect(deprecations).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure.")
end
- pending "should fail with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", :bundler => "3"
end
context "when Bundler.setup is run in a ruby script" do
before do
- create_file "gems.rb"
+ create_file "gems.rb", "source \"#{file_uri_for(gem_repo1)}\""
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", :group => :test
G
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require '#{entrypoint}'
Bundler.setup
Bundler.setup
@@ -422,14 +524,14 @@ RSpec.describe "major deprecations" do
RUBY
end
- it "should print a capistrano deprecation warning", :bundler => "2" do
+ it "should print a capistrano deprecation warning", :bundler => "< 3" do
expect(deprecations).to include("Bundler no longer integrates " \
"with Capistrano, but Capistrano provides " \
"its own integration with Bundler via the " \
"capistrano-bundler gem. Use it instead.")
end
- pending "should fail with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", :bundler => "3"
end
describe Bundler::Dsl do
@@ -439,7 +541,7 @@ RSpec.describe "major deprecations" do
end
context "with github gems" do
- it "does not warn about removal", :bundler => "2" do
+ it "does not warn about removal", :bundler => "< 3" do
expect(Bundler.ui).not_to receive(:warn)
subject.gem("sparks", :github => "indirect/sparks")
github_uri = "https://github.com/indirect/sparks.git"
@@ -461,7 +563,7 @@ The :github git source is deprecated, and will be removed in the future. Change
end
context "with bitbucket gems" do
- it "does not warn about removal", :bundler => "2" do
+ it "does not warn about removal", :bundler => "< 3" do
expect(Bundler.ui).not_to receive(:warn)
subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails")
end
@@ -483,7 +585,7 @@ The :bitbucket git source is deprecated, and will be removed in the future. Add
end
context "with gist gems" do
- it "does not warn about removal", :bundler => "2" do
+ it "does not warn about removal", :bundler => "< 3" do
expect(Bundler.ui).not_to receive(:warn)
subject.gem("not-really-a-gem", :gist => "1234")
end
@@ -509,73 +611,32 @@ The :gist git source is deprecated, and will be removed in the future. Add this
G
end
- context "without flags" do
- before do
- bundle :show
- end
-
- it "prints a deprecation warning recommending `bundle list`", :bundler => "2" do
- expect(deprecations).to include("use `bundle list` instead of `bundle show`")
- end
-
- pending "fails with a helpful message", :bundler => "3"
- end
-
context "with --outdated flag" do
before do
bundle "show --outdated"
end
- it "prints a deprecation warning informing about its removal", :bundler => "2" do
+ it "prints a deprecation warning informing about its removal", :bundler => "< 3" do
expect(deprecations).to include("the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement")
end
pending "fails with a helpful message", :bundler => "3"
end
+ end
- context "with --verbose flag" do
- before do
- bundle "show --verbose"
- end
-
- it "prints a deprecation warning informing about its removal", :bundler => "2" do
- expect(deprecations).to include("the `--verbose` flag to `bundle show` was undocumented and will be removed without replacement")
- end
-
- pending "fails with a helpful message", :bundler => "3"
- end
-
- context "with a gem argument" do
- before do
- bundle "show rack"
- end
-
- it "prints a deprecation warning recommending `bundle info`", :bundler => "2" do
- expect(deprecations).to include("use `bundle info rack` instead of `bundle show rack`")
- end
-
- pending "fails with a helpful message", :bundler => "3"
- end
-
- context "with the --paths option" do
- before do
- bundle "show --paths"
- end
-
- it "prints a deprecation warning recommending `bundle list`", :bundler => "2" do
- expect(deprecations).to include("use `bundle list` instead of `bundle show --paths`")
- end
-
- pending "fails with a helpful message", :bundler => "3"
+ context "bundle remove" do
+ before do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ G
end
- context "with a gem argument and the --paths option" do
- before do
- bundle "show rack --paths"
- end
+ context "with --install" do
+ it "shows a deprecation warning", :bundler => "< 3" do
+ bundle "remove rack --install"
- it "prints deprecation warning recommending `bundle info`", :bundler => "2" do
- expect(deprecations).to include("use `bundle info rack --path` instead of `bundle show rack --paths`")
+ expect(err).to include "[DEPRECATED] The `--install` flag has been deprecated. `bundle install` is triggered by default."
end
pending "fails with a helpful message", :bundler => "3"
@@ -587,7 +648,7 @@ The :gist git source is deprecated, and will be removed in the future. Add this
bundle "console", :raise_on_error => false
end
- it "prints a deprecation warning", :bundler => "2" do
+ it "prints a deprecation warning", :bundler => "< 3" do
expect(deprecations).to include \
"bundle console will be replaced by `bin/console` generated by `bundle gem <name>`"
end
@@ -599,14 +660,60 @@ The :gist git source is deprecated, and will be removed in the future. Add this
before do
graphviz_version = RUBY_VERSION >= "2.4" ? "1.2.5" : "1.2.4"
realworld_system_gems "ruby-graphviz --version #{graphviz_version}"
- create_file "gems.rb"
+ create_file "gems.rb", "source \"#{file_uri_for(gem_repo1)}\""
bundle "viz"
end
- it "prints a deprecation warning", :bundler => "2" do
- expect(deprecations).to include "The `viz` command has been moved to the `bundle-viz` gem, see https://github.com/bundler/bundler-viz"
+ it "prints a deprecation warning", :bundler => "< 3" do
+ expect(deprecations).to include "The `viz` command has been renamed to `graph` and moved to a plugin. See https://github.com/rubygems/bundler-graph"
end
pending "fails with a helpful message", :bundler => "3"
end
+
+ describe "deprecating rubocop", :readline do
+ context "bundle gem --rubocop" do
+ before do
+ bundle "gem my_new_gem --rubocop", :raise_on_error => false
+ end
+
+ it "prints a deprecation warning", :bundler => "< 3" do
+ expect(deprecations).to include \
+ "--rubocop is deprecated, use --linter=rubocop"
+ end
+ end
+
+ context "bundle gem --no-rubocop" do
+ before do
+ bundle "gem my_new_gem --no-rubocop", :raise_on_error => false
+ end
+
+ it "prints a deprecation warning", :bundler => "< 3" do
+ expect(deprecations).to include \
+ "--no-rubocop is deprecated, use --linter"
+ end
+ end
+
+ context "bundle gem with gem.rubocop set to true" do
+ before do
+ bundle "gem my_new_gem", :env => { "BUNDLE_GEM__RUBOCOP" => "true" }, :raise_on_error => false
+ end
+
+ it "prints a deprecation warning", :bundler => "< 3" do
+ expect(deprecations).to include \
+ "config gem.rubocop is deprecated; we've updated your config to use gem.linter instead"
+ end
+ end
+
+ context "bundle gem with gem.rubocop set to false" do
+ before do
+ bundle "gem my_new_gem", :env => { "BUNDLE_GEM__RUBOCOP" => "false" }, :raise_on_error => false
+ end
+
+ it "prints a deprecation warning", :bundler => "< 3" do
+ expect(deprecations).to include \
+ "config gem.rubocop is deprecated; we've updated your config to use gem.linter instead"
+ end
+ end
+ end
end
diff --git a/spec/bundler/other/platform_spec.rb b/spec/bundler/other/platform_spec.rb
index eb3539b412..5693d6bce6 100644
--- a/spec/bundler/other/platform_spec.rb
+++ b/spec/bundler/other/platform_spec.rb
@@ -213,11 +213,13 @@ G
it "handles when there is a locked requirement" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
ruby "< 1.8.7"
G
lockfile <<-L
GEM
+ remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
@@ -238,6 +240,7 @@ G
it "handles when there is a requirement in the gemfile" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
ruby ">= 1.8.7"
G
@@ -247,6 +250,7 @@ G
it "handles when there are multiple requirements in the gemfile" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
ruby ">= 1.8.7", "< 2.0.0"
G
@@ -685,6 +689,7 @@ G
it "copies the .gem file to vendor/cache when ruby version matches" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack'
#{ruby_version_correct}
@@ -708,6 +713,7 @@ G
it "fails if the ruby version doesn't match" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack'
#{ruby_version_incorrect}
@@ -719,6 +725,7 @@ G
it "fails if the engine doesn't match" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack'
#{engine_incorrect}
@@ -730,6 +737,7 @@ G
it "fails if the engine version doesn't match", :jruby do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack'
#{engine_version_incorrect}
@@ -762,6 +770,7 @@ G
it "copies the .gem file to vendor/cache when ruby version matches" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack'
#{ruby_version_correct}
@@ -785,6 +794,7 @@ G
it "fails if the ruby version doesn't match" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack'
#{ruby_version_incorrect}
@@ -796,6 +806,7 @@ G
it "fails if the engine doesn't match" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack'
#{engine_incorrect}
@@ -807,6 +818,7 @@ G
it "fails if the engine version doesn't match", :jruby do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'rack'
#{engine_version_incorrect}
@@ -837,6 +849,7 @@ G
it "activates the correct gem when ruby version matches" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
#{ruby_version_correct}
@@ -849,6 +862,7 @@ G
it "activates the correct gem when ruby version matches any engine", :jruby do
system_gems "rack-1.0.0", "rack-0.9.1", :path => default_bundle_path
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
#{ruby_version_correct_engineless}
@@ -860,6 +874,7 @@ G
it "fails when the ruby version doesn't match" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
#{ruby_version_incorrect}
@@ -871,6 +886,7 @@ G
it "fails when the engine doesn't match" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
#{engine_incorrect}
diff --git a/spec/bundler/plugins/command_spec.rb b/spec/bundler/plugins/command_spec.rb
index 4567a39081..3a7adf4b48 100644
--- a/spec/bundler/plugins/command_spec.rb
+++ b/spec/bundler/plugins/command_spec.rb
@@ -69,12 +69,10 @@ RSpec.describe "command plugins" do
end
end
- bundle "plugin install copycat --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install copycat --source #{file_uri_for(gem_repo2)}", :raise_on_error => false
expect(out).not_to include("Installed plugin copycat")
- expect(err).to include("Failed to install the following plugins: `copycat`")
-
- expect(err).to include("Command(s) `mahcommand` declared by copycat are already registered.")
+ expect(err).to include("Failed to install plugin `copycat`, due to Bundler::Plugin::Index::CommandConflict (Command(s) `mahcommand` declared by copycat are already registered.)")
end
end
diff --git a/spec/bundler/plugins/install_spec.rb b/spec/bundler/plugins/install_spec.rb
index 370973ad1a..009516260a 100644
--- a/spec/bundler/plugins/install_spec.rb
+++ b/spec/bundler/plugins/install_spec.rb
@@ -22,6 +22,19 @@ RSpec.describe "bundler plugin install" do
plugin_should_be_installed("foo")
end
+ it "installs from sources configured as Gem.sources without any flags" do
+ bundle "plugin install foo", :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s }
+
+ expect(out).to include("Installed plugin foo")
+ plugin_should_be_installed("foo")
+ end
+
+ it "shows help when --help flag is given" do
+ bundle "plugin install --help"
+
+ expect(out).to include("bundle plugin install PLUGINS # Install the plugin from the source")
+ end
+
context "plugin is already installed" do
before do
bundle "plugin install foo --source #{file_uri_for(gem_repo2)}"
@@ -56,6 +69,21 @@ RSpec.describe "bundler plugin install" do
plugin_should_be_installed("foo", "kung-foo")
end
+ it "installs the latest version if not installed" do
+ update_repo2 do
+ build_plugin "foo", "1.1"
+ end
+
+ bundle "plugin install foo --version 1.0 --source #{file_uri_for(gem_repo2)} --verbose"
+ expect(out).to include("Installing foo 1.0")
+
+ bundle "plugin install foo --source #{file_uri_for(gem_repo2)} --verbose"
+ expect(out).to include("Installing foo 1.1")
+
+ bundle "plugin install foo --source #{file_uri_for(gem_repo2)} --verbose"
+ expect(out).to include("Using foo 1.1")
+ end
+
it "works with different load paths" do
build_repo2 do
build_plugin "testing" do |s|
@@ -96,9 +124,9 @@ RSpec.describe "bundler plugin install" do
build_gem "charlie"
end
- bundle "plugin install charlie --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install charlie --source #{file_uri_for(gem_repo2)}", :raise_on_error => false
- expect(err).to include("Failed to install the following plugins: `charlie`. The underlying error was: plugins.rb was not found")
+ expect(err).to include("Failed to install plugin `charlie`, due to Bundler::Plugin::MalformattedPlugin (plugins.rb was not found in the plugin.)")
expect(global_plugin_gem("charlie-1.0")).not_to be_directory
@@ -115,7 +143,7 @@ RSpec.describe "bundler plugin install" do
end
end
- bundle "plugin install chaplin --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install chaplin --source #{file_uri_for(gem_repo2)}", :raise_on_error => false
expect(global_plugin_gem("chaplin-1.0")).not_to be_directory
@@ -201,6 +229,7 @@ RSpec.describe "bundler plugin install" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
plugin 'ga-plugin', :git => "#{lib_path("ga-plugin-1.0")}"
G
@@ -214,6 +243,7 @@ RSpec.describe "bundler plugin install" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
plugin 'ga-plugin', :path => "#{lib_path("ga-plugin-1.0")}"
G
@@ -228,7 +258,7 @@ RSpec.describe "bundler plugin install" do
gem 'rack', "1.0.0"
G
- bundle "config --local deployment true"
+ bundle "config set --local deployment true"
install_gemfile <<-G
source '#{file_uri_for(gem_repo2)}'
plugin 'foo'
diff --git a/spec/bundler/plugins/source/example_spec.rb b/spec/bundler/plugins/source/example_spec.rb
index 03a377ac93..7d098997ec 100644
--- a/spec/bundler/plugins/source/example_spec.rb
+++ b/spec/bundler/plugins/source/example_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe "real source plugins" do
it "writes to lock file" do
bundle "install"
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PLUGIN SOURCE
remote: #{lib_path("a-path-gem-1.0")}
type: mpath
@@ -132,7 +132,7 @@ RSpec.describe "real source plugins" do
end
it "copies repository to vendor cache and uses it even when installed with `path` configured" do
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle :install
bundle "config set cache_all true"
bundle :cache
@@ -144,7 +144,7 @@ RSpec.describe "real source plugins" do
end
it "bundler package copies repository to vendor cache" do
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle :install
bundle "config set cache_all true"
bundle :cache
@@ -342,7 +342,7 @@ RSpec.describe "real source plugins" do
revision = revision_for(lib_path("ma-gitp-gem-1.0"))
bundle "install"
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PLUGIN SOURCE
remote: #{file_uri_for(lib_path("ma-gitp-gem-1.0"))}
type: gitp
diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb
index 2b1e28fa30..62f3722a39 100644
--- a/spec/bundler/quality_spec.rb
+++ b/spec/bundler/quality_spec.rb
@@ -3,25 +3,6 @@
require "set"
RSpec.describe "The library itself" do
- def check_for_debugging_mechanisms(filename)
- debugging_mechanisms_regex = /
- (binding\.pry)|
- (debugger)|
- (sleep\s*\(?\d+)|
- (fit\s*\(?("|\w))
- /x
-
- failing_lines = []
- each_line(filename) do |line, number|
- if line =~ debugging_mechanisms_regex && !line.end_with?("# ignore quality_spec\n")
- failing_lines << number + 1
- end
- end
-
- return if failing_lines.empty?
- "#{filename} has debugging mechanisms (like binding.pry, sleep, debugger, rspec focusing, etc.) on lines #{failing_lines.join(", ")}"
- end
-
def check_for_git_merge_conflicts(filename)
merge_conflicts_regex = /
<<<<<<<|
@@ -125,16 +106,6 @@ RSpec.describe "The library itself" do
expect(error_messages.compact).to be_well_formed
end
- it "does not include any leftover debugging or development mechanisms" do
- exempt = %r{quality_spec.rb|support/helpers|vcr_cassettes|\.md|\.ronn|index\.txt|\.5|\.1}
- error_messages = []
- tracked_files.each do |filename|
- next if filename =~ exempt
- error_messages << check_for_debugging_mechanisms(filename)
- end
- expect(error_messages.compact).to be_well_formed
- end
-
it "does not include any unresolved merge conflicts" do
error_messages = []
exempt = %r{lock/lockfile_spec|quality_spec|vcr_cassettes|\.ronn|lockfile_parser\.rb}
@@ -169,11 +140,17 @@ RSpec.describe "The library itself" do
it "documents all used settings" do
exemptions = %w[
- deployment_means_frozen
forget_cli_options
+ gem.changelog
+ gem.ci
gem.coc
+ gem.linter
gem.mit
+ gem.rubocop
+ gem.test
+ git.allow_insecure
inline
+ trust-policy
use_gem_version_promoter_for_major_updates
]
@@ -183,6 +160,7 @@ RSpec.describe "The library itself" do
Bundler::Settings::BOOL_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::BOOL_KEYS" }
Bundler::Settings::NUMBER_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::NUMBER_KEYS" }
Bundler::Settings::ARRAY_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::ARRAY_KEYS" }
+ Bundler::Settings::STRING_KEYS.each {|k| all_settings[k] << "in Bundler::Settings::STRING_KEYS" }
key_pattern = /([a-z\._-]+)/i
lib_tracked_files.each do |filename|
@@ -216,7 +194,7 @@ RSpec.describe "The library itself" do
end
it "ships the correct set of files" do
- git_list = git_ls_files(ruby_core? ? "lib/bundler lib/bundler.rb man/bundle* man/gemfile* libexec/bundle*" : "lib man exe CHANGELOG.md LICENSE.md README.md bundler.gemspec")
+ git_list = git_ls_files(ruby_core? ? "lib/bundler lib/bundler.rb libexec/bundle*" : "lib exe CHANGELOG.md LICENSE.md README.md bundler.gemspec")
gem_list = loaded_gemspec.files
diff --git a/spec/bundler/realworld/double_check_spec.rb b/spec/bundler/realworld/double_check_spec.rb
index 07697f080e..d7f28d10bb 100644
--- a/spec/bundler/realworld/double_check_spec.rb
+++ b/spec/bundler/realworld/double_check_spec.rb
@@ -25,9 +25,9 @@ RSpec.describe "double checking sources", :realworld => true do
RUBY
cmd = <<-RUBY
- require "#{lib_dir}/bundler"
+ require "#{entrypoint}"
require "#{spec_dir}/support/artifice/vcr"
- require "#{lib_dir}/bundler/inline"
+ require "#{entrypoint}/inline"
gemfile(true) do
source "https://rubygems.org"
gem "rails", path: "."
diff --git a/spec/bundler/realworld/edgecases_spec.rb b/spec/bundler/realworld/edgecases_spec.rb
index 1925f76c06..df5eeda9fe 100644
--- a/spec/bundler/realworld/edgecases_spec.rb
+++ b/spec/bundler/realworld/edgecases_spec.rb
@@ -4,9 +4,9 @@ RSpec.describe "real world edgecases", :realworld => true do
def rubygems_version(name, requirement)
ruby <<-RUBY
require "#{spec_dir}/support/artifice/vcr"
- require "#{lib_dir}/bundler"
- require "#{lib_dir}/bundler/source/rubygems/remote"
- require "#{lib_dir}/bundler/fetcher"
+ require "#{entrypoint}"
+ require "#{entrypoint}/source/rubygems/remote"
+ require "#{entrypoint}/fetcher"
rubygem = Bundler.ui.silence do
source = Bundler::Source::Rubygems::Remote.new(Bundler::URI("https://rubygems.org"))
fetcher = Bundler::Fetcher.new(source)
@@ -196,167 +196,26 @@ RSpec.describe "real world edgecases", :realworld => true do
expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0"))
end
- # https://github.com/rubygems/bundler/issues/1500
- it "does not fail install because of gem plugins" do
- realworld_system_gems("open_gem --version 1.4.2", "rake --version 0.9.2")
- gemfile <<-G
- source "https://rubygems.org"
-
- gem 'rack', '1.0.1'
- G
-
- bundle "config set --local path vendor/bundle"
- bundle :install
- expect(err).not_to include("Could not find rake")
- expect(err).to be_empty
- end
-
- it "checks out git repos when the lockfile is corrupted" do
- gemfile <<-G
- source "https://rubygems.org"
- git_source(:github) {|repo| "https://github.com/\#{repo}.git" }
-
- gem 'activerecord', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
- gem 'activesupport', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
- gem 'actionpack', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
- G
-
- lockfile <<-L
- GIT
- remote: https://github.com/carlhuda/rails-bundler-test.git
- revision: 369e28a87419565f1940815219ea9200474589d4
- branch: master
- specs:
- actionpack (3.2.2)
- activemodel (= 3.2.2)
- activesupport (= 3.2.2)
- builder (~> 3.0.0)
- erubis (~> 2.7.0)
- journey (~> 1.0.1)
- rack (~> 1.4.0)
- rack-cache (~> 1.2)
- rack-test (~> 0.6.1)
- sprockets (~> 2.1.2)
- activemodel (3.2.2)
- activesupport (= 3.2.2)
- builder (~> 3.0.0)
- activerecord (3.2.2)
- activemodel (= 3.2.2)
- activesupport (= 3.2.2)
- arel (~> 3.0.2)
- tzinfo (~> 0.3.29)
- activesupport (3.2.2)
- i18n (~> 0.6)
- multi_json (~> 1.0)
-
- GIT
- remote: https://github.com/carlhuda/rails-bundler-test.git
- revision: 369e28a87419565f1940815219ea9200474589d4
- branch: master
- specs:
- actionpack (3.2.2)
- activemodel (= 3.2.2)
- activesupport (= 3.2.2)
- builder (~> 3.0.0)
- erubis (~> 2.7.0)
- journey (~> 1.0.1)
- rack (~> 1.4.0)
- rack-cache (~> 1.2)
- rack-test (~> 0.6.1)
- sprockets (~> 2.1.2)
- activemodel (3.2.2)
- activesupport (= 3.2.2)
- builder (~> 3.0.0)
- activerecord (3.2.2)
- activemodel (= 3.2.2)
- activesupport (= 3.2.2)
- arel (~> 3.0.2)
- tzinfo (~> 0.3.29)
- activesupport (3.2.2)
- i18n (~> 0.6)
- multi_json (~> 1.0)
-
- GIT
- remote: https://github.com/carlhuda/rails-bundler-test.git
- revision: 369e28a87419565f1940815219ea9200474589d4
- branch: master
- specs:
- actionpack (3.2.2)
- activemodel (= 3.2.2)
- activesupport (= 3.2.2)
- builder (~> 3.0.0)
- erubis (~> 2.7.0)
- journey (~> 1.0.1)
- rack (~> 1.4.0)
- rack-cache (~> 1.2)
- rack-test (~> 0.6.1)
- sprockets (~> 2.1.2)
- activemodel (3.2.2)
- activesupport (= 3.2.2)
- builder (~> 3.0.0)
- activerecord (3.2.2)
- activemodel (= 3.2.2)
- activesupport (= 3.2.2)
- arel (~> 3.0.2)
- tzinfo (~> 0.3.29)
- activesupport (3.2.2)
- i18n (~> 0.6)
- multi_json (~> 1.0)
-
- GEM
- remote: https://rubygems.org/
- specs:
- arel (3.0.2)
- builder (3.0.0)
- erubis (2.7.0)
- hike (1.2.1)
- i18n (0.6.0)
- journey (1.0.3)
- multi_json (1.1.0)
- rack (1.4.1)
- rack-cache (1.2)
- rack (>= 0.4)
- rack-test (0.6.1)
- rack (>= 1.0)
- sprockets (2.1.2)
- hike (~> 1.2)
- rack (~> 1.0)
- tilt (~> 1.1, != 1.3.0)
- tilt (1.3.3)
- tzinfo (0.3.32)
-
- PLATFORMS
- ruby
-
- DEPENDENCIES
- actionpack!
- activerecord!
- activesupport!
- L
-
- bundle :lock
- expect(err).to be_empty
- end
-
it "outputs a helpful error message when gems have invalid gemspecs" do
- install_gemfile <<-G, :standalone => true, :raise_on_error => false
+ install_gemfile <<-G, :standalone => true, :raise_on_error => false, :env => { "BUNDLE_FORCE_RUBY_PLATFORM" => "1" }
source 'https://rubygems.org'
gem "resque-scheduler", "2.2.0"
gem "redis-namespace", "1.6.0" # for a consistent resolution including ruby 2.3.0
+ gem "ruby2_keywords", "0.0.5"
G
expect(err).to include("You have one or more invalid gemspecs that need to be fixed.")
expect(err).to include("resque-scheduler 2.2.0 has an invalid gemspec")
end
it "doesn't hang on big gemfile" do
- skip "Only for ruby 2.7.2" if RUBY_VERSION != "2.7.2"
+ skip "Only for ruby 2.7.3" if RUBY_VERSION != "2.7.3" || RUBY_PLATFORM =~ /darwin/
gemfile <<~G
# frozen_string_literal: true
source "https://rubygems.org"
- ruby "2.7.2"
+ ruby "2.7.3"
gem "rails"
gem "pg", ">= 0.18", "< 2.0"
@@ -461,7 +320,7 @@ RSpec.describe "real world edgecases", :realworld => true do
end
it "doesn't hang on tricky gemfile" do
- skip "Only for ruby 2.7.2" if RUBY_VERSION != "2.7.2"
+ skip "Only for ruby 2.7.3" if RUBY_VERSION != "2.7.3" || RUBY_PLATFORM =~ /darwin/
gemfile <<~G
source 'https://rubygems.org'
@@ -487,7 +346,7 @@ RSpec.describe "real world edgecases", :realworld => true do
end
it "doesn't hang on nix gemfile" do
- skip "Only for ruby 3.0.0" if RUBY_VERSION != "3.0.0"
+ skip "Only for ruby 3.0.1" if RUBY_VERSION != "3.0.1" || RUBY_PLATFORM =~ /darwin/
gemfile <<~G
source "https://rubygems.org" do
diff --git a/spec/bundler/realworld/ffi_spec.rb b/spec/bundler/realworld/ffi_spec.rb
new file mode 100644
index 0000000000..083ea38901
--- /dev/null
+++ b/spec/bundler/realworld/ffi_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+RSpec.describe "loading dinamically linked library on a bundle exec context", :realworld => true do
+ it "passes ENV right after argv in memory" do
+ create_file "foo.rb", <<~RUBY
+ require 'ffi'
+
+ module FOO
+ extend FFI::Library
+ ffi_lib './libfoo.so'
+
+ attach_function :Hello, [], :void
+ end
+
+ FOO.Hello()
+ RUBY
+
+ create_file "libfoo.c", <<~'C'
+ #include <stdio.h>
+
+ static int foo_init(int argc, char** argv, char** envp) {
+ if (argv[argc+1] == NULL) {
+ printf("FAIL\n");
+ } else {
+ printf("OK\n");
+ }
+
+ return 0;
+ }
+
+ #if defined(__APPLE__) && defined(__MACH__)
+ __attribute__((section("__DATA,__mod_init_func"), used, aligned(sizeof(void*))))
+ #else
+ __attribute__((section(".init_array")))
+ #endif
+ static void *ctr = &foo_init;
+
+ extern char** environ;
+
+ void Hello() {
+ return;
+ }
+ C
+
+ sys_exec "gcc -g -o libfoo.so -shared -fpic libfoo.c"
+
+ install_gemfile <<-G
+ source "https://rubygems.org"
+
+ gem 'ffi'
+ G
+
+ bundle "exec ruby foo.rb"
+
+ expect(out).to eq("OK")
+ end
+end
diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock
index 6945be3ed2..05bcb877db 100644
--- a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock
+++ b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock
@@ -6,7 +6,7 @@ PATH
GEM
remote: https://rubygems.org/
specs:
- jruby-jars (9.2.14.0)
+ jruby-jars (9.2.16.0)
jruby-rack (1.1.21)
rake (13.0.1)
rubyzip (1.3.0)
@@ -19,6 +19,7 @@ GEM
PLATFORMS
java
ruby
+ universal-java-11
DEPENDENCIES
demo!
@@ -26,4 +27,4 @@ DEPENDENCIES
warbler (~> 2.0)
BUNDLED WITH
- 2.2.0.rc.2
+ 2.3.0.dev
diff --git a/spec/bundler/realworld/mirror_probe_spec.rb b/spec/bundler/realworld/mirror_probe_spec.rb
index a2b5c89150..84d6a9c782 100644
--- a/spec/bundler/realworld/mirror_probe_spec.rb
+++ b/spec/bundler/realworld/mirror_probe_spec.rb
@@ -74,10 +74,10 @@ RSpec.describe "fetching dependencies with a not available mirror", :realworld =
bundle :install, :artifice => nil, :raise_on_error => false
expect(out).to include("Fetching source index from #{mirror}")
- expect(err).to include("Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>")
- expect(err).to include("Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>")
- expect(err).to include("Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>")
- expect(err).to include("Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>")
+ expect(err).to include("Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2)")
+ expect(err).to include("Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2)")
+ expect(err).to include("Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2)")
+ expect(err).to include("Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2)")
end
it "prints each error and warning on a new line" do
@@ -89,12 +89,7 @@ RSpec.describe "fetching dependencies with a not available mirror", :realworld =
bundle :install, :artifice => nil, :raise_on_error => false
expect(out).to include "Fetching source index from #{mirror}/"
- expect(err).to include <<-EOS.strip
-Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>
-Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>
-Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>
-Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>
- EOS
+ expect(err.split("\n").count).to eq(4)
end
end
@@ -112,10 +107,10 @@ Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUS
bundle :install, :artifice => nil, :raise_on_error => false
expect(out).to include("Fetching source index from #{mirror}")
- expect(err).to include("Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>")
- expect(err).to include("Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>")
- expect(err).to include("Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>")
- expect(err).to include("Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2) for \"#{host}\" port #{@mirror_port}) (#{mirror}/specs.4.8.gz)>")
+ expect(err).to include("Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2)")
+ expect(err).to include("Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2)")
+ expect(err).to include("Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2)")
+ expect(err).to include("Could not fetch specs from #{mirror}/ due to underlying error <Errno::ECONNREFUSED: Failed to open TCP connection to #{host}:#{@mirror_port} (Connection refused - connect(2)")
end
end
diff --git a/spec/bundler/realworld/slow_perf_spec.rb b/spec/bundler/realworld/slow_perf_spec.rb
index 518da2800b..aced5a1641 100644
--- a/spec/bundler/realworld/slow_perf_spec.rb
+++ b/spec/bundler/realworld/slow_perf_spec.rb
@@ -4,17 +4,19 @@ require "spec_helper"
RSpec.describe "bundle install with complex dependencies", :realworld => true do
it "resolves quickly" do
- start_time = Time.now
-
- install_gemfile <<-G
+ gemfile <<-G
source 'https://rubygems.org'
gem "actionmailer"
gem "mongoid", ">= 0.10.2"
G
+ start_time = Time.now
+
+ bundle "lock"
+
duration = Time.now - start_time
- expect(duration.to_f).to be < 120 # seconds
+ expect(duration.to_f).to be < 12 # seconds
end
end
diff --git a/spec/bundler/resolver/platform_spec.rb b/spec/bundler/resolver/platform_spec.rb
index 7169ba4b95..bc4081f8b5 100644
--- a/spec/bundler/resolver/platform_spec.rb
+++ b/spec/bundler/resolver/platform_spec.rb
@@ -28,6 +28,23 @@ RSpec.describe "Resolving platform craziness" do
end
end
+ it "resolves multiplatform gems with redundant platforms correctly" do
+ @index = build_index do
+ gem "zookeeper", "1.4.11"
+ gem "zookeeper", "1.4.11", "java" do
+ dep "slyphon-log4j", "= 1.2.15"
+ dep "slyphon-zookeeper_jar", "= 3.3.5"
+ end
+ gem "slyphon-log4j", "1.2.15"
+ gem "slyphon-zookeeper_jar", "3.3.5", "java"
+ end
+
+ dep "zookeeper"
+ platforms "java", "ruby", "universal-java-11"
+
+ should_resolve_as %w[zookeeper-1.4.11 zookeeper-1.4.11-java slyphon-log4j-1.2.15 slyphon-zookeeper_jar-3.3.5-java]
+ end
+
it "takes the latest ruby gem, even if an older platform specific version is available" do
@index = build_index do
gem "foo", "1.0.0"
diff --git a/spec/bundler/runtime/executable_spec.rb b/spec/bundler/runtime/executable_spec.rb
index e39338e425..a11f547648 100644
--- a/spec/bundler/runtime/executable_spec.rb
+++ b/spec/bundler/runtime/executable_spec.rb
@@ -63,6 +63,7 @@ RSpec.describe "Running bin/* commands" do
end
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack", :path => "#{lib_path("rack")}"
G
diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb
index b46da20096..3b5ede41ee 100644
--- a/spec/bundler/runtime/inline_spec.rb
+++ b/spec/bundler/runtime/inline_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe "bundler/inline#gemfile" do
def script(code, options = {})
- requires = ["#{lib_dir}/bundler/inline"]
+ requires = ["#{entrypoint}/inline"]
requires.unshift "#{spec_dir}/support/artifice/" + options.delete(:artifice) if options.key?(:artifice)
requires = requires.map {|r| "require '#{r}'" }.join("\n")
ruby("#{requires}\n\n" + code, options)
@@ -46,10 +46,9 @@ RSpec.describe "bundler/inline#gemfile" do
end
it "requires the gems" do
- skip "gems not found" if Gem.win_platform?
-
script <<-RUBY
gemfile do
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "two"
end
@@ -60,6 +59,7 @@ RSpec.describe "bundler/inline#gemfile" do
script <<-RUBY, :raise_on_error => false
gemfile do
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "eleven"
end
@@ -94,10 +94,8 @@ RSpec.describe "bundler/inline#gemfile" do
end
it "lets me use my own ui object" do
- skip "prints just one CONFIRMED" if Gem.win_platform?
-
script <<-RUBY, :artifice => "endpoint"
- require '#{lib_dir}/bundler'
+ require '#{entrypoint}'
class MyBundlerUI < Bundler::UI::Silent
def confirm(msg, newline = nil)
puts "CONFIRMED!"
@@ -114,7 +112,7 @@ RSpec.describe "bundler/inline#gemfile" do
it "has an option for quiet installation" do
script <<-RUBY, :artifice => "endpoint"
- require '#{lib_dir}/bundler/inline'
+ require '#{entrypoint}/inline'
gemfile(true, :quiet => true) do
source "https://notaserver.com"
@@ -140,9 +138,10 @@ RSpec.describe "bundler/inline#gemfile" do
it "does not mutate the option argument" do
script <<-RUBY
- require '#{lib_dir}/bundler'
+ require '#{entrypoint}'
options = { :ui => Bundler::UI::Shell.new }
gemfile(false, options) do
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "two"
end
@@ -172,6 +171,7 @@ RSpec.describe "bundler/inline#gemfile" do
baz_ref = build_git("baz", "2.0.0").ref_for("HEAD")
script <<-RUBY
gemfile do
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => #{lib_path("foo-1.0.0").to_s.dump}
gem "baz", :git => #{lib_path("baz-2.0.0").to_s.dump}, :ref => #{baz_ref.dump}
end
@@ -188,12 +188,14 @@ RSpec.describe "bundler/inline#gemfile" do
script <<-RUBY
gemfile do
path "#{lib_path}" do
+ source "#{file_uri_for(gem_repo1)}"
gem "two"
end
end
gemfile do
path "#{lib_path}" do
+ source "#{file_uri_for(gem_repo1)}"
gem "four"
end
end
@@ -222,7 +224,7 @@ RSpec.describe "bundler/inline#gemfile" do
rake
BUNDLED WITH
- 1.13.6
+ #{Bundler::VERSION}
G
script <<-RUBY
@@ -250,6 +252,19 @@ RSpec.describe "bundler/inline#gemfile" do
expect(last_command.stderr).to be_empty
end
+ it "installs inline gems when deployment is set" do
+ script <<-RUBY, :env => { "BUNDLE_DEPLOYMENT" => "true" }
+ gemfile do
+ source "#{file_uri_for(gem_repo1)}"
+ gem "rack"
+ end
+
+ puts RACK
+ RUBY
+
+ expect(last_command.stderr).to be_empty
+ end
+
it "installs inline gems when BUNDLE_GEMFILE is set to an empty string" do
ENV["BUNDLE_GEMFILE"] = ""
@@ -358,6 +373,7 @@ RSpec.describe "bundler/inline#gemfile" do
script <<-RUBY, :dir => tmp("path_without_gemfile")
gemfile do
+ source "#{file_uri_for(gem_repo2)}"
path "#{lib_path}" do
gem "foo", require: false
end
@@ -374,7 +390,7 @@ RSpec.describe "bundler/inline#gemfile" do
dependency_installer_loads_fileutils = ruby "require 'rubygems/dependency_installer'; puts $LOADED_FEATURES.grep(/fileutils/)", :raise_on_error => false
skip "does not work if rubygems/dependency_installer loads fileutils, which happens until rubygems 3.2.0" unless dependency_installer_loads_fileutils.empty?
- skip "does not work on ruby 3.0 because it changes the path to look for default gems, tsort is a default gem there, and we can't install it either like we do with fiddle because it doesn't yet exist" unless RUBY_VERSION < "3.0.0"
+ skip "pathname does not install cleanly on this ruby" if RUBY_VERSION < "2.7.0"
Dir.mkdir tmp("path_without_gemfile")
@@ -383,6 +399,8 @@ RSpec.describe "bundler/inline#gemfile" do
realworld_system_gems "fileutils --version 1.4.1"
+ realworld_system_gems "pathname --version 0.2.0"
+
realworld_system_gems "fiddle" # not sure why, but this is needed on Windows to boot rubygems successfully
realworld_system_gems "timeout uri" # this spec uses net/http which requires these default gems
diff --git a/spec/bundler/runtime/load_spec.rb b/spec/bundler/runtime/load_spec.rb
index 0274ba18b8..96a22a46cc 100644
--- a/spec/bundler/runtime/load_spec.rb
+++ b/spec/bundler/runtime/load_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe "Bundler.load" do
G
ruby <<-RUBY
- require "#{lib_dir}/bundler"
+ require "#{entrypoint}"
Bundler.setup :default
Bundler.require :default
puts RACK
diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb
index f28b5ea16b..433396d106 100644
--- a/spec/bundler/runtime/platform_spec.rb
+++ b/spec/bundler/runtime/platform_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
ruby <<-R
begin
- require '#{lib_dir}/bundler'
+ require '#{entrypoint}'
Bundler.ui.silence { Bundler.setup }
rescue Bundler::GemNotFound => e
puts "WIN"
@@ -57,6 +57,126 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
expect(the_bundle).to include_gems "nokogiri 1.4.2"
end
+ it "will keep both platforms when both ruby and a specific ruby platform are locked and the bundle is unlocked" do
+ build_repo4 do
+ build_gem "nokogiri", "1.11.1" do |s|
+ s.add_dependency "mini_portile2", "~> 2.5.0"
+ s.add_dependency "racc", "~> 1.5.2"
+ end
+
+ build_gem "nokogiri", "1.11.1" do |s|
+ s.platform = Bundler.local_platform
+ s.add_dependency "racc", "~> 1.4"
+ end
+
+ build_gem "mini_portile2", "2.5.0"
+ build_gem "racc", "1.5.2"
+ end
+
+ good_lockfile = <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ mini_portile2 (2.5.0)
+ nokogiri (1.11.1)
+ mini_portile2 (~> 2.5.0)
+ racc (~> 1.5.2)
+ nokogiri (1.11.1-#{Bundler.local_platform})
+ racc (~> 1.4)
+ racc (1.5.2)
+
+ PLATFORMS
+ #{lockfile_platforms_for(["ruby"] + local_platforms)}
+
+ DEPENDENCIES
+ nokogiri (~> 1.11)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "nokogiri", "~> 1.11"
+ G
+
+ lockfile good_lockfile
+
+ bundle "update nokogiri"
+
+ expect(lockfile).to eq(good_lockfile)
+ end
+
+ it "will not try to install platform specific gems when they don't match the current ruby if locked only to ruby" do
+ build_repo4 do
+ build_gem "nokogiri", "1.11.1"
+
+ build_gem "nokogiri", "1.11.1" do |s|
+ s.platform = Bundler.local_platform
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gems.repo4"
+ gem "nokogiri"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gems.repo4/
+ specs:
+ nokogiri (1.11.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ expect(out).to include("Fetching nokogiri 1.11.1")
+ expect(the_bundle).to include_gems "nokogiri 1.11.1"
+ expect(the_bundle).not_to include_gems "nokogiri 1.11.1 #{Bundler.local_platform}"
+ end
+
+ it "will use the java platform if both generic java and generic ruby platforms are locked", :jruby do
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "nokogiri"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: #{file_uri_for(gem_repo1)}/
+ specs:
+ nokogiri (1.4.2)
+ nokogiri (1.4.2-java)
+ weakling (>= 0.0.3)
+ weakling (0.0.3)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ 2.1.4
+ G
+
+ bundle "install"
+
+ expect(out).to include("Fetching nokogiri 1.4.2 (java)")
+ expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA"
+ end
+
it "will add the resolve for the current platform" do
lockfile <<-G
GEM
diff --git a/spec/bundler/runtime/require_spec.rb b/spec/bundler/runtime/require_spec.rb
index ad30529e86..d91b5f8666 100644
--- a/spec/bundler/runtime/require_spec.rb
+++ b/spec/bundler/runtime/require_spec.rb
@@ -46,6 +46,7 @@ RSpec.describe "Bundler.require" do
end
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "one", :group => :bar, :require => %w[baz qux]
gem "two"
@@ -112,6 +113,7 @@ RSpec.describe "Bundler.require" do
it "raises an exception if a require is specified but the file does not exist" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "two", :require => 'fail'
end
@@ -130,6 +132,7 @@ RSpec.describe "Bundler.require" do
end
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "faulty"
end
@@ -146,6 +149,7 @@ RSpec.describe "Bundler.require" do
end
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "loadfuuu"
end
@@ -172,6 +176,7 @@ RSpec.describe "Bundler.require" do
it "requires gem names that are namespaced" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path '#{lib_path}' do
gem 'jquery-rails'
end
@@ -186,13 +191,15 @@ RSpec.describe "Bundler.require" do
s.write "lib/brcrypt.rb", "BCrypt = '1.0.0'"
end
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+
path "#{lib_path}" do
gem "bcrypt-ruby"
end
G
cmd = <<-RUBY
- require '#{lib_dir}/bundler'
+ require '#{entrypoint}'
Bundler.require
RUBY
ruby(cmd)
@@ -202,6 +209,7 @@ RSpec.describe "Bundler.require" do
it "does not mangle explicitly given requires" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem 'jquery-rails', :require => 'jquery-rails'
end
@@ -219,6 +227,7 @@ RSpec.describe "Bundler.require" do
end
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "load-fuuu"
end
@@ -242,6 +251,7 @@ RSpec.describe "Bundler.require" do
end
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "load-fuuu"
end
@@ -300,6 +310,7 @@ RSpec.describe "Bundler.require" do
it "works when the gems are in the Gemfile in the correct order" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "two"
gem "one"
@@ -318,6 +329,7 @@ RSpec.describe "Bundler.require" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "multi_gem", :require => "one", :group => :one
gem "multi_gem", :require => "two", :group => :two
G
@@ -341,6 +353,7 @@ RSpec.describe "Bundler.require" do
it "fails when the gems are in the Gemfile in the wrong order" do
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
path "#{lib_path}" do
gem "one"
gem "two"
@@ -358,6 +371,7 @@ RSpec.describe "Bundler.require" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "busted_require"
G
@@ -396,6 +410,7 @@ RSpec.describe "Bundler.require" do
build_git "foo"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb
index 7af1cd9801..35873dcaa9 100644
--- a/spec/bundler/runtime/setup_spec.rb
+++ b/spec/bundler/runtime/setup_spec.rb
@@ -108,8 +108,8 @@ RSpec.describe "Bundler.setup" do
context "load order" do
def clean_load_path(lp)
without_bundler_load_path = ruby("puts $LOAD_PATH").split("\n")
- lp -= without_bundler_load_path
- lp.map! {|p| p.sub(/^#{Regexp.union system_gem_path.to_s, default_bundle_path.to_s, lib_dir.to_s}/i, "") }
+ lp -= [*without_bundler_load_path, lib_dir.to_s]
+ lp.map! {|p| p.sub(system_gem_path.to_s, "") }
end
it "puts loaded gems after -I and RUBYLIB", :ruby_repo do
@@ -143,12 +143,8 @@ RSpec.describe "Bundler.setup" do
gem "rails"
G
- # We require an absolute path because relying on the $LOAD_PATH behaves
- # inconsistently depending on whether we're in a ruby-core setup (and
- # bundler's lib is in RUBYLIB) or not.
-
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.setup
puts $LOAD_PATH
RUBY
@@ -157,7 +153,6 @@ RSpec.describe "Bundler.setup" do
expect(load_path).to start_with(
"/gems/rails-2.3.2/lib",
- "/gems/bundler-#{Bundler::VERSION}/lib",
"/gems/activeresource-2.3.2/lib",
"/gems/activerecord-2.3.2/lib",
"/gems/actionpack-2.3.2/lib",
@@ -168,6 +163,8 @@ RSpec.describe "Bundler.setup" do
end
it "falls back to order the load path alphabetically for backwards compatibility" do
+ bundle "config set path.system true"
+
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "weakling"
@@ -175,12 +172,8 @@ RSpec.describe "Bundler.setup" do
gem "terranova"
G
- # We require an absolute path because relying on the $LOAD_PATH behaves
- # inconsistently depending on whether we're in a ruby-core setup (and
- # bundler's lib is in RUBYLIB) or not.
-
ruby <<-RUBY
- require '#{lib_dir}/bundler/setup'
+ require 'bundler/setup'
puts $LOAD_PATH
RUBY
@@ -200,8 +193,6 @@ RSpec.describe "Bundler.setup" do
gem "rack"
G
- entrypoint = mis_activates_prerelease_default_bundler? ? "#{lib_dir}/bundler" : "bundler"
-
ruby <<-R
require '#{entrypoint}'
@@ -439,6 +430,7 @@ RSpec.describe "Bundler.setup" do
build_git "rack", "1.0.0"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rack", :git => "#{lib_path("rack-1.0.0")}"
G
end
@@ -474,8 +466,6 @@ RSpec.describe "Bundler.setup" do
break_git!
- entrypoint = mis_activates_prerelease_default_bundler? ? "#{lib_dir}/bundler" : "bundler"
-
ruby <<-R
require "#{entrypoint}"
@@ -493,14 +483,14 @@ RSpec.describe "Bundler.setup" do
end
it "works even when the cache directory has been deleted" do
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle :install
FileUtils.rm_rf vendored_gems("cache")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "does not randomly change the path when specifying --path and the bundle directory becomes read only" do
- bundle "config --local path vendor/bundle"
+ bundle "config set --local path vendor/bundle"
bundle :install
with_read_only("#{bundled_app}/**/*") do
@@ -604,7 +594,7 @@ RSpec.describe "Bundler.setup" do
describe "when excluding groups" do
it "doesn't change the resolve if --without is used" do
- bundle "config --local without rails"
+ bundle "config set --local without rails"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
@@ -620,7 +610,7 @@ RSpec.describe "Bundler.setup" do
end
it "remembers --without and does not bail on bare Bundler.setup" do
- bundle "config --local without rails"
+ bundle "config set --local without rails"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
@@ -636,7 +626,7 @@ RSpec.describe "Bundler.setup" do
end
it "remembers --without and does not bail on bare Bundler.setup, even in the case of path gems no longer available" do
- bundle "config --local without development"
+ bundle "config set --local without development"
path = bundled_app(File.join("vendor", "foo"))
build_lib "foo", :path => path
@@ -655,8 +645,27 @@ RSpec.describe "Bundler.setup" do
expect(err).to be_empty
end
+ it "doesn't re-resolve when a pre-release bundler is used and a dependency includes a dependency on bundler" do
+ system_gems "bundler-9.99.9.beta1"
+
+ build_repo4 do
+ build_gem "depends_on_bundler", "1.0" do |s|
+ s.add_dependency "bundler", ">= 1.5.0"
+ end
+ end
+
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "depends_on_bundler"
+ G
+
+ ruby "require '#{system_gem_path("gems/bundler-9.99.9.beta1/lib/bundler.rb")}'; Bundler.setup", :env => { "DEBUG" => "1" }
+ expect(out).to include("Found no changes, using resolution from the lockfile")
+ expect(err).to be_empty
+ end
+
it "remembers --without and does not include groups passed to Bundler.setup" do
- bundle "config --local without rails"
+ bundle "config set --local without rails"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
@@ -736,41 +745,68 @@ end
expect(err).to be_empty
end
- describe "$MANPATH" do
- before do
+ context "when the user has `MANPATH` set", :man do
+ before { ENV["MANPATH"] = "/foo#{File::PATH_SEPARATOR}" }
+
+ it "adds the gem's man dir to the MANPATH" do
build_repo4 do
build_gem "with_man" do |s|
s.write("man/man1/page.1", "MANPAGE")
end
end
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "with_man"
+ G
+
+ run "puts ENV['MANPATH']"
+ expect(out).to eq("#{default_bundle_path("gems/with_man-1.0/man")}#{File::PATH_SEPARATOR}/foo")
end
+ end
- context "when the user has one set" do
- before { ENV["MANPATH"] = "/foo#{File::PATH_SEPARATOR}" }
+ context "when the user does not have `MANPATH` set", :man do
+ before { ENV.delete("MANPATH") }
- it "adds the gem's man dir to the MANPATH" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "with_man"
- G
+ it "adds the gem's man dir to the MANPATH, leaving : in the end so that system man pages still work" do
+ build_repo4 do
+ build_gem "with_man" do |s|
+ s.write("man/man1/page.1", "MANPAGE")
+ end
- run "puts ENV['MANPATH']"
- expect(out).to eq("#{default_bundle_path("gems/with_man-1.0/man")}#{File::PATH_SEPARATOR}/foo")
+ build_gem "with_man_overriding_system_man" do |s|
+ s.write("man/man1/ls.1", "LS MANPAGE")
+ end
end
- end
- context "when the user does not have one set" do
- before { ENV.delete("MANPATH") }
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "with_man"
+ G
- it "adds the gem's man dir to the MANPATH" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "with_man"
- G
+ run <<~RUBY
+ puts ENV['MANPATH']
+ require "open3"
+ puts Open3.capture2e("man", "ls")[1].success?
+ RUBY
- run "puts ENV['MANPATH']"
- expect(out).to eq(default_bundle_path("gems/with_man-1.0/man").to_s)
- end
+ expect(out).to eq("#{default_bundle_path("gems/with_man-1.0/man")}#{File::PATH_SEPARATOR}\ntrue")
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "with_man_overriding_system_man"
+ G
+
+ run <<~RUBY
+ puts ENV['MANPATH']
+ require "open3"
+ puts Open3.capture2e("man", "ls")[0]
+ RUBY
+
+ lines = out.split("\n")
+
+ expect(lines).to include("#{default_bundle_path("gems/with_man_overriding_system_man-1.0/man")}#{File::PATH_SEPARATOR}")
+ expect(lines).to include("LS MANPAGE")
end
end
@@ -849,7 +885,7 @@ end
end
it "should not remove itself from the LOAD_PATH and require a different copy of 'bundler/setup'" do
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
ruby <<-R, :env => { "GEM_PATH" => symlinked_gem_home }
TracePoint.trace(:class) do |tp|
@@ -898,6 +934,7 @@ end
FileUtils.rm(File.join(path, "foo.gemspec"))
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', '1.2.3', :path => 'vendor/foo'
G
@@ -918,6 +955,7 @@ end
FileUtils.rm(File.join(absolute_path, "foo.gemspec"))
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', '1.2.3', :path => '#{relative_path}'
G
@@ -936,6 +974,7 @@ end
build_git "no_gemspec", :gemspec => false
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "no_gemspec", "1.0", :git => "#{lib_path("no_gemspec-1.0")}"
G
end
@@ -1038,6 +1077,7 @@ end
end
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "bar", :git => "#{lib_path("bar-1.0")}"
G
end
@@ -1058,7 +1098,6 @@ end
expect(err.lines.map(&:chomp)).to include(
a_string_starting_with("[!] There was an error while loading `bar.gemspec`:"),
- a_string_starting_with("Does it try to require a relative path? That's been removed in Ruby 1.9."),
" # from #{default_bundle_path "bundler", "gems", "bar-1.0-#{ref[0, 12]}", "bar.gemspec"}:1",
" > require 'foobarbaz'"
)
@@ -1085,6 +1124,7 @@ end
describe "when Bundler is bundled" do
it "doesn't blow up" do
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "bundler", :path => "#{root}"
G
@@ -1095,7 +1135,7 @@ end
describe "when BUNDLED WITH" do
def lock_with(bundler_version = nil)
- lock = <<-L
+ lock = <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
@@ -1109,7 +1149,7 @@ end
L
if bundler_version
- lock += "\n BUNDLED WITH\n #{bundler_version}\n"
+ lock += "\nBUNDLED WITH\n #{bundler_version}\n"
end
lock
@@ -1126,10 +1166,9 @@ end
context "is not present" do
it "does not change the lock" do
- entrypoint = mis_activates_prerelease_default_bundler? ? "#{lib_dir}/bundler/setup" : "bundler/setup"
lockfile lock_with(nil)
- ruby "require '#{entrypoint}'"
- lockfile_should_be lock_with(nil)
+ ruby "require '#{entrypoint}/setup'"
+ expect(lockfile).to eq lock_with(nil)
end
end
@@ -1139,17 +1178,16 @@ end
ruby "require 'bundler/setup'"
expect(out).to be_empty
expect(err).to be_empty
- lockfile_should_be lock_with(Bundler::VERSION.succ)
+ expect(lockfile).to eq lock_with(Bundler::VERSION.succ)
end
end
context "is older" do
it "does not change the lock" do
- entrypoint = mis_activates_prerelease_default_bundler? ? "#{lib_dir}/bundler/setup" : "bundler/setup"
system_gems "bundler-1.10.1"
lockfile lock_with("1.10.1")
- ruby "require '#{entrypoint}'"
- lockfile_should_be lock_with("1.10.1")
+ ruby "require '#{entrypoint}/setup'"
+ expect(lockfile).to eq lock_with("1.10.1")
end
end
end
@@ -1158,7 +1196,7 @@ end
let(:ruby_version) { nil }
def lock_with(ruby_version = nil)
- lock = <<-L
+ lock = <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
@@ -1172,10 +1210,10 @@ end
L
if ruby_version
- lock += "\n RUBY VERSION\n ruby #{ruby_version}\n"
+ lock += "\nRUBY VERSION\n ruby #{ruby_version}\n"
end
- lock += <<-L
+ lock += <<~L
BUNDLED WITH
#{Bundler::VERSION}
@@ -1217,11 +1255,45 @@ end
end
describe "with gemified standard libraries" do
+ it "does not load Digest", :ruby_repo do
+ skip "Only for Ruby 3.0+" unless RUBY_VERSION >= "3.0"
+
+ build_git "bar", :gemspec => false do |s|
+ s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
+ s.write "bar.gemspec", <<-G
+ require_relative 'lib/bar/version'
+
+ Gem::Specification.new do |s|
+ s.name = 'bar'
+ s.version = BAR_VERSION
+ s.summary = 'Bar'
+ s.files = Dir["lib/**/*.rb"]
+ s.author = 'no one'
+
+ s.add_runtime_dependency 'digest'
+ end
+ G
+ end
+
+ gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+
+ bundle :install
+
+ ruby <<-RUBY
+ require '#{entrypoint}/setup'
+ puts defined?(::Digest) ? "Digest defined" : "Digest undefined"
+ require 'digest'
+ RUBY
+ expect(out).to eq("Digest undefined")
+ end
+
it "does not load Psych" do
- gemfile ""
- entrypoint = mis_activates_prerelease_default_bundler? ? "#{lib_dir}/bundler/setup" : "bundler/setup"
+ gemfile "source \"#{file_uri_for(gem_repo1)}\""
ruby <<-RUBY
- require '#{entrypoint}'
+ require '#{entrypoint}/setup'
puts defined?(Psych::VERSION) ? Psych::VERSION : "undefined"
require 'psych'
puts Psych::VERSION
@@ -1232,7 +1304,7 @@ end
end
it "does not load openssl" do
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
ruby <<-RUBY
require "bundler/setup"
puts defined?(OpenSSL) || "undefined"
@@ -1244,16 +1316,18 @@ end
describe "default gem activation" do
let(:exemptions) do
- exempts = if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.7")
+ exempts = if Gem.rubygems_version >= Gem::Version.new("2.7")
%w[did_you_mean]
else
%w[io-console openssl]
end << "bundler"
- exempts << "fiddle" if Gem.win_platform? && Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.7")
- exempts << "uri" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7")
- exempts << "pathname" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0")
- exempts << "set" unless Gem::Version.new(Gem::VERSION) >= Gem::Version.new("3.2.6")
- exempts << "tsort" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0")
+ exempts << "fiddle" if Gem.win_platform? && Gem.rubygems_version >= Gem::Version.new("2.7")
+ exempts << "uri" if Gem.ruby_version >= Gem::Version.new("2.7")
+ exempts << "pathname" if Gem.ruby_version >= Gem::Version.new("3.0")
+ exempts << "set" unless Gem.rubygems_version >= Gem::Version.new("3.2.6")
+ exempts << "tsort" unless Gem.rubygems_version >= Gem::Version.new("3.2.31")
+ exempts << "error_highlight" # added in Ruby 3.1 as a default gem
+ exempts << "ruby2_keywords" # added in Ruby 3.1 as a default gem
exempts
end
@@ -1291,13 +1365,13 @@ end
RUBY
it "activates no gems with -rbundler/setup" do
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
ruby code, :env => { "RUBYOPT" => activation_warning_hack_rubyopt + " -rbundler/setup" }
expect(out).to eq("{}")
end
it "activates no gems with bundle exec" do
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
create_file("script.rb", code)
bundle "exec ruby ./script.rb", :env => { "RUBYOPT" => activation_warning_hack_rubyopt }
expect(out).to eq("{}")
@@ -1306,7 +1380,7 @@ end
it "activates no gems with bundle exec that is loaded" do
skip "not executable" if Gem.win_platform?
- install_gemfile ""
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
create_file("script.rb", "#!/usr/bin/env ruby\n\n#{code}")
FileUtils.chmod(0o777, bundled_app("script.rb"))
bundle "exec ./script.rb", :artifice => nil, :env => { "RUBYOPT" => activation_warning_hack_rubyopt }
@@ -1421,10 +1495,44 @@ end
expect(last_command.stdboth).to eq("true")
end
- end
- # Tested rubygems does not include https://github.com/rubygems/rubygems/pull/2728 and will not always end up activating the current bundler
- def mis_activates_prerelease_default_bundler?
- Gem.rubygems_version < Gem::Version.new("3.1.a")
+ it "memoizes initial set of specs when requiring bundler/setup, so that even if further code mutates dependencies, Bundler.definition.specs is not affected" do
+ install_gemfile <<~G
+ source "#{file_uri_for(gem_repo1)}"
+ gem "yard"
+ gem "rack", :group => :test
+ G
+
+ ruby <<-RUBY, :raise_on_error => false
+ require "bundler/setup"
+ Bundler.require(:test).select! {|d| (d.groups & [:test]).any? }
+ puts Bundler.definition.specs.map(&:name).join(", ")
+ RUBY
+
+ expect(out).to include("rack, yard")
+ end
+
+ it "does not cause double loads when higher versions of default gems are activated before bundler" do
+ build_repo2 do
+ build_gem "json", "999.999.999" do |s|
+ s.write "lib/json.rb", <<~RUBY
+ module JSON
+ VERSION = "999.999.999"
+ end
+ RUBY
+ end
+ end
+
+ system_gems "json-999.999.999", :gem_repo => gem_repo2
+
+ install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
+ ruby <<-RUBY
+ require "json"
+ require "bundler/setup"
+ require "json"
+ RUBY
+
+ expect(err).to be_empty
+ end
end
end
diff --git a/spec/bundler/runtime/with_unbundled_env_spec.rb b/spec/bundler/runtime/with_unbundled_env_spec.rb
index 03de830ea0..731a9921a2 100644
--- a/spec/bundler/runtime/with_unbundled_env_spec.rb
+++ b/spec/bundler/runtime/with_unbundled_env_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe "Bundler.with_env helpers" do
def build_bundler_context(options = {})
bundle "config set path vendor/bundle"
- gemfile ""
+ gemfile "source \"#{file_uri_for(gem_repo1)}\""
bundle "install", options
end
diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb
index 68d8537715..4d558b9907 100644
--- a/spec/bundler/spec_helper.rb
+++ b/spec/bundler/spec_helper.rb
@@ -13,6 +13,7 @@ require "bundler"
require "rspec/core"
require "rspec/expectations"
require "rspec/mocks"
+require "rspec/support/differ"
require_relative "support/builders"
require_relative "support/build_metadata"
@@ -65,20 +66,15 @@ RSpec.configure do |config|
mocks.allow_message_expectations_on_nil = false
end
- config.around :each do |example|
- if ENV["RUBY"]
- orig_ruby = Gem.ruby
- Gem.ruby = ENV["RUBY"]
- end
- example.run
- Gem.ruby = orig_ruby if ENV["RUBY"]
- end
-
config.before :suite do
+ Gem.ruby = ENV["RUBY"] if ENV["RUBY"]
+
require_relative "support/rubygems_ext"
Spec::Rubygems.test_setup
ENV["BUNDLE_SPEC_RUN"] = "true"
ENV["BUNDLE_USER_CONFIG"] = ENV["BUNDLE_USER_CACHE"] = ENV["BUNDLE_USER_PLUGIN"] = nil
+ ENV["RUBYGEMS_GEMDEPS"] = nil
+ ENV["XDG_CONFIG_HOME"] = nil
ENV["GEMRC"] = nil
# Don't wrap output in tests
@@ -89,6 +85,8 @@ RSpec.configure do |config|
end
config.before :all do
+ check_test_gems!
+
build_repo1
reset_paths!
diff --git a/spec/bundler/support/api_request_limit_hax.rb b/spec/bundler/support/api_request_limit_hax.rb
new file mode 100644
index 0000000000..37ff0203b3
--- /dev/null
+++ b/spec/bundler/support/api_request_limit_hax.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+if ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"]
+ require_relative "path"
+ require "bundler/source"
+ require "bundler/source/rubygems"
+
+ module Bundler
+ class Source
+ class Rubygems < Source
+ remove_const :API_REQUEST_LIMIT
+ API_REQUEST_LIMIT = ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"].to_i
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/compact_index.rb b/spec/bundler/support/artifice/compact_index.rb
index 5cf3a79f29..1b314e89ef 100644
--- a/spec/bundler/support/artifice/compact_index.rb
+++ b/spec/bundler/support/artifice/compact_index.rb
@@ -62,7 +62,7 @@ class CompactIndexAPI < Endpoint
body.byteslice(range)
end
- def gems(gem_repo = GEM_REPO)
+ def gems(gem_repo = default_gem_repo)
@gems ||= {}
@gems[gem_repo] ||= begin
specs = Bundler::Deprecate.skip_during do
@@ -80,7 +80,7 @@ class CompactIndexAPI < Endpoint
CompactIndex::Dependency.new(d.name, reqs)
end
checksum = begin
- Digest(:SHA256).file("#{GEM_REPO}/gems/#{spec.original_name}.gem").base64digest
+ Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").base64digest
rescue StandardError
nil
end
diff --git a/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb b/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb
new file mode 100644
index 0000000000..acf76dfbf0
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require_relative "compact_index"
+
+Artifice.deactivate
+
+class CompactIndexPartialUpdateNoEtagNotIncremental < CompactIndexAPI
+ def partial_update_no_etag
+ response_body = yield
+ headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
+ content_type "text/plain"
+ requested_range_for(response_body)
+ end
+
+ get "/versions" do
+ partial_update_no_etag do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ lines = file.contents([], :calculate_info_checksums => true).split("\n")
+ name, versions, checksum = lines.last.split(" ")
+
+ # shuffle versions so new versions are not appended to the end
+ [*lines[0..-2], [name, versions.split(",").reverse.join(","), checksum].join(" ")].join("\n")
+ end
+ end
+
+ get "/info/:name" do
+ partial_update_no_etag do
+ gem = gems.find {|g| g.name == params[:name] }
+ lines = CompactIndex.info(gem ? gem.versions : []).split("\n")
+
+ # shuffle versions so new versions are not appended to the end
+ [lines.first, lines.last, *lines[1..-2]].join("\n")
+ end
+ end
+end
+
+Artifice.activate_with(CompactIndexPartialUpdateNoEtagNotIncremental)
diff --git a/spec/bundler/support/artifice/compact_index_rate_limited.rb b/spec/bundler/support/artifice/compact_index_rate_limited.rb
index ba17476045..570105e2a0 100644
--- a/spec/bundler/support/artifice/compact_index_rate_limited.rb
+++ b/spec/bundler/support/artifice/compact_index_rate_limited.rb
@@ -7,7 +7,7 @@ Artifice.deactivate
class CompactIndexRateLimited < CompactIndexAPI
class RequestCounter
def self.queue
- @queue ||= Queue.new
+ @queue ||= Thread::Queue.new
end
def self.size
diff --git a/spec/bundler/support/artifice/endpoint.rb b/spec/bundler/support/artifice/endpoint.rb
index 1dc7101389..4a820e5a3f 100644
--- a/spec/bundler/support/artifice/endpoint.rb
+++ b/spec/bundler/support/artifice/endpoint.rb
@@ -8,7 +8,7 @@ require "artifice"
require "sinatra/base"
ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant
-ALL_REQUESTS_MUTEX = Mutex.new
+ALL_REQUESTS_MUTEX = Thread::Mutex.new
at_exit do
if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"]
@@ -26,7 +26,6 @@ class Endpoint < Sinatra::Base
@all_requests ||= []
end
- GEM_REPO = Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"] || Spec::Path.gem_repo1)
set :raise_errors, true
set :show_exceptions, false
@@ -41,7 +40,26 @@ class Endpoint < Sinatra::Base
helpers do
include Spec::Path
- def dependencies_for(gem_names, gem_repo = GEM_REPO)
+ def default_gem_repo
+ if ENV["BUNDLER_SPEC_GEM_REPO"]
+ Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"])
+ else
+ case request.host
+ when "gem.repo1"
+ Spec::Path.gem_repo1
+ when "gem.repo2"
+ Spec::Path.gem_repo2
+ when "gem.repo3"
+ Spec::Path.gem_repo3
+ when "gem.repo4"
+ Spec::Path.gem_repo4
+ else
+ Spec::Path.gem_repo1
+ end
+ end
+ end
+
+ def dependencies_for(gem_names, gem_repo = default_gem_repo)
return [] if gem_names.nil? || gem_names.empty?
all_specs = %w[specs.4.8 prerelease_specs.4.8].map do |filename|
@@ -74,11 +92,11 @@ class Endpoint < Sinatra::Base
end
get "/fetch/actual/gem/:id" do
- File.binread("#{GEM_REPO}/quick/Marshal.4.8/#{params[:id]}")
+ File.binread("#{default_gem_repo}/quick/Marshal.4.8/#{params[:id]}")
end
get "/gems/:id" do
- File.binread("#{GEM_REPO}/gems/#{params[:id]}")
+ File.binread("#{default_gem_repo}/gems/#{params[:id]}")
end
get "/api/v1/dependencies" do
@@ -86,11 +104,11 @@ class Endpoint < Sinatra::Base
end
get "/specs.4.8.gz" do
- File.binread("#{GEM_REPO}/specs.4.8.gz")
+ File.binread("#{default_gem_repo}/specs.4.8.gz")
end
get "/prerelease_specs.4.8.gz" do
- File.binread("#{GEM_REPO}/prerelease_specs.4.8.gz")
+ File.binread("#{default_gem_repo}/prerelease_specs.4.8.gz")
end
end
diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb
index 88c33d93dc..0d51201bef 100644
--- a/spec/bundler/support/artifice/vcr.rb
+++ b/spec/bundler/support/artifice/vcr.rb
@@ -133,6 +133,19 @@ class BundlerVCRHTTP < Net::HTTP
end
end
+ def start_with_vcr
+ if ENV["BUNDLER_SPEC_PRE_RECORDED"]
+ raise IOError, "HTTP session already opened" if @started
+ @socket = nil
+ @started = true
+ else
+ start_without_vcr
+ end
+ end
+
+ alias_method :start_without_vcr, :start
+ alias_method :start, :start_with_vcr
+
def request_with_vcr(request, *args, &block)
handler = request.instance_eval do
remove_instance_variable(:@__vcr_request_handler) if defined?(@__vcr_request_handler)
diff --git a/spec/bundler/support/artifice/windows.rb b/spec/bundler/support/artifice/windows.rb
index f5b4baae30..ddbbd62b96 100644
--- a/spec/bundler/support/artifice/windows.rb
+++ b/spec/bundler/support/artifice/windows.rb
@@ -14,7 +14,7 @@ class Windows < Sinatra::Base
set :show_exceptions, false
helpers do
- def gem_repo
+ def default_gem_repo
Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"] || Spec::Path.gem_repo1)
end
end
@@ -26,7 +26,7 @@ class Windows < Sinatra::Base
files.each do |file|
get "/#{file}" do
- File.binread gem_repo.join(file)
+ File.binread default_gem_repo.join(file)
end
end
diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb
index c76c3f505e..90e9cbb242 100644
--- a/spec/bundler/support/builders.rb
+++ b/spec/bundler/support/builders.rb
@@ -30,7 +30,16 @@ module Spec
end
def build_repo1
+ rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
+
build_repo gem_repo1 do
+ FileUtils.cp rake_path, "#{gem_repo1}/gems/"
+
+ build_gem "coffee-script-source"
+ build_gem "git"
+ build_gem "puma"
+ build_gem "minitest"
+
build_gem "rack", %w[0.9.1 1.0.0] do |s|
s.executables = "rackup"
s.post_install_message = "Rack's post install message"
@@ -150,32 +159,6 @@ module Spec
build_gem "duradura", "7.0"
- build_gem "with_implicit_rake_dep" do |s|
- s.extensions << "Rakefile"
- s.write "Rakefile", <<-RUBY
- task :default do
- path = File.expand_path("../lib", __FILE__)
- FileUtils.mkdir_p(path)
- File.open("\#{path}/implicit_rake_dep.rb", "w") do |f|
- f.puts "IMPLICIT_RAKE_DEP = 'YES'"
- end
- end
- RUBY
- end
-
- build_gem "another_implicit_rake_dep" do |s|
- s.extensions << "Rakefile"
- s.write "Rakefile", <<-RUBY
- task :default do
- path = File.expand_path("../lib", __FILE__)
- FileUtils.mkdir_p(path)
- File.open("\#{path}/another_implicit_rake_dep.rb", "w") do |f|
- f.puts "ANOTHER_IMPLICIT_RAKE_DEP = 'YES'"
- end
- end
- RUBY
- end
-
build_gem "very_simple_binary", &:add_c_extension
build_gem "simple_binary", &:add_c_extension
@@ -214,13 +197,6 @@ module Spec
update_repo2(&blk) if block_given?
end
- def build_repo3
- build_repo gem_repo3 do
- build_gem "rack"
- end
- FileUtils.rm_rf Dir[gem_repo3("prerelease*")]
- end
-
# A repo that has no pre-installed gems included. (The caller completely
# determines the contents with the block.)
def build_repo4(&blk)
@@ -255,6 +231,13 @@ module Spec
def build_repo(path, &blk)
return if File.directory?(path)
+
+ FileUtils.mkdir_p("#{path}/gems")
+
+ update_repo(path, &blk)
+ end
+
+ def check_test_gems!
rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
if rake_path.nil?
@@ -263,14 +246,9 @@ module Spec
rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
end
- if rake_path
- FileUtils.mkdir_p("#{path}/gems")
- FileUtils.cp rake_path, "#{path}/gems/"
- else
+ if rake_path.nil?
abort "Your test gems are missing! Run `rm -rf #{tmp}` and try again."
end
-
- update_repo(path, &blk)
end
def update_repo(path)
@@ -575,17 +553,8 @@ module Spec
update_gemspec = options[:gemspec] || false
source = options[:source] || "git@#{libpath}"
- @context.git "checkout master", libpath
-
if branch = options[:branch]
- raise "You can't specify `master` as the branch" if branch == "master"
- escaped_branch = Shellwords.shellescape(branch)
-
- if @context.git("branch -l #{escaped_branch}", libpath).empty?
- @context.git("branch #{escaped_branch}", libpath)
- end
-
- @context.git("checkout #{escaped_branch}", libpath)
+ @context.git("checkout -b #{Shellwords.shellescape(branch)}", libpath)
elsif tag = options[:tag]
@context.git("tag #{Shellwords.shellescape(tag)}", libpath)
elsif options[:remote]
@@ -599,8 +568,7 @@ module Spec
_default_files[path] += "\n#{Builders.constantize(name)}_PREV_REF = '#{current_ref}'"
end
super(options.merge(:path => libpath, :gemspec => update_gemspec, :source => source))
- @context.git("add *", libpath)
- @context.git("commit -m BUMP", libpath, :raise_on_error => false)
+ @context.git("commit -am BUMP", libpath)
end
end
diff --git a/spec/bundler/support/bundle.rb b/spec/bundler/support/bundle.rb
new file mode 100644
index 0000000000..bb21526d35
--- /dev/null
+++ b/spec/bundler/support/bundle.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require "rubygems"
+require_relative "path"
+bundler_gemspec = Spec::Path.loaded_gemspec
+bundler_gemspec.instance_variable_set(:@full_gem_path, Spec::Path.source_root)
+bundler_gemspec.activate if bundler_gemspec.respond_to?(:activate)
+load File.expand_path("bundle", Spec::Path.bindir)
diff --git a/spec/bundler/support/filters.rb b/spec/bundler/support/filters.rb
index 0c1f27e470..3b91897a2e 100644
--- a/spec/bundler/support/filters.rb
+++ b/spec/bundler/support/filters.rb
@@ -34,6 +34,7 @@ RSpec.configure do |config|
config.filter_run_excluding :readline => Gem.win_platform?
config.filter_run_excluding :jruby => RUBY_ENGINE != "jruby"
config.filter_run_excluding :truffleruby => RUBY_ENGINE != "truffleruby"
+ config.filter_run_excluding :man => Gem.win_platform?
config.filter_run_when_matching :focus unless ENV["CI"]
end
diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb
index fc8e0ad55d..0ad5239128 100644
--- a/spec/bundler/support/hax.rb
+++ b/spec/bundler/support/hax.rb
@@ -9,7 +9,10 @@ module Gem
Gem.ruby = ENV["RUBY"]
end
- @default_dir = ENV["BUNDLER_GEM_DEFAULT_DIR"] if ENV["BUNDLER_GEM_DEFAULT_DIR"]
+ if ENV["BUNDLER_GEM_DEFAULT_DIR"]
+ @default_dir = ENV["BUNDLER_GEM_DEFAULT_DIR"]
+ @default_specifications_dir = nil
+ end
if ENV["BUNDLER_SPEC_PLATFORM"]
class Platform
@@ -28,35 +31,14 @@ module Gem
end
end
+ if ENV["BUNDLER_SPEC_GEM_SOURCES"]
+ @sources = [ENV["BUNDLER_SPEC_GEM_SOURCES"]]
+ end
+
# We only need this hack for rubygems versions without the BundlerVersionFinder
- if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
+ if Gem.rubygems_version < Gem::Version.new("2.7.0")
@path_to_default_spec_map.delete_if do |_path, spec|
spec.name == "bundler"
end
end
end
-
-if ENV["BUNDLER_SPEC_WINDOWS"] == "true"
- require_relative "path"
- require "bundler/constants"
-
- module Bundler
- remove_const :WINDOWS if defined?(WINDOWS)
- WINDOWS = true
- end
-end
-
-if ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"]
- require_relative "path"
- require "bundler/source"
- require "bundler/source/rubygems"
-
- module Bundler
- class Source
- class Rubygems < Source
- remove_const :API_REQUEST_LIMIT
- API_REQUEST_LIMIT = ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"].to_i
- end
- end
- end
-end
diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb
index a7cc1ce810..e44c67835f 100644
--- a/spec/bundler/support/helpers.rb
+++ b/spec/bundler/support/helpers.rb
@@ -10,7 +10,7 @@ module Spec
def reset!
Dir.glob("#{tmp}/{gems/*,*}", File::FNM_DOTMATCH).each do |dir|
- next if %w[base base_system remote1 gems rubygems . ..].include?(File.basename(dir))
+ next if %w[base base_system remote1 rubocop standard gems rubygems . ..].include?(File.basename(dir))
FileUtils.rm_rf(dir)
end
FileUtils.mkdir_p(home)
@@ -60,7 +60,7 @@ module Spec
def run(cmd, *args)
opts = args.last.is_a?(Hash) ? args.pop : {}
groups = args.map(&:inspect).join(", ")
- setup = "require '#{lib_dir}/bundler' ; Bundler.ui.silence { Bundler.setup(#{groups}) }"
+ setup = "require '#{entrypoint}' ; Bundler.ui.silence { Bundler.setup(#{groups}) }"
ruby([setup, cmd].join(" ; "), opts)
end
@@ -130,7 +130,7 @@ module Spec
def ruby(ruby, options = {})
ruby_cmd = build_ruby_cmd
- escaped_ruby = RUBY_PLATFORM == "java" ? ruby.shellescape.dump : ruby.shellescape
+ escaped_ruby = ruby.shellescape
sys_exec(%(#{ruby_cmd} -w -e #{escaped_ruby}), options)
end
@@ -218,7 +218,7 @@ module Spec
end
def all_commands_output
- return [] if command_executions.empty?
+ return "" if command_executions.empty?
"\n\nCommands:\n#{command_executions.map(&:to_s_verbose).join("\n\n")}"
end
@@ -455,20 +455,15 @@ module Spec
end
def simulate_windows(platform = mswin)
- old = ENV["BUNDLER_SPEC_WINDOWS"]
- ENV["BUNDLER_SPEC_WINDOWS"] = "true"
simulate_platform platform do
simulate_bundler_version_when_missing_prerelease_default_gem_activation do
yield
end
end
- ensure
- ENV["BUNDLER_SPEC_WINDOWS"] = old
end
- # workaround for missing https://github.com/rubygems/rubygems/commit/929e92d752baad3a08f3ac92eaec162cb96aedd1
def simulate_bundler_version_when_missing_prerelease_default_gem_activation
- return yield unless Gem.rubygems_version < Gem::Version.new("3.1.0.pre.1")
+ return yield unless rubygems_version_failing_to_activate_bundler_prereleases
old = ENV["BUNDLER_VERSION"]
ENV["BUNDLER_VERSION"] = Bundler::VERSION
@@ -477,15 +472,20 @@ module Spec
ENV["BUNDLER_VERSION"] = old
end
- # workaround for missing https://github.com/rubygems/rubygems/commit/929e92d752baad3a08f3ac92eaec162cb96aedd1
def env_for_missing_prerelease_default_gem_activation
- if Gem.rubygems_version < Gem::Version.new("3.1.0.pre.1")
+ if rubygems_version_failing_to_activate_bundler_prereleases
{ "BUNDLER_VERSION" => Bundler::VERSION }
else
{}
end
end
+ # versions providing a bundler version finder but not including
+ # https://github.com/rubygems/rubygems/commit/929e92d752baad3a08f3ac92eaec162cb96aedd1
+ def rubygems_version_failing_to_activate_bundler_prereleases
+ Gem.rubygems_version < Gem::Version.new("3.1.0.pre.1") && Gem.rubygems_version >= Gem::Version.new("2.7.0")
+ end
+
def revision_for(path)
sys_exec("git rev-parse HEAD", :dir => path).strip
end
diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb
index 45b68541f5..638f394e76 100644
--- a/spec/bundler/support/indexes.rb
+++ b/spec/bundler/support/indexes.rb
@@ -17,20 +17,19 @@ module Spec
def resolve(args = [])
@platforms ||= ["ruby"]
deps = []
- default_source = instance_double("Bundler::Source::Rubygems", :specs => @index)
+ default_source = instance_double("Bundler::Source::Rubygems", :specs => @index, :to_s => "locally install gems")
source_requirements = { :default => default_source }
@deps.each do |d|
+ source_requirements[d.name] = d.source = default_source
@platforms.each do |p|
- source_requirements[d.name] = d.source = default_source
deps << Bundler::DepProxy.get_proxy(d, p)
end
end
- source_requirements ||= {}
args[0] ||= [] # base
args[1] ||= Bundler::GemVersionPromoter.new # gem_version_promoter
args[2] ||= [] # additional_base_requirements
args[3] ||= @platforms # platforms
- Bundler::Resolver.resolve(deps, @index, source_requirements, *args)
+ Bundler::Resolver.resolve(deps, source_requirements, *args)
end
def should_resolve_as(specs)
diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb
index a6944495ff..3c2a7f9f58 100644
--- a/spec/bundler/support/matchers.rb
+++ b/spec/bundler/support/matchers.rb
@@ -114,30 +114,49 @@ module Spec
match do
opts = names.last.is_a?(Hash) ? names.pop : {}
source = opts.delete(:source)
- groups = Array(opts[:groups])
+ groups = Array(opts.delete(:groups)).map(&:inspect).join(", ")
opts[:raise_on_error] = false
- groups << opts
- @errors = names.map do |name|
- name, version, platform = name.split(/\s+/)
- require_path = name == "bundler" ? "#{lib_dir}/bundler" : name.tr("-", "/")
+ @errors = names.map do |full_name|
+ name, version, platform = full_name.split(/\s+/)
+ require_path = name.tr("-", "/")
version_const = name == "bundler" ? "Bundler::VERSION" : Spec::Builders.constantize(name)
- code = []
- code << "require '#{require_path}.rb'"
- code << "puts #{version_const}"
- run code.join("; "), *groups
- actual_version, actual_platform = out.strip.split(/\s+/, 2)
- unless Gem::Version.new(actual_version) == Gem::Version.new(version)
+ source_const = "#{Spec::Builders.constantize(name)}_SOURCE"
+ ruby <<~R, opts
+ require 'bundler'
+ Bundler.setup(#{groups})
+
+ require '#{require_path}'
+ actual_version, actual_platform = #{version_const}.split(/\s+/, 2)
+ unless Gem::Version.new(actual_version) == Gem::Version.new('#{version}')
+ puts actual_version
+ exit 64
+ end
+ unless actual_platform.to_s == '#{platform}'
+ puts actual_platform
+ exit 65
+ end
+ require '#{require_path}/source'
+ exit 0 if #{source.nil?}
+ actual_source = #{source_const}
+ unless actual_source == '#{source}'
+ puts actual_source
+ exit 66
+ end
+ R
+ next if exitstatus == 0
+ if exitstatus == 64
+ actual_version = out.split("\n").last
next "#{name} was expected to be at version #{version} but was #{actual_version}"
end
- unless actual_platform == platform
+ if exitstatus == 65
+ actual_platform = out.split("\n").last
next "#{name} was expected to be of platform #{platform} but was #{actual_platform}"
end
- next unless source
- source_const = "#{Spec::Builders.constantize(name)}_SOURCE"
- run "require '#{require_path}/source'; puts #{source_const}", *groups
- unless out.strip == source
- next "Expected #{name} (#{version}) to be installed from `#{source}`, was actually from `#{out}`"
+ if exitstatus == 66
+ actual_source = out.split("\n").last
+ next "Expected #{name} (#{version}) to be installed from `#{source}`, was actually from `#{actual_source}`"
end
+ next "Command to check for inclusion of gem #{full_name} failed"
end.compact
@errors.empty?
@@ -145,23 +164,34 @@ module Spec
match_when_negated do
opts = names.last.is_a?(Hash) ? names.pop : {}
- groups = Array(opts[:groups]) || []
+ groups = Array(opts.delete(:groups)).map(&:inspect).join(", ")
opts[:raise_on_error] = false
@errors = names.map do |name|
name, version = name.split(/\s+/, 2)
- run <<-R, *(groups + [opts])
+ ruby <<-R, opts
+ begin
+ require 'bundler'
+ Bundler.setup(#{groups})
+ rescue Bundler::GemNotFound, Bundler::GitError
+ exit 0
+ end
+
begin
require '#{name}'
- puts #{Spec::Builders.constantize(name)}
+ name_constant = '#{Spec::Builders.constantize(name)}'
+ if #{version.nil?} || name_constant == '#{version}'
+ exit 64
+ else
+ exit 0
+ end
rescue LoadError, NameError
- puts "WIN"
+ exit 0
end
R
- next if out == "WIN"
+ next if exitstatus == 0
+ next "command to check version of #{name} installed failed" unless exitstatus == 64
next "expected #{name} to not be installed, but it was" if version.nil?
- if Gem::Version.new(out) == Gem::Version.new(version)
- next "expected #{name} (#{version}) not to be installed, but it was"
- end
+ next "expected #{name} (#{version}) not to be installed, but it was"
end.compact
@errors.empty?
@@ -178,10 +208,6 @@ module Spec
RSpec::Matchers.define_negated_matcher :not_include_gems, :include_gems
RSpec::Matchers.alias_matcher :include_gem, :include_gems
- def have_lockfile(expected)
- read_as(strip_whitespace(expected))
- end
-
def plugin_should_be_installed(*names)
names.each do |name|
expect(Bundler::Plugin).to be_installed(name)
@@ -195,13 +221,5 @@ module Spec
expect(Bundler::Plugin).not_to be_installed(name)
end
end
-
- def lockfile_should_be(expected)
- expect(bundled_app_lock).to have_lockfile(expected)
- end
-
- def gemfile_should_be(expected)
- expect(bundled_app_gemfile).to read_as(strip_whitespace(expected))
- end
end
end
diff --git a/spec/bundler/support/path.rb b/spec/bundler/support/path.rb
index 56d3c71f3c..a73b3e699e 100644
--- a/spec/bundler/support/path.rb
+++ b/spec/bundler/support/path.rb
@@ -6,7 +6,7 @@ require "rbconfig"
module Spec
module Path
def source_root
- @source_root ||= Pathname.new(ruby_core? ? "../../../.." : "../../..").expand_path(__FILE__)
+ @source_root ||= Pathname.new(ruby_core? ? "../../.." : "../..").expand_path(__dir__)
end
def root
@@ -30,7 +30,15 @@ module Spec
end
def test_gemfile
- @test_gemfile ||= source_root.join(ruby_core? ? "tool/bundler/test_gems.rb" : "test_gems.rb")
+ @test_gemfile ||= source_root.join("tool/bundler/test_gems.rb")
+ end
+
+ def rubocop_gemfile
+ @rubocop_gemfile ||= source_root.join(rubocop_gemfile_basename)
+ end
+
+ def standard_gemfile
+ @standard_gemfile ||= source_root.join(standard_gemfile_basename)
end
def dev_gemfile
@@ -63,6 +71,10 @@ module Spec
@spec_dir ||= source_root.join(ruby_core? ? "spec/bundler" : "spec")
end
+ def api_request_limit_hack_file
+ spec_dir.join("support/api_request_limit_hax.rb")
+ end
+
def man_dir
@man_dir ||= lib_dir.join("bundler/man")
end
@@ -119,7 +131,7 @@ module Spec
end
def vendored_gems(path = nil)
- bundled_app(*["vendor/bundle", Gem.ruby_engine, RbConfig::CONFIG["ruby_version"], path].compact)
+ scoped_gem_path(bundled_app("vendor/bundle")).join(*[path].compact)
end
def cached_gem(path)
@@ -138,6 +150,14 @@ module Spec
tmp.join("gems/base")
end
+ def rubocop_gems
+ tmp.join("gems/rubocop")
+ end
+
+ def standard_gems
+ tmp.join("gems/standard")
+ end
+
def file_uri_for(path)
protocol = "file://"
root = Gem.win_platform? ? "/" : ""
@@ -178,7 +198,11 @@ module Spec
end
def local_gem_path(*path, base: bundled_app)
- base.join(*[".bundle", Gem.ruby_engine, RbConfig::CONFIG["ruby_version"], *path].compact)
+ scoped_gem_path(base.join(".bundle")).join(*path)
+ end
+
+ def scoped_gem_path(base)
+ base.join(Gem.ruby_engine, RbConfig::CONFIG["ruby_version"])
end
def lib_path(*args)
@@ -193,6 +217,13 @@ module Spec
root.join("lib")
end
+ # Sometimes rubygems version under test does not include
+ # https://github.com/rubygems/rubygems/pull/2728 and will not always end up
+ # activating the current bundler. In that case, require bundler absolutely.
+ def entrypoint
+ Gem.rubygems_version < Gem::Version.new("3.1.a") ? "#{lib_dir}/bundler" : "bundler"
+ end
+
def global_plugin_gem(*args)
home ".bundle", "plugin", "gems", *args
end
@@ -251,6 +282,28 @@ module Spec
!git_root.join(".git").directory?
end
+ def rubocop_gemfile_basename
+ filename = if RUBY_VERSION.start_with?("2.3")
+ "rubocop23_gems"
+ elsif RUBY_VERSION.start_with?("2.4")
+ "rubocop24_gems"
+ else
+ "rubocop_gems"
+ end
+ source_root.join("tool/bundler/#{filename}.rb")
+ end
+
+ def standard_gemfile_basename
+ filename = if RUBY_VERSION.start_with?("2.3")
+ "standard23_gems"
+ elsif RUBY_VERSION.start_with?("2.4")
+ "standard24_gems"
+ else
+ "standard_gems"
+ end
+ source_root.join("tool/bundler/#{filename}.rb")
+ end
+
extend self
end
end
diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb
index 0cb7f7cd29..07973fd727 100644
--- a/spec/bundler/support/platforms.rb
+++ b/spec/bundler/support/platforms.rb
@@ -90,7 +90,11 @@ module Spec
end
def lockfile_platforms
- local_platforms.map(&:to_s).sort.join("\n ")
+ lockfile_platforms_for(local_platforms)
+ end
+
+ def lockfile_platforms_for(platforms)
+ platforms.map(&:to_s).sort.join("\n ")
end
def local_platforms
diff --git a/spec/bundler/support/rubygems_ext.rb b/spec/bundler/support/rubygems_ext.rb
index d743a76391..f957440ab0 100644
--- a/spec/bundler/support/rubygems_ext.rb
+++ b/spec/bundler/support/rubygems_ext.rb
@@ -9,7 +9,7 @@ module Spec
extend self
def dev_setup
- install_gems(dev_gemfile, dev_lockfile)
+ install_gems(dev_gemfile)
end
def gem_load(gem_name, bin_container)
@@ -39,18 +39,19 @@ module Spec
end
def install_parallel_test_deps
+ Gem.clear_paths
+
require "parallel"
+ require "fileutils"
- prev_env_test_number = ENV["TEST_ENV_NUMBER"]
+ install_test_deps
- begin
- Parallel.processor_count.times do |n|
- ENV["TEST_ENV_NUMBER"] = (n + 1).to_s
+ (2..Parallel.processor_count).each do |n|
+ source = Path.source_root.join("tmp", "1")
+ destination = Path.source_root.join("tmp", n.to_s)
- install_test_deps
- end
- ensure
- ENV["TEST_ENV_NUMBER"] = prev_env_test_number
+ FileUtils.rm_rf destination
+ FileUtils.cp_r source, destination
end
end
@@ -66,30 +67,18 @@ module Spec
def install_test_deps
setup_test_paths
- workaround_loaded_specs_issue
-
- install_gems(test_gemfile, test_lockfile)
+ install_gems(test_gemfile)
+ install_gems(rubocop_gemfile, Path.rubocop_gems.to_s)
+ install_gems(standard_gemfile, Path.standard_gems.to_s)
end
private
- # Some rubygems versions include loaded specs when loading gemspec stubs
- # from the file system. In this situation, that makes bundler incorrectly
- # assume that `rake` is already installed at `tmp/` because it's installed
- # globally, and makes it skip installing it to the proper location for our
- # tests. To workaround, we remove `rake` from the loaded specs when running
- # under those versions, so that `bundler` does the right thing.
- def workaround_loaded_specs_issue
- current_rubygems_version = Gem::Version.new(Gem::VERSION)
-
- Gem.loaded_specs.delete("rake") if current_rubygems_version >= Gem::Version.new("3.0.0.beta2") && current_rubygems_version < Gem::Version.new("3.2.0")
- end
-
def gem_load_and_activate(gem_name, bin_container)
gem_activate(gem_name)
load Gem.bin_path(gem_name, bin_container)
rescue Gem::LoadError => e
- abort "We couln't activate #{gem_name} (#{e.requirement}). Run `gem install #{gem_name}:'#{e.requirement}'`"
+ abort "We couldn't activate #{gem_name} (#{e.requirement}). Run `gem install #{gem_name}:'#{e.requirement}'`"
end
def gem_activate(gem_name)
@@ -98,14 +87,27 @@ module Spec
gem gem_name, gem_requirement
end
- def install_gems(gemfile, lockfile)
+ def install_gems(gemfile, path = nil)
old_gemfile = ENV["BUNDLE_GEMFILE"]
ENV["BUNDLE_GEMFILE"] = gemfile.to_s
- require "bundler"
- definition = Bundler::Definition.build(gemfile, lockfile, nil)
- definition.validate_runtime!
- Bundler::Installer.install(Path.source_root, definition, :path => ENV["GEM_HOME"])
+
+ if path
+ old_path = ENV["BUNDLE_PATH"]
+ ENV["BUNDLE_PATH"] = path
+ else
+ old_path__system = ENV["BUNDLE_PATH__SYSTEM"]
+ ENV["BUNDLE_PATH__SYSTEM"] = "true"
+ end
+
+ output = `#{Gem.ruby} #{File.expand_path("support/bundle.rb", Path.spec_dir)} install`
+ raise "Error when installing gems in #{gemfile}: #{output}" unless $?.success?
ensure
+ if path
+ ENV["BUNDLE_PATH"] = old_path
+ else
+ ENV["BUNDLE_PATH__SYSTEM"] = old_path__system
+ end
+
ENV["BUNDLE_GEMFILE"] = old_gemfile
end
@@ -113,8 +115,12 @@ module Spec
Path.test_gemfile
end
- def test_lockfile
- lockfile_for(test_gemfile)
+ def rubocop_gemfile
+ Path.rubocop_gemfile
+ end
+
+ def standard_gemfile
+ Path.standard_gemfile
end
def dev_gemfile
diff --git a/spec/bundler/support/rubygems_version_manager.rb b/spec/bundler/support/rubygems_version_manager.rb
index c2e5a5f484..d1b1f8dd03 100644
--- a/spec/bundler/support/rubygems_version_manager.rb
+++ b/spec/bundler/support/rubygems_version_manager.rb
@@ -24,12 +24,6 @@ class RubygemsVersionManager
def assert_system_features_not_loaded!
at_exit do
- errors = if $?.nil?
- ""
- else
- all_commands_output
- end
-
rubylibdir = RbConfig::CONFIG["rubylibdir"]
rubygems_path = rubylibdir + "/rubygems"
@@ -43,11 +37,11 @@ class RubygemsVersionManager
(loaded_feature.start_with?(bundler_path) && !bundler_exemptions.any? {|bundler_exemption| loaded_feature.start_with?(bundler_exemption) })
end
- if bad_loaded_features.any?
- errors += "the following features were incorrectly loaded:\n#{bad_loaded_features.join("\n")}"
+ errors = if bad_loaded_features.any?
+ all_commands_output + "the following features were incorrectly loaded:\n#{bad_loaded_features.join("\n")}"
end
- raise errors unless errors.empty?
+ raise errors if errors
end
end
diff --git a/spec/bundler/update/git_spec.rb b/spec/bundler/update/git_spec.rb
index bf078fa576..f02fb37d0f 100644
--- a/spec/bundler/update/git_spec.rb
+++ b/spec/bundler/update/git_spec.rb
@@ -7,12 +7,13 @@ RSpec.describe "bundle update" do
update_git "foo", :branch => "omg"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo-1.0")}", :branch => "omg" do
gem 'foo'
end
G
- update_git "foo", :branch => "omg" do |s|
+ update_git "foo" do |s|
s.write "lib/foo.rb", "FOO = '1.1'"
end
@@ -28,6 +29,7 @@ RSpec.describe "bundle update" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rails", :git => "#{file_uri_for(lib_path("rails"))}"
G
@@ -40,12 +42,13 @@ RSpec.describe "bundle update" do
update_git "foo", :branch => "omg", :path => lib_path("foo")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
git "#{lib_path("foo")}", :branch => "omg" do
gem 'foo'
end
G
- update_git "foo", :branch => "omg", :path => lib_path("foo") do |s|
+ update_git "foo", :path => lib_path("foo") do |s|
s.write "lib/foo.rb", "FOO = '1.1'"
end
@@ -61,6 +64,7 @@ RSpec.describe "bundle update" do
end
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo"))}"
gem "bar"
G
@@ -79,12 +83,14 @@ RSpec.describe "bundle update" do
build_git "foo", :path => lib_path("foo_two")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.0", :git => "#{file_uri_for(lib_path("foo_one"))}"
G
FileUtils.rm_rf lib_path("foo_one")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", "1.0", :git => "#{file_uri_for(lib_path("foo_two"))}"
G
@@ -100,6 +106,7 @@ RSpec.describe "bundle update" do
update_git "foo", :push => "master"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :git => "#{@remote.path}"
G
@@ -108,6 +115,7 @@ RSpec.describe "bundle update" do
update_git "foo", :push => "fubar"
gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem 'foo', :git => "#{@remote.path}", :tag => "fubar"
G
@@ -116,6 +124,9 @@ RSpec.describe "bundle update" do
describe "with submodules" do
before :each do
+ # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(*%W[git config --global protocol.file.allow always])
+
build_repo4 do
build_gem "submodule" do |s|
s.write "lib/submodule.rb", "puts 'GEM'"
@@ -183,6 +194,7 @@ RSpec.describe "bundle update" do
build_git "foo", "1.0"
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}"
G
@@ -214,6 +226,7 @@ RSpec.describe "bundle update" do
build_git "rails", "2.3.2", :path => lib_path("rails")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "rails", :git => "#{file_uri_for(lib_path("rails"))}"
G
@@ -283,43 +296,7 @@ RSpec.describe "bundle update" do
G
end
- it "the --source flag updates version of gems that were originally pulled in by the source", :bundler => "< 3" do
- spec_lines = lib_path("bar/foo.gemspec").read.split("\n")
- spec_lines[5] = "s.version = '2.0'"
-
- update_git "foo", "2.0", :path => @git.path do |s|
- s.write "foo.gemspec", spec_lines.join("\n")
- end
-
- ref = @git.ref_for "master"
-
- bundle "update --source bar"
-
- lockfile_should_be <<-G
- GIT
- remote: #{@git.path}
- revision: #{ref}
- specs:
- foo (2.0)
-
- GEM
- remote: #{file_uri_for(gem_repo2)}/
- specs:
- rack (1.0.0)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- foo!
- rack
-
- BUNDLED WITH
- #{Bundler::VERSION}
- G
- end
-
- it "the --source flag updates version of gems that were originally pulled in by the source", :bundler => "3" do
+ it "the --source flag updates version of gems that were originally pulled in by the source" do
spec_lines = lib_path("bar/foo.gemspec").read.split("\n")
spec_lines[5] = "s.version = '2.0'"
@@ -331,7 +308,7 @@ RSpec.describe "bundle update" do
bundle "update --source bar"
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GIT
remote: #{@git.path}
revision: #{ref}
diff --git a/spec/bundler/update/path_spec.rb b/spec/bundler/update/path_spec.rb
index 38c125e04b..756770313b 100644
--- a/spec/bundler/update/path_spec.rb
+++ b/spec/bundler/update/path_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe "path sources" do
build_lib "activesupport", "2.3.5", :path => lib_path("rails/activesupport")
install_gemfile <<-G
+ source "#{file_uri_for(gem_repo1)}"
gem "activesupport", :path => "#{lib_path("rails/activesupport")}"
G
diff --git a/spec/ruby/core/hash/transform_keys_spec.rb b/spec/ruby/core/hash/transform_keys_spec.rb
index 2c5d4124e0..a7ea80d053 100644
--- a/spec/ruby/core/hash/transform_keys_spec.rb
+++ b/spec/ruby/core/hash/transform_keys_spec.rb
@@ -84,7 +84,7 @@ describe "Hash#transform_keys!" do
end
end
- ruby_version_is "2.5.1" do
+ ruby_version_is "2.5.1"..."3.0.2" do
it "returns the processed keys if we broke from the block" do
@hash.transform_keys! do |v|
break if v == :c
@@ -94,6 +94,16 @@ describe "Hash#transform_keys!" do
end
end
+ ruby_version_is "3.0.2" do
+ it "returns the processed keys and non evaluated keys if we broke from the block" do
+ @hash.transform_keys! do |v|
+ break if v == :c
+ v.succ
+ end
+ @hash.should == { b: 1, c: 2, d: 4 }
+ end
+ end
+
it "keeps later pair if new keys conflict" do
@hash.transform_keys! { |_| :a }.should == { a: 4 }
end
diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb
index 9d42b9df4a..97023c1640 100644
--- a/spec/ruby/core/marshal/shared/load.rb
+++ b/spec/ruby/core/marshal/shared/load.rb
@@ -20,42 +20,62 @@ describe :marshal_load, shared: true do
end
describe "when called with a proc" do
- it "returns the value of the proc" do
- Marshal.send(@method, Marshal.dump([1,2]), proc { [3,4] }).should == [3,4]
- end
+ ruby_bug "#18141", ""..."3.1" do
+ it "call the proc with fully initialized strings" do
+ utf8_string = "foo".encode(Encoding::UTF_8)
+ Marshal.send(@method, Marshal.dump(utf8_string), proc { |arg|
+ if arg.is_a?(String)
+ arg.should == utf8_string
+ arg.encoding.should == Encoding::UTF_8
+ end
+ arg
+ })
+ end
- it "calls the proc for recursively visited data" do
- a = [1]
- a << a
- ret = []
- Marshal.send(@method, Marshal.dump(a), proc { |arg| ret << arg; arg })
- ret.first.should == 1
- ret[1].should == [1,a]
- ret[2].should == a
- ret.size.should == 3
+ it "no longer mutate the object after it was passed to the proc" do
+ string = Marshal.load(Marshal.dump("foo"), :freeze.to_proc)
+ string.should.frozen?
+ end
end
- it "loads an Array with proc" do
- arr = []
- s = 'hi'
- s.instance_variable_set(:@foo, 5)
- st = Struct.new("Brittle", :a).new
- st.instance_variable_set(:@clue, 'none')
- st.a = 0.0
- h = Hash.new('def')
- h['nine'] = 9
- a = [:a, :b, :c]
- a.instance_variable_set(:@two, 2)
- obj = [s, 10, s, s, st, a]
- obj.instance_variable_set(:@zoo, 'ant')
- proc = Proc.new { |o| arr << o; o}
-
- Marshal.send(@method, "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F", proc)
+ it "returns the value of the proc" do
+ Marshal.send(@method, Marshal.dump([1,2]), proc { [3,4] }).should == [3,4]
+ end
- arr.should == ["hi", false, 5, 10, "hi", "hi", 0.0, st, "none", false,
- :b, :c, a, 2, ["hi", 10, "hi", "hi", st, [:a, :b, :c]], "ant", false]
+ ruby_bug "#18141", ""..."3.1" do
+ it "calls the proc for recursively visited data" do
+ a = [1]
+ a << a
+ ret = []
+ Marshal.send(@method, Marshal.dump(a), proc { |arg| ret << arg.inspect; arg })
+ ret[0].should == 1.inspect
+ ret[1].should == a.inspect
+ ret.size.should == 2
+ end
- Struct.send(:remove_const, :Brittle)
+ it "loads an Array with proc" do
+ arr = []
+ s = 'hi'
+ s.instance_variable_set(:@foo, 5)
+ st = Struct.new("Brittle", :a).new
+ st.instance_variable_set(:@clue, 'none')
+ st.a = 0.0
+ h = Hash.new('def')
+ h['nine'] = 9
+ a = [:a, :b, :c]
+ a.instance_variable_set(:@two, 2)
+ obj = [s, 10, s, s, st, a]
+ obj.instance_variable_set(:@zoo, 'ant')
+ proc = Proc.new { |o| arr << o.dup; o}
+
+ Marshal.send(@method, "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F", proc)
+
+ arr.should == [
+ false, 5, "hi", 10, "hi", "hi", 0.0, false, "none", st,
+ :b, :c, 2, a, false, "ant", ["hi", 10, "hi", "hi", st, [:a, :b, :c]],
+ ]
+ Struct.send(:remove_const, :Brittle)
+ end
end
end
@@ -119,28 +139,39 @@ describe :marshal_load, shared: true do
end
end
- it "loads an array containing objects having _dump method, and with proc" do
- arr = []
- myproc = Proc.new { |o| arr << o; o }
- o1 = UserDefined.new;
- o2 = UserDefinedWithIvar.new
- obj = [o1, o2, o1, o2]
+ ruby_bug "#18141", ""..."3.1" do
+ it "loads an array containing objects having _dump method, and with proc" do
+ arr = []
+ myproc = Proc.new { |o| arr << o.dup; o }
+ o1 = UserDefined.new;
+ o2 = UserDefinedWithIvar.new
+ obj = [o1, o2, o1, o2]
+
+ Marshal.send(@method, "\x04\b[\tu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06u:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a@\x06@\a", myproc)
- Marshal.send(@method, "\x04\b[\tu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06u:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a@\x06@\a", myproc)
+ arr[0].should == o1
+ arr[1].should == o2
+ arr[2].should == obj
+ arr.size.should == 3
+ end
- arr.should == [o1, o2, o1, o2, obj]
- end
+ it "loads an array containing objects having marshal_dump method, and with proc" do
+ arr = []
+ proc = Proc.new { |o| arr << o.dup; o }
+ o1 = UserMarshal.new
+ o2 = UserMarshalWithIvar.new
- it "loads an array containing objects having marshal_dump method, and with proc" do
- arr = []
- proc = Proc.new { |o| arr << o; o }
- o1 = UserMarshal.new
- o2 = UserMarshalWithIvar.new
- obj = [o1, o2, o1, o2]
+ Marshal.send(@method, "\004\b[\tU:\020UserMarshal\"\nstuffU:\030UserMarshalWithIvar[\006\"\fmy data@\006@\b", proc)
- Marshal.send(@method, "\004\b[\tU:\020UserMarshal\"\nstuffU:\030UserMarshalWithIvar[\006\"\fmy data@\006@\b", proc)
+ arr[0].should == 'stuff'
+ arr[1].should == o1
+ arr[2].should == 'my data'
+ arr[3].should == ['my data']
+ arr[4].should == o2
+ arr[5].should == [o1, o2, o1, o2]
- arr.should == ['stuff', o1, 'my data', ['my data'], o2, o1, o2, obj]
+ arr.size.should == 6
+ end
end
it "assigns classes to nested subclasses of Array correctly" do
diff --git a/spec/ruby/core/string/lstrip_spec.rb b/spec/ruby/core/string/lstrip_spec.rb
index 12c7b13200..6b40189500 100644
--- a/spec/ruby/core/string/lstrip_spec.rb
+++ b/spec/ruby/core/string/lstrip_spec.rb
@@ -7,11 +7,13 @@ describe "String#lstrip" do
" hello world ".lstrip.should == "hello world "
"\n\r\t\n\v\r hello world ".lstrip.should == "hello world "
"hello".lstrip.should == "hello"
- "\000 \000hello\000 \000".lstrip.should == "\000 \000hello\000 \000"
end
- it "does not strip leading \\0" do
- "\x00hello".lstrip.should == "\x00hello"
+ ruby_version_is '3.1' do
+ it "strips leading \\0" do
+ "\x00hello".lstrip.should == "hello"
+ "\000 \000hello\000 \000".lstrip.should == "hello\000 \000"
+ end
end
ruby_version_is ''...'2.7' do
@@ -28,10 +30,14 @@ describe "String#lstrip!" do
a = " hello "
a.lstrip!.should equal(a)
a.should == "hello "
+ end
- a = "\000 \000hello\000 \000"
- a.lstrip!
- a.should == "\000 \000hello\000 \000"
+ ruby_version_is '3.1' do
+ it "strips leading \\0" do
+ a = "\000 \000hello\000 \000"
+ a.lstrip!
+ a.should == "hello\000 \000"
+ end
end
it "returns nil if no modifications were made" do
diff --git a/spec/ruby/core/string/strip_spec.rb b/spec/ruby/core/string/strip_spec.rb
index 252d4a9cc3..463a9fedf3 100644
--- a/spec/ruby/core/string/strip_spec.rb
+++ b/spec/ruby/core/string/strip_spec.rb
@@ -6,11 +6,12 @@ describe "String#strip" do
" hello ".strip.should == "hello"
" hello world ".strip.should == "hello world"
"\tgoodbye\r\v\n".strip.should == "goodbye"
- "\x00 goodbye \x00".strip.should == "\x00 goodbye"
end
- it "returns a copy of self with trailing NULL bytes and whitespace" do
- " \x00 goodbye \x00 ".strip.should == "\x00 goodbye"
+ ruby_version_is '3.1' do
+ it "returns a copy of self without leading and trailing NULL bytes and whitespace" do
+ " \x00 goodbye \x00 ".strip.should == "goodbye"
+ end
end
ruby_version_is ''...'2.7' do
@@ -31,11 +32,6 @@ describe "String#strip!" do
a = "\tgoodbye\r\v\n"
a.strip!
a.should == "goodbye"
-
- a = "\000 goodbye \000"
- a.strip!
- a.should == "\000 goodbye"
-
end
it "returns nil if no modifications where made" do
@@ -44,10 +40,12 @@ describe "String#strip!" do
a.should == "hello"
end
- it "modifies self removing trailing NULL bytes and whitespace" do
- a = " \x00 goodbye \x00 "
- a.strip!
- a.should == "\x00 goodbye"
+ ruby_version_is '3.1' do
+ it "removes leading and trailing NULL bytes and whitespace" do
+ a = "\000 goodbye \000"
+ a.strip!
+ a.should == "goodbye"
+ end
end
it "raises a FrozenError on a frozen instance that is modified" do
diff --git a/spec/ruby/core/time/shared/local.rb b/spec/ruby/core/time/shared/local.rb
index 43f331c4c1..997b7186f1 100644
--- a/spec/ruby/core/time/shared/local.rb
+++ b/spec/ruby/core/time/shared/local.rb
@@ -6,6 +6,7 @@ describe :time_local, shared: true do
end
end
+=begin
platform_is_not :windows do
describe "timezone changes" do
it "correctly adjusts the timezone change to 'CEST' on 'Europe/Amsterdam'" do
@@ -16,6 +17,7 @@ describe :time_local, shared: true do
end
end
end
+=end
end
describe :time_local_10_arg, shared: true do
diff --git a/spec/ruby/library/cgi/cookie/name_spec.rb b/spec/ruby/library/cgi/cookie/name_spec.rb
index 14226824c8..326a43ade3 100644
--- a/spec/ruby/library/cgi/cookie/name_spec.rb
+++ b/spec/ruby/library/cgi/cookie/name_spec.rb
@@ -6,18 +6,18 @@ describe "CGI::Cookie#name" do
cookie = CGI::Cookie.new("test-cookie")
cookie.name.should == "test-cookie"
- cookie = CGI::Cookie.new("name" => "another cookie")
- cookie.name.should == "another cookie"
+ cookie = CGI::Cookie.new("name" => "another-cookie")
+ cookie.name.should == "another-cookie"
end
end
describe "CGI::Cookie#name=" do
it "sets self's expiration date" do
cookie = CGI::Cookie.new("test-cookie")
- cookie.name = "another name"
- cookie.name.should == "another name"
+ cookie.name = "another-name"
+ cookie.name.should == "another-name"
- cookie.name = "and one more"
- cookie.name.should == "and one more"
+ cookie.name = "and-one-more"
+ cookie.name.should == "and-one-more"
end
end
diff --git a/spec/ruby/library/cgi/cookie/parse_spec.rb b/spec/ruby/library/cgi/cookie/parse_spec.rb
index 90d2c3d148..d484c7bad9 100644
--- a/spec/ruby/library/cgi/cookie/parse_spec.rb
+++ b/spec/ruby/library/cgi/cookie/parse_spec.rb
@@ -6,16 +6,16 @@ describe "CGI::Cookie.parse" do
expected = { "test-cookie" => ["one", "two", "three"] }
CGI::Cookie.parse("test-cookie=one&two&three").should == expected
- expected = { "second cookie" => ["three", "four"], "first cookie" => ["one", "two"] }
- CGI::Cookie.parse("first cookie=one&two;second cookie=three&four").should == expected
+ expected = { "second-cookie" => ["three", "four"], "first-cookie" => ["one", "two"] }
+ CGI::Cookie.parse("first-cookie=one&two;second-cookie=three&four").should == expected
end
it "does not use , for cookie separators" do
expected = {
- "first cookie" => ["one", "two"],
- "second cookie" => ["three", "four,third_cookie=five", "six"]
+ "first-cookie" => ["one", "two"],
+ "second-cookie" => ["three", "four,third_cookie=five", "six"]
}
- CGI::Cookie.parse("first cookie=one&two;second cookie=three&four,third_cookie=five&six").should == expected
+ CGI::Cookie.parse("first-cookie=one&two;second-cookie=three&four,third_cookie=five&six").should == expected
end
it "unescapes the Cookie values" do
diff --git a/st.c b/st.c
index dd7870562e..9131dc13c2 100644
--- a/st.c
+++ b/st.c
@@ -1356,7 +1356,6 @@ st_shift(st_table *tab, st_data_t *key, st_data_t *value)
return 1;
}
}
- tab->entries_start = tab->entries_bound = 0;
if (value != 0) *value = 0;
return 0;
}
diff --git a/string.c b/string.c
index 2a95a8357c..8f2a937758 100644
--- a/string.c
+++ b/string.c
@@ -414,6 +414,11 @@ setup_fake_str(struct RString *fake_str, const char *name, long len, int encidx)
fake_str->basic.flags = T_STRING|RSTRING_NOEMBED|STR_NOFREE|STR_FAKESTR;
/* SHARED to be allocated by the callback */
+ if (!name) {
+ RUBY_ASSERT_ALWAYS(len == 0);
+ name = "";
+ }
+
ENCODING_SET_INLINED((VALUE)fake_str, encidx);
RBASIC_SET_CLASS_RAW((VALUE)fake_str, rb_cString);
@@ -698,6 +703,24 @@ rb_enc_cr_str_exact_copy(VALUE dest, VALUE src)
ENC_CODERANGE_SET(dest, ENC_CODERANGE(src));
}
+static int
+enc_coderange_scan(VALUE str, rb_encoding *enc, int encidx)
+{
+ if (rb_enc_mbminlen(enc) > 1 && rb_enc_dummy_p(enc) &&
+ rb_enc_mbminlen(enc = get_actual_encoding(encidx, str)) == 1) {
+ return ENC_CODERANGE_BROKEN;
+ }
+ else {
+ return coderange_scan(RSTRING_PTR(str), RSTRING_LEN(str), enc);
+ }
+}
+
+int
+rb_enc_str_coderange_scan(VALUE str, rb_encoding *enc)
+{
+ return enc_coderange_scan(str, enc, rb_enc_to_index(enc));
+}
+
int
rb_enc_str_coderange(VALUE str)
{
@@ -706,14 +729,7 @@ rb_enc_str_coderange(VALUE str)
if (cr == ENC_CODERANGE_UNKNOWN) {
int encidx = ENCODING_GET(str);
rb_encoding *enc = rb_enc_from_index(encidx);
- if (rb_enc_mbminlen(enc) > 1 && rb_enc_dummy_p(enc) &&
- rb_enc_mbminlen(enc = get_actual_encoding(encidx, str)) == 1) {
- cr = ENC_CODERANGE_BROKEN;
- }
- else {
- cr = coderange_scan(RSTRING_PTR(str), RSTRING_LEN(str),
- enc);
- }
+ cr = enc_coderange_scan(str, enc, encidx);
ENC_CODERANGE_SET(str, cr);
}
return cr;
@@ -955,6 +971,15 @@ static VALUE str_cat_conv_enc_opts(VALUE newstr, long ofs, const char *ptr, long
rb_encoding *from, rb_encoding *to,
int ecflags, VALUE ecopts);
+static inline bool
+is_enc_ascii_string(VALUE str, rb_encoding *enc)
+{
+ int encidx = rb_enc_to_index(enc);
+ if (rb_enc_get_index(str) == encidx)
+ return is_ascii_string(str);
+ return enc_coderange_scan(str, enc, encidx) == ENC_CODERANGE_7BIT;
+}
+
VALUE
rb_str_conv_enc_opts(VALUE str, rb_encoding *from, rb_encoding *to, int ecflags, VALUE ecopts)
{
@@ -965,7 +990,7 @@ rb_str_conv_enc_opts(VALUE str, rb_encoding *from, rb_encoding *to, int ecflags,
if (!to) return str;
if (!from) from = rb_enc_get(str);
if (from == to) return str;
- if ((rb_enc_asciicompat(to) && is_ascii_string(str)) ||
+ if ((rb_enc_asciicompat(to) && is_enc_ascii_string(str, from)) ||
to == rb_ascii8bit_encoding()) {
if (STR_ENC_GET(str) != to) {
str = rb_str_dup(str);
@@ -1716,7 +1741,7 @@ rb_str_init(int argc, VALUE *argv, VALUE str)
const size_t osize = RSTRING(str)->as.heap.len + TERM_LEN(str);
char *new_ptr = ALLOC_N(char, (size_t)capa + termlen);
memcpy(new_ptr, old_ptr, osize < size ? osize : size);
- FL_UNSET_RAW(str, STR_SHARED);
+ FL_UNSET_RAW(str, STR_SHARED|STR_NOFREE);
RSTRING(str)->as.heap.ptr = new_ptr;
}
else if (STR_HEAP_SIZE(str) != (size_t)capa + termlen) {
@@ -3135,13 +3160,13 @@ rb_str_concat_literals(size_t num, const VALUE *strary)
/*
* call-seq:
- * string.concat(*objects) -> new_string
+ * string.concat(*objects) -> string
*
- * Returns a new \String containing the concatenation
- * of +self+ and all objects in +objects+:
+ * Concatenates each object in +objects+ to +self+ and returns +self+:
*
* s = 'foo'
* s.concat('bar', 'baz') # => "foobarbaz"
+ * s # => "foobarbaz"
*
* For each given object +object+ that is an \Integer,
* the value is considered a codepoint and converted to a character before concatenation:
@@ -3173,12 +3198,13 @@ rb_str_concat_multi(int argc, VALUE *argv, VALUE str)
/*
* call-seq:
- * string << object -> str
+ * string << object -> string
+ *
+ * Concatenates +object+ to +self+ and returns +self+:
*
- * Returns a new \String containing the concatenation
- * of +self+ and +object+:
* s = 'foo'
* s << 'bar' # => "foobar"
+ * s # => "foobar"
*
* If +object+ is an \Integer,
* the value is considered a codepoint and converted to a character before concatenation:
@@ -3253,12 +3279,12 @@ rb_str_concat(VALUE str1, VALUE str2)
/*
* call-seq:
- * string.prepend(*other_strings) -> str
+ * string.prepend(*other_strings) -> string
*
- * Returns a new \String containing the concatenation
- * of all given +other_strings+ and +self+:
+ * Prepends each string in +other_strings+ to +self+ and returns +self+:
* s = 'foo'
* s.prepend('bar', 'baz') # => "barbazfoo"
+ * s # => "barbazfoo"
*
* Related: String#concat.
*/
@@ -3434,9 +3460,9 @@ rb_str_eql(VALUE str1, VALUE str2)
* string <=> other_string -> -1, 0, 1, or nil
*
* Compares +self+ and +other_string+, returning:
- * - -1 if +other_string+ is smaller.
+ * - -1 if +other_string+ is larger.
* - 0 if the two are equal.
- * - 1 if +other_string+ is larger.
+ * - 1 if +other_string+ is smaller.
* - +nil+ if the two are incomparable.
*
* Examples:
@@ -3468,9 +3494,9 @@ static VALUE str_casecmp_p(VALUE str1, VALUE str2);
* str.casecmp(other_str) -> -1, 0, 1, or nil
*
* Compares +self+ and +other_string+, ignoring case, and returning:
- * - -1 if +other_string+ is smaller.
+ * - -1 if +other_string+ is larger.
* - 0 if the two are equal.
- * - 1 if +other_string+ is larger.
+ * - 1 if +other_string+ is smaller.
* - +nil+ if the two are incomparable.
*
* Examples:
@@ -3825,7 +3851,6 @@ rb_str_rindex(VALUE str, VALUE sub, long pos)
return str_rindex(str, sub, s, pos, enc);
}
-
/*
* call-seq:
* string.rindex(substring, offset = self.length) -> integer or nil
@@ -3845,6 +3870,23 @@ rb_str_rindex(VALUE str, VALUE sub, long pos)
* 'foo'.rindex(/oo/) # => 1
* 'foo'.rindex(/ooo/) # => nil
*
+ * The _last_ match means starting at the possible last position, not
+ * the last of longest matches.
+ *
+ * 'foo'.rindex(/o+/) # => 2
+ * $~ #=> #<MatchData "o">
+ *
+ * To get the last longest match, needs to combine with negative
+ * lookbehind.
+ *
+ * 'foo'.rindex(/(?<!o)o+/) # => 1
+ * $~ #=> #<MatchData "oo">
+ *
+ * Or String#index with negative lookforward.
+ *
+ * 'foo'.index(/o+(?!.*o)/) # => 1
+ * $~ #=> #<MatchData "oo">
+ *
* \Integer argument +offset+, if given and non-negative, specifies the maximum starting position in the
* string to _end_ the search:
* 'foo'.rindex('o', 0) # => nil
@@ -9265,14 +9307,14 @@ lstrip_offset(VALUE str, const char *s, const char *e, rb_encoding *enc)
/* remove spaces at head */
if (single_byte_optimizable(str)) {
- while (s < e && ascii_isspace(*s)) s++;
+ while (s < e && (*s == '\0' || ascii_isspace(*s))) s++;
}
else {
while (s < e) {
int n;
unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
- if (!rb_isspace(cc)) break;
+ if (cc && !rb_isspace(cc)) break;
s += n;
}
}
@@ -10080,6 +10122,20 @@ rb_str_partition(VALUE str, VALUE sep)
* "hello".rpartition("l") #=> ["hel", "l", "o"]
* "hello".rpartition("x") #=> ["", "", "hello"]
* "hello".rpartition(/.l/) #=> ["he", "ll", "o"]
+ *
+ * The match from the end means starting at the possible last position, not
+ * the last of longest matches.
+ *
+ * "hello".rpartition(/l+/) #=> ["hel", "l", "o"]
+ *
+ * To partition at the last longest match, needs to combine with
+ * negative lookbehind.
+ *
+ * "hello".rpartition(/(?<!l)l+/) #=> ["he", "ll", "o"]
+ *
+ * Or String#partition with negative lookforward.
+ *
+ * "hello".partition(/l+(?!.*l)/) #=> ["he", "ll", "o"]
*/
static VALUE
@@ -11505,6 +11561,10 @@ rb_interned_str_cstr(const char *ptr)
VALUE
rb_enc_interned_str(const char *ptr, long len, rb_encoding *enc)
{
+ if (UNLIKELY(rb_enc_autoload_p(enc))) {
+ rb_enc_autoload(enc);
+ }
+
struct RString fake_str;
return register_fstring(rb_setup_fake_str(&fake_str, ptr, len, enc), TRUE);
}
diff --git a/template/Makefile.in b/template/Makefile.in
index f5a31499b3..bcd4532359 100644
--- a/template/Makefile.in
+++ b/template/Makefile.in
@@ -684,4 +684,4 @@ mjit_build_dir.$(SOEXT): $(MJIT_MIN_HEADER) $(srcdir)/ruby-runner.c ruby-runner.
# yes-test-basic: leaked-globals
leaked-globals: $(COMMONOBJS) prog $(tooldir)/leaked-globals PHONY
- $(Q) $(XRUBY) $(tooldir)/leaked-globals NM=$(NM) SYMBOL_PREFIX=$(SYMBOL_PREFIX) $(srcdir)/configure.ac $(COMMONOBJS)
+ $(Q) $(XRUBY) $(tooldir)/leaked-globals NM="$(NM) -Pgp" SYMBOL_PREFIX=$(SYMBOL_PREFIX) PLATFORM=$(hdrdir)/ruby/$(PLATFORM_DIR).h $(srcdir)/configure.ac $(COMMONOBJS)
diff --git a/test/-ext-/array/test_to_ary_concat.rb b/test/-ext-/array/test_to_ary_concat.rb
new file mode 100644
index 0000000000..feb1bc1109
--- /dev/null
+++ b/test/-ext-/array/test_to_ary_concat.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: false
+require 'test/unit'
+require '-test-/array/to_ary_concat'
+
+class TestConcatStress < Test::Unit::TestCase
+ def setup
+ @stress_level = GC.stress
+ GC.stress = true
+ end
+
+ def teardown
+ GC.stress = @stress_level
+ end
+
+ def test_concat
+ arr = [nil]
+ bar = Bug::Bar.new
+ arr.concat(bar)
+ end
+end
diff --git a/test/-ext-/postponed_job/test_postponed_job.rb b/test/-ext-/postponed_job/test_postponed_job.rb
index 7dc28776d0..fee0172d11 100644
--- a/test/-ext-/postponed_job/test_postponed_job.rb
+++ b/test/-ext-/postponed_job/test_postponed_job.rb
@@ -25,4 +25,11 @@ class TestPostponed_job < Test::Unit::TestCase
Bug.postponed_job_register_one(ary = [])
assert_equal [1], ary
end
+
+ if Bug.respond_to?(:postponed_job_register_in_c_thread)
+ def test_register_in_c_thread
+ assert Bug.postponed_job_register_in_c_thread(ary = [])
+ assert_equal [1], ary
+ end
+ end
end
diff --git a/test/-ext-/string/test_enc_str_buf_cat.rb b/test/-ext-/string/test_enc_str_buf_cat.rb
index 72f903903c..b9a63ec2de 100644
--- a/test/-ext-/string/test_enc_str_buf_cat.rb
+++ b/test/-ext-/string/test_enc_str_buf_cat.rb
@@ -13,4 +13,13 @@ class Test_StringEncStrBufCat < Test::Unit::TestCase
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
+
+ def test_str_conv_enc
+ str = Bug::String.new("aaa".encode("US-ASCII"))
+ assert_same(str, str.str_conv_enc_opts("UTF-8", "US-ASCII", 0, nil))
+
+ str = Bug::String.new("aaa".encode("UTF-16LE").force_encoding("UTF-8"))
+ assert_predicate(str, :ascii_only?) # cache coderange
+ assert_equal("aaa", str.str_conv_enc_opts("UTF-16LE", "UTF-8", 0, nil))
+ end
end
diff --git a/test/-ext-/string/test_fstring.rb b/test/-ext-/string/test_fstring.rb
index 76afa30e14..9b4956ecef 100644
--- a/test/-ext-/string/test_fstring.rb
+++ b/test/-ext-/string/test_fstring.rb
@@ -12,6 +12,22 @@ class Test_String_Fstring < Test::Unit::TestCase
yield fstr
end
+ def test_rb_enc_interned_str_autoloaded_encoding
+ assert_separately([], <<~RUBY)
+ require '-test-/string'
+ assert_include(Encoding::Windows_31J.inspect, 'autoload')
+ Bug::String.rb_enc_interned_str(Encoding::Windows_31J)
+ RUBY
+ end
+
+ def test_rb_enc_str_new_autoloaded_encoding
+ assert_separately([], <<~RUBY)
+ require '-test-/string'
+ assert_include(Encoding::Windows_31J.inspect, 'autoload')
+ Bug::String.rb_enc_str_new(Encoding::Windows_31J)
+ RUBY
+ end
+
def test_instance_variable
str = __method__.to_s * 3
str.instance_variable_set(:@test, 42)
diff --git a/test/cgi/test_cgi_cookie.rb b/test/cgi/test_cgi_cookie.rb
index 115a57e4a1..e3ec4bea52 100644
--- a/test/cgi/test_cgi_cookie.rb
+++ b/test/cgi/test_cgi_cookie.rb
@@ -60,6 +60,24 @@ class CGICookieTest < Test::Unit::TestCase
end
+ def test_cgi_cookie_new_with_domain
+ h = {'name'=>'name1', 'value'=>'value1'}
+ cookie = CGI::Cookie.new('domain'=>'a.example.com', **h)
+ assert_equal('a.example.com', cookie.domain)
+
+ cookie = CGI::Cookie.new('domain'=>'1.example.com', **h)
+ assert_equal('1.example.com', cookie.domain, 'enhanced by RFC 1123')
+
+ assert_raise(ArgumentError) {
+ CGI::Cookie.new('domain'=>'-a.example.com', **h)
+ }
+
+ assert_raise(ArgumentError) {
+ CGI::Cookie.new('domain'=>'a-.example.com', **h)
+ }
+ end
+
+
def test_cgi_cookie_scriptname
cookie = CGI::Cookie.new('name1', 'value1')
assert_equal('', cookie.path)
@@ -101,6 +119,11 @@ class CGICookieTest < Test::Unit::TestCase
end
end
+ def test_cgi_cookie_parse_not_decode_name
+ cookie_str = "%66oo=baz;foo=bar"
+ cookies = CGI::Cookie.parse(cookie_str)
+ assert_equal({"%66oo" => ["baz"], "foo" => ["bar"]}, cookies)
+ end
def test_cgi_cookie_arrayinterface
cookie = CGI::Cookie.new('name1', 'a', 'b', 'c')
@@ -113,6 +136,70 @@ class CGICookieTest < Test::Unit::TestCase
end
+ def test_cgi_cookie_domain_injection_into_name
+ name = "a=b; domain=example.com;"
+ path = "/"
+ domain = "example.jp"
+ assert_raise(ArgumentError) do
+ CGI::Cookie.new('name' => name,
+ 'value' => "value",
+ 'domain' => domain,
+ 'path' => path)
+ end
+ end
+
+
+ def test_cgi_cookie_newline_injection_into_name
+ name = "a=b;\r\nLocation: http://example.com#"
+ path = "/"
+ domain = "example.jp"
+ assert_raise(ArgumentError) do
+ CGI::Cookie.new('name' => name,
+ 'value' => "value",
+ 'domain' => domain,
+ 'path' => path)
+ end
+ end
+
+
+ def test_cgi_cookie_multibyte_injection_into_name
+ name = "a=b;\u3042"
+ path = "/"
+ domain = "example.jp"
+ assert_raise(ArgumentError) do
+ CGI::Cookie.new('name' => name,
+ 'value' => "value",
+ 'domain' => domain,
+ 'path' => path)
+ end
+ end
+
+
+ def test_cgi_cookie_injection_into_path
+ name = "name"
+ path = "/; samesite=none"
+ domain = "example.jp"
+ assert_raise(ArgumentError) do
+ CGI::Cookie.new('name' => name,
+ 'value' => "value",
+ 'domain' => domain,
+ 'path' => path)
+ end
+ end
+
+
+ def test_cgi_cookie_injection_into_domain
+ name = "name"
+ path = "/"
+ domain = "example.jp; samesite=none"
+ assert_raise(ArgumentError) do
+ CGI::Cookie.new('name' => name,
+ 'value' => "value",
+ 'domain' => domain,
+ 'path' => path)
+ end
+ end
+
instance_methods.each do |method|
private method if method =~ /^test_(.*)/ && $1 != ENV['TEST']
diff --git a/test/cgi/test_cgi_header.rb b/test/cgi/test_cgi_header.rb
index bab2d0348a..ec2f4deb72 100644
--- a/test/cgi/test_cgi_header.rb
+++ b/test/cgi/test_cgi_header.rb
@@ -176,6 +176,14 @@ class CGIHeaderTest < Test::Unit::TestCase
end
+ def test_cgi_http_header_crlf_injection
+ cgi = CGI.new
+ assert_raise(RuntimeError) { cgi.http_header("text/xhtml\r\nBOO") }
+ assert_raise(RuntimeError) { cgi.http_header("type" => "text/xhtml\r\nBOO") }
+ assert_raise(RuntimeError) { cgi.http_header("status" => "200 OK\r\nBOO") }
+ assert_raise(RuntimeError) { cgi.http_header("location" => "text/xhtml\r\nBOO") }
+ end
+
instance_methods.each do |method|
private method if method =~ /^test_(.*)/ && $1 != ENV['TEST']
diff --git a/test/date/test_date_parse.rb b/test/date/test_date_parse.rb
index 9f92635387..34a672b069 100644
--- a/test/date/test_date_parse.rb
+++ b/test/date/test_date_parse.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'test/unit'
require 'date'
+require 'timeout'
class TestDateParse < Test::Unit::TestCase
@@ -847,6 +848,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._iso8601('')
assert_equal({}, h)
+
+ h = Date._iso8601(nil)
+ assert_equal({}, h)
+
+ h = Date._iso8601('01-02-03T04:05:06Z'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__rfc3339
@@ -862,6 +870,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._rfc3339('')
assert_equal({}, h)
+
+ h = Date._rfc3339(nil)
+ assert_equal({}, h)
+
+ h = Date._rfc3339('2001-02-03T04:05:06Z'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__xmlschema
@@ -944,6 +959,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._xmlschema('')
assert_equal({}, h)
+
+ h = Date._xmlschema(nil)
+ assert_equal({}, h)
+
+ h = Date._xmlschema('2001-02-03'.to_sym)
+ assert_equal([2001, 2, 3, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__rfc2822
@@ -976,6 +998,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._rfc2822('')
assert_equal({}, h)
+
+ h = Date._rfc2822(nil)
+ assert_equal({}, h)
+
+ h = Date._rfc2822('Sat, 3 Feb 2001 04:05:06 UT'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__httpdate
@@ -996,6 +1025,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._httpdate('')
assert_equal({}, h)
+
+ h = Date._httpdate(nil)
+ assert_equal({}, h)
+
+ h = Date._httpdate('Sat, 03 Feb 2001 04:05:06 GMT'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__jisx0301
@@ -1072,6 +1108,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._jisx0301('')
assert_equal({}, h)
+
+ h = Date._jisx0301(nil)
+ assert_equal({}, h)
+
+ h = Date._jisx0301('H13.02.03T04:05:06.07+0100'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test_iso8601
@@ -1228,4 +1271,32 @@ class TestDateParse < Test::Unit::TestCase
assert_equal(s0, s)
end
+ def test_length_limit
+ assert_raise(ArgumentError) { Date._parse("1" * 1000) }
+ assert_raise(ArgumentError) { Date._iso8601("1" * 1000) }
+ assert_raise(ArgumentError) { Date._rfc3339("1" * 1000) }
+ assert_raise(ArgumentError) { Date._xmlschema("1" * 1000) }
+ assert_raise(ArgumentError) { Date._rfc2822("1" * 1000) }
+ assert_raise(ArgumentError) { Date._rfc822("1" * 1000) }
+ assert_raise(ArgumentError) { Date._jisx0301("1" * 1000) }
+
+ assert_raise(ArgumentError) { Date.parse("1" * 1000) }
+ assert_raise(ArgumentError) { Date.iso8601("1" * 1000) }
+ assert_raise(ArgumentError) { Date.rfc3339("1" * 1000) }
+ assert_raise(ArgumentError) { Date.xmlschema("1" * 1000) }
+ assert_raise(ArgumentError) { Date.rfc2822("1" * 1000) }
+ assert_raise(ArgumentError) { Date.rfc822("1" * 1000) }
+ assert_raise(ArgumentError) { Date.jisx0301("1" * 1000) }
+
+ assert_raise(ArgumentError) { DateTime.parse("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.iso8601("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.rfc3339("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.xmlschema("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.rfc2822("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.rfc822("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.jisx0301("1" * 1000) }
+
+ assert_raise(ArgumentError) { Date._parse("Jan " + "9" * 1000000) }
+ assert_raise(Timeout::Error) { Timeout.timeout(1) { Date._parse("Jan " + "9" * 1000000, limit: nil) } }
+ end
end
diff --git a/test/drb/test_drb.rb b/test/drb/test_drb.rb
index 47b2966ae2..6d7b10e347 100644
--- a/test/drb/test_drb.rb
+++ b/test/drb/test_drb.rb
@@ -323,7 +323,7 @@ class TestDRbAnyToS < Test::Unit::TestCase
end
def test_any_to_s
- server = DRb::DRbServer.new('druby://:0')
+ server = DRb::DRbServer.new('druby://localhost:0')
server.singleton_class.send(:public, :any_to_s)
assert_equal("foo:String", server.any_to_s("foo"))
assert_match(/\A#<DRbTests::TestDRbAnyToS::BO:0x[0-9a-f]+>\z/, server.any_to_s(BO.new))
@@ -335,7 +335,7 @@ end
class TestDRbTCP < Test::Unit::TestCase
def test_immediate_close
- server = DRb::DRbServer.new('druby://:0')
+ server = DRb::DRbServer.new('druby://localhost:0')
host, port, = DRb::DRbTCPSocket.send(:parse_uri, server.uri)
socket = TCPSocket.open host, port
socket.shutdown
diff --git a/test/drb/test_drbssl.rb b/test/drb/test_drbssl.rb
index 1763b38448..0254c7ab50 100644
--- a/test/drb/test_drbssl.rb
+++ b/test/drb/test_drbssl.rb
@@ -34,7 +34,7 @@ class DRbSSLService < DRbService
[ ["C","JP"], ["O","Foo.DRuby.Org"], ["CN", "Sample"] ]
end
- @server = DRb::DRbServer.new('drbssl://:0', manager, config)
+ @server = DRb::DRbServer.new('drbssl://localhost:0', manager, config)
end
end
diff --git a/test/etc/test_etc.rb b/test/etc/test_etc.rb
index dc224d0d32..2eddcf49d7 100644
--- a/test/etc/test_etc.rb
+++ b/test/etc/test_etc.rb
@@ -171,6 +171,7 @@ class TestEtc < Test::Unit::TestCase
def test_ractor
return unless Etc.passwd # => skip test if no platform support
+ Etc.endpwent
assert_ractor(<<~RUBY, require: 'etc')
ractor = Ractor.new do
diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb
index b3c3eaff59..df82c2ff93 100644
--- a/test/fiber/scheduler.rb
+++ b/test/fiber/scheduler.rb
@@ -100,8 +100,10 @@ class Scheduler
self.run
ensure
- @urgent.each(&:close)
- @urgent = nil
+ if @urgent
+ @urgent.each(&:close)
+ @urgent = nil
+ end
@closed = true
@@ -188,3 +190,21 @@ class Scheduler
return fiber
end
end
+
+class BrokenUnblockScheduler < Scheduler
+ def unblock(blocker, fiber)
+ super
+
+ raise "Broken unblock!"
+ end
+end
+
+class SleepingUnblockScheduler < Scheduler
+ # This method is invoked when the thread is exiting.
+ def unblock(blocker, fiber)
+ super
+
+ # This changes the current thread state to `THREAD_RUNNING` which causes `thread_join_sleep` to hang.
+ sleep(0.1)
+ end
+end
diff --git a/test/fiber/test_io.rb b/test/fiber/test_io.rb
index f01cc3af1b..fafda7c310 100644
--- a/test/fiber/test_io.rb
+++ b/test/fiber/test_io.rb
@@ -62,4 +62,39 @@ class TestFiberIO < Test::Unit::TestCase
end
end.each(&:join)
end
+
+ def test_epipe_on_read
+ skip "UNIXSocket is not defined!" unless defined?(UNIXSocket)
+
+ i, o = UNIXSocket.pair
+
+ unless i.nonblock? && o.nonblock?
+ i.close
+ o.close
+ skip "I/O is not non-blocking!"
+ end
+
+ error = nil
+
+ thread = Thread.new do
+ scheduler = Scheduler.new
+ Fiber.set_scheduler scheduler
+
+ Fiber.schedule do
+ begin
+ i.close
+ o.write(MESSAGE)
+ rescue => error
+ # Saved into error.
+ end
+ end
+ end
+
+ thread.join
+
+ i.close
+ o.close
+
+ assert_kind_of Errno::EPIPE, error
+ end
end
diff --git a/test/fiber/test_process.rb b/test/fiber/test_process.rb
index c6583cac9b..a5990be204 100644
--- a/test/fiber/test_process.rb
+++ b/test/fiber/test_process.rb
@@ -33,4 +33,19 @@ class TestFiberProcess < Test::Unit::TestCase
end
end.join
end
+
+ def test_fork
+ omit 'fork not supported' unless Process.respond_to?(:fork)
+ Thread.new do
+ scheduler = Scheduler.new
+ Fiber.set_scheduler scheduler
+
+ Fiber.schedule do
+ pid = Process.fork {}
+ Process.wait(pid)
+
+ assert_predicate $?, :success?
+ end
+ end.join
+ end
end
diff --git a/test/fiber/test_scheduler.rb b/test/fiber/test_scheduler.rb
index 72bde9fcc3..d1fb89d693 100644
--- a/test/fiber/test_scheduler.rb
+++ b/test/fiber/test_scheduler.rb
@@ -66,9 +66,23 @@ class TestFiberScheduler < Test::Unit::TestCase
RUBY
end
- def test_optional_close
+ def test_minimal_interface
+ scheduler = Object.new
+
+ def scheduler.block
+ end
+
+ def scheduler.unblock
+ end
+
+ def scheduler.io_wait
+ end
+
+ def scheduler.kernel_sleep
+ end
+
thread = Thread.new do
- Fiber.set_scheduler Object.new
+ Fiber.set_scheduler scheduler
end
thread.join
diff --git a/test/fiber/test_sleep.rb b/test/fiber/test_sleep.rb
index e882766345..a7e88c0367 100644
--- a/test/fiber/test_sleep.rb
+++ b/test/fiber/test_sleep.rb
@@ -43,4 +43,29 @@ class TestFiberSleep < Test::Unit::TestCase
assert_operator seconds, :>=, 2, "actual: %p" % seconds
end
+
+ def test_broken_sleep
+ thread = Thread.new do
+ Thread.current.report_on_exception = false
+
+ scheduler = Scheduler.new
+
+ def scheduler.kernel_sleep(duration = nil)
+ raise "Broken sleep!"
+ end
+
+ Fiber.set_scheduler scheduler
+
+ Fiber.schedule do
+ sleep 0
+ end
+
+ ensure
+ scheduler.close
+ end
+
+ assert_raise(RuntimeError) do
+ thread.join
+ end
+ end
end
diff --git a/test/fiber/test_thread.rb b/test/fiber/test_thread.rb
new file mode 100644
index 0000000000..5c25c43de2
--- /dev/null
+++ b/test/fiber/test_thread.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+require "test/unit"
+require_relative 'scheduler'
+
+class TestFiberThread < Test::Unit::TestCase
+ def test_thread_join
+ thread = Thread.new do
+ scheduler = Scheduler.new
+ Fiber.set_scheduler scheduler
+
+ result = nil
+ Fiber.schedule do
+ result = Thread.new{:done}.value
+ end
+
+ scheduler.run
+ result
+ end
+
+ assert_equal :done, thread.value
+ end
+
+ def test_thread_join_implicit
+ sleeping = false
+ finished = false
+
+ thread = Thread.new do
+ scheduler = Scheduler.new
+ Fiber.set_scheduler scheduler
+
+ Fiber.schedule do
+ sleeping = true
+ sleep(0.1)
+ finished = true
+ end
+
+ :done
+ end
+
+ Thread.pass until sleeping
+
+ thread.join
+
+ assert_equal :done, thread.value
+ assert finished, "Scheduler thread's task should be finished!"
+ end
+
+ def test_thread_join_blocking
+ thread = Thread.new do
+ scheduler = Scheduler.new
+ Fiber.set_scheduler scheduler
+
+ result = nil
+ Fiber.schedule do
+ Fiber.new(blocking: true) do
+ # This can deadlock if the blocking state is not taken into account:
+ Thread.new do
+ sleep(0)
+ result = :done
+ end.join
+ end.resume
+ end
+
+ scheduler.run
+ result
+ end
+
+ assert_equal :done, thread.value
+ end
+
+ def test_broken_unblock
+ thread = Thread.new do
+ Thread.current.report_on_exception = false
+
+ scheduler = BrokenUnblockScheduler.new
+
+ Fiber.set_scheduler scheduler
+
+ Fiber.schedule do
+ Thread.new{
+ Thread.current.report_on_exception = false
+ }.join
+ end
+
+ scheduler.run
+ ensure
+ scheduler.close
+ end
+
+ assert_raise(RuntimeError) do
+ thread.join
+ end
+ end
+
+ def test_thread_join_hang
+ thread = Thread.new do
+ scheduler = SleepingUnblockScheduler.new
+
+ Fiber.set_scheduler scheduler
+
+ Fiber.schedule do
+ Thread.new{sleep(0.01)}.value
+ end
+ end
+
+ thread.join
+ end
+end
diff --git a/test/fiddle/helper.rb b/test/fiddle/helper.rb
index f38f9036a3..a6e2019924 100644
--- a/test/fiddle/helper.rb
+++ b/test/fiddle/helper.rb
@@ -47,8 +47,8 @@ when /linux/
libm_so = libc_so
else
# glibc
- libc_so = File.join(libdir, "libc.so.6")
- libm_so = File.join(libdir, "libm.so.6")
+ libc_so = "libc.so.6"
+ libm_so = "libm.so.6"
end
when /mingw/, /mswin/
require "rbconfig"
diff --git a/test/fiddle/test_closure.rb b/test/fiddle/test_closure.rb
index 2de0660725..9e748bf5ee 100644
--- a/test/fiddle/test_closure.rb
+++ b/test/fiddle/test_closure.rb
@@ -20,6 +20,18 @@ module Fiddle
end
end
+ def test_type_symbol
+ closure = Closure.new(:int, [:void])
+ assert_equal([
+ TYPE_INT,
+ [TYPE_VOID],
+ ],
+ [
+ closure.instance_variable_get(:@ctype),
+ closure.instance_variable_get(:@args),
+ ])
+ end
+
def test_call
closure = Class.new(Closure) {
def call
@@ -42,6 +54,19 @@ module Fiddle
assert_equal 10, func.call(10)
end
+ def test_const_string
+ closure_class = Class.new(Closure) do
+ def call(string)
+ @return_string = "Hello! #{string}"
+ @return_string
+ end
+ end
+ closure = closure_class.new(:const_string, [:const_string])
+
+ func = Function.new(closure, [:const_string], :const_string)
+ assert_equal("Hello! World!", func.call("World!"))
+ end
+
def test_block_caller
cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one|
one
diff --git a/test/fiddle/test_cparser.rb b/test/fiddle/test_cparser.rb
index ef8cec5daa..24e1800e59 100644
--- a/test/fiddle/test_cparser.rb
+++ b/test/fiddle/test_cparser.rb
@@ -12,53 +12,77 @@ module Fiddle
def test_char_ctype
assert_equal(TYPE_CHAR, parse_ctype('char'))
+ assert_equal(TYPE_CHAR, parse_ctype('const char'))
assert_equal(TYPE_CHAR, parse_ctype('signed char'))
+ assert_equal(TYPE_CHAR, parse_ctype('const signed char'))
assert_equal(-TYPE_CHAR, parse_ctype('unsigned char'))
+ assert_equal(-TYPE_CHAR, parse_ctype('const unsigned char'))
end
def test_short_ctype
assert_equal(TYPE_SHORT, parse_ctype('short'))
+ assert_equal(TYPE_SHORT, parse_ctype('const short'))
assert_equal(TYPE_SHORT, parse_ctype('short int'))
+ assert_equal(TYPE_SHORT, parse_ctype('const short int'))
assert_equal(TYPE_SHORT, parse_ctype('signed short'))
+ assert_equal(TYPE_SHORT, parse_ctype('const signed short'))
assert_equal(TYPE_SHORT, parse_ctype('signed short int'))
+ assert_equal(TYPE_SHORT, parse_ctype('const signed short int'))
assert_equal(-TYPE_SHORT, parse_ctype('unsigned short'))
+ assert_equal(-TYPE_SHORT, parse_ctype('const unsigned short'))
assert_equal(-TYPE_SHORT, parse_ctype('unsigned short int'))
+ assert_equal(-TYPE_SHORT, parse_ctype('const unsigned short int'))
end
def test_int_ctype
assert_equal(TYPE_INT, parse_ctype('int'))
+ assert_equal(TYPE_INT, parse_ctype('const int'))
assert_equal(TYPE_INT, parse_ctype('signed int'))
+ assert_equal(TYPE_INT, parse_ctype('const signed int'))
assert_equal(-TYPE_INT, parse_ctype('uint'))
+ assert_equal(-TYPE_INT, parse_ctype('const uint'))
assert_equal(-TYPE_INT, parse_ctype('unsigned int'))
+ assert_equal(-TYPE_INT, parse_ctype('const unsigned int'))
end
def test_long_ctype
assert_equal(TYPE_LONG, parse_ctype('long'))
+ assert_equal(TYPE_LONG, parse_ctype('const long'))
assert_equal(TYPE_LONG, parse_ctype('long int'))
+ assert_equal(TYPE_LONG, parse_ctype('const long int'))
assert_equal(TYPE_LONG, parse_ctype('signed long'))
+ assert_equal(TYPE_LONG, parse_ctype('const signed long'))
assert_equal(TYPE_LONG, parse_ctype('signed long int'))
+ assert_equal(TYPE_LONG, parse_ctype('const signed long int'))
assert_equal(-TYPE_LONG, parse_ctype('unsigned long'))
+ assert_equal(-TYPE_LONG, parse_ctype('const unsigned long'))
assert_equal(-TYPE_LONG, parse_ctype('unsigned long int'))
+ assert_equal(-TYPE_LONG, parse_ctype('const unsigned long int'))
end
def test_size_t_ctype
assert_equal(TYPE_SIZE_T, parse_ctype("size_t"))
+ assert_equal(TYPE_SIZE_T, parse_ctype("const size_t"))
end
def test_ssize_t_ctype
assert_equal(TYPE_SSIZE_T, parse_ctype("ssize_t"))
+ assert_equal(TYPE_SSIZE_T, parse_ctype("const ssize_t"))
end
def test_ptrdiff_t_ctype
assert_equal(TYPE_PTRDIFF_T, parse_ctype("ptrdiff_t"))
+ assert_equal(TYPE_PTRDIFF_T, parse_ctype("const ptrdiff_t"))
end
def test_intptr_t_ctype
assert_equal(TYPE_INTPTR_T, parse_ctype("intptr_t"))
+ assert_equal(TYPE_INTPTR_T, parse_ctype("const intptr_t"))
end
def test_uintptr_t_ctype
assert_equal(TYPE_UINTPTR_T, parse_ctype("uintptr_t"))
+ assert_equal(TYPE_UINTPTR_T, parse_ctype("const uintptr_t"))
end
def test_undefined_ctype
@@ -66,7 +90,10 @@ module Fiddle
end
def test_undefined_ctype_with_type_alias
- assert_equal(-TYPE_LONG, parse_ctype('DWORD', {"DWORD" => "unsigned long"}))
+ assert_equal(-TYPE_LONG,
+ parse_ctype('DWORD', {"DWORD" => "unsigned long"}))
+ assert_equal(-TYPE_LONG,
+ parse_ctype('const DWORD', {"DWORD" => "unsigned long"}))
end
def expand_struct_types(types)
@@ -83,11 +110,21 @@ module Fiddle
end
def test_struct_basic
- assert_equal [[TYPE_INT, TYPE_CHAR], ['i', 'c']], parse_struct_signature(['int i', 'char c'])
+ assert_equal([[TYPE_INT, TYPE_CHAR], ['i', 'c']],
+ parse_struct_signature(['int i', 'char c']))
+ assert_equal([[TYPE_INT, TYPE_CHAR], ['i', 'c']],
+ parse_struct_signature(['const int i', 'const char c']))
end
def test_struct_array
- assert_equal [[[TYPE_CHAR,80],[TYPE_INT,5]], ['buffer','x']], parse_struct_signature(['char buffer[80]', 'int[5] x'])
+ assert_equal([[[TYPE_CHAR, 80], [TYPE_INT, 5]],
+ ['buffer', 'x']],
+ parse_struct_signature(['char buffer[80]',
+ 'int[5] x']))
+ assert_equal([[[TYPE_CHAR, 80], [TYPE_INT, 5]],
+ ['buffer', 'x']],
+ parse_struct_signature(['const char buffer[80]',
+ 'const int[5] x']))
end
def test_struct_nested_struct
@@ -178,15 +215,22 @@ module Fiddle
end
def test_struct_array_str
- assert_equal [[[TYPE_CHAR,80],[TYPE_INT,5]], ['buffer','x']], parse_struct_signature('char buffer[80], int[5] x')
+ assert_equal([[[TYPE_CHAR, 80], [TYPE_INT, 5]],
+ ['buffer', 'x']],
+ parse_struct_signature('char buffer[80], int[5] x'))
+ assert_equal([[[TYPE_CHAR, 80], [TYPE_INT, 5]],
+ ['buffer', 'x']],
+ parse_struct_signature('const char buffer[80], const int[5] x'))
end
def test_struct_function_pointer
- assert_equal [[TYPE_VOIDP], ['cb']], parse_struct_signature(['void (*cb)(const char*)'])
+ assert_equal([[TYPE_VOIDP], ['cb']],
+ parse_struct_signature(['void (*cb)(const char*)']))
end
def test_struct_function_pointer_str
- assert_equal [[TYPE_VOIDP,TYPE_VOIDP], ['cb', 'data']], parse_struct_signature('void (*cb)(const char*), const char* data')
+ assert_equal([[TYPE_VOIDP, TYPE_VOIDP], ['cb', 'data']],
+ parse_struct_signature('void (*cb)(const char*), const char* data'))
end
def test_struct_string
diff --git a/test/io/console/test_io_console.rb b/test/io/console/test_io_console.rb
index 3962de3790..bec10c5da0 100644
--- a/test/io/console/test_io_console.rb
+++ b/test/io/console/test_io_console.rb
@@ -235,6 +235,15 @@ defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do
assert_equal("\r\n", r.gets)
assert_equal("\"asdf\"", r.gets.chomp)
end
+
+ run_pty("p IO.console.getpass('> ')") do |r, w|
+ assert_equal("> ", r.readpartial(10))
+ sleep 0.1
+ w.print "asdf\C-D\C-D"
+ sleep 0.1
+ assert_equal("\r\n", r.gets)
+ assert_equal("\"asdf\"", r.gets.chomp)
+ end
end
def test_iflush
diff --git a/test/io/wait/test_io_wait_uncommon.rb b/test/io/wait/test_io_wait_uncommon.rb
index 28b4a0f8c4..b6f1c29bcd 100644
--- a/test/io/wait/test_io_wait_uncommon.rb
+++ b/test/io/wait/test_io_wait_uncommon.rb
@@ -6,15 +6,10 @@ require 'io/wait'
# We may optimize IO#wait_*able for non-Linux kernels in the future
class TestIOWaitUncommon < Test::Unit::TestCase
def test_tty_wait
- begin
- tty = File.open('/dev/tty', 'w+')
- rescue Errno::ENOENT, Errno::ENXIO => e
- skip "/dev/tty: #{e.message} (#{e.class})"
+ check_dev('/dev/tty', mode: 'w+') do |tty|
+ assert_include [ nil, tty ], tty.wait_readable(0)
+ assert_equal tty, tty.wait_writable(1), 'portability test'
end
- assert_include [ nil, tty ], tty.wait_readable(0)
- assert_equal tty, tty.wait_writable(1), 'portability test'
- ensure
- tty&.close
end
def test_fifo_wait
@@ -44,36 +39,40 @@ class TestIOWaitUncommon < Test::Unit::TestCase
# used to find portability problems because some ppoll implementations
# are incomplete and do not work for certain "file" types
- def check_dev(dev, m = :wait_readable)
+ def check_dev(dev, m = :wait_readable, mode: m == :wait_readable ? 'r' : 'w', &block)
begin
- fp = File.open("/dev/#{dev}", m == :wait_readable ? 'r' : 'w')
+ fp = File.open(dev, mode)
+ rescue Errno::ENOENT
+ return # Ignore silently
rescue SystemCallError => e
skip "#{dev} could not be opened #{e.message} (#{e.class})"
end
- assert_same fp, fp.__send__(m)
+ if block
+ yield fp
+ else
+ assert_same fp, fp.__send__(m)
+ end
ensure
fp&.close
end
def test_wait_readable_urandom
- check_dev 'urandom'
+ check_dev('/dev/urandom')
end
def test_wait_readable_random
- File.open('/dev/random') do |fp|
+ check_dev('/dev/random') do |fp|
assert_nothing_raised do
fp.wait_readable(0)
end
end
- rescue SystemCallError => e
- skip "/dev/random could not be opened #{e.message} (#{e.class})"
end
def test_wait_readable_zero
- check_dev 'zero'
+ check_dev('/dev/zero')
end
def test_wait_writable_null
- check_dev 'null', :wait_writable
+ check_dev(IO::NULL, :wait_writable)
end
end
diff --git a/test/io/wait/test_ractor.rb b/test/io/wait/test_ractor.rb
new file mode 100644
index 0000000000..3d286af77f
--- /dev/null
+++ b/test/io/wait/test_ractor.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+require 'test/unit'
+require 'rbconfig'
+require 'io/wait'
+
+class TestIOWaitInRactor < Test::Unit::TestCase
+ def setup
+ omit unless defined? Ractor
+ end
+
+ def test_ractor
+ ext = "/io/wait.#{RbConfig::CONFIG['DLEXT']}"
+ path = $".find {|path| path.end_with?(ext)}
+ assert_in_out_err(%W[-r#{path}], <<-"end;", ["true"], [])
+ $VERBOSE = nil
+ r = Ractor.new do
+ $stdout.equal?($stdout.wait_writable)
+ end
+ puts r.take
+ end;
+ end
+end
diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb
index 41f84f1922..044d852a32 100644
--- a/test/irb/test_cmd.rb
+++ b/test/irb/test_cmd.rb
@@ -5,6 +5,32 @@ require "irb/extend-command"
module TestIRB
class ExtendCommand < Test::Unit::TestCase
+ class TestInputMethod < ::IRB::InputMethod
+ attr_reader :list, :line_no
+
+ def initialize(list = [])
+ super("test")
+ @line_no = 0
+ @list = list
+ end
+
+ def gets
+ @list[@line_no]&.tap {@line_no += 1}
+ end
+
+ def eof?
+ @line_no >= @list.size
+ end
+
+ def encoding
+ Encoding.default_external
+ end
+
+ def reset
+ @line_no = 0
+ end
+ end
+
def setup
@pwd = Dir.pwd
@tmpdir = File.join(Dir.tmpdir, "test_reline_config_#{$$}")
@@ -17,12 +43,14 @@ module TestIRB
Dir.chdir(@tmpdir)
@home_backup = ENV["HOME"]
ENV["HOME"] = @tmpdir
+ @xdg_config_home_backup = ENV.delete("XDG_CONFIG_HOME")
@default_encoding = [Encoding.default_external, Encoding.default_internal]
@stdio_encodings = [STDIN, STDOUT, STDERR].map {|io| [io.external_encoding, io.internal_encoding] }
IRB.instance_variable_get(:@CONF).clear
end
def teardown
+ ENV["XDG_CONFIG_HOME"] = @xdg_config_home_backup
ENV["HOME"] = @home_backup
Dir.chdir(@pwd)
FileUtils.rm_rf(@tmpdir)
@@ -42,12 +70,12 @@ module TestIRB
IRB.conf[:USE_SINGLELINE] = false
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
- irb = IRB::Irb.new(workspace)
+ irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
- InputMethod:\sReidlineInputMethod\swith\sReline .+ and .+\n
+ InputMethod:\sAbstract\sInputMethod\n
\.irbrc\spath: .+\n
RUBY_PLATFORM: .+
}x
@@ -62,12 +90,12 @@ module TestIRB
IRB.conf[:USE_SINGLELINE] = true
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
- irb = IRB::Irb.new(workspace)
+ irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
- InputMethod:\sReadlineInputMethod\swith .+ and .+\n
+ InputMethod:\sAbstract\sInputMethod\n
\.irbrc\spath: .+\n
RUBY_PLATFORM: .+
}x
@@ -85,12 +113,12 @@ module TestIRB
IRB.conf[:USE_SINGLELINE] = false
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
- irb = IRB::Irb.new(workspace)
+ irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
- InputMethod:\sReidlineInputMethod\swith\sReline\s[^ ]+(?!\sand\s.+)\n
+ InputMethod:\sAbstract\sInputMethod\n
RUBY_PLATFORM: .+\n
\z
}x
@@ -112,12 +140,12 @@ module TestIRB
IRB.conf[:USE_SINGLELINE] = true
IRB.conf[:VERBOSE] = false
workspace = IRB::WorkSpace.new(self)
- irb = IRB::Irb.new(workspace)
+ irb = IRB::Irb.new(workspace, TestInputMethod.new([]))
IRB.conf[:MAIN_CONTEXT] = irb.context
expected = %r{
Ruby\sversion: .+\n
IRB\sversion:\sirb .+\n
- InputMethod:\sReadlineInputMethod\swith\s(?~.*\sand\s.+)\n
+ InputMethod:\sAbstract\sInputMethod\n
RUBY_PLATFORM: .+\n
\z
}x
@@ -128,32 +156,6 @@ module TestIRB
IRB.const_set(:IRBRC_EXT, ext_backup)
end
- class TestInputMethod < ::IRB::InputMethod
- attr_reader :list, :line_no
-
- def initialize(list = [])
- super("test")
- @line_no = 0
- @list = list
- end
-
- def gets
- @list[@line_no]&.tap {@line_no += 1}
- end
-
- def eof?
- @line_no >= @list.size
- end
-
- def encoding
- Encoding.default_external
- end
-
- def reset
- @line_no = 0
- end
- end
-
def test_measure
IRB.init_config(nil)
IRB.conf[:PROMPT] = {
@@ -372,5 +374,56 @@ module TestIRB
/=> "bug17564"\n/,
], out)
end
+
+ def test_ls
+ input = TestInputMethod.new([
+ "ls Object.new.tap { |o| o.instance_variable_set(:@a, 1) }\n",
+ ])
+ IRB.init_config(nil)
+ workspace = IRB::WorkSpace.new(self)
+ IRB.conf[:VERBOSE] = false
+ irb = IRB::Irb.new(workspace, input)
+ IRB.conf[:MAIN_CONTEXT] = irb.context
+ irb.context.return_format = "=> %s\n"
+ out, err = capture_output do
+ irb.eval_input
+ end
+ assert_empty err
+ assert_match(/^instance variables:\s+@a\n/m, out)
+ end
+
+ def test_show_source
+ input = TestInputMethod.new([
+ "show_source 'IRB.conf'\n",
+ ])
+ IRB.init_config(nil)
+ workspace = IRB::WorkSpace.new(self)
+ irb = IRB::Irb.new(workspace, input)
+ IRB.conf[:VERBOSE] = false
+ IRB.conf[:MAIN_CONTEXT] = irb.context
+ irb.context.return_format = "=> %s\n"
+ out, err = capture_output do
+ irb.eval_input
+ end
+ assert_empty err
+ assert_match(%r[/irb\.rb], out)
+ end
+
+ def test_whereami
+ input = TestInputMethod.new([
+ "whereami\n",
+ ])
+ IRB.init_config(nil)
+ workspace = IRB::WorkSpace.new(self)
+ IRB.conf[:VERBOSE] = false
+ irb = IRB::Irb.new(workspace, input)
+ IRB.conf[:MAIN_CONTEXT] = irb.context
+ irb.context.return_format = "=> %s\n"
+ out, err = capture_output do
+ irb.eval_input
+ end
+ assert_empty err
+ assert_match(/^From: .+ @ line \d+ :\n/, out)
+ end
end
end
diff --git a/test/irb/test_color.rb b/test/irb/test_color.rb
index 9976008124..a28ae06117 100644
--- a/test/irb/test_color.rb
+++ b/test/irb/test_color.rb
@@ -66,6 +66,7 @@ module TestIRB
"\t" => "\t", # not ^I
"foo(*%W(bar))" => "foo(*#{RED}#{BOLD}%W(#{CLEAR}#{RED}bar#{CLEAR}#{RED}#{BOLD})#{CLEAR})",
"$stdout" => "#{GREEN}#{BOLD}$stdout#{CLEAR}",
+ "__END__" => "#{GREEN}__END__#{CLEAR}",
}
# specific to Ruby 2.7+
diff --git a/test/irb/test_color_printer.rb b/test/irb/test_color_printer.rb
index 1b28837658..1afc7ccf55 100644
--- a/test/irb/test_color_printer.rb
+++ b/test/irb/test_color_printer.rb
@@ -34,6 +34,7 @@ module TestIRB
end
{
1 => "#{BLUE}#{BOLD}1#{CLEAR}\n",
+ "a\nb" => %[#{RED}#{BOLD}"#{CLEAR}#{RED}a\\nb#{CLEAR}#{RED}#{BOLD}"#{CLEAR}\n],
IRBTestColorPrinter.new('test') => "#{GREEN}#<struct TestIRB::TestColorPrinter::IRBTestColorPrinter#{CLEAR} a#{GREEN}=#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{RED}test#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{GREEN}>#{CLEAR}\n",
Ripper::Lexer.new('1').scan => "[#{GREEN}#<Ripper::Lexer::Elem:#{CLEAR} on_int@1:0 END token: #{RED}#{BOLD}\"#{CLEAR}#{RED}1#{CLEAR}#{RED}#{BOLD}\"#{CLEAR}#{GREEN}>#{CLEAR}]\n",
Class.new{define_method(:pretty_print){|q| q.text("[__FILE__, __LINE__, __ENCODING__]")}}.new => "[#{CYAN}#{BOLD}__FILE__#{CLEAR}, #{CYAN}#{BOLD}__LINE__#{CLEAR}, #{CYAN}#{BOLD}__ENCODING__#{CLEAR}]\n",
diff --git a/test/irb/test_completion.rb b/test/irb/test_completion.rb
index 984453d059..535690ae22 100644
--- a/test/irb/test_completion.rb
+++ b/test/irb/test_completion.rb
@@ -55,5 +55,33 @@ module TestIRB
namespace = IRB::InputCompletor.retrieve_completion_data("1.positive?", bind: binding, doc_namespace: true)
assert_equal "Integer.positive?", namespace
end
+
+ def test_complete_require
+ candidates = IRB::InputCompletor::CompletionProc.("'irb", "require ", "")
+ %w['irb/init 'irb/ruby-lex].each do |word|
+ assert_include candidates, word
+ end
+ # Test cache
+ candidates = IRB::InputCompletor::CompletionProc.("'irb", "require ", "")
+ %w['irb/init 'irb/ruby-lex].each do |word|
+ assert_include candidates, word
+ end
+ end
+
+ def test_complete_require_relative
+ candidates = Dir.chdir(__dir__ + "/../..") do
+ IRB::InputCompletor::CompletionProc.("'lib/irb", "require_relative ", "")
+ end
+ %w['lib/irb/init 'lib/irb/ruby-lex].each do |word|
+ assert_include candidates, word
+ end
+ # Test cache
+ candidates = Dir.chdir(__dir__ + "/../..") do
+ IRB::InputCompletor::CompletionProc.("'lib/irb", "require_relative ", "")
+ end
+ %w['lib/irb/init 'lib/irb/ruby-lex].each do |word|
+ assert_include candidates, word
+ end
+ end
end
end
diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb
index 392a6afa9a..81b7fe8679 100644
--- a/test/irb/test_history.rb
+++ b/test/irb/test_history.rb
@@ -127,11 +127,43 @@ module TestIRB
INPUT
end
+ def test_history_concurrent_use
+ omit "Skip Editline" if /EditLine/n.match(Readline::VERSION)
+ IRB.conf[:SAVE_HISTORY] = 1
+ assert_history(<<~EXPECTED_HISTORY, <<~INITIAL_HISTORY, <<~INPUT) do |history_file|
+ exit
+ 5
+ exit
+ EXPECTED_HISTORY
+ 1
+ 2
+ 3
+ 4
+ INITIAL_HISTORY
+ 5
+ exit
+ INPUT
+ assert_history(<<~EXPECTED_HISTORY2, <<~INITIAL_HISTORY2, <<~INPUT2)
+ exit
+ EXPECTED_HISTORY2
+ 1
+ 2
+ 3
+ 4
+ INITIAL_HISTORY2
+ 5
+ exit
+ INPUT2
+ File.utime(File.atime(history_file), File.mtime(history_file) + 2, history_file)
+ end
+ end
+
private
def assert_history(expected_history, initial_irb_history, input)
backup_verbose, $VERBOSE = $VERBOSE, nil
backup_home = ENV["HOME"]
+ backup_xdg_config_home = ENV.delete("XDG_CONFIG_HOME")
IRB.conf[:LC_MESSAGES] = IRB::Locale.new
actual_history = nil
Dir.mktmpdir("test_irb_history_#{$$}") do |tmpdir|
@@ -143,6 +175,11 @@ module TestIRB
io = TestInputMethod.new
io.class::HISTORY.clear
io.load_history
+ if block_given?
+ history = io.class::HISTORY.dup
+ yield IRB.rc_file("_history")
+ io.class::HISTORY.replace(history)
+ end
io.class::HISTORY.concat(input.split)
io.save_history
@@ -160,6 +197,7 @@ module TestIRB
ensure
$VERBOSE = backup_verbose
ENV["HOME"] = backup_home
+ ENV["XDG_CONFIG_HOME"] = backup_xdg_config_home
end
def with_temp_stdio
diff --git a/test/irb/test_init.rb b/test/irb/test_init.rb
index 83b4b5a543..2c50b5da3a 100644
--- a/test/irb/test_init.rb
+++ b/test/irb/test_init.rb
@@ -64,6 +64,12 @@ module TestIRB
ENV["IRBRC"] = backup_irbrc
end
+ def test_recovery_sigint
+ bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
+ status = assert_in_out_err(bundle_exec + %w[-W0 -rirb -e binding.irb;loop{Process.kill("SIGINT",$$)} -- -f --], "exit\n", //, //)
+ Process.kill("SIGKILL", status.pid) if !status.exited? && !status.stopped? && !status.signaled?
+ end
+
private
def with_argv(argv)
diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb
index a45ca668b9..556afbd776 100644
--- a/test/irb/test_ruby_lex.rb
+++ b/test/irb/test_ruby_lex.rb
@@ -136,6 +136,20 @@ module TestIRB
end
end
+ def test_endless_range_at_end_of_line
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.6.0')
+ skip 'Endless range is available in 2.6.0 or later'
+ end
+ input_with_prompt = [
+ PromptRow.new('001:0: :> ', %q(a = 3..)),
+ PromptRow.new('002:0: :* ', %q()),
+ ]
+
+ lines = input_with_prompt.map(&:content)
+ expected_prompt_list = input_with_prompt.map(&:prompt)
+ assert_dynamic_prompt(lines, expected_prompt_list)
+ end
+
def test_incomplete_coding_magic_comment
input_with_correct_indents = [
Row.new(%q(#coding:u), nil, 0),
@@ -544,8 +558,7 @@ module TestIRB
skip 'This test needs Ripper::Lexer#scan to take broken tokens'
end
- ruby_lex = RubyLex.new
- tokens = ruby_lex.ripper_lex_without_warning('%wwww')
+ tokens = RubyLex.ripper_lex_without_warning('%wwww')
pos_to_index = {}
tokens.each_with_index { |t, i|
assert_nil(pos_to_index[t[0]], "There is already another token in the position of #{t.inspect}.")
@@ -558,8 +571,7 @@ module TestIRB
skip 'This test needs Ripper::Lexer#scan to take broken tokens'
end
- ruby_lex = RubyLex.new
- tokens = ruby_lex.ripper_lex_without_warning(<<~EOC.chomp)
+ tokens = RubyLex.ripper_lex_without_warning(<<~EOC.chomp)
def foo
%wwww
end
diff --git a/test/irb/yamatanooroti/test_rendering.rb b/test/irb/yamatanooroti/test_rendering.rb
new file mode 100644
index 0000000000..8f55b38a93
--- /dev/null
+++ b/test/irb/yamatanooroti/test_rendering.rb
@@ -0,0 +1,165 @@
+require 'irb'
+
+begin
+ require 'yamatanooroti'
+
+ class IRB::TestRendering < Yamatanooroti::TestCase
+ def setup
+ @pwd = Dir.pwd
+ suffix = '%010d' % Random.rand(0..65535)
+ @tmpdir = File.join(File.expand_path(Dir.tmpdir), "test_irb_#{$$}_#{suffix}")
+ begin
+ Dir.mkdir(@tmpdir)
+ rescue Errno::EEXIST
+ FileUtils.rm_rf(@tmpdir)
+ Dir.mkdir(@tmpdir)
+ end
+ @irbrc_backup = ENV['IRBRC']
+ @irbrc_file = ENV['IRBRC'] = File.join(@tmpdir, 'temporaty_irbrc')
+ File.unlink(@irbrc_file) if File.exist?(@irbrc_file)
+ end
+
+ def teardown
+ FileUtils.rm_rf(@tmpdir)
+ ENV['IRBRC'] = @irbrc_backup
+ ENV.delete('RELINE_TEST_PROMPT') if ENV['RELINE_TEST_PROMPT']
+ end
+
+ def test_launch
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(25, 80, %W{ruby -I#{@pwd}/lib -I#{@pwd}/../reline/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write(<<~EOC)
+ 'Hello, World!'
+ EOC
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001:0> 'Hello, World!'
+ => "Hello, World!"
+ irb(main):002:0>
+ EOC
+ end
+
+ def test_multiline_paste
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(25, 80, %W{ruby -I#{@pwd}/lib -I#{@pwd}/../reline/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write(<<~EOC)
+ class A
+ def inspect; '#<A>'; end
+ def a; self; end
+ def b; true; end
+ end
+
+ a = A.new
+
+ a
+ .a
+ .b
+ EOC
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001:1* class A
+ irb(main):002:1* def inspect; '#<A>'; end
+ irb(main):003:1* def a; self; end
+ irb(main):004:1* def b; true; end
+ irb(main):005:0> end
+ => :b
+ irb(main):006:0>
+ irb(main):007:0> a = A.new
+ => #<A>
+ irb(main):008:0>
+ irb(main):009:0> a
+ irb(main):010:0> .a
+ irb(main):011:0> .b
+ => true
+ irb(main):012:0>
+ EOC
+ end
+
+ def test_evaluate_each_toplevel_statement_by_multiline_paste
+ write_irbrc <<~'LINES'
+ puts 'start IRB'
+ LINES
+ start_terminal(40, 80, %W{ruby -I#{@pwd}/lib -I#{@pwd}/../reline/lib #{@pwd}/exe/irb}, startup_message: 'start IRB')
+ write(<<~EOC)
+ class A
+ def inspect; '#<A>'; end
+ def b; self; end
+ def c; true; end
+ end
+
+ a = A.new
+
+ a
+ .b
+ # aaa
+ .c
+
+ (a)
+ &.b()
+
+
+ class A def b; self; end; def c; true; end; end;
+ a = A.new
+ a
+ .b
+ # aaa
+ .c
+ (a)
+ &.b()
+ EOC
+ close
+ assert_screen(<<~EOC)
+ start IRB
+ irb(main):001:1* class A
+ irb(main):002:1* def inspect; '#<A>'; end
+ irb(main):003:1* def b; self; end
+ irb(main):004:1* def c; true; end
+ irb(main):005:0> end
+ => :c
+ irb(main):006:0>
+ irb(main):007:0> a = A.new
+ => #<A>
+ irb(main):008:0>
+ irb(main):009:0> a
+ irb(main):010:0> .b
+ irb(main):011:0> # aaa
+ irb(main):012:0> .c
+ => true
+ irb(main):013:0>
+ irb(main):014:0> (a)
+ irb(main):015:0> &.b()
+ => #<A>
+ irb(main):016:0>
+ irb(main):017:0>
+ irb(main):018:0> class A def b; self; end; def c; true; end; end;
+ => :c
+ irb(main):019:0> a = A.new
+ => #<A>
+ irb(main):020:0> a
+ irb(main):021:0> .b
+ irb(main):022:0> # aaa
+ irb(main):023:0> .c
+ => true
+ irb(main):024:0> (a)
+ irb(main):025:0> &.b()
+ => #<A>
+ irb(main):026:0>
+ EOC
+ end
+
+ private def write_irbrc(content)
+ File.open(@irbrc_file, 'w') do |f|
+ f.write content
+ end
+ end
+ end
+rescue LoadError, NameError
+ # On Ruby repository, this test suit doesn't run because Ruby repo doesn't
+ # have the yamatanooroti gem.
+end
diff --git a/test/mkmf/test_install.rb b/test/mkmf/test_install.rb
new file mode 100644
index 0000000000..7f8c603d42
--- /dev/null
+++ b/test/mkmf/test_install.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: false
+require_relative 'base'
+
+class TestMkmf
+ class TestInstall < TestMkmf
+ def test_install_dirs
+ Dir.mktmpdir do |dir|
+ File.write(dir+"/extconf.rb", "require 'mkmf'; create_makefile('test')")
+ all_assertions do |a|
+ a.foreach(
+ ["site"],
+ ["vendor", "--vendor"],
+ ) do |dest, *options|
+ assert_ruby_status(["-C", dir, "extconf.rb", *options])
+ mf = File.read(dir+"/Makefile")
+ a.foreach(
+ ["RUBYCOMMONDIR", "$(#{dest}dir)$(target_prefix)"],
+ ["RUBYLIBDIR", "$(#{dest}libdir)$(target_prefix)"],
+ ["RUBYARCHDIR", "$(#{dest}archdir)$(target_prefix)"],
+ ["HDRDIR", "$(#{dest}hdrdir)$(target_prefix)"],
+ ["ARCHHDRDIR", "$(#{dest}archhdrdir)$(target_prefix)"],
+ ) do |(var, path)|
+ assert_equal path, mf[/^#{var}\s*=\s*(.*)$/, 1]
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/test/monitor/test_monitor.rb b/test/monitor/test_monitor.rb
index 734b639d4c..0f17d58f71 100644
--- a/test/monitor/test_monitor.rb
+++ b/test/monitor/test_monitor.rb
@@ -10,6 +10,13 @@ class TestMonitor < Test::Unit::TestCase
@monitor = Monitor.new
end
+ def test_enter_in_different_fibers
+ @monitor.enter
+ Fiber.new {
+ assert_equal false, @monitor.try_enter
+ }.resume
+ end
+
def test_enter
ary = []
queue = Queue.new
diff --git a/test/net/ftp/test_ftp.rb b/test/net/ftp/test_ftp.rb
index 023e79435a..a480da4a4f 100644
--- a/test/net/ftp/test_ftp.rb
+++ b/test/net/ftp/test_ftp.rb
@@ -61,7 +61,7 @@ class FTPTest < Test::Unit::TestCase
end
def test_parse227
- ftp = Net::FTP.new
+ ftp = Net::FTP.new(nil, use_pasv_ip: true)
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)
@@ -80,6 +80,14 @@ class FTPTest < Test::Unit::TestCase
assert_raise(Net::FTPProtoError) do
ftp.send(:parse227, "227 ) foo bar (")
end
+
+ ftp = Net::FTP.new
+ sock = OpenStruct.new
+ sock.remote_address = OpenStruct.new
+ sock.remote_address.ip_address = "10.0.0.1"
+ ftp.instance_variable_set(:@bare_sock, sock)
+ host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)")
+ assert_equal("10.0.0.1", host)
end
def test_parse228
@@ -425,7 +433,7 @@ class FTPTest < Test::Unit::TestCase
end
conn.print(l, "\r\n")
end
- rescue Errno::EPIPE
+ rescue Errno::EPIPE, Errno::ECONNRESET
ensure
assert_nil($!)
conn.close
@@ -882,6 +890,40 @@ class FTPTest < Test::Unit::TestCase
end
end
+ def test_getbinaryfile_error
+ 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("450 No Dice\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.passive = true
+ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ 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.getbinaryfile("foo", nil)}
+ assert_match(/\A(PASV|EPSV)\r\n/, 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
@@ -1935,7 +1977,7 @@ EOF
assert_equal(nil, commands.shift)
# FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
# See https://github.com/openssl/openssl/pull/5967 for details.
- if OpenSSL::OPENSSL_LIBRARY_VERSION !~ /OpenSSL 1.1.0h/
+ if OpenSSL::OPENSSL_LIBRARY_VERSION !~ /OpenSSL 1.1.0h|LibreSSL/
assert_equal(true, session_reused_for_data_connection)
end
ensure
@@ -2019,7 +2061,7 @@ EOF
assert_equal("RETR foo\r\n", commands.shift)
assert_equal(nil, commands.shift)
# FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
- if OpenSSL::OPENSSL_LIBRARY_VERSION !~ /OpenSSL 1.1.0h/
+ if OpenSSL::OPENSSL_LIBRARY_VERSION !~ /OpenSSL 1.1.0h|LibreSSL/
assert_equal(true, session_reused_for_data_connection)
end
ensure
@@ -2474,10 +2516,172 @@ EOF
end
end
+ def test_time_parser
+ s = "20371231000000"
+ assert_equal(Time.utc(2037, 12, 31, 0, 0, 0),
+ Net::FTP::TIME_PARSER[s])
+ s = "20371231000000.123456"
+ assert_equal(Time.utc(2037, 12, 31, 0, 0, 0, 123456),
+ Net::FTP::TIME_PARSER[s])
+ s = "20371231000000." + "9" * 999999
+ assert_equal(Time.utc(2037, 12, 31, 0, 0, 0,
+ 99999999999999999r / 100000000000),
+ Net::FTP::TIME_PARSER[s])
+ e = assert_raise(Net::FTPProtoError) {
+ Net::FTP::TIME_PARSER["x" * 999999]
+ }
+ assert_equal("invalid time-val: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...", e.message)
+ end
+
+ def test_ignore_pasv_ip
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server(nil, "127.0.0.1") { |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)
+ data_server = TCPServer.new("127.0.0.1", 0)
+ port = data_server.local_address.ip_port
+ sock.printf("227 Entering Passive Mode (999,0,0,1,%s).\r\n",
+ port.divmod(256).join(","))
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = data_server.accept
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.shutdown(Socket::SHUT_WR)
+ conn.read
+ conn.close
+ data_server.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.passive = true
+ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ ftp.connect("127.0.0.1", server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ftp.getbinaryfile("foo", nil)
+ assert_equal(binary_data, buf)
+ assert_equal(Encoding::ASCII_8BIT, buf.encoding)
+ assert_equal("PASV\r\n", 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_use_pasv_ip
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server(nil, "127.0.0.1") { |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)
+ data_server = TCPServer.new("127.0.0.1", 0)
+ port = data_server.local_address.ip_port
+ sock.printf("227 Entering Passive Mode (127,0,0,1,%s).\r\n",
+ port.divmod(256).join(","))
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = data_server.accept
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.shutdown(Socket::SHUT_WR)
+ conn.read
+ conn.close
+ data_server.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.passive = true
+ ftp.use_pasv_ip = true
+ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ ftp.connect("127.0.0.1", server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ftp.getbinaryfile("foo", nil)
+ assert_equal(binary_data, buf)
+ assert_equal(Encoding::ASCII_8BIT, buf.encoding)
+ assert_equal("PASV\r\n", 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_use_pasv_invalid_ip
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server(nil, "127.0.0.1") { |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("227 Entering Passive Mode (999,0,0,1,48,57).\r\n")
+ commands.push(sock.gets)
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.passive = true
+ ftp.use_pasv_ip = true
+ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ ftp.connect("127.0.0.1", server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(SocketError) do
+ ftp.getbinaryfile("foo", nil)
+ end
+ 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)
+ def create_ftp_server(sleep_time = nil, addr = SERVER_ADDR)
+ server = TCPServer.new(addr, 0)
@thread = Thread.start do
if sleep_time
sleep(sleep_time)
diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb
index e9aee15bd3..7b97e39586 100644
--- a/test/net/http/test_https.rb
+++ b/test/net/http/test_https.rb
@@ -143,6 +143,11 @@ class TestNetHTTPS < Test::Unit::TestCase
# See https://github.com/openssl/openssl/pull/5967 for details.
skip if OpenSSL::OPENSSL_LIBRARY_VERSION =~ /OpenSSL 1.1.0h/
+ # FIXME: GitHub Actions for MinGW failed. Maybe it's because of OpenSSL on MiNGW
+ if /mingw/ =~ RUBY_PLATFORM
+ skip "Skip net/https test using openssl on MinGW"
+ end
+
http = Net::HTTP.new("localhost", config("port"))
http.use_ssl = true
http.cert_store = TEST_STORE
diff --git a/test/net/imap/test_imap.rb b/test/net/imap/test_imap.rb
index 8b924b524e..85fb71d440 100644
--- a/test/net/imap/test_imap.rb
+++ b/test/net/imap/test_imap.rb
@@ -127,6 +127,16 @@ class IMAPTest < Test::Unit::TestCase
imap.disconnect
end
end
+
+ def test_starttls_stripping
+ starttls_stripping_test do |port|
+ imap = Net::IMAP.new("localhost", :port => port)
+ assert_raise(Net::IMAP::UnknownResponseError) do
+ imap.starttls(:ca_file => CA_FILE)
+ end
+ imap
+ end
+ end
end
def start_server
@@ -834,6 +844,27 @@ EOF
end
end
+ def starttls_stripping_test
+ server = create_tcp_server
+ port = server.addr[1]
+ start_server do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("RUBY0001 BUG unhandled command\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = yield(port)
+ ensure
+ imap.disconnect if imap && !imap.disconnected?
+ end
+ end
+
def create_tcp_server
return TCPServer.new(server_addr, 0)
end
diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb
index 6b956e6d14..5fd41134ba 100644
--- a/test/objspace/test_objspace.rb
+++ b/test/objspace/test_objspace.rb
@@ -243,6 +243,19 @@ class TestObjSpace < Test::Unit::TestCase
GC.enable
end
+ def test_trace_object_allocations_gc_stress
+ prev = GC.stress
+ GC.stress = true
+
+ ObjectSpace.trace_object_allocations{
+ proc{}
+ }
+
+ assert true # success
+ ensure
+ GC.stress = prev
+ end
+
def test_dump_flags
info = ObjectSpace.dump("foo".freeze)
assert_match(/"wb_protected":true, "old":true/, info)
diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb
index 178f5aba0e..ef8c4f3a4a 100644
--- a/test/openssl/test_cipher.rb
+++ b/test/openssl/test_cipher.rb
@@ -174,6 +174,54 @@ class OpenSSL::TestCipher < OpenSSL::TestCase
assert_not_predicate(cipher, :authenticated?)
end
+ def test_aes_ccm
+ # RFC 3610 Section 8, Test Case 1
+ key = ["c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"].pack("H*")
+ iv = ["00000003020100a0a1a2a3a4a5"].pack("H*")
+ aad = ["0001020304050607"].pack("H*")
+ pt = ["08090a0b0c0d0e0f101112131415161718191a1b1c1d1e"].pack("H*")
+ ct = ["588c979a61c663d2f066d0c2c0f989806d5f6b61dac384"].pack("H*")
+ tag = ["17e8d12cfdf926e0"].pack("H*")
+
+ kwargs = {auth_tag_len: 8, iv_len: 13, key: key, iv: iv}
+ cipher = new_encryptor("aes-128-ccm", **kwargs, ccm_data_len: pt.length, auth_data: aad)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ assert_equal tag, cipher.auth_tag
+ cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag, auth_data: aad)
+ assert_equal pt, cipher.update(ct) << cipher.final
+
+ # truncated tag is accepted
+ cipher = new_encryptor("aes-128-ccm", **kwargs, ccm_data_len: pt.length, auth_data: aad)
+ assert_equal ct, cipher.update(pt) << cipher.final
+ assert_equal tag[0, 8], cipher.auth_tag(8)
+ cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag[0, 8], auth_data: aad)
+ assert_equal pt, cipher.update(ct) << cipher.final
+
+ # wrong tag is rejected
+ tag2 = tag.dup
+ tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff)
+ cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag2, auth_data: aad)
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct) }
+
+ # wrong aad is rejected
+ aad2 = aad[0..-2] << aad[-1].succ
+ cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag, auth_data: aad2)
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct) }
+
+ # wrong ciphertext is rejected
+ ct2 = ct[0..-2] << ct[-1].succ
+ cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct2.length, auth_tag: tag, auth_data: aad)
+ assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct2) }
+ rescue OpenSSL::Cipher::CipherError
+ if /mingw/i =~ RUBY_PLATFORM
+ omit "skip on OpenSSL::Cipher::CipherError from 'ccm_data_len=': Maybe it's because of OpenSSL in MinGW"
+ else
+ raise
+ end
+ end if has_cipher?("aes-128-ccm") &&
+ OpenSSL::Cipher.new("aes-128-ccm").authenticated? &&
+ OpenSSL::OPENSSL_VERSION_NUMBER >= 0x1010103f # version >= 1.1.1c
+
def test_aes_gcm
# GCM spec Appendix B Test Case 4
key = ["feffe9928665731c6d6a8f9467308308"].pack("H*")
diff --git a/test/openssl/test_config.rb b/test/openssl/test_config.rb
index f65392c18d..e041c1a3ba 100644
--- a/test/openssl/test_config.rb
+++ b/test/openssl/test_config.rb
@@ -61,14 +61,14 @@ foo\\bar::foo\\bar = baz
[default1 default2]\t\t # space is allowed in section name
fo =b ar # space allowed in value
[emptysection]
- [doller ]
+ [dollar ]
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(['default', 'default1 default2', 'dollar', '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'])
@@ -84,12 +84,12 @@ __EOC__
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'])
+ # dollar
+ assert_equal('bar', c['dollar']['foo'])
+ assert_equal('bar', c['dollar']['bar'])
+ assert_equal('123baz456bar798', c['dollar']['baz'])
+ assert_equal('123baz456bar798', c['dollar']['qux'])
+ assert_equal('123baz456bar798.123baz456bar798', c['dollar']['quxx'])
excn = assert_raise(OpenSSL::ConfigError) do
OpenSSL::Config.parse("foo = $bar")
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 6095d545b5..2d9ee7bc70 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -257,7 +257,10 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_client_auth_success
vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
- start_server(verify_mode: vflag) { |port|
+ start_server(verify_mode: vflag,
+ ctx_proc: proc { |ctx|
+ ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
+ }) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = @cli_key
ctx.cert = @cli_cert
@@ -303,6 +306,8 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_client_ca
+ pend "LibreSSL 3.2 has broken client CA support" if libressl?(3, 2, 0)
+
ctx_proc = Proc.new do |ctx|
ctx.client_ca = [@ca_cert]
end
@@ -481,6 +486,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ ctx.max_version = :TLS1_2 if libressl?(3, 2, 0) && !libressl?(3, 3, 0)
server_connect(port, ctx) { |ssl|
ssl.puts "abc"; ssl.gets
@@ -868,11 +874,13 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_verify_hostname_on_connect
ctx_proc = proc { |ctx|
+ san = "DNS:a.example.com,DNS:*.b.example.com"
+ san += ",DNS:c*.example.com,DNS:d.*.example.com" unless libressl?(3, 2, 2)
exts = [
["keyUsage", "keyEncipherment,digitalSignature", true],
- ["subjectAltName", "DNS:a.example.com,DNS:*.b.example.com," \
- "DNS:c*.example.com,DNS:d.*.example.com"],
+ ["subjectAltName", san],
]
+
ctx.cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key)
ctx.key = @svr_key
}
@@ -893,6 +901,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
["cx.example.com", true],
["d.x.example.com", false],
].each do |name, expected_ok|
+ next if name.start_with?('cx') if libressl?(3, 2, 2)
begin
sock = TCPSocket.new("127.0.0.1", port)
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
@@ -1581,12 +1590,13 @@ end
end
end
- def test_ecdh_curves
+ def test_ecdh_curves_tls12
pend "EC is disabled" unless defined?(OpenSSL::PKey::EC)
ctx_proc = -> ctx {
# Enable both ECDHE (~ TLS 1.2) cipher suites and TLS 1.3
- ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
+ ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+ ctx.ciphers = "kEECDH"
ctx.ecdh_curves = "P-384:P-521"
}
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
@@ -1595,13 +1605,9 @@ end
server_connect(port, ctx) { |ssl|
cs = ssl.cipher[0]
- if /\ATLS/ =~ cs # Is TLS 1.3 is used?
+ assert_match (/\AECDH/), cs
+ if ssl.respond_to?(:tmp_key)
assert_equal "secp384r1", ssl.tmp_key.group.curve_name
- else
- assert_match (/\AECDH/), cs
- if ssl.respond_to?(:tmp_key)
- assert_equal "secp384r1", ssl.tmp_key.group.curve_name
- end
end
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
@@ -1625,6 +1631,26 @@ end
end
end
+ def test_ecdh_curves_tls13
+ pend "EC is disabled" unless defined?(OpenSSL::PKey::EC)
+ pend "TLS 1.3 not supported" unless tls13_supported?
+
+ ctx_proc = -> ctx {
+ # Assume TLS 1.3 is enabled and chosen by default
+ ctx.ecdh_curves = "P-384:P-521"
+ }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ecdh_curves = "P-256:P-384" # disable P-521
+
+ server_connect(port, ctx) { |ssl|
+ assert_equal "TLSv1.3", ssl.ssl_version
+ assert_equal "secp384r1", ssl.tmp_key.group.curve_name
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ end
+ end
+
def test_security_level
ctx = OpenSSL::SSL::SSLContext.new
begin
diff --git a/test/openssl/test_ssl_session.rb b/test/openssl/test_ssl_session.rb
index 89726d4463..a98efdae2a 100644
--- a/test/openssl/test_ssl_session.rb
+++ b/test/openssl/test_ssl_session.rb
@@ -122,6 +122,7 @@ __EOS__
ctx.options &= ~OpenSSL::SSL::OP_NO_TICKET
# Disable server-side session cache which is enabled by default
ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_OFF
+ ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
}
start_server(ctx_proc: ctx_proc) do |port|
sess1 = server_connect_with_session(port, nil, nil) { |ssl|
diff --git a/test/openssl/test_ts.rb b/test/openssl/test_ts.rb
index 6e9c30894b..7cb1a1fe8e 100644
--- a/test/openssl/test_ts.rb
+++ b/test/openssl/test_ts.rb
@@ -181,6 +181,12 @@ _end_of_pem_
assert_equal(42, qer2.nonce)
end
+ def test_request_invalid_asn1
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ OpenSSL::Timestamp::Request.new("*" * 44)
+ end
+ end
+
def test_response_constants
assert_equal(0, OpenSSL::Timestamp::Response::GRANTED)
assert_equal(1, OpenSSL::Timestamp::Response::GRANTED_WITH_MODS)
@@ -222,6 +228,11 @@ _end_of_pem_
assert_equal(token.to_der, resp.token.to_der)
end
+ def test_response_failure_info
+ resp = OpenSSL::Timestamp::Response.new("0\"0 \x02\x01\x020\x17\f\x15Invalid TimeStampReq.\x03\x02\x06\x80")
+ assert_equal(:BAD_ALG, resp.failure_info)
+ end
+
def test_response_mandatory_fields
fac = OpenSSL::Timestamp::Factory.new
req = OpenSSL::Timestamp::Request.new
@@ -333,6 +344,12 @@ _end_of_pem_
end
end
+ def test_response_invalid_asn1
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ OpenSSL::Timestamp::Response.new("*" * 44)
+ end
+ end
+
def test_no_cert_requested
req = OpenSSL::Timestamp::Request.new
req.algorithm = "SHA1"
@@ -585,6 +602,12 @@ _end_of_pem_
assert_equal(123, info.nonce)
end
+ def test_token_info_invalid_asn1
+ assert_raise(OpenSSL::Timestamp::TimestampError) do
+ OpenSSL::Timestamp::TokenInfo.new("*" * 44)
+ end
+ end
+
private
def assert_cert expected, actual
diff --git a/test/openssl/test_x509store.rb b/test/openssl/test_x509store.rb
index 1cbc73d539..e9602e3434 100644
--- a/test/openssl/test_x509store.rb
+++ b/test/openssl/test_x509store.rb
@@ -66,7 +66,7 @@ class OpenSSL::TestX509Store < OpenSSL::TestCase
ee1_cert = issue_cert(@ee1, @dsa256, 10, ee_exts, ca2_cert, @rsa1024)
ee2_cert = issue_cert(@ee2, @dsa512, 20, ee_exts, ca2_cert, @rsa1024)
ee3_cert = issue_cert(@ee2, @dsa512, 30, ee_exts, ca2_cert, @rsa1024,
- not_before: now-100, not_after: now-50)
+ not_before: now-100, not_after: now-1)
ee4_cert = issue_cert(@ee2, @dsa512, 40, ee_exts, ca2_cert, @rsa1024,
not_before: now+1000, not_after: now+2000,)
@@ -128,7 +128,7 @@ class OpenSSL::TestX509Store < OpenSSL::TestCase
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), "now=#{now.inspect} Time.now=#{Time.now.inspect} store=#{store.inspect} ee3_cert=#{ee3_cert.inspect}")
+ 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))
diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb
index 3776fbac4e..ee734d98a4 100644
--- a/test/openssl/utils.rb
+++ b/test/openssl/utils.rb
@@ -199,6 +199,14 @@ class OpenSSL::SSLTestCase < OpenSSL::TestCase
rescue
end
+ def tls13_supported?
+ return false unless defined?(OpenSSL::SSL::TLS1_3_VERSION)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_3_VERSION
+ true
+ rescue
+ end
+
def readwrite_loop(ctx, ssl)
while line = ssl.gets
ssl.write(line)
diff --git a/test/optparse/test_acceptable.rb b/test/optparse/test_acceptable.rb
index 5c3fbdb10c..12f7886538 100644
--- a/test/optparse/test_acceptable.rb
+++ b/test/optparse/test_acceptable.rb
@@ -196,4 +196,3 @@ class TestOptionParser::Acceptable < TestOptionParser
end
end
-
diff --git a/test/optparse/test_optparse.rb b/test/optparse/test_optparse.rb
index e4aeb07aac..5f5ea183b0 100644
--- a/test/optparse/test_optparse.rb
+++ b/test/optparse/test_optparse.rb
@@ -75,4 +75,34 @@ class TestOptionParser < Test::Unit::TestCase
assert_equal({host: "localhost", port: 8000, verbose: true}, result)
assert_equal(true, @verbose)
end
+
+ def test_require_exact
+ @opt.def_option('-F', '--zrs=IRS', 'zrs')
+ %w(--zrs --zr --z -zfoo -z -F -Ffoo).each do |arg|
+ result = {}
+ @opt.parse([arg, 'foo'], into: result)
+ assert_equal({zrs: 'foo'}, result)
+ end
+
+ @opt.require_exact = true
+ %w(--zrs -F -Ffoo).each do |arg|
+ result = {}
+ @opt.parse([arg, 'foo'], into: result)
+ assert_equal({zrs: 'foo'}, result)
+ end
+
+ assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(--zr foo))}
+ assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(--z foo))}
+ assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-zrs foo))}
+ assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-zr foo))}
+ assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-z foo))}
+ end
+
+ def test_nonopt_pattern
+ @opt.def_option(/^[^-]/) do |arg|
+ assert(false, "Never gets called")
+ end
+ e = assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-t))}
+ assert_equal(["-t"], e.args)
+ end
end
diff --git a/test/psych/helper.rb b/test/psych/helper.rb
index 9348457958..0643139d8c 100644
--- a/test/psych/helper.rb
+++ b/test/psych/helper.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'minitest/autorun'
+require 'test/unit'
require 'stringio'
require 'tempfile'
require 'date'
@@ -7,13 +7,7 @@ require 'date'
require 'psych'
module Psych
- superclass = if defined?(Minitest::Test)
- Minitest::Test
- else
- MiniTest::Unit::TestCase
- end
-
- class TestCase < superclass
+ class TestCase < Test::Unit::TestCase
def self.suppress_warning
verbose, $VERBOSE = $VERBOSE, nil
yield
@@ -47,24 +41,30 @@ module Psych
# Convert between Psych and the object to verify correct parsing and
# emitting
#
- def assert_to_yaml( obj, yaml )
- assert_equal( obj, Psych::load( yaml ) )
+ def assert_to_yaml( obj, yaml, loader = :load )
+ assert_equal( obj, Psych.send(loader, yaml) )
assert_equal( obj, Psych::parse( yaml ).transform )
- assert_equal( obj, Psych::load( obj.to_yaml ) )
+ assert_equal( obj, Psych.send(loader, obj.to_yaml) )
assert_equal( obj, Psych::parse( obj.to_yaml ).transform )
- assert_equal( obj, Psych::load(
+ assert_equal( obj, Psych.send(loader,
obj.to_yaml(
:UseVersion => true, :UseHeader => true, :SortKeys => true
)
))
+ rescue Psych::DisallowedClass, Psych::BadAlias
+ assert_to_yaml obj, yaml, :unsafe_load
end
#
# Test parser only
#
def assert_parse_only( obj, yaml )
- assert_equal( obj, Psych::load( yaml ) )
- assert_equal( obj, Psych::parse( yaml ).transform )
+ begin
+ assert_equal obj, Psych::load( yaml )
+ rescue Psych::DisallowedClass, Psych::BadAlias
+ assert_equal obj, Psych::unsafe_load( yaml )
+ end
+ assert_equal obj, Psych::parse( yaml ).transform
end
def assert_cycle( obj )
@@ -75,9 +75,15 @@ module Psych
assert_nil Psych::load(Psych.dump(obj))
assert_nil Psych::load(obj.to_yaml)
else
- assert_equal(obj, Psych.load(v.tree.yaml))
- assert_equal(obj, Psych::load(Psych.dump(obj)))
- assert_equal(obj, Psych::load(obj.to_yaml))
+ begin
+ assert_equal(obj, Psych.load(v.tree.yaml))
+ assert_equal(obj, Psych::load(Psych.dump(obj)))
+ assert_equal(obj, Psych::load(obj.to_yaml))
+ rescue Psych::DisallowedClass, Psych::BadAlias
+ assert_equal(obj, Psych.unsafe_load(v.tree.yaml))
+ assert_equal(obj, Psych::unsafe_load(Psych.dump(obj)))
+ assert_equal(obj, Psych::unsafe_load(obj.to_yaml))
+ end
end
end
diff --git a/test/psych/test_alias_and_anchor.rb b/test/psych/test_alias_and_anchor.rb
index 91c09dfdfa..81ebd66bed 100644
--- a/test/psych/test_alias_and_anchor.rb
+++ b/test/psych/test_alias_and_anchor.rb
@@ -19,7 +19,7 @@ module Psych
- *id001
- *id001
EOYAML
- result = Psych.load yaml
+ result = Psych.unsafe_load yaml
result.each {|el| assert_same(result[0], el) }
end
@@ -33,7 +33,7 @@ EOYAML
- *id001
EOYAML
- result = Psych.load yaml
+ result = Psych.unsafe_load yaml
result.each do |el|
assert_same(result[0], el)
assert_equal('test1', el.var1)
@@ -50,7 +50,7 @@ EOYAML
- *id001
- *id001
EOYAML
- result = Psych.load yaml
+ result = Psych.unsafe_load yaml
result.each do |el|
assert_same(result[0], el)
assert_equal('test', el.var1)
@@ -62,7 +62,7 @@ EOYAML
original = [o,o,o]
yaml = Psych.dump original
- result = Psych.load yaml
+ result = Psych.unsafe_load yaml
result.each {|el| assert_same(result[0], el) }
end
@@ -73,7 +73,7 @@ EOYAML
original = [o,o,o]
yaml = Psych.dump original
- result = Psych.load yaml
+ result = Psych.unsafe_load yaml
result.each do |el|
assert_same(result[0], el)
assert_equal('test1', el.var1)
@@ -87,7 +87,7 @@ EOYAML
original = [o,o,o]
yaml = Psych.dump original
- result = Psych.load yaml
+ result = Psych.unsafe_load yaml
result.each do |el|
assert_same(result[0], el)
assert_equal('test', el.var1)
diff --git a/test/psych/test_array.rb b/test/psych/test_array.rb
index f2bbdcab88..28b76da785 100644
--- a/test/psych/test_array.rb
+++ b/test/psych/test_array.rb
@@ -24,7 +24,7 @@ module Psych
def test_another_subclass_with_attributes
y = Y.new.tap {|o| o.val = 1}
y << "foo" << "bar"
- y = Psych.load Psych.dump y
+ y = Psych.unsafe_load Psych.dump y
assert_equal %w{foo bar}, y
assert_equal Y, y.class
@@ -42,13 +42,13 @@ module Psych
end
def test_subclass_with_attributes
- y = Psych.load Psych.dump Y.new.tap {|o| o.val = 1}
+ y = Psych.unsafe_load Psych.dump Y.new.tap {|o| o.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"
+ x = Psych.unsafe_load "--- !seq:#{X.name} []\n\n"
assert_equal X, x.class
end
diff --git a/test/psych/test_class.rb b/test/psych/test_class.rb
index 71f7ec31fd..faa504c7e2 100644
--- a/test/psych/test_class.rb
+++ b/test/psych/test_class.rb
@@ -7,13 +7,13 @@ module Psych
end
def test_cycle_anonymous_class
- assert_raises(::TypeError) do
+ assert_raise(::TypeError) do
assert_cycle(Class.new)
end
end
def test_cycle_anonymous_module
- assert_raises(::TypeError) do
+ assert_raise(::TypeError) do
assert_cycle(Module.new)
end
end
diff --git a/test/psych/test_coder.rb b/test/psych/test_coder.rb
index 5ea8cab966..b2be0a4109 100644
--- a/test/psych/test_coder.rb
+++ b/test/psych/test_coder.rb
@@ -112,9 +112,19 @@ module Psych
end
end
+ class CustomEncode
+ def initialize(**opts)
+ @opts = opts
+ end
+
+ def encode_with(coder)
+ @opts.each { |k,v| coder.public_send :"#{k}=", v }
+ end
+ end
+
def test_self_referential
x = Referential.new
- copy = Psych.load Psych.dump x
+ copy = Psych.unsafe_load Psych.dump x
assert_equal copy, copy.a
end
@@ -153,23 +163,23 @@ module Psych
end
def test_represent_map
- thing = Psych.load(Psych.dump(RepresentWithMap.new))
+ thing = Psych.unsafe_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))
+ thing = Psych.unsafe_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))
+ thing = Psych.unsafe_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)))
+ assert_instance_of(Represent, Psych.unsafe_load(Psych.dump(Represent.new)))
end
def test_scalar_coder
@@ -179,7 +189,7 @@ module Psych
def test_load_dumped_tagging
foo = InitApi.new
- bar = Psych.load(Psych.dump(foo))
+ bar = Psych.unsafe_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
@@ -198,10 +208,121 @@ module Psych
def test_dump_init_with
foo = InitApi.new
- bar = Psych.load(Psych.dump(foo))
+ bar = Psych.unsafe_load(Psych.dump(foo))
assert_equal foo.a, bar.a
assert_equal foo.b, bar.b
assert_nil bar.c
end
+
+ def test_coder_style_map_default
+ foo = Psych.dump a: 1, b: 2
+ assert_equal "---\n:a: 1\n:b: 2\n", foo
+ end
+
+ def test_coder_style_map_any
+ foo = Psych.dump CustomEncode.new \
+ map: {a: 1, b: 2},
+ style: Psych::Nodes::Mapping::ANY,
+ tag: nil
+ assert_equal "---\n:a: 1\n:b: 2\n", foo
+ end
+
+ def test_coder_style_map_block
+ foo = Psych.dump CustomEncode.new \
+ map: {a: 1, b: 2},
+ style: Psych::Nodes::Mapping::BLOCK,
+ tag: nil
+ assert_equal "---\n:a: 1\n:b: 2\n", foo
+ end
+
+ def test_coder_style_map_flow
+ foo = Psych.dump CustomEncode.new \
+ map: { a: 1, b: 2 },
+ style: Psych::Nodes::Mapping::FLOW,
+ tag: nil
+ assert_equal "--- {! ':a': 1, ! ':b': 2}\n", foo
+ end
+
+ def test_coder_style_seq_default
+ foo = Psych.dump [ 1, 2, 3 ]
+ assert_equal "---\n- 1\n- 2\n- 3\n", foo
+ end
+
+ def test_coder_style_seq_any
+ foo = Psych.dump CustomEncode.new \
+ seq: [ 1, 2, 3 ],
+ style: Psych::Nodes::Sequence::ANY,
+ tag: nil
+ assert_equal "---\n- 1\n- 2\n- 3\n", foo
+ end
+
+ def test_coder_style_seq_block
+ foo = Psych.dump CustomEncode.new \
+ seq: [ 1, 2, 3 ],
+ style: Psych::Nodes::Sequence::BLOCK,
+ tag: nil
+ assert_equal "---\n- 1\n- 2\n- 3\n", foo
+ end
+
+ def test_coder_style_seq_flow
+ foo = Psych.dump CustomEncode.new \
+ seq: [ 1, 2, 3 ],
+ style: Psych::Nodes::Sequence::FLOW,
+ tag: nil
+ assert_equal "--- [1, 2, 3]\n", foo
+ end
+
+ def test_coder_style_scalar_default
+ foo = Psych.dump 'some scalar'
+ assert_match(/\A--- some scalar\n(?:\.\.\.\n)?\z/, foo)
+ end
+
+ def test_coder_style_scalar_any
+ foo = Psych.dump CustomEncode.new \
+ scalar: 'some scalar',
+ style: Psych::Nodes::Scalar::ANY,
+ tag: nil
+ assert_match(/\A--- some scalar\n(?:\.\.\.\n)?\z/, foo)
+ end
+
+ def test_coder_style_scalar_plain
+ foo = Psych.dump CustomEncode.new \
+ scalar: 'some scalar',
+ style: Psych::Nodes::Scalar::PLAIN,
+ tag: nil
+ assert_match(/\A--- some scalar\n(?:\.\.\.\n)?\z/, foo)
+ end
+
+ def test_coder_style_scalar_single_quoted
+ foo = Psych.dump CustomEncode.new \
+ scalar: 'some scalar',
+ style: Psych::Nodes::Scalar::SINGLE_QUOTED,
+ tag: nil
+ assert_equal "--- ! 'some scalar'\n", foo
+ end
+
+ def test_coder_style_scalar_double_quoted
+ foo = Psych.dump CustomEncode.new \
+ scalar: 'some scalar',
+ style: Psych::Nodes::Scalar::DOUBLE_QUOTED,
+ tag: nil
+ assert_equal %Q'--- ! "some scalar"\n', foo
+ end
+
+ def test_coder_style_scalar_literal
+ foo = Psych.dump CustomEncode.new \
+ scalar: 'some scalar',
+ style: Psych::Nodes::Scalar::LITERAL,
+ tag: nil
+ assert_equal "--- ! |-\n some scalar\n", foo
+ end
+
+ def test_coder_style_scalar_folded
+ foo = Psych.dump CustomEncode.new \
+ scalar: 'some scalar',
+ style: Psych::Nodes::Scalar::FOLDED,
+ tag: nil
+ assert_equal "--- ! >-\n some scalar\n", foo
+ end
end
end
diff --git a/test/psych/test_date_time.rb b/test/psych/test_date_time.rb
index f73f34628f..6f1e8b509e 100644
--- a/test/psych/test_date_time.rb
+++ b/test/psych/test_date_time.rb
@@ -22,7 +22,7 @@ module Psych
def test_timezone_offset
times = [Time.new(2017, 4, 13, 12, 0, 0, "+09:00"),
Time.new(2017, 4, 13, 12, 0, 0, "-05:00")]
- cycled = Psych::load(Psych.dump times)
+ cycled = Psych::unsafe_load(Psych.dump times)
assert_match(/12:00:00 \+0900/, cycled.first.to_s)
assert_match(/12:00:00 -0500/, cycled.last.to_s)
end
@@ -39,7 +39,7 @@ module Psych
def test_datetime_timezone_offset
times = [DateTime.new(2017, 4, 13, 12, 0, 0, "+09:00"),
DateTime.new(2017, 4, 13, 12, 0, 0, "-05:00")]
- cycled = Psych::load(Psych.dump times)
+ cycled = Psych::unsafe_load(Psych.dump times)
assert_match(/12:00:00\+09:00/, cycled.first.to_s)
assert_match(/12:00:00-05:00/, cycled.last.to_s)
end
diff --git a/test/psych/test_deprecated.rb b/test/psych/test_deprecated.rb
index 624f4379a6..af3379909a 100644
--- a/test/psych/test_deprecated.rb
+++ b/test/psych/test_deprecated.rb
@@ -41,7 +41,7 @@ module Psych
def test_recursive_quick_emit_encode_with
qeew = QuickEmitterEncodeWith.new
hash = { :qe => qeew }
- hash2 = Psych.load Psych.dump hash
+ hash2 = Psych.unsafe_load Psych.dump hash
qe = hash2[:qe]
assert_equal qeew.name, qe.name
@@ -72,7 +72,7 @@ module Psych
# receive the yaml_initialize call.
def test_yaml_initialize_and_init_with
hash = { :yi => YamlInitAndInitWith.new }
- hash2 = Psych.load Psych.dump hash
+ hash2 = Psych.unsafe_load Psych.dump hash
yi = hash2[:yi]
assert_equal 'TGIF!', yi.name
diff --git a/test/psych/test_document.rb b/test/psych/test_document.rb
index a88dd32f0d..cf3b7001fc 100644
--- a/test/psych/test_document.rb
+++ b/test/psych/test_document.rb
@@ -30,7 +30,7 @@ module Psych
end
def test_emit_bad_tag
- assert_raises(RuntimeError) do
+ assert_raise(RuntimeError) do
@doc.tag_directives = [['!']]
@stream.yaml
end
diff --git a/test/psych/test_emitter.rb b/test/psych/test_emitter.rb
index 52d5e9d1c1..506d72241c 100644
--- a/test/psych/test_emitter.rb
+++ b/test/psych/test_emitter.rb
@@ -40,7 +40,7 @@ module Psych
end
def test_start_stream_arg_error
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
@emitter.start_stream 'asdfasdf'
end
end
@@ -56,7 +56,7 @@ module Psych
[[], [nil,nil], false],
[[1,1], [[nil, "tag:TALOS"]], 0],
].each do |args|
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
@emitter.start_document(*args)
end
end
@@ -73,7 +73,7 @@ module Psych
['foo', nil, nil, false, true, :foo],
[nil, nil, nil, false, true, 1],
].each do |args|
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
@emitter.scalar(*args)
end
end
@@ -83,11 +83,11 @@ module Psych
@emitter.start_stream Psych::Nodes::Stream::UTF8
@emitter.start_document [], [], false
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
@emitter.start_sequence(nil, Object.new, true, 1)
end
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
@emitter.start_sequence(nil, nil, true, :foo)
end
end
diff --git a/test/psych/test_encoding.rb b/test/psych/test_encoding.rb
index ef6653142f..e5831c9045 100644
--- a/test/psych/test_encoding.rb
+++ b/test/psych/test_encoding.rb
@@ -63,7 +63,7 @@ module Psych
# 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
+ assert_raise Psych::SyntaxError do
Psych.load(f)
end
end
@@ -121,7 +121,7 @@ module Psych
def test_emit_alias
@emitter.start_stream Psych::Parser::UTF8
@emitter.start_document [], [], true
- e = assert_raises(RuntimeError) do
+ e = assert_raise(RuntimeError) do
@emitter.alias 'ドラãˆã‚‚ã‚“'.encode('EUC-JP')
end
assert_match(/alias value/, e.message)
diff --git a/test/psych/test_exception.rb b/test/psych/test_exception.rb
index 78601d09c7..d2ae76a7d2 100644
--- a/test/psych/test_exception.rb
+++ b/test/psych/test_exception.rb
@@ -33,42 +33,42 @@ module Psych
def test_backtrace
err = make_ex
- new_err = Psych.load(Psych.dump(err))
+ new_err = Psych.unsafe_load(Psych.dump(err))
assert_equal err.backtrace, new_err.backtrace
end
def test_naming_exception
err = String.xxx rescue $!
- new_err = Psych.load(Psych.dump(err))
+ new_err = Psych.unsafe_load(Psych.dump(err))
assert_equal err.message, new_err.message
end
def test_load_takes_file
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.load '--- `'
end
assert_nil ex.file
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.load '--- `', filename: 'meow'
end
assert_equal 'meow', ex.file
# deprecated interface
- ex = assert_raises(Psych::SyntaxError) do
- Psych.load '--- `', 'deprecated'
+ ex = assert_raise(Psych::SyntaxError) do
+ Psych.unsafe_load '--- `', 'deprecated'
end
assert_equal 'deprecated', ex.file
end
def test_psych_parse_stream_takes_file
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.parse_stream '--- `'
end
assert_nil ex.file
assert_match '(<unknown>)', ex.message
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.parse_stream '--- `', filename: 'omg!'
end
assert_equal 'omg!', ex.file
@@ -76,19 +76,19 @@ module Psych
end
def test_load_stream_takes_file
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.load_stream '--- `'
end
assert_nil ex.file
assert_match '(<unknown>)', ex.message
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.load_stream '--- `', filename: 'omg!'
end
assert_equal 'omg!', ex.file
# deprecated interface
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.load_stream '--- `', 'deprecated'
end
assert_equal 'deprecated', ex.file
@@ -99,7 +99,7 @@ module Psych
t.binmode
t.write '--- `'
t.close
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.parse_file t.path
end
assert_equal t.path, ex.file
@@ -111,7 +111,7 @@ module Psych
t.binmode
t.write '--- `'
t.close
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.load_file t.path
end
assert_equal t.path, ex.file
@@ -123,7 +123,7 @@ module Psych
t.binmode
t.write '--- `'
t.close
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.safe_load_file t.path
end
assert_equal t.path, ex.file
@@ -131,26 +131,26 @@ module Psych
end
def test_psych_parse_takes_file
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.parse '--- `'
end
assert_match '(<unknown>)', ex.message
assert_nil ex.file
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.parse '--- `', filename: 'omg!'
end
assert_match 'omg!', ex.message
# deprecated interface
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
Psych.parse '--- `', 'deprecated'
end
assert_match 'deprecated', ex.message
end
def test_attributes
- e = assert_raises(Psych::SyntaxError) {
+ e = assert_raise(Psych::SyntaxError) {
Psych.load '--- `foo'
}
@@ -165,7 +165,7 @@ module Psych
end
def test_convert
- w = Psych.load(Psych.dump(@wups))
+ w = Psych.unsafe_load(Psych.dump(@wups))
assert_equal @wups.message, w.message
assert_equal @wups.backtrace, w.backtrace
assert_equal 1, w.foo
diff --git a/test/psych/test_hash.rb b/test/psych/test_hash.rb
index ba11b827da..5374781339 100644
--- a/test/psych/test_hash.rb
+++ b/test/psych/test_hash.rb
@@ -39,7 +39,7 @@ module Psych
def test_hash_with_ivar
t1 = HashWithIvar.new
t1[:foo] = :bar
- t2 = Psych.load(Psych.dump(t1))
+ t2 = Psych.unsafe_load(Psych.dump(t1))
assert_equal t1, t2
assert_cycle t1
end
@@ -54,14 +54,14 @@ module Psych
def test_custom_initialized
a = [1,2,3,4,5]
t1 = HashWithCustomInit.new(a)
- t2 = Psych.load(Psych.dump(t1))
+ t2 = Psych.unsafe_load(Psych.dump(t1))
assert_equal t1, t2
assert_cycle t1
end
def test_custom_initialize_no_ivar
t1 = HashWithCustomInitNoIvar.new(nil)
- t2 = Psych.load(Psych.dump(t1))
+ t2 = Psych.unsafe_load(Psych.dump(t1))
assert_equal t1, t2
assert_cycle t1
end
@@ -70,25 +70,25 @@ module Psych
x = X.new
x[:a] = 'b'
x.instance_variable_set :@foo, 'bar'
- dup = Psych.load Psych.dump x
+ dup = Psych.unsafe_load Psych.dump x
assert_cycle x
assert_equal 'bar', dup.instance_variable_get(:@foo)
assert_equal X, dup.class
end
def test_load_with_class_syck_compatibility
- hash = Psych.load "--- !ruby/object:Hash\n:user_id: 7\n:username: Lucas\n"
+ hash = Psych.unsafe_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
+ x = Psych.unsafe_load Psych.dump X.new
assert_equal X, x.class
end
def test_map
- x = Psych.load "--- !map:#{X} { }\n"
+ x = Psych.unsafe_load "--- !map:#{X} { }\n"
assert_equal X, x.class
end
@@ -102,7 +102,7 @@ module Psych
end
def test_ref_append
- hash = Psych.load(<<-eoyml)
+ hash = Psych.unsafe_load(<<-eoyml)
---
foo: &foo
hello: world
@@ -114,7 +114,7 @@ eoyml
def test_key_deduplication
unless String.method_defined?(:-@) && (-("a" * 20)).equal?((-("a" * 20)))
- skip "This Ruby implementation doesn't support string deduplication"
+ pend "This Ruby implementation doesn't support string deduplication"
end
hashes = Psych.load(<<-eoyml)
diff --git a/test/psych/test_marshalable.rb b/test/psych/test_marshalable.rb
index b1f4a837f5..74ee902887 100644
--- a/test/psych/test_marshalable.rb
+++ b/test/psych/test_marshalable.rb
@@ -6,7 +6,7 @@ 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))
+ loaded = Psych.unsafe_load(Psych.dump(sd))
assert_instance_of(SimpleDelegator, loaded)
assert_equal(sd, loaded)
@@ -46,7 +46,15 @@ module Psych
def test_init_with_takes_priority_over_marshal_methods
obj = PsychCustomMarshalable.new(1)
- loaded = Psych.load(Psych.dump(obj))
+ loaded = Psych.unsafe_load(Psych.dump(obj))
+
+ assert(PsychCustomMarshalable === loaded)
+ assert_equal(2, loaded.foo)
+ end
+
+ def test_init_symbolize_names
+ obj = PsychCustomMarshalable.new(1)
+ loaded = Psych.unsafe_load(Psych.dump(obj), symbolize_names: true)
assert(PsychCustomMarshalable === loaded)
assert_equal(2, loaded.foo)
diff --git a/test/psych/test_merge_keys.rb b/test/psych/test_merge_keys.rb
index 08ffe58fa8..dcf4f1fce3 100644
--- a/test/psych/test_merge_keys.rb
+++ b/test/psych/test_merge_keys.rb
@@ -34,7 +34,7 @@ map:
end
def test_explicit_string
- doc = Psych.load <<-eoyml
+ doc = Psych.unsafe_load <<-eoyml
a: &me { hello: world }
b: { !!str '<<': *me }
eoyml
@@ -55,7 +55,7 @@ product:
!ruby/object:#{Product.name}
<<: *foo
eoyml
- hash = Psych.load s
+ hash = Psych.unsafe_load s
assert_equal({"bar" => 10}, hash["foo"])
product = hash["product"]
assert_equal 10, product.bar
@@ -67,7 +67,7 @@ defaults: &defaults
development:
<<: *defaults
eoyml
- assert_equal({'<<' => nil }, Psych.load(yaml)['development'])
+ assert_equal({'<<' => nil }, Psych.unsafe_load(yaml)['development'])
end
def test_merge_array
@@ -77,7 +77,7 @@ foo: &hello
baz:
<<: *hello
eoyml
- assert_equal({'<<' => [1]}, Psych.load(yaml)['baz'])
+ assert_equal({'<<' => [1]}, Psych.unsafe_load(yaml)['baz'])
end
def test_merge_is_not_partial
@@ -89,9 +89,9 @@ foo: &hello
baz:
<<: [*hello, *default]
eoyml
- doc = Psych.load yaml
+ doc = Psych.unsafe_load yaml
refute doc['baz'].key? 'hello'
- assert_equal({'<<' => [[1], {"hello"=>"world"}]}, Psych.load(yaml)['baz'])
+ assert_equal({'<<' => [[1], {"hello"=>"world"}]}, Psych.unsafe_load(yaml)['baz'])
end
def test_merge_seq_nil
@@ -100,7 +100,7 @@ foo: &hello
baz:
<<: [*hello]
eoyml
- assert_equal({'<<' => [nil]}, Psych.load(yaml)['baz'])
+ assert_equal({'<<' => [nil]}, Psych.unsafe_load(yaml)['baz'])
end
def test_bad_seq_merge
@@ -109,7 +109,7 @@ defaults: &defaults [1, 2, 3]
development:
<<: *defaults
eoyml
- assert_equal({'<<' => [1,2,3]}, Psych.load(yaml)['development'])
+ assert_equal({'<<' => [1,2,3]}, Psych.unsafe_load(yaml)['development'])
end
def test_missing_merge_key
@@ -117,7 +117,7 @@ development:
bar:
<< : *foo
eoyml
- exp = assert_raises(Psych::BadAlias) { Psych.load yaml }
+ exp = assert_raise(Psych::BadAlias) { Psych.load yaml }
assert_match 'foo', exp.message
end
@@ -134,7 +134,7 @@ bar:
hash = {
"foo" => { "hello" => "world"},
"bar" => { "hello" => "world", "baz" => "boo" } }
- assert_equal hash, Psych.load(yaml)
+ assert_equal hash, Psych.unsafe_load(yaml)
end
def test_multiple_maps
@@ -159,7 +159,7 @@ bar:
'label' => 'center/big'
}
- assert_equal hash, Psych.load(yaml)[4]
+ assert_equal hash, Psych.unsafe_load(yaml)[4]
end
def test_override
@@ -185,7 +185,7 @@ bar:
'label' => 'center/big'
}
- assert_equal hash, Psych.load(yaml)[4]
+ assert_equal hash, Psych.unsafe_load(yaml)[4]
end
end
end
diff --git a/test/psych/test_object.rb b/test/psych/test_object.rb
index f1c61451d0..0faf6b244d 100644
--- a/test/psych/test_object.rb
+++ b/test/psych/test_object.rb
@@ -28,7 +28,7 @@ module Psych
def test_tag_round_trip
tag = Tagged.new
- tag2 = Psych.load(Psych.dump(tag))
+ tag2 = Psych.unsafe_load(Psych.dump(tag))
assert_equal tag.baz, tag2.baz
assert_instance_of(Tagged, tag2)
end
@@ -36,7 +36,7 @@ module Psych
def test_cyclic_references
foo = Foo.new(nil)
foo.parent = foo
- loaded = Psych.load Psych.dump foo
+ loaded = Psych.unsafe_load Psych.dump foo
assert_instance_of(Foo, loaded)
assert_equal loaded, loaded.parent
diff --git a/test/psych/test_object_references.rb b/test/psych/test_object_references.rb
index ca69c7d288..269d72242e 100644
--- a/test/psych/test_object_references.rb
+++ b/test/psych/test_object_references.rb
@@ -34,12 +34,16 @@ module Psych
def assert_reference_trip obj
yml = Psych.dump([obj, obj])
assert_match(/\*-?\d+/, yml)
- data = Psych.load yml
+ begin
+ data = Psych.load yml
+ rescue Psych::DisallowedClass
+ data = Psych.unsafe_load yml
+ end
assert_equal data.first.object_id, data.last.object_id
end
def test_float_references
- data = Psych.load <<-eoyml
+ data = Psych.unsafe_load <<-eoyml
---\s
- &name 1.2
- *name
@@ -49,7 +53,7 @@ module Psych
end
def test_binary_references
- data = Psych.load <<-eoyml
+ data = Psych.unsafe_load <<-eoyml
---
- &name !binary |-
aGVsbG8gd29ybGQh
@@ -60,7 +64,7 @@ module Psych
end
def test_regexp_references
- data = Psych.load <<-eoyml
+ data = Psych.unsafe_load <<-eoyml
---\s
- &name !ruby/regexp /pattern/i
- *name
diff --git a/test/psych/test_omap.rb b/test/psych/test_omap.rb
index 98636ded97..6de0286406 100644
--- a/test/psych/test_omap.rb
+++ b/test/psych/test_omap.rb
@@ -4,7 +4,7 @@ require_relative 'helper'
module Psych
class TestOmap < TestCase
def test_parse_as_map
- o = Psych.load "--- !!omap\na: 1\nb: 2"
+ o = Psych.unsafe_load "--- !!omap\na: 1\nb: 2"
assert_kind_of Psych::Omap, o
assert_equal 1, o['a']
assert_equal 2, o['b']
@@ -14,7 +14,7 @@ module Psych
map = Psych::Omap.new
map['foo'] = 'bar'
map['self'] = map
- assert_equal(map, Psych.load(Psych.dump(map)))
+ assert_equal(map, Psych.unsafe_load(Psych.dump(map)))
end
def test_keys
diff --git a/test/psych/test_parser.rb b/test/psych/test_parser.rb
index e8225dabb6..3604e7c985 100644
--- a/test/psych/test_parser.rb
+++ b/test/psych/test_parser.rb
@@ -63,7 +63,7 @@ module Psych
parser = Psych::Parser.new klass.new
2.times {
- assert_raises(RuntimeError, method.to_s) do
+ assert_raise(RuntimeError, method.to_s) do
parser.parse yaml
end
}
@@ -77,7 +77,7 @@ module Psych
end
def test_filename
- ex = assert_raises(Psych::SyntaxError) do
+ ex = assert_raise(Psych::SyntaxError) do
@parser.parse '--- `', 'omg!'
end
assert_match 'omg!', ex.message
@@ -180,7 +180,7 @@ module Psych
def o.external_encoding; nil end
def o.read len; self end
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
@parser.parse o
end
end
@@ -193,23 +193,23 @@ module Psych
end
def test_syntax_error
- assert_raises(Psych::SyntaxError) do
+ assert_raise(Psych::SyntaxError) do
@parser.parse("---\n\"foo\"\n\"bar\"\n")
end
end
def test_syntax_error_twice
- assert_raises(Psych::SyntaxError) do
+ assert_raise(Psych::SyntaxError) do
@parser.parse("---\n\"foo\"\n\"bar\"\n")
end
- assert_raises(Psych::SyntaxError) do
+ assert_raise(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
+ e = assert_raise(Psych::SyntaxError) do
@parser.parse("---\n\"foo\"\n\"bar\"\n")
end
assert_match '(<unknown>):', e.message
@@ -219,7 +219,7 @@ module Psych
io = StringIO.new "---\n\"foo\"\n\"bar\"\n"
def io.path; "hello!"; end
- e = assert_raises(Psych::SyntaxError) do
+ e = assert_raise(Psych::SyntaxError) do
@parser.parse(io)
end
assert_match "(#{io.path}):", e.message
diff --git a/test/psych/test_psych.rb b/test/psych/test_psych.rb
index 30612ded8f..912bcb9a78 100644
--- a/test/psych/test_psych.rb
+++ b/test/psych/test_psych.rb
@@ -16,7 +16,7 @@ class TestPsych < Psych::TestCase
end
def test_line_width_invalid
- assert_raises(ArgumentError) { Psych.dump('x', { :line_width => -2 }) }
+ assert_raise(ArgumentError) { Psych.dump('x', { :line_width => -2 }) }
end
def test_line_width_no_limit
@@ -61,7 +61,7 @@ class TestPsych < Psych::TestCase
end
def test_load_argument_error
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
Psych.load nil
end
end
@@ -75,7 +75,7 @@ class TestPsych < Psych::TestCase
end
def test_parse_raises_on_bad_input
- assert_raises(Psych::SyntaxError) { Psych.parse("--- `") }
+ assert_raise(Psych::SyntaxError) { Psych.parse("--- `") }
end
def test_parse_with_fallback
@@ -83,8 +83,8 @@ class TestPsych < Psych::TestCase
end
def test_non_existing_class_on_deserialize
- e = assert_raises(ArgumentError) do
- Psych.load("--- !ruby/object:NonExistent\nfoo: 1")
+ e = assert_raise(ArgumentError) do
+ Psych.unsafe_load("--- !ruby/object:NonExistent\nfoo: 1")
end
assert_equal 'undefined class/module NonExistent', e.message
end
@@ -143,7 +143,7 @@ class TestPsych < Psych::TestCase
end
def test_load_stream_raises_on_bad_input
- assert_raises(Psych::SyntaxError) { Psych.load_stream("--- `") }
+ assert_raise(Psych::SyntaxError) { Psych.load_stream("--- `") }
end
def test_parse_stream
@@ -175,7 +175,7 @@ class TestPsych < Psych::TestCase
end
def test_parse_stream_raises_on_bad_input
- assert_raises(Psych::SyntaxError) { Psych.parse_stream("--- `") }
+ assert_raise(Psych::SyntaxError) { Psych.parse_stream("--- `") }
end
def test_add_builtin_type
@@ -214,7 +214,7 @@ class TestPsych < Psych::TestCase
def test_load_freeze_deduplication
unless String.method_defined?(:-@) && (-("a" * 20)).equal?((-("a" * 20)))
- skip "This Ruby implementation doesn't support string deduplication"
+ pend "This Ruby implementation doesn't support string deduplication"
end
data = Psych.load("--- ['a']", freeze: true)
@@ -222,28 +222,28 @@ class TestPsych < Psych::TestCase
end
def test_load_default_fallback
- assert_equal false, Psych.load("")
+ assert_equal false, Psych.unsafe_load("")
end
def test_load_with_fallback
- assert_equal 42, Psych.load("", "file", fallback: 42)
+ assert_equal 42, Psych.load("", filename: "file", fallback: 42)
end
def test_load_with_fallback_nil_or_false
- assert_nil Psych.load("", "file", fallback: nil)
- assert_equal false, Psych.load("", "file", fallback: false)
+ assert_nil Psych.load("", filename: "file", fallback: nil)
+ assert_equal false, Psych.load("", filename: "file", fallback: false)
end
def test_load_with_fallback_hash
- assert_equal Hash.new, Psych.load("", "file", fallback: Hash.new)
+ assert_equal Hash.new, Psych.load("", filename: "file", fallback: Hash.new)
end
def test_load_with_fallback_for_nil
- assert_nil Psych.load("--- null", "file", fallback: 42)
+ assert_nil Psych.unsafe_load("--- null", "file", fallback: 42)
end
def test_load_with_fallback_for_false
- assert_equal false, Psych.load("--- false", "file", fallback: 42)
+ assert_equal false, Psych.unsafe_load("--- false", "file", fallback: 42)
end
def test_load_file
@@ -278,7 +278,7 @@ class TestPsych < Psych::TestCase
def test_load_file_default_fallback
Tempfile.create(['empty', 'yml']) {|t|
- assert_equal false, Psych.load_file(t.path)
+ assert_equal false, Psych.unsafe_load_file(t.path)
}
end
@@ -325,7 +325,7 @@ class TestPsych < Psych::TestCase
t.write("--- !ruby/range\nbegin: 0\nend: 42\nexcl: false\n")
t.close
assert_equal 0..42, Psych.safe_load_file(t.path, permitted_classes: [Range])
- assert_raises(Psych::DisallowedClass) {
+ assert_raise(Psych::DisallowedClass) {
Psych.safe_load_file(t.path)
}
}
@@ -347,9 +347,9 @@ class TestPsych < Psych::TestCase
end
def test_degenerate_strings
- assert_equal false, Psych.load(' ')
+ assert_equal false, Psych.unsafe_load(' ')
assert_equal false, Psych.parse(' ')
- assert_equal false, Psych.load('')
+ assert_equal false, Psych.unsafe_load('')
assert_equal false, Psych.parse('')
end
@@ -371,17 +371,18 @@ class TestPsych < Psych::TestCase
yaml = <<-eoyml
foo:
bar: baz
+ 1: 2
hoge:
- fuga: piyo
eoyml
result = Psych.load(yaml)
- assert_equal result, { "foo" => { "bar" => "baz"}, "hoge" => [{ "fuga" => "piyo" }] }
+ assert_equal result, { "foo" => { "bar" => "baz", 1 => 2 }, "hoge" => [{ "fuga" => "piyo" }] }
result = Psych.load(yaml, symbolize_names: true)
- assert_equal result, { foo: { bar: "baz" }, hoge: [{ fuga: "piyo" }] }
+ assert_equal result, { foo: { bar: "baz", 1 => 2 }, hoge: [{ fuga: "piyo" }] }
result = Psych.safe_load(yaml, symbolize_names: true)
- assert_equal result, { foo: { bar: "baz" }, hoge: [{ fuga: "piyo" }] }
+ assert_equal result, { foo: { bar: "baz", 1 => 2 }, hoge: [{ fuga: "piyo" }] }
end
end
diff --git a/test/psych/test_ractor.rb b/test/psych/test_ractor.rb
index c6bed7ce69..1b0d810609 100644
--- a/test/psych/test_ractor.rb
+++ b/test/psych/test_ractor.rb
@@ -6,7 +6,7 @@ class TestPsychRactor < Test::Unit::TestCase
assert_ractor(<<~RUBY, require_relative: 'helper')
obj = {foo: [42]}
obj2 = Ractor.new(obj) do |obj|
- Psych.load(Psych.dump(obj))
+ Psych.unsafe_load(Psych.dump(obj))
end.take
assert_equal obj, obj2
RUBY
@@ -47,4 +47,4 @@ class TestPsychRactor < Test::Unit::TestCase
assert_equal true, r
RUBY
end
-end if defined?(Test::Unit::TestCase)
+end if defined?(Ractor)
diff --git a/test/psych/test_safe_load.rb b/test/psych/test_safe_load.rb
index e3972712fc..d13ce7c722 100644
--- a/test/psych/test_safe_load.rb
+++ b/test/psych/test_safe_load.rb
@@ -22,7 +22,7 @@ module Psych
def test_no_recursion
x = []
x << x
- assert_raises(Psych::BadAlias) do
+ assert_raise(Psych::BadAlias) do
Psych.safe_load Psych.dump(x)
end
end
@@ -37,7 +37,7 @@ module Psych
def test_permitted_symbol
yml = Psych.dump :foo
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
Psych.safe_load yml
end
assert_equal(
@@ -54,15 +54,15 @@ module Psych
end
def test_symbol
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
assert_safe_cycle :foo
end
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
Psych.safe_load '--- !ruby/symbol foo', permitted_classes: []
end
# deprecated interface
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
Psych.safe_load '--- !ruby/symbol foo', []
end
@@ -75,16 +75,16 @@ module Psych
end
def test_foo
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
Psych.safe_load '--- !ruby/object:Foo {}', permitted_classes: [Foo]
end
# deprecated interface
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
Psych.safe_load '--- !ruby/object:Foo {}', [Foo]
end
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
assert_safe_cycle Foo.new
end
assert_kind_of(Foo, Psych.safe_load(Psych.dump(Foo.new), permitted_classes: [Foo]))
@@ -96,7 +96,7 @@ module Psych
X = Struct.new(:x)
def test_struct_depends_on_sym
assert_safe_cycle(X.new, permitted_classes: [X, Symbol])
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
cycle X.new, permitted_classes: [X]
end
end
@@ -107,14 +107,14 @@ module Psych
foo: bar
eoyml
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
Psych.safe_load(<<-eoyml, permitted_classes: [Struct])
--- !ruby/struct
foo: bar
eoyml
end
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
Psych.safe_load(<<-eoyml, permitted_classes: [Symbol])
--- !ruby/struct
foo: bar
@@ -128,14 +128,14 @@ module Psych
foo: bar
eoyml
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
Psych.safe_load(<<-eoyml, [Struct])
--- !ruby/struct
foo: bar
eoyml
end
- assert_raises(Psych::DisallowedClass) do
+ assert_raise(Psych::DisallowedClass) do
Psych.safe_load(<<-eoyml, [Symbol])
--- !ruby/struct
foo: bar
@@ -152,7 +152,7 @@ module Psych
end
def test_safe_load_raises_on_bad_input
- assert_raises(Psych::SyntaxError) { Psych.safe_load("--- `") }
+ assert_raise(Psych::SyntaxError) { Psych.safe_load("--- `") }
end
private
diff --git a/test/psych/test_serialize_subclasses.rb b/test/psych/test_serialize_subclasses.rb
index 8e1d0d354d..344c79b3ef 100644
--- a/test/psych/test_serialize_subclasses.rb
+++ b/test/psych/test_serialize_subclasses.rb
@@ -17,7 +17,7 @@ module Psych
def test_some_object
so = SomeObject.new('foo', [1,2,3])
- assert_equal so, Psych.load(Psych.dump(so))
+ assert_equal so, Psych.unsafe_load(Psych.dump(so))
end
class StructSubclass < Struct.new(:foo)
@@ -33,7 +33,7 @@ module Psych
def test_struct_subclass
so = StructSubclass.new('foo', [1,2,3])
- assert_equal so, Psych.load(Psych.dump(so))
+ assert_equal so, Psych.unsafe_load(Psych.dump(so))
end
end
end
diff --git a/test/psych/test_set.rb b/test/psych/test_set.rb
index 5690957eff..87944d839e 100644
--- a/test/psych/test_set.rb
+++ b/test/psych/test_set.rb
@@ -21,7 +21,7 @@ module Psych
###
# FIXME: Syck should also support !!set as shorthand
def test_load_from_yaml
- loaded = Psych.load(<<-eoyml)
+ loaded = Psych.unsafe_load(<<-eoyml)
--- !set
foo: bar
bar: baz
@@ -30,11 +30,11 @@ bar: baz
end
def test_loaded_class
- assert_instance_of(Psych::Set, Psych.load(Psych.dump(@set)))
+ assert_instance_of(Psych::Set, Psych.unsafe_load(Psych.dump(@set)))
end
def test_set_shorthand
- loaded = Psych.load(<<-eoyml)
+ loaded = Psych.unsafe_load(<<-eoyml)
--- !!set
foo: bar
bar: baz
diff --git a/test/psych/test_string.rb b/test/psych/test_string.rb
index 973f38b9c2..20ab79c05a 100644
--- a/test/psych/test_string.rb
+++ b/test/psych/test_string.rb
@@ -104,7 +104,7 @@ module Psych
end
def test_string_subclass_with_anchor
- y = Psych.load <<-eoyml
+ y = Psych.unsafe_load <<-eoyml
---
body:
string: &70121654388580 !ruby/string
@@ -116,7 +116,7 @@ body:
end
def test_self_referential_string
- y = Psych.load <<-eoyml
+ y = Psych.unsafe_load <<-eoyml
---
string: &70121654388580 !ruby/string
str: ! 'foo'
@@ -129,32 +129,32 @@ string: &70121654388580 !ruby/string
end
def test_another_subclass_with_attributes
- y = Psych.load Psych.dump Y.new("foo").tap {|o| o.val = 1}
+ y = Psych.unsafe_load Psych.dump Y.new("foo").tap {|o| o.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"
+ x = Psych.unsafe_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
+ x = Psych.unsafe_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
+ x = Psych.unsafe_load Psych.dump Z.new
assert_equal Z, x.class
end
def test_subclass_with_attributes
- y = Psych.load Psych.dump Y.new.tap {|o| o.val = 1}
+ y = Psych.unsafe_load Psych.dump Y.new.tap {|o| o.val = 1}
assert_equal Y, y.class
assert_equal 1, y.val
end
diff --git a/test/psych/test_struct.rb b/test/psych/test_struct.rb
index 721df44216..1479798b1f 100644
--- a/test/psych/test_struct.rb
+++ b/test/psych/test_struct.rb
@@ -22,7 +22,7 @@ module Psych
ss = StructSubclass.new(nil, 'foo')
ss.foo = ss
- loaded = Psych.load(Psych.dump(ss))
+ loaded = Psych.unsafe_load(Psych.dump(ss))
assert_instance_of(StructSubclass, loaded.foo)
assert_equal(ss, loaded)
@@ -30,14 +30,14 @@ module Psych
def test_roundtrip
thing = PsychStructWithIvar.new('bar')
- struct = Psych.load(Psych.dump(thing))
+ struct = Psych.unsafe_load(Psych.dump(thing))
assert_equal 'hello', struct.bar
assert_equal 'bar', struct.foo
end
def test_load
- obj = Psych.load(<<-eoyml)
+ obj = Psych.unsafe_load(<<-eoyml)
--- !ruby/struct:PsychStructWithIvar
:foo: bar
:@bar: hello
diff --git a/test/psych/test_yaml.rb b/test/psych/test_yaml.rb
index 84cbe268fa..e12b9769fe 100644
--- a/test/psych/test_yaml.rb
+++ b/test/psych/test_yaml.rb
@@ -17,7 +17,7 @@ class Psych_Unit_Tests < Psych::TestCase
end
def test_y_method
- assert_raises(NoMethodError) do
+ assert_raise(NoMethodError) do
OpenStruct.new.y 1
end
end
@@ -573,7 +573,7 @@ EOY
end
def test_spec_root_mapping
- y = Psych::load( <<EOY
+ y = Psych::unsafe_load( <<EOY
# This stream is an example of a top-level mapping.
invoice : 34843
date : 2001-01-23
@@ -1077,7 +1077,7 @@ 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") }
+ assert_raise( ArgumentError ) { Psych.unsafe_load("!ruby/object:Rational INVALID/RATIONAL\n") }
end
def test_ruby_complex
@@ -1089,7 +1089,7 @@ 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") }
+ assert_raise( ArgumentError ) { Psych.unsafe_load("!ruby/object:Complex INVALID+COMPLEXi\n") }
end
def test_emitting_indicators
@@ -1209,7 +1209,7 @@ EOY
def test_circular_references
a = []; a[0] = a; a[1] = a
inspect_str = "[[...], [...]]"
- assert_equal( inspect_str, Psych::load(Psych.dump(a)).inspect )
+ assert_equal( inspect_str, Psych::unsafe_load(Psych.dump(a)).inspect )
end
#
@@ -1264,11 +1264,11 @@ EOY
end
def test_date_out_of_range
- Psych::load('1900-01-01T00:00:00+00:00')
+ Psych::unsafe_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")
+ Psych.unsafe_load("2000-01-01 00:00:00.#{"0"*1000} +00:00\n")
# '[ruby-core:13735]'
end
diff --git a/test/psych/test_yaml_special_cases.rb b/test/psych/test_yaml_special_cases.rb
index 4501704030..205457bcae 100644
--- a/test/psych/test_yaml_special_cases.rb
+++ b/test/psych/test_yaml_special_cases.rb
@@ -13,7 +13,7 @@ module Psych
def test_empty_string
s = ""
- assert_equal false, Psych.load(s)
+ assert_equal false, Psych.unsafe_load(s)
assert_equal [], Psych.load_stream(s)
assert_equal false, Psych.parse(s)
assert_equal [], Psych.parse_stream(s).transform
@@ -58,8 +58,8 @@ module Psych
def test_NaN
s = ".NaN"
- assert Float::NAN, Psych.load(s).nan?
- assert [Float::NAN], Psych.load_stream(s).first.nan?
+ assert Psych.load(s).nan?
+ assert Psych.load_stream(s).first.nan?
assert Psych.parse(s).transform.nan?
assert Psych.parse_stream(s).transform.first.nan?
assert Psych.safe_load(s).nan?
diff --git a/test/psych/test_yamlstore.rb b/test/psych/test_yamlstore.rb
index d1e927cefe..1a1be3700e 100644
--- a/test/psych/test_yamlstore.rb
+++ b/test/psych/test_yamlstore.rb
@@ -4,7 +4,22 @@ require 'yaml/store'
require 'tmpdir'
module Psych
- Psych::Store = YAML::Store unless defined?(Psych::Store)
+ class YAML::Store
+ alias :old_load :load
+
+ def load(content)
+ table = YAML.load(content, fallback: false)
+ if table == false
+ {}
+ else
+ table
+ end
+ end
+ end
+
+ unless defined?(Psych::Store)
+ Psych::Store = YAML::Store
+ end
class YAMLStoreTest < TestCase
def setup
@@ -24,61 +39,61 @@ module Psych
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]
+ 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]
+ 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"
+ @yamlstore["foo"] = "bar"
end
@yamlstore.transaction(true) do
- assert_equal "bar", @yamlstore[:foo]
+ assert_equal "bar", @yamlstore["foo"]
end
end
def test_data_should_be_loaded_correctly_when_in_readwrite_mode
@yamlstore.transaction do
- @yamlstore[:foo] = "bar"
+ @yamlstore["foo"] = "bar"
end
@yamlstore.transaction do
- assert_equal "bar", @yamlstore[:foo]
+ assert_equal "bar", @yamlstore["foo"]
end
end
def test_changes_after_commit_are_discarded
@yamlstore.transaction do
- @yamlstore[:foo] = "bar"
+ @yamlstore["foo"] = "bar"
@yamlstore.commit
- @yamlstore[:foo] = "baz"
+ @yamlstore["foo"] = "baz"
end
@yamlstore.transaction(true) do
- assert_equal "bar", @yamlstore[:foo]
+ assert_equal "bar", @yamlstore["foo"]
end
end
def test_changes_are_not_written_on_abort
@yamlstore.transaction do
- @yamlstore[:foo] = "bar"
+ @yamlstore["foo"] = "bar"
@yamlstore.abort
end
@yamlstore.transaction(true) do
- assert_nil @yamlstore[:foo]
+ assert_nil @yamlstore["foo"]
end
end
def test_writing_inside_readonly_transaction_raises_error
- assert_raises(PStore::Error) do
+ assert_raise(PStore::Error) do
@yamlstore.transaction(true) do
- @yamlstore[:foo] = "bar"
+ @yamlstore["foo"] = "bar"
end
end
end
diff --git a/test/psych/visitors/test_to_ruby.rb b/test/psych/visitors/test_to_ruby.rb
index e1a0056a61..3d4608b903 100644
--- a/test/psych/visitors/test_to_ruby.rb
+++ b/test/psych/visitors/test_to_ruby.rb
@@ -20,13 +20,13 @@ module Psych
end
def test_tz_00_00_loads_without_error
- assert Psych.load('1900-01-01T00:00:00+00:00')
+ assert Psych.unsafe_load('1900-01-01T00:00:00+00:00')
end
def test_legacy_struct
Struct.send(:remove_const, :AWESOME) if Struct.const_defined?(:AWESOME)
foo = Struct.new('AWESOME', :bar)
- assert_equal foo.new('baz'), Psych.load(<<-eoyml)
+ assert_equal foo.new('baz'), Psych.unsafe_load(<<-eoyml)
!ruby/struct:AWESOME
bar: baz
eoyml
diff --git a/test/psych/visitors/test_yaml_tree.rb b/test/psych/visitors/test_yaml_tree.rb
index 69885ee9c6..6a9dbc1ec3 100644
--- a/test/psych/visitors/test_yaml_tree.rb
+++ b/test/psych/visitors/test_yaml_tree.rb
@@ -62,19 +62,19 @@ module Psych
def test_struct_anon
s = Struct.new(:foo).new('bar')
- obj = Psych.load(Psych.dump(s))
+ obj = Psych.unsafe_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))
+ obj = Psych.unsafe_load(Psych.dump(s))
assert_equal s.method, obj.method
end
def test_exception
ex = Exception.new 'foo'
- loaded = Psych.load(Psych.dump(ex))
+ loaded = Psych.unsafe_load(Psych.dump(ex))
assert_equal ex.message, loaded.message
assert_equal ex.class, loaded.class
@@ -88,7 +88,7 @@ module Psych
def test_time
t = Time.now
- assert_equal t, Psych.load(Psych.dump(t))
+ assert_equal t, Psych.unsafe_load(Psych.dump(t))
end
def test_date
@@ -127,11 +127,11 @@ module Psych
end
def test_anon_class
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
@v.accept Class.new
end
- assert_raises(TypeError) do
+ assert_raise(TypeError) do
Psych.dump(Class.new)
end
end
diff --git a/test/rdoc/test_rdoc_markdown.rb b/test/rdoc/test_rdoc_markdown.rb
index 9c7a406224..ad53e9473c 100644
--- a/test/rdoc/test_rdoc_markdown.rb
+++ b/test/rdoc/test_rdoc_markdown.rb
@@ -143,7 +143,7 @@ a block quote
end
def test_parse_code_github
- doc = parse <<-MD
+ doc = <<-MD
Example:
```
@@ -156,11 +156,25 @@ code goes here
para("Example:"),
verb("code goes here\n"))
- assert_equal expected, doc
+ assert_equal expected, parse(doc)
+ assert_equal expected, parse(doc.sub(/^\n/, ''))
+
+ @parser.github = false
+
+ expected =
+ doc(para("Example:"),
+ para("<code>\n""code goes here\n</code>"))
+
+ assert_equal expected, parse(doc)
+
+ expected =
+ doc(para("Example:\n<code>\n""code goes here\n</code>"))
+
+ assert_equal expected, parse(doc.sub(/^\n/, ''))
end
def test_parse_code_github_format
- doc = parse <<-MD
+ doc = <<-MD
Example:
``` ruby
@@ -176,7 +190,21 @@ code goes here
para("Example:"),
code)
- assert_equal expected, doc
+ assert_equal expected, parse(doc)
+ assert_equal expected, parse(doc.sub(/^\n/, ''))
+
+ @parser.github = false
+
+ expected =
+ doc(para("Example:"),
+ para("<code>ruby\n""code goes here\n</code>"))
+
+ assert_equal expected, parse(doc)
+
+ expected =
+ doc(para("Example:\n<code>ruby\n""code goes here\n</code>"))
+
+ assert_equal expected, parse(doc.sub(/^\n/, ''))
end
def test_parse_definition_list
@@ -1012,6 +1040,29 @@ and an extra note.[^2]
assert_equal expected, doc
end
+ def test_gfm_table
+ doc = parse <<~MD
+ | | |compare-ruby|built-ruby|
+ |------|:----------------|-----------:|---------:|
+ |test | 1 | 11.618M| 10.757M|
+ | | | 1.08x| -|
+ |test | 10 | 1.849M| 1.723M|
+ | | | 1.07x| -|
+ MD
+
+ head = ["", "", "compare-ruby", "built-ruby"]
+ align = [nil, :left, :right, :right]
+ body = [
+ ["test", "1", "11.618M", "10.757M"],
+ ["", "", "1.08x", "-"],
+ ["test", "10", "1.849M", "1.723M"],
+ ["", "", "1.07x", "-"],
+ ]
+ expected = doc(@RM::Table.new(head, align, body))
+
+ assert_equal expected, doc
+ end
+
def parse text
@parser.parse text
end
diff --git a/test/rdoc/test_rdoc_markup_attribute_manager.rb b/test/rdoc/test_rdoc_markup_attribute_manager.rb
index a180666867..944364ba89 100644
--- a/test/rdoc/test_rdoc_markup_attribute_manager.rb
+++ b/test/rdoc/test_rdoc_markup_attribute_manager.rb
@@ -172,22 +172,25 @@ class TestRDocMarkupAttributeManager < RDoc::TestCase
def test_convert_attrs
str = '+foo+'.dup
- attrs = RDoc::Markup::AttrSpan.new str.length
+ attrs = RDoc::Markup::AttrSpan.new str.length, @am.exclusive_bitmap
+ @am.convert_attrs str, attrs, true
@am.convert_attrs str, attrs
assert_equal "\000foo\000", str
str = '+:foo:+'.dup
- attrs = RDoc::Markup::AttrSpan.new str.length
+ attrs = RDoc::Markup::AttrSpan.new str.length, @am.exclusive_bitmap
+ @am.convert_attrs str, attrs, true
@am.convert_attrs str, attrs
assert_equal "\000:foo:\000", str
str = '+x-y+'.dup
- attrs = RDoc::Markup::AttrSpan.new str.length
+ attrs = RDoc::Markup::AttrSpan.new str.length, @am.exclusive_bitmap
+ @am.convert_attrs str, attrs, true
@am.convert_attrs str, attrs
assert_equal "\000x-y\000", str
@@ -243,6 +246,22 @@ class TestRDocMarkupAttributeManager < RDoc::TestCase
output('unhandled <p>tag</p> unchanged')
end
+ def test_exclude_tag
+ assert_equal '<CODE>aaa</CODE>[:symbol]', output('+aaa+[:symbol]')
+ assert_equal '<CODE>aaa[:symbol]</CODE>', output('+aaa[:symbol]+')
+ assert_equal 'aaa[:symbol]', output('aaa[:symbol]')
+ assert_equal '<B><CODE>index</CODE></B>', output('<b><tt>index</tt></b>')
+ end
+
+ def test_exclude_tag_flow
+ assert_equal [@tt_on, "aaa", @tt_off, "[:symbol]"],
+ @am.flow("+aaa+[:symbol]")
+ assert_equal [@tt_on, "aaa[:symbol]", @tt_off],
+ @am.flow("+aaa[:symbol]+")
+ assert_equal ["aaa[:symbol]"],
+ @am.flow("aaa[:symbol]")
+ 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>")
diff --git a/test/rdoc/test_rdoc_markup_to_html.rb b/test/rdoc/test_rdoc_markup_to_html.rb
index fb94269064..29da968abc 100644
--- a/test/rdoc/test_rdoc_markup_to_html.rb
+++ b/test/rdoc/test_rdoc_markup_to_html.rb
@@ -704,6 +704,17 @@ EXPECTED
assert_equal "\n<p><a href=\"irc://irc.freenode.net/#ruby-lang\">ruby-lang</a></p>\n", result
end
+ def test_convert_with_exclude_tag
+ assert_equal "\n<p><code>aaa</code>[:symbol]</p>\n", @to.convert('+aaa+[:symbol]')
+ assert_equal "\n<p><code>aaa[:symbol]</code></p>\n", @to.convert('+aaa[:symbol]+')
+ assert_equal "\n<p><a href=\":symbol\">aaa</a></p>\n", @to.convert('aaa[:symbol]')
+ end
+
+ def test_convert_underscore_adjacent_to_code
+ assert_equal "\n<p><code>aaa</code>_</p>\n", @to.convert(%q{+aaa+_})
+ assert_equal "\n<p>`<code>i386-mswin32_</code><em>MSRTVERSION</em>&#39;</p>\n", @to.convert(%q{`+i386-mswin32_+_MSRTVERSION_'})
+ end
+
def test_gen_url
assert_equal '<a href="example">example</a>',
@to.gen_url('link:example', 'example')
@@ -727,6 +738,27 @@ EXPECTED
assert_equal '<img src="https://example.com/image.png" />', @to.gen_url('https://example.com/image.png', 'ignored')
end
+ def test_gen_url_rdoc_file
+ assert_equal '<a href="doc/example_rdoc.html">example</a>',
+ @to.gen_url('doc/example.rdoc', 'example')
+ assert_equal '<a href="../ex_doc/example_rdoc.html">example</a>',
+ @to.gen_url('../ex.doc/example.rdoc', 'example')
+ end
+
+ def test_gen_url_md_file
+ assert_equal '<a href="doc/example_md.html">example</a>',
+ @to.gen_url('doc/example.md', 'example')
+ assert_equal '<a href="../ex_doc/example_md.html">example</a>',
+ @to.gen_url('../ex.doc/example.md', 'example')
+ end
+
+ def test_gen_url_rb_file
+ assert_equal '<a href="doc/example_rb.html">example</a>',
+ @to.gen_url('doc/example.rb', 'example')
+ assert_equal '<a href="../ex_doc/example_rb.html">example</a>',
+ @to.gen_url('../ex.doc/example.rb', 'example')
+ end
+
def test_handle_regexp_HYPERLINK_link
target = RDoc::Markup::RegexpHandling.new 0, 'link:README.txt'
diff --git a/test/rdoc/test_rdoc_options.rb b/test/rdoc/test_rdoc_options.rb
index 140c4afc9b..f547f5bff3 100644
--- a/test/rdoc/test_rdoc_options.rb
+++ b/test/rdoc/test_rdoc_options.rb
@@ -145,7 +145,7 @@ class TestRDocOptions < RDoc::TestCase
@options.encoding = Encoding::IBM437
- options = YAML.load YAML.dump @options
+ options = YAML.safe_load(YAML.dump(@options), permitted_classes: [RDoc::Options, Symbol])
assert_equal Encoding::IBM437, options.encoding
end
@@ -161,7 +161,7 @@ rdoc_include:
- /etc
YAML
- options = YAML.load yaml
+ options = YAML.safe_load(yaml, permitted_classes: [RDoc::Options, Symbol])
assert_empty options.rdoc_include
assert_empty options.static_path
@@ -749,7 +749,7 @@ rdoc_include:
assert File.exist? '.rdoc_options'
- assert_equal @options, YAML.load(File.read('.rdoc_options'))
+ assert_equal @options, YAML.safe_load(File.read('.rdoc_options'), permitted_classes: [RDoc::Options, Symbol])
end
end
diff --git a/test/rdoc/test_rdoc_parser_changelog.rb b/test/rdoc/test_rdoc_parser_changelog.rb
index d93cb7deca..6584840572 100644
--- a/test/rdoc/test_rdoc_parser_changelog.rb
+++ b/test/rdoc/test_rdoc_parser_changelog.rb
@@ -212,6 +212,8 @@ Mon Dec 3 20:28:02 2012 Koichi Sasada <ko1@atdot.net>
change condition of using `opt_send_simple'.
More method invocations can be simple.
+commit\ 8187228de0142d3ac7950b7d977c2849e934c637
+
Other note that will be ignored
ChangeLog
@@ -270,6 +272,24 @@ Other note that will be ignored
assert_equal expected, parser.parse_entries
end
+ def test_parse_entries_git
+ parser = util_parser <<-ChangeLog
+commit\ 709bed2afaee50e2ce803f87bf1ee8291bea41e3
+ Author: git <svn-admin@ruby-lang.org>
+ Date: 2021-01-21 01:03:52 +0900
+
+ * 2021-01-21 [ci skip]
+ChangeLog
+
+ expected = [
+ [ "709bed2afaee50e2ce80",
+ [ "git", "svn-admin@ruby-lang.org",
+ "2021-01-21 01:03:52 +0900",
+ "* 2021-01-21 [ci skip]\n"]]]
+
+ 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>
@@ -309,10 +329,157 @@ Mon Dec 3 20:37:22 2012 Koichi Sasada <ko1@atdot.net>
assert_equal expected, @top_level.comment
end
+ def test_scan_git
+ parser = util_parser <<-ChangeLog
+commit\ 38816887962ec167ee46acf500f68df5c3013163
+Author: git <svn-admin@ruby-lang.org>
+Date: Sun Jan 24 14:35:51 2021 +0900
+
+ * 2021-01-24 [ci skip]
+
+commit\ db7d0b89f6eca66cc7eb155c95f9123133da1ffc
+Author: git <svn-admin@ruby-lang.org>
+Date: Sat, 23 Jan 2021 06:01:39 +0900
+
+ * 2021-01-23 [ci skip]
+
+commit\ a3efbda7128ef20b55505b32d1608ea48f80af4a
+Author: git <svn-admin@ruby-lang.org>
+Date: 2021-01-22T02:49:39+09:00
+
+ * 2021-01-22 [ci skip]
+
+commit\ 709bed2afaee50e2ce803f87bf1ee8291bea41e3
+ Author: git <svn-admin@ruby-lang.org>
+ Date: 2021-01-21 01:03:52 +0900
+
+ * 2021-01-21 [ci skip]
+
+commit\ a8dc5156e183489c5121fb1759bda5d9406d9175
+ Author: git <svn-admin@ruby-lang.org>
+ Date: 2021-01-20 01:58:26 +0900
+
+ * 2021-01-20 [ci skip]
+
+commit\ de5f8a92d5001799bedb3b1a271a2d9b23c6c8fb
+ Author: Masataka Pocke Kuwabara <kuwabara@pocke.me>
+ Date: 2021-01-01 14:25:08 +0900
+
+ Make args info for RubyVM::AST to available on endless method without parens
+
+ Problem
+ ===
+
+ Arguments information is missing for endless method without parens.
+ For example:
+
+ ```ruby
+ # ok
+ ```
+
+ It causes an error if a program expects `args` node exists.
+
+ Solution
+ ===
+
+ Call `new_args` on this case.
+ChangeLog
+
+ parser.scan
+
+ expected = doc(
+ head(1, File.basename(@tempfile.path)),
+ blank_line,
+ head(2, '2021-01-24'),
+ blank_line,
+ log_entry(nil, '38816887962ec167ee46',
+ 'git', 'svn-admin@ruby-lang.org', 'Sun Jan 24 14:35:51 2021 +0900',
+ [list(:BULLET, item(nil, para('2021-01-24 [ci skip]')))]),
+ head(2, '2021-01-23'),
+ blank_line,
+ log_entry(nil, 'db7d0b89f6eca66cc7eb',
+ 'git', 'svn-admin@ruby-lang.org', 'Sat, 23 Jan 2021 06:01:39 +0900',
+ [list(:BULLET, item(nil, para('2021-01-23 [ci skip]')))]),
+ head(2, '2021-01-22'),
+ blank_line,
+ log_entry(nil, 'a3efbda7128ef20b5550',
+ 'git', 'svn-admin@ruby-lang.org', '2021-01-22T02:49:39+09:00',
+ [list(:BULLET, item(nil, para('2021-01-22 [ci skip]')))]),
+ head(2, '2021-01-21'),
+ blank_line,
+ log_entry(nil, '709bed2afaee50e2ce80',
+ 'git', 'svn-admin@ruby-lang.org', '2021-01-21 01:03:52 +0900',
+ [list(:BULLET, item(nil, para('2021-01-21 [ci skip]')))]),
+ head(2, '2021-01-20'),
+ blank_line,
+ log_entry(nil, 'a8dc5156e183489c5121',
+ 'git', 'svn-admin@ruby-lang.org', '2021-01-20 01:58:26 +0900',
+ [list(:BULLET, item(nil, para('2021-01-20 [ci skip]')))]),
+ head(2, '2021-01-01'),
+ blank_line,
+ log_entry(nil, 'de5f8a92d5001799bedb',
+ 'Masataka Pocke Kuwabara', 'kuwabara@pocke.me', '2021-01-01 14:25:08 +0900',
+ [head(4, 'Make args info for RubyVM::AST to available on endless method without parens'),
+ head(5, 'Problem'),
+ para("Arguments information is missing for endless method without parens.\n" +
+ "For example:"),
+ verb("# ok\n").tap {|v| v.format = :ruby},
+ para('It causes an error if a program expects <code>args</code> node exists.'),
+ head(5, 'Solution'),
+ para('Call <code>new_args</code> on this case.')]))
+
+ expected.file = @top_level
+
+ assert_equal expected, @top_level.comment
+ end
+
+ def test_scan_git_commit_date
+ parser = util_parser <<-ChangeLog
+commit\ ee1e690a2df901adb279d7a63fbd92c64e0a5ae6
+ Author: Igor Zubkov <igor.zubkov@gmail.com>
+ AuthorDate: 2016-10-25 03:56:11 +0900
+ Commit: Nobuyoshi Nakada <nobu@ruby-lang.org>
+ CommitDate: 2021-01-07 13:40:42 +0900
+
+ We don't need "require 'uri'" after "require 'net/http'".
+
+commit\ 4d0985a7bd8f591dff4b430e288bfd83af782e51
+ Author: git <svn-admin@ruby-lang.org>
+ AuthorDate: 2021-01-07 10:21:34 +0900
+ Commit: git <svn-admin@ruby-lang.org>
+ CommitDate: 2021-01-07 10:21:34 +0900
+
+ * 2021-01-07 [ci skip]
+ChangeLog
+
+ parser.scan
+
+ expected = doc(
+ head(1, File.basename(@tempfile.path)),
+ blank_line,
+ head(2, "2021-01-07"),
+ blank_line,
+ log_entry(nil, 'ee1e690a2df901adb279',
+ 'Igor Zubkov', 'igor.zubkov@gmail.com',
+ '2016-10-25 03:56:11 +0900',
+ [head(4, %[We don't need "require 'uri'" after "require 'net/http'".])]),
+ log_entry(nil, '4d0985a7bd8f591dff4b',
+ 'git', 'svn-admin@ruby-lang.org',
+ '2021-01-07 10:21:34 +0900',
+ [list(:BULLET, item(nil, para("2021-01-07 [ci skip]")))]))
+
+ 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
+ def log_entry(*a)
+ RDoc::Parser::ChangeLog::Git::LogEntry.new(*a)
+ end
end
diff --git a/test/rdoc/test_rdoc_rdoc.rb b/test/rdoc/test_rdoc_rdoc.rb
index f7d9b8659f..7b84bb698a 100644
--- a/test/rdoc/test_rdoc_rdoc.rb
+++ b/test/rdoc/test_rdoc_rdoc.rb
@@ -133,6 +133,29 @@ class TestRDocRDoc < RDoc::TestCase
end
end
+ def test_load_options_empty_file
+ temp_dir do
+ File.open '.rdoc_options', 'w' do |io|
+ end
+
+ options = @rdoc.load_options
+
+ assert_equal 'rdoc', options.markup
+ end
+ end
+
+ def test_load_options_partial_override
+ temp_dir do
+ File.open '.rdoc_options', 'w' do |io|
+ io.write "markup: Markdown"
+ end
+
+ options = @rdoc.load_options
+
+ assert_equal 'Markdown', options.markup
+ end
+ end
+
def load_options_no_file
temp_dir do
options = @rdoc.load_options
@@ -433,6 +456,18 @@ class TestRDocRDoc < RDoc::TestCase
end
end
+ def test_remove_unparseable_CVE_2021_31799
+ temp_dir do
+ file_list = ['| touch evil.txt && echo tags']
+ file_list.each do |f|
+ FileUtils.touch f rescue omit
+ end
+
+ assert_equal file_list, @rdoc.remove_unparseable(file_list)
+ assert_equal file_list, Dir.children('.')
+ end
+ end
+
def test_setup_output_dir
Dir.mktmpdir {|d|
path = File.join d, 'testdir'
diff --git a/test/rdoc/test_rdoc_rubygems_hook.rb b/test/rdoc/test_rdoc_rubygems_hook.rb
index 7d59577d97..959214b474 100644
--- a/test/rdoc/test_rdoc_rubygems_hook.rb
+++ b/test/rdoc/test_rdoc_rubygems_hook.rb
@@ -1,31 +1,62 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require "rubygems"
+require "fileutils"
+require "tmpdir"
require 'rdoc/rubygems_hook'
+require "test/unit"
-class TestRDocRubygemsHook < Gem::TestCase
-
+class TestRDocRubygemsHook < Test::Unit::TestCase
def setup
- super
-
- @a = util_spec 'a', 2 do |s|
+ @a = Gem::Specification.new do |s|
+ s.platform = Gem::Platform::RUBY
+ s.name = "a"
+ s.version = 2
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
+ @tempdir = File.realpath(Dir.mktmpdir("test_rubygems_hook_"))
+
+ @orig_envs = %w[
+ GEM_VENDOR
+ GEMRC
+ XDG_CACHE_HOME
+ XDG_CONFIG_HOME
+ XDG_DATA_HOME
+ SOURCE_DATE_EPOCH
+ BUNDLER_VERSION
+ HOME
+ RDOCOPT
+ ].map {|e| [e, ENV.delete(e)]}.to_h
+ ENV["HOME"] = @tempdir
+
+ Gem.configuration = nil
+
+ @a.instance_variable_set(:@doc_dir, File.join(@tempdir, "doc"))
+ @a.instance_variable_set(:@gem_dir, File.join(@tempdir, "a-2"))
+ @a.instance_variable_set(:@full_gem_path, File.join(@tempdir, "a-2"))
+ @a.loaded_from = File.join(@tempdir, 'a-2', 'a-2.gemspec')
+
+ FileUtils.mkdir_p File.join(@tempdir, 'a-2', 'lib')
+ FileUtils.touch File.join(@tempdir, 'a-2', 'lib', 'a.rb')
+ FileUtils.touch File.join(@tempdir, 'a-2', 'README')
@hook = RDoc::RubygemsHook.new @a
begin
RDoc::RubygemsHook.load_rdoc
rescue Gem::DocumentError => e
- skip e.message
+ omit e.message
end
+ @old_ui = Gem::DefaultUserInteraction.ui
+ Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
+ end
- Gem.configuration[:rdoc] = nil
+ def teardown
+ ui = Gem::DefaultUserInteraction.ui
+ Gem::DefaultUserInteraction.ui = @old_ui
+ FileUtils.rm_rf @tempdir
+ ui.close
+ ENV.update(@orig_envs)
end
def test_initialize
@@ -78,9 +109,11 @@ class TestRDocRubygemsHook < Gem::TestCase
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
+
+ omit "skip rdoc rubygems integration test: maybe some fixes are required in rubygems."
+ assert_equal %w[README lib], rdoc.options.files.sort
end
def test_generate_all
@@ -99,9 +132,11 @@ class TestRDocRubygemsHook < Gem::TestCase
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
+
+ omit "skip rdoc rubygems integration test: maybe some fixes are required in rubygems."
+ assert_equal %w[README lib], rdoc.options.files.sort
end
def test_generate_configuration_rdoc_array
@@ -165,8 +200,8 @@ class TestRDocRubygemsHook < Gem::TestCase
@hook.generate
- refute_path_exists File.join(@a.doc_dir('rdoc'), 'index.html')
- assert_path_exists File.join(@a.doc_dir('ri'), 'cache.ri')
+ assert_path_not_exist File.join(@a.doc_dir('rdoc'), 'index.html')
+ assert_path_exist File.join(@a.doc_dir('ri'), 'cache.ri')
end
def test_generate_no_overwrite
@@ -176,8 +211,8 @@ class TestRDocRubygemsHook < Gem::TestCase
@hook.generate
- refute_path_exists File.join(@a.doc_dir('rdoc'), 'index.html')
- refute_path_exists File.join(@a.doc_dir('ri'), 'cache.ri')
+ assert_path_not_exist File.join(@a.doc_dir('rdoc'), 'index.html')
+ assert_path_not_exist File.join(@a.doc_dir('ri'), 'cache.ri')
end
def test_new_rdoc
@@ -201,17 +236,17 @@ class TestRDocRubygemsHook < Gem::TestCase
refute @hook.rdoc_installed?
refute @hook.ri_installed?
- assert_path_exists @a.doc_dir
+ assert_path_exist @a.doc_dir
end
def test_remove_unwritable
- skip 'chmod not supported' if Gem.win_platform?
- skip "assumes that euid is not root" if Process.euid == 0
+ omit 'chmod not supported' if Gem.win_platform?
+ omit "assumes that euid is not root" if Process.euid == 0
FileUtils.mkdir_p @a.base_dir
FileUtils.chmod 0, @a.base_dir
- e = assert_raises Gem::FilePermissionError do
+ e = assert_raise Gem::FilePermissionError do
@hook.remove
end
@@ -231,17 +266,17 @@ class TestRDocRubygemsHook < Gem::TestCase
def test_setup
@hook.setup
- assert_path_exists @a.doc_dir
+ assert_path_exist @a.doc_dir
end
def test_setup_unwritable
- skip 'chmod not supported' if Gem.win_platform?
- skip "assumes that euid is not root" if Process.euid == 0
+ omit 'chmod not supported' if Gem.win_platform?
+ omit "assumes that euid is not root" if Process.euid == 0
FileUtils.mkdir_p @a.doc_dir
FileUtils.chmod 0, @a.doc_dir
- e = assert_raises Gem::FilePermissionError do
+ e = assert_raise Gem::FilePermissionError do
@hook.setup
end
diff --git a/test/rdoc/test_rdoc_top_level.rb b/test/rdoc/test_rdoc_top_level.rb
index e396791ab8..a954fde981 100644
--- a/test/rdoc/test_rdoc_top_level.rb
+++ b/test/rdoc/test_rdoc_top_level.rb
@@ -157,6 +157,9 @@ class TestRDocTopLevel < XrefTestCase
def test_http_url
assert_equal 'prefix/path/top_level_rb.html', @top_level.http_url('prefix')
+
+ other_level = @store.add_file 'path.other/level.rb'
+ assert_equal 'prefix/path_other/level_rb.html', other_level.http_url('prefix')
end
def test_last_modified
diff --git a/test/reline/test_reline.rb b/test/reline/test_reline.rb
index d2de4690d5..0f32ec4421 100644
--- a/test/reline/test_reline.rb
+++ b/test/reline/test_reline.rb
@@ -65,6 +65,8 @@ class Reline::Test < Reline::TestCase
Reline.completer_word_break_characters = "[".encode(Encoding::ASCII)
assert_equal("[", Reline.completer_word_break_characters)
assert_equal(get_reline_encoding, Reline.completer_word_break_characters.encoding)
+
+ assert_nothing_raised { Reline.completer_word_break_characters = '' }
ensure
Reline.completer_word_break_characters = completer_word_break_characters
end
@@ -89,6 +91,8 @@ class Reline::Test < Reline::TestCase
Reline.completer_quote_characters = "`".encode(Encoding::ASCII)
assert_equal("`", Reline.completer_quote_characters)
assert_equal(get_reline_encoding, Reline.completer_quote_characters.encoding)
+
+ assert_nothing_raised { Reline.completer_quote_characters = '' }
ensure
Reline.completer_quote_characters = completer_quote_characters
end
diff --git a/test/reline/test_string_processing.rb b/test/reline/test_string_processing.rb
index e76fa384f2..0e0ee9cc04 100644
--- a/test/reline/test_string_processing.rb
+++ b/test/reline/test_string_processing.rb
@@ -20,4 +20,58 @@ class Reline::LineEditor::StringProcessingTest < Reline::TestCase
width = @line_editor.send(:calculate_width, "\1\e[31m\2RubyColor\1\e[34m\2 default string \1\e[m\2>", true)
assert_equal('RubyColor default string >'.size, width)
end
+
+ def test_completion_proc_with_preposing_and_postposing
+ buf = ['def hoge', ' puts :aaa', 'end']
+
+ @line_editor.instance_variable_set(:@is_multiline, true)
+ @line_editor.instance_variable_set(:@buffer_of_lines, buf)
+ @line_editor.instance_variable_set(:@line, buf[1])
+ @line_editor.instance_variable_set(:@byte_pointer, 3)
+ @line_editor.instance_variable_set(:@cursor, 3)
+ @line_editor.instance_variable_set(:@cursor_max, 11)
+ @line_editor.instance_variable_set(:@line_index, 1)
+ @line_editor.instance_variable_set(:@completion_proc, proc { |target|
+ assert_equal('p', target)
+ })
+ @line_editor.__send__(:call_completion_proc)
+
+ @line_editor.instance_variable_set(:@is_multiline, true)
+ @line_editor.instance_variable_set(:@buffer_of_lines, buf)
+ @line_editor.instance_variable_set(:@line, buf[1])
+ @line_editor.instance_variable_set(:@byte_pointer, 6)
+ @line_editor.instance_variable_set(:@cursor, 6)
+ @line_editor.instance_variable_set(:@cursor_max, 11)
+ @line_editor.instance_variable_set(:@line_index, 1)
+ @line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
+ assert_equal('puts', target)
+ assert_equal("def hoge\n ", pre)
+ assert_equal(" :aaa\nend", post)
+ })
+ @line_editor.__send__(:call_completion_proc)
+
+ @line_editor.instance_variable_set(:@line, buf[0])
+ @line_editor.instance_variable_set(:@byte_pointer, 6)
+ @line_editor.instance_variable_set(:@cursor, 6)
+ @line_editor.instance_variable_set(:@cursor_max, 8)
+ @line_editor.instance_variable_set(:@line_index, 0)
+ @line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
+ assert_equal('ho', target)
+ assert_equal('def ', pre)
+ assert_equal("ge\n puts :aaa\nend", post)
+ })
+ @line_editor.__send__(:call_completion_proc)
+
+ @line_editor.instance_variable_set(:@line, buf[2])
+ @line_editor.instance_variable_set(:@byte_pointer, 1)
+ @line_editor.instance_variable_set(:@cursor, 1)
+ @line_editor.instance_variable_set(:@cursor_max, 3)
+ @line_editor.instance_variable_set(:@line_index, 2)
+ @line_editor.instance_variable_set(:@completion_proc, proc { |target, pre, post|
+ assert_equal('e', target)
+ assert_equal("def hoge\n puts :aaa\n", pre)
+ assert_equal('nd', post)
+ })
+ @line_editor.__send__(:call_completion_proc)
+ end
end
diff --git a/test/reline/test_within_pipe.rb b/test/reline/test_within_pipe.rb
index 70a0e0a5de..36a2f1e805 100644
--- a/test/reline/test_within_pipe.rb
+++ b/test/reline/test_within_pipe.rb
@@ -8,7 +8,7 @@ class Reline::WithinPipeTest < Reline::TestCase
@reader, @output_writer = IO.pipe((RELINE_TEST_ENCODING rescue Encoding.default_external))
@output = Reline.output = @output_writer
@config = Reline.send(:core).config
- @config.keyseq_timeout *= 600 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait CI
+ @config.keyseq_timeout *= 600 if defined?(RubyVM::JIT) && RubyVM::JIT.enabled? # for --jit-wait CI
@line_editor = Reline.send(:core).line_editor
end
@@ -59,4 +59,17 @@ class Reline::WithinPipeTest < Reline::TestCase
@writer.write("abcde\C-b\C-b\C-b\C-x\C-d\C-x\C-h\C-x\C-v\C-a\C-f\C-f EF\C-x\C-t gh\C-x\M-t\C-b\C-b\C-b\C-b\C-b\C-b\C-b\C-b\C-x\M-u\C-x\M-l\C-x\M-c\n")
assert_equal "a\C-aDE gh Fe", Reline.readmultiline(&proc{ true })
end
+
+ def test_delete_text_in_multiline
+ @writer.write("abc\ndef\nxyz\n")
+ result = Reline.readmultiline(&proc{ |str|
+ if str.include?('xyz')
+ Reline.delete_text
+ true
+ else
+ false
+ end
+ })
+ assert_equal "abc\ndef", result
+ end
end
diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb
index 6f9a14de67..13693e7c4d 100644
--- a/test/reline/yamatanooroti/test_rendering.rb
+++ b/test/reline/yamatanooroti/test_rendering.rb
@@ -719,6 +719,17 @@ begin
EOC
end
+ def test_reset_rest_height_when_clear_screen
+ start_terminal(5, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
+ write("\n\n\n\C-l3\n")
+ close
+ assert_screen(<<~EOC)
+ prompt> 3
+ => 3
+ prompt>
+ EOC
+ end
+
private def write_inputrc(content)
File.open(@inputrc_file, 'w') do |f|
f.write content
diff --git a/test/rinda/test_rinda.rb b/test/rinda/test_rinda.rb
index 00e1ba7877..f155e88de1 100644
--- a/test/rinda/test_rinda.rb
+++ b/test/rinda/test_rinda.rb
@@ -402,7 +402,7 @@ module TupleSpaceTestModule
end
def test_cancel_02
- skip 'this test is unstable with --jit-wait' if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
+ skip 'this test is unstable with --jit-wait' if defined?(RubyVM::JIT) && RubyVM::JIT.enabled?
entry = @ts.write([:removeme, 1])
assert_equal([[:removeme, 1]], @ts.read_all([nil, nil]))
entry.cancel
@@ -662,7 +662,7 @@ class TestRingServer < Test::Unit::TestCase
end
def test_do_reply_local
- skip 'timeout-based test becomes unstable with --jit-wait' if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
+ skip 'timeout-based test becomes unstable with --jit-wait' if defined?(RubyVM::JIT) && RubyVM::JIT.enabled?
with_timeout(30) {_test_do_reply_local}
end
diff --git a/test/ripper/test_lexer.rb b/test/ripper/test_lexer.rb
index 3eaeb8fefa..975976a381 100644
--- a/test/ripper/test_lexer.rb
+++ b/test/ripper/test_lexer.rb
@@ -215,4 +215,16 @@ class TestRipper::Lexer < Test::Unit::TestCase
end
end
end
+
+ def test_lex_with_syntax_error_and_heredo
+ bug = '[Bug #17644]'
+ s = <<~EOF
+ foo
+ end
+ <<~EOS
+ bar
+ EOS
+ EOF
+ assert_equal([[5, 0], :on_heredoc_end, "EOS\n", state(:EXPR_BEG)], Ripper.lex(s).last, bug)
+ end
end
diff --git a/test/ruby/test_alias.rb b/test/ruby/test_alias.rb
index 271d552bf5..99f2223b49 100644
--- a/test/ruby/test_alias.rb
+++ b/test/ruby/test_alias.rb
@@ -253,4 +253,33 @@ class TestAlias < Test::Unit::TestCase
assert_equal(:foo, k.instance_method(:bar).original_name)
assert_equal(:foo, name)
end
+
+ def test_alias_suppressing_redefinition
+ assert_in_out_err(%w[-w], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class A
+ def foo; end
+ alias foo foo
+ def foo; end
+ end
+ end;
+ end
+
+ def test_alias_memory_leak
+ assert_no_memory_leak([], "#{<<~"begin;"}", "#{<<~'end;'}", rss: true)
+ begin;
+ class A
+ 500.times do
+ 1000.times do |i|
+ define_method(:"foo_#{i}") {}
+
+ alias :"foo_#{i}" :"foo_#{i}"
+
+ remove_method :"foo_#{i}"
+ end
+ GC.start
+ end
+ end
+ end;
+ end
end
diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb
index 522b58e214..30bf13795c 100644
--- a/test/ruby/test_array.rb
+++ b/test/ruby/test_array.rb
@@ -654,6 +654,15 @@ class TestArray < Test::Unit::TestCase
assert_raise(TypeError) { [0].concat(:foo) }
assert_raise(FrozenError) { [0].freeze.concat(:foo) }
+
+ a = @cls[nil]
+ def (x = Object.new).to_ary
+ ary = Array.new(2)
+ ary << [] << [] << :ok
+ end
+ EnvUtil.under_gc_stress {a.concat(x)}
+ GC.start
+ assert_equal(:ok, a.last)
end
def test_count
@@ -754,6 +763,15 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 5, 6, 7, 8, 9, 10 ]
assert_equal(9, a.delete_if {|i| break i if i > 8; i < 7})
assert_equal(@cls[7, 8, 9, 10], a, bug2545)
+
+ assert_raise(FrozenError) do
+ a = @cls[1, 2, 3, 42]
+ a.delete_if do
+ a.freeze
+ true
+ end
+ end
+ assert_equal(@cls[1, 2, 3, 42], a)
end
def test_dup
@@ -1322,6 +1340,15 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 5, 6, 7, 8, 9, 10 ]
assert_equal(9, a.reject! {|i| break i if i > 8; i < 7})
assert_equal(@cls[7, 8, 9, 10], a, bug2545)
+
+ assert_raise(FrozenError) do
+ a = @cls[1, 2, 3, 42]
+ a.reject! do
+ a.freeze
+ true
+ end
+ end
+ assert_equal(@cls[1, 2, 3, 42], a)
end
def test_shared_array_reject!
@@ -1545,6 +1572,8 @@ class TestArray < Test::Unit::TestCase
assert_nil(a.slice(10, -3))
assert_equal @cls[], a.slice(10..7)
+
+ assert_equal([100], a.slice(-1, 1_000_000_000))
end
def test_slice!
@@ -1593,6 +1622,21 @@ class TestArray < Test::Unit::TestCase
assert_raise(ArgumentError) { @cls[1].slice!(0, 0, 0) }
end
+ def test_slice_out_of_range!
+ a = @cls[*(1..100).to_a]
+
+ assert_nil(a.clone.slice!(-101..-1))
+ assert_nil(a.clone.slice!(-101..))
+
+ # assert_raise_with_message(RangeError, "((-101..-1).%(2)) out of range") { a.clone.slice!((-101..-1)%2) }
+ # assert_raise_with_message(RangeError, "((-101..).%(2)) out of range") { a.clone.slice!((-101..)%2) }
+
+ assert_nil(a.clone.slice!(10, -3))
+ assert_equal @cls[], a.clone.slice!(10..7)
+
+ assert_equal([100], a.clone.slice!(-1, 1_000_000_000))
+ end
+
def test_sort
a = @cls[ 4, 1, 2, 3 ]
assert_equal(@cls[1, 2, 3, 4], a.sort)
@@ -2599,6 +2643,15 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 1, 2, 3, 4, 5 ]
a.select! {|i| a.clear if i == 5; false }
assert_equal(0, a.size, bug13053)
+
+ assert_raise(FrozenError) do
+ a = @cls[1, 2, 3, 42]
+ a.select! do
+ a.freeze
+ false
+ end
+ end
+ assert_equal(@cls[1, 2, 3, 42], a)
end
# also select!
@@ -2614,6 +2667,15 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 1, 2, 3, 4, 5 ]
assert_equal(a, a.keep_if { |i| i > 3 })
assert_equal(@cls[4, 5], a)
+
+ assert_raise(FrozenError) do
+ a = @cls[1, 2, 3, 42]
+ a.keep_if do
+ a.freeze
+ false
+ end
+ end
+ assert_equal(@cls[1, 2, 3, 42], a)
end
def test_filter
diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb
index 00c96b3b9f..aa79db24cb 100644
--- a/test/ruby/test_backtrace.rb
+++ b/test/ruby/test_backtrace.rb
@@ -138,10 +138,66 @@ class TestBacktrace < Test::Unit::TestCase
rec[m]
end
+ def test_caller_with_limit
+ x = nil
+ c = Class.new do
+ define_method(:bar) do
+ x = caller(1, 1)
+ end
+ end
+ [c.new].group_by(&:bar)
+ assert_equal 1, x.length
+ assert_equal caller(0), caller(0, nil)
+ end
+
def test_caller_with_nil_length
assert_equal caller(0), caller(0, nil)
end
+ def test_caller_locations_first_label
+ def self.label
+ caller_locations.first.label
+ end
+
+ def self.label_caller
+ label
+ end
+
+ assert_equal 'label_caller', label_caller
+
+ [1].group_by do
+ assert_equal 'label_caller', label_caller
+ end
+ end
+
+ def test_caller_limit_cfunc_iseq_no_pc
+ def self.a; [1].group_by { b } end
+ def self.b
+ [
+ caller_locations(2, 1).first.base_label,
+ caller_locations(3, 1).first.base_label
+ ]
+ end
+ assert_equal({["each", "group_by"]=>[1]}, a)
+ end
+
+ def test_caller_location_inspect_cfunc_iseq_no_pc
+ def self.foo
+ @res = caller_locations(2, 1).inspect
+ end
+ @line = __LINE__ + 1
+ 1.times.map { 1.times.map { foo } }
+ assert_equal("[\"#{__FILE__}:#{@line}:in `times'\"]", @res)
+ end
+
+ def test_caller_location_path_cfunc_iseq_no_pc
+ def self.foo
+ @res = caller_locations(2, 1)[0].path
+ end
+ 1.times.map { 1.times.map { foo } }
+ assert_equal(__FILE__, @res)
+ end
+
def test_caller_locations
cs = caller(0); locs = caller_locations(0).map{|loc|
loc.to_s
diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb
index 126b100b03..c527191eff 100644
--- a/test/ruby/test_enum.rb
+++ b/test/ruby/test_enum.rb
@@ -134,6 +134,11 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal([1, 2, 3, 1, 2], @obj.to_a)
end
+ def test_to_a_keywords
+ def @obj.each(foo:) yield foo end
+ assert_equal([1], @obj.to_a(foo: 1))
+ end
+
def test_to_a_size_symbol
sym = Object.new
class << sym
@@ -248,11 +253,13 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(15, [3, 5, 7].inject(:+))
assert_float_equal(15.0, [3, 5, 7.0].inject(:+))
assert_equal(2*FIXNUM_MAX, Array.new(2, FIXNUM_MAX).inject(:+))
+ assert_equal(3*FIXNUM_MAX, Array.new(3, FIXNUM_MAX).inject(:+))
assert_equal(2*(FIXNUM_MAX+1), Array.new(2, FIXNUM_MAX+1).inject(:+))
assert_equal(10*FIXNUM_MAX, Array.new(10, FIXNUM_MAX).inject(:+))
assert_equal(0, ([FIXNUM_MAX, 1, -FIXNUM_MAX, -1]*10).inject(:+))
assert_equal(FIXNUM_MAX*10, ([FIXNUM_MAX+1, -1]*10).inject(:+))
assert_equal(2*FIXNUM_MIN, Array.new(2, FIXNUM_MIN).inject(:+))
+ assert_equal(3*FIXNUM_MIN, Array.new(3, FIXNUM_MIN).inject(:+))
assert_equal((FIXNUM_MAX+1).to_f, [FIXNUM_MAX, 1, 0.0].inject(:+))
assert_float_equal(10.0, [3.0, 5].inject(2.0, :+))
assert_float_equal((FIXNUM_MAX+1).to_f, [0.0, FIXNUM_MAX+1].inject(:+))
@@ -414,6 +421,17 @@ class TestEnumerable < Test::Unit::TestCase
empty.first
empty.block.call
end;
+
+ bug18475 = '[ruby-dev:107059]'
+ assert_in_out_err([], <<-'end;', [], /unexpected break/, bug18475)
+ e = Enumerator.new do |g|
+ Thread.new do
+ g << 1
+ end.join
+ end
+
+ e.first
+ end;
end
def test_sort
diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb
index 087bcda5ac..6ab72e7954 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -181,6 +181,27 @@ class TestException < Test::Unit::TestCase
}
end
+ def test_catch_throw_in_require_cant_be_rescued
+ bug18562 = '[ruby-core:107403]'
+ Tempfile.create(["dep", ".rb"]) {|t|
+ t.puts("throw :extdep, 42")
+ t.close
+
+ rescue_all = Class.new(Exception)
+ def rescue_all.===(_)
+ raise "should not reach here"
+ end
+
+ v = assert_throw(:extdep, bug18562) do
+ require t.path
+ rescue rescue_all => e
+ assert(false, "should not reach here")
+ end
+
+ assert_equal(42, v, bug18562)
+ }
+ end
+
def test_throw_false
bug12743 = '[ruby-core:77229] [Bug #12743]'
Thread.start {
@@ -1100,6 +1121,14 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
assert_empty warning
end
+ def test_undef_Warning_warn
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ Warning.undef_method(:warn)
+ assert_raise(NoMethodError) { warn "" }
+ end;
+ end
+
def test_undefined_backtrace
assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
begin;
diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb
index e0cc59bd3c..9e994c7349 100644
--- a/test/ruby/test_fiber.rb
+++ b/test/ruby/test_fiber.rb
@@ -397,7 +397,7 @@ class TestFiber < Test::Unit::TestCase
Fiber.new {}.transfer
Fiber.new { Fiber.yield }
end
- exit!(0)
+ exit!(true)
end
}.transfer
_, status = Process.waitpid2(xpid)
@@ -406,8 +406,13 @@ class TestFiber < Test::Unit::TestCase
end.resume
end
pid, status = Process.waitpid2(pid)
- assert_equal(0, status.exitstatus, bug5700)
- assert_equal(false, status.signaled?, bug5700)
+ assert_not_predicate(status, :signaled?, bug5700)
+ assert_predicate(status, :success?, bug5700)
+
+ pid = Fiber.new {fork}.resume
+ pid, status = Process.waitpid2(pid)
+ assert_not_predicate(status, :signaled?)
+ assert_predicate(status, :success?)
end
def test_exit_in_fiber
diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb
index e2314424b3..b9e2a05b8a 100644
--- a/test/ruby/test_file_exhaustive.rb
+++ b/test/ruby/test_file_exhaustive.rb
@@ -160,9 +160,7 @@ class TestFileExhaustive < Test::Unit::TestCase
end
def chardev
- return @chardev if defined? @chardev
- @chardev = File::NULL == "/dev/null" ? "/dev/null" : nil
- @chardev
+ File::NULL
end
def blockdev
@@ -319,7 +317,7 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_file.not_chardev?(regular_file)
assert_file.not_chardev?(utf8_file)
assert_file.not_chardev?(nofile)
- assert_file.chardev?(chardev) if chardev
+ assert_file.chardev?(chardev)
end
def test_exist_p
@@ -1481,6 +1479,31 @@ class TestFileExhaustive < Test::Unit::TestCase
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))
+
+ stat = File.stat(f)
+ assert_equal(stat.atime, File.atime(f), f)
+ assert_equal(stat.ctime, File.ctime(f), f)
+ assert_equal(stat.mtime, File.mtime(f), f)
+ assert_equal(stat.blockdev?, File.blockdev?(f), f)
+ assert_equal(stat.chardev?, File.chardev?(f), f)
+ assert_equal(stat.directory?, File.directory?(f), f)
+ assert_equal(stat.file?, File.file?(f), f)
+ assert_equal(stat.setgid?, File.setgid?(f), f)
+ assert_equal(stat.grpowned?, File.grpowned?(f), f)
+ assert_equal(stat.sticky?, File.sticky?(f), f)
+ assert_equal(File.lstat(f).symlink?, File.symlink?(f), f)
+ assert_equal(stat.owned?, File.owned?(f), f)
+ assert_equal(stat.pipe?, File.pipe?(f), f)
+ assert_equal(stat.readable?, File.readable?(f), f)
+ assert_equal(stat.readable_real?, File.readable_real?(f), f)
+ assert_equal(stat.size?, File.size?(f), f)
+ assert_equal(stat.socket?, File.socket?(f), f)
+ assert_equal(stat.setuid?, File.setuid?(f), f)
+ assert_equal(stat.writable?, File.writable?(f), f)
+ assert_equal(stat.writable_real?, File.writable_real?(f), f)
+ assert_equal(stat.executable?, File.executable?(f), f)
+ assert_equal(stat.executable_real?, File.executable_real?(f), f)
+ assert_equal(stat.zero?, File.zero?(f), f)
end
assert_equal(false, test(?-, @dir, fn1))
assert_equal(true, test(?-, fn1, fn1))
@@ -1590,7 +1613,7 @@ class TestFileExhaustive < Test::Unit::TestCase
def test_stat_chardev_p
assert_not_predicate(File::Stat.new(@dir), :chardev?)
assert_not_predicate(File::Stat.new(regular_file), :chardev?)
- assert_predicate(File::Stat.new(chardev), :chardev?) if chardev
+ assert_predicate(File::Stat.new(chardev), :chardev?)
end
def test_stat_readable_p
diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb
index fbf0d87f8e..b218b72db5 100644
--- a/test/ruby/test_float.rb
+++ b/test/ruby/test_float.rb
@@ -171,6 +171,24 @@ class TestFloat < Test::Unit::TestCase
assert_raise(ArgumentError, n += z + "A") {Float(n)}
assert_raise(ArgumentError, n += z + ".0") {Float(n)}
end
+
+ x = nil
+ 2000.times do
+ x = Float("0x"+"0"*30)
+ break unless x == 0.0
+ end
+ assert_equal(0.0, x, ->{"%a" % x})
+ x = nil
+ 2000.times do
+ begin
+ x = Float("0x1."+"0"*270)
+ rescue ArgumentError => e
+ raise unless /"0x1\.0{270}"/ =~ e.message
+ else
+ break
+ end
+ end
+ assert_nil(x, ->{"%a" % x})
end
def test_divmod
diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb
index 1f75a34cac..01df198b0d 100644
--- a/test/ruby/test_gc.rb
+++ b/test/ruby/test_gc.rb
@@ -321,7 +321,7 @@ class TestGc < Test::Unit::TestCase
base_length = GC.stat[:heap_eden_pages]
(base_length * 500).times{ 'a' }
GC.start
- assert_in_epsilon base_length, (v = GC.stat[:heap_eden_pages]), 1/8r,
+ assert_in_epsilon base_length, (v = GC.stat[:heap_eden_pages]), 1/4r,
"invalid heap expanding (base_length: #{base_length}, GC.stat[:heap_eden_pages]: #{v})"
a = []
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index 62d8b3f836..d217776a2c 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -419,6 +419,15 @@ class TestHash < Test::Unit::TestCase
true
}
assert_equal(base.size, n)
+
+ h = base.dup
+ assert_raise(FrozenError) do
+ h.delete_if do
+ h.freeze
+ true
+ end
+ end
+ assert_equal(base.dup, h)
end
def test_keep_if
@@ -426,6 +435,14 @@ class TestHash < Test::Unit::TestCase
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})
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_raise(FrozenError) do
+ h.keep_if do
+ h.freeze
+ false
+ end
+ end
+ assert_equal(@cls[1=>2,3=>4,5=>6], h)
end
def test_compact
@@ -722,6 +739,15 @@ class TestHash < Test::Unit::TestCase
h = base.dup
assert_equal(h3, h.reject! {|k,v| v })
assert_equal(h3, h)
+
+ h = base.dup
+ assert_raise(FrozenError) do
+ h.reject! do
+ h.freeze
+ true
+ end
+ end
+ assert_equal(base.dup, h)
end
def test_replace
@@ -985,6 +1011,19 @@ class TestHash < Test::Unit::TestCase
assert_equal("FOO", h.shift)
end
+ def test_shift_for_empty_hash
+ # [ruby-dev:51159]
+ h = @cls[]
+ 100.times{|n|
+ while h.size < n
+ k = Random.rand 0..1<<30
+ h[k] = 1
+ end
+ 0 while h.shift
+ assert_equal({}, h)
+ }
+ 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 })
@@ -1025,6 +1064,14 @@ class TestHash < Test::Unit::TestCase
assert_equal({3=>4,5=>6}, h)
h = @cls[1=>2,3=>4,5=>6]
assert_equal(nil, h.select!{true})
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_raise(FrozenError) do
+ h.select! do
+ h.freeze
+ false
+ end
+ end
+ assert_equal(@cls[1=>2,3=>4,5=>6], h)
end
def test_slice
@@ -1077,6 +1124,14 @@ class TestHash < Test::Unit::TestCase
assert_equal({3=>4,5=>6}, h)
h = @cls[1=>2,3=>4,5=>6]
assert_equal(nil, h.filter!{true})
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_raise(FrozenError) do
+ h.filter! do
+ h.freeze
+ false
+ end
+ end
+ assert_equal(@cls[1=>2,3=>4,5=>6], h)
end
def test_clear2
@@ -1674,6 +1729,10 @@ class TestHash < Test::Unit::TestCase
x.transform_keys! {|k| -k }
assert_equal([-1, :a, 1, :b], x.flatten)
+ x = @cls[a: 1, b: 2, c: 3]
+ x.transform_keys! { |k| k == :b && break }
+ assert_equal({false => 1, b: 2, c: 3}, x)
+
x = @cls[true => :a, false => :b]
x.transform_keys! {|k| !k }
assert_equal([false, :a, true, :b], x.flatten)
@@ -1710,8 +1769,21 @@ class TestHash < Test::Unit::TestCase
assert_same(x, y)
x = @cls[a: 1, b: 2, c: 3]
+ x.transform_values! { |v| v == 2 && break }
+ assert_equal({a: false, b: 2, c: 3}, x)
+
+ x = @cls[a: 1, b: 2, c: 3]
y = x.transform_values!.with_index {|v, i| "#{v}.#{i}" }
assert_equal(%w(1.0 2.1 3.2), y.values_at(:a, :b, :c))
+
+ x = @cls[a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10]
+ assert_raise(FrozenError) do
+ x.transform_values!() do |v|
+ x.freeze if v == 2
+ v.succ
+ end
+ end
+ assert_equal(@cls[a: 2, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], x)
end
def test_broken_hash_value
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index 9f9318eaf7..e178e7579b 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -394,6 +394,24 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_each_byte_closed
+ pipe(proc do |w|
+ w << "abc def"
+ w.close
+ end, proc do |r|
+ assert_raise(IOError) do
+ r.each_byte {|byte| r.close if byte == 32 }
+ end
+ end)
+ make_tempfile {|t|
+ File.open(t, 'rt') {|f|
+ assert_raise(IOError) do
+ f.each_byte {|c| f.close if c == 10}
+ end
+ }
+ }
+ end
+
def test_each_codepoint
make_tempfile {|t|
bug2959 = '[ruby-core:28650]'
@@ -405,6 +423,24 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_each_codepoint_closed
+ pipe(proc do |w|
+ w.print("abc def")
+ w.close
+ end, proc do |r|
+ assert_raise(IOError) do
+ r.each_codepoint {|c| r.close if c == 32}
+ end
+ end)
+ make_tempfile {|t|
+ File.open(t, 'rt') {|f|
+ assert_raise(IOError) do
+ f.each_codepoint {|c| f.close if c == 10}
+ end
+ }
+ }
+ end
+
def test_rubydev33072
t = make_tempfile
path = t.path
@@ -440,6 +476,18 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_copy_stream_append_to_nonempty
+ with_srccontent("foobar") {|src, content|
+ preface = 'preface'
+ File.write('dst', preface)
+ File.open('dst', 'ab') do |dst|
+ ret = IO.copy_stream(src, dst)
+ assert_equal(content.bytesize, ret)
+ assert_equal(preface + content, File.read("dst"))
+ end
+ }
+ end
+
def test_copy_stream_smaller
with_srccontent {|src, content|
@@ -1446,6 +1494,13 @@ class TestIO < Test::Unit::TestCase
end)
end
+ def test_readpartial_zero_size
+ File.open(IO::NULL) do |r|
+ assert_empty(r.readpartial(0, s = "01234567"))
+ assert_empty(s)
+ end
+ end
+
def test_readpartial_buffer_error
with_pipe do |r, w|
s = ""
@@ -1491,6 +1546,13 @@ class TestIO < Test::Unit::TestCase
end)
end
+ def test_read_zero_size
+ File.open(IO::NULL) do |r|
+ assert_empty(r.read(0, s = "01234567"))
+ assert_empty(s)
+ end
+ end
+
def test_read_buffer_error
with_pipe do |r, w|
s = ""
@@ -1528,6 +1590,13 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_read_nonblock_zero_size
+ File.open(IO::NULL) do |r|
+ assert_empty(r.read_nonblock(0, s = "01234567"))
+ assert_empty(s)
+ end
+ end
+
def test_write_nonblock_simple_no_exceptions
pipe(proc do |w|
w.write_nonblock('1', exception: false)
diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb
index e3ca1ba926..08340c662c 100644
--- a/test/ruby/test_iseq.rb
+++ b/test/ruby/test_iseq.rb
@@ -82,6 +82,16 @@ class TestISeq < Test::Unit::TestCase
end;
end if defined?(RubyVM::InstructionSequence.load)
+ def test_lambda_with_ractor_roundtrip
+ iseq = compile(<<~EOF)
+ x = 42
+ y = lambda { x }
+ Ractor.make_shareable(y)
+ y.call
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
def test_disasm_encoding
src = "\u{3042} = 1; \u{3042}; \u{3043}"
asm = compile(src).disasm
diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb
index 3a38b1a998..be3550033d 100644
--- a/test/ruby/test_jit.rb
+++ b/test/ruby/test_jit.rb
@@ -857,6 +857,18 @@ class TestJIT < Test::Unit::TestCase
end;
end
+ def test_inlined_getconstant
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '11', success_count: 1, min_calls: 2)
+ begin;
+ FOO = 1
+ def const
+ FOO
+ end
+ print const
+ print const
+ end;
+ end
+
def test_attr_reader
assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "4nil\nnil\n6", success_count: 2, min_calls: 2)
begin;
diff --git a/test/ruby/test_lazy_enumerator.rb b/test/ruby/test_lazy_enumerator.rb
index 3f5a05555b..2116d0ee31 100644
--- a/test/ruby/test_lazy_enumerator.rb
+++ b/test/ruby/test_lazy_enumerator.rb
@@ -682,4 +682,8 @@ EOS
ary = (0..Float::INFINITY).lazy.with_index.take(2).to_a
assert_equal([[0, 0], [1, 1]], ary)
end
+
+ def test_with_index_size
+ assert_equal(3, Enumerator::Lazy.new([1, 2, 3], 3){|y, v| y << v}.with_index.size)
+ end
end
diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb
index ef8b261321..ae6e8708ee 100644
--- a/test/ruby/test_marshal.rb
+++ b/test/ruby/test_marshal.rb
@@ -672,6 +672,23 @@ class TestMarshal < Test::Unit::TestCase
assert_equal(['X', 'X'], Marshal.load(Marshal.dump(obj), ->(v) { v == str ? v.upcase : v }))
end
+ def test_marshal_proc_string_encoding
+ string = "foo"
+ payload = Marshal.dump(string)
+ Marshal.load(payload, ->(v) {
+ if v.is_a?(String)
+ assert_equal(string, v)
+ assert_equal(string.encoding, v.encoding)
+ end
+ v
+ })
+ end
+
+ def test_marshal_proc_freeze
+ object = { foo: [42, "bar"] }
+ assert_equal object, Marshal.load(Marshal.dump(object), :freeze.to_proc)
+ end
+
def test_marshal_load_extended_class_crash
assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
begin;
@@ -785,8 +802,22 @@ class TestMarshal < Test::Unit::TestCase
def test_marshal_with_ruby2_keywords_hash
flagged_hash = ruby2_keywords_hash(key: 42)
- hash = Marshal.load(Marshal.dump(flagged_hash))
+ data = Marshal.dump(flagged_hash)
+ hash = Marshal.load(data)
assert_equal(42, ruby2_keywords_test(*[hash]))
+
+ hash2 = Marshal.load(data.sub(/\x06K(?=T\z)/, "\x08KEY"))
+ assert_raise(ArgumentError, /\(given 1, expected 0\)/) {
+ ruby2_keywords_test(*[hash2])
+ }
+ end
+
+ def test_invalid_byte_sequence_symbol
+ data = Marshal.dump(:K)
+ data = data.sub(/:\x06K/, "I\\&\x06:\x0dencoding\"\x0dUTF-16LE")
+ assert_raise(ArgumentError, /UTF-16LE: "\\x4B"/) {
+ Marshal.load(data)
+ }
end
def exception_test
@@ -819,4 +850,16 @@ class TestMarshal < Test::Unit::TestCase
assert_nil(e2.backtrace_locations) # temporal
end
end
+
+ class TestMarshalFreezeProc < Test::Unit::TestCase
+ include MarshalTestLib
+
+ def encode(o)
+ Marshal.dump(o)
+ end
+
+ def decode(s)
+ Marshal.load(s, :freeze.to_proc)
+ end
+ end
end
diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb
index cc7421b700..0bd5dc63dd 100644
--- a/test/ruby/test_method.rb
+++ b/test/ruby/test_method.rb
@@ -1168,6 +1168,19 @@ class TestMethod < Test::Unit::TestCase
assert_nil(m.super_method)
end
+ # Bug 17780
+ def test_super_method_module_alias
+ m = Module.new do
+ def foo
+ end
+ alias :f :foo
+ end
+
+ method = m.instance_method(:f)
+ super_method = method.super_method
+ assert_nil(super_method)
+ end
+
def rest_parameter(*rest)
rest
end
@@ -1290,6 +1303,21 @@ class TestMethod < Test::Unit::TestCase
end;
end
+ def test_override_optimized_method_on_class_using_prepend
+ assert_separately(%w(--disable-gems), <<-'end;', timeout: 30)
+ # Bug #17725 [ruby-core:102884]
+ $VERBOSE = nil
+ String.prepend(Module.new)
+ class String
+ def + other
+ 'blah blah'
+ end
+ end
+
+ assert_equal('blah blah', 'a' + 'b')
+ end;
+ end
+
def test_eqq
assert_operator(0.method(:<), :===, 5)
assert_not_operator(0.method(:<), :===, -5)
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 43f0c51753..5db6732fee 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -2237,6 +2237,137 @@ class TestModule < Test::Unit::TestCase
assert_equal(0, 1 / 2)
end
+ def test_visibility_after_refine_and_visibility_change_with_origin_class
+ m = Module.new
+ c = Class.new do
+ def x; :x end
+ end
+ c.prepend(m)
+ Module.new do
+ refine c do
+ def x; :y end
+ end
+ end
+
+ o1 = c.new
+ o2 = c.new
+ assert_equal(:x, o1.public_send(:x))
+ assert_equal(:x, o2.public_send(:x))
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
+ def test_visibility_after_multiple_refine_and_visibility_change_with_origin_class
+ m = Module.new
+ c = Class.new do
+ def x; :x end
+ end
+ c.prepend(m)
+ Module.new do
+ refine c do
+ def x; :y end
+ end
+ end
+ Module.new do
+ refine c do
+ def x; :z end
+ end
+ end
+
+ o1 = c.new
+ o2 = c.new
+ assert_equal(:x, o1.public_send(:x))
+ assert_equal(:x, o2.public_send(:x))
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
+ def test_visibility_after_refine_and_visibility_change_without_origin_class
+ c = Class.new do
+ def x; :x end
+ end
+ Module.new do
+ refine c do
+ def x; :y end
+ end
+ end
+ o1 = c.new
+ o2 = c.new
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
+ def test_visibility_after_multiple_refine_and_visibility_change_without_origin_class
+ c = Class.new do
+ def x; :x end
+ end
+ Module.new do
+ refine c do
+ def x; :y end
+ end
+ end
+ Module.new do
+ refine c do
+ def x; :z end
+ end
+ end
+ o1 = c.new
+ o2 = c.new
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
+ def test_visibility_after_refine_and_visibility_change_with_superclass
+ c = Class.new do
+ def x; :x end
+ end
+ sc = Class.new(c)
+ Module.new do
+ refine sc do
+ def x; :y end
+ end
+ end
+ o1 = sc.new
+ o2 = sc.new
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
+ def test_visibility_after_multiple_refine_and_visibility_change_with_superclass
+ c = Class.new do
+ def x; :x end
+ end
+ sc = Class.new(c)
+ Module.new do
+ refine sc do
+ def x; :y end
+ end
+ end
+ Module.new do
+ refine sc do
+ def x; :z end
+ end
+ end
+ o1 = sc.new
+ o2 = sc.new
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
def test_prepend_visibility
bug8005 = '[ruby-core:53106] [Bug #8005]'
c = Class.new do
diff --git a/test/ruby/test_nomethod_error.rb b/test/ruby/test_nomethod_error.rb
index 170a6c6c57..8b81052905 100644
--- a/test/ruby/test_nomethod_error.rb
+++ b/test/ruby/test_nomethod_error.rb
@@ -90,4 +90,20 @@ class TestNoMethodError < Test::Unit::TestCase
str.__send__(id)
end
end
+
+ def test_to_s
+ pre = Module.new do
+ def name
+ BasicObject.new
+ end
+ end
+ mod = Module.new
+ mod.singleton_class.prepend(pre)
+
+ err = assert_raise(NoMethodError) do
+ mod.this_method_does_not_exist
+ end
+
+ assert_match(/undefined method.+this_method_does_not_exist.+for.+Module/, err.to_s)
+ end
end
diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb
index 5f638afa01..d316a00b47 100644
--- a/test/ruby/test_parse.rb
+++ b/test/ruby/test_parse.rb
@@ -562,6 +562,21 @@ class TestParse < Test::Unit::TestCase
assert_syntax_error("\"\\M-\x01\"", 'Invalid escape character syntax')
assert_syntax_error("\"\\M-\\C-\x01\"", 'Invalid escape character syntax')
assert_syntax_error("\"\\C-\\M-\x01\"", 'Invalid escape character syntax')
+
+ e = assert_syntax_error('"\c\u0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~'"\n", e.message.lines.last)
+ e = assert_syntax_error('"\c\U0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~'"\n", e.message.lines.last)
+
+ e = assert_syntax_error('"\C-\u0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~~'"\n", e.message.lines.last)
+ e = assert_syntax_error('"\C-\U0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~~'"\n", e.message.lines.last)
+
+ e = assert_syntax_error('"\M-\u0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~~'"\n", e.message.lines.last)
+ e = assert_syntax_error('"\M-\U0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~~'"\n", e.message.lines.last)
end
def test_question
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index c364de46e4..ed31faec42 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -2488,6 +2488,77 @@ class TestRefinement < Test::Unit::TestCase
}
end
+ def test_defining_after_cached
+ klass = Class.new
+ refinement = Module.new { refine(klass) { def foo; end } }
+ klass.new.foo rescue nil # cache the refinement method entry
+ klass.define_method(:foo) { 42 }
+ assert_equal(42, klass.new.foo)
+ end
+
+ # [Bug #17806]
+ def test_two_refinements_for_prepended_class
+ assert_normal_exit %q{
+ module R1
+ refine Hash do
+ def foo; :r1; end
+ end
+ end
+
+ class Hash
+ prepend(Module.new)
+ end
+
+ class Hash
+ def foo; end
+ end
+
+ {}.method(:foo) # put it on pCMC
+
+ module R2
+ refine Hash do
+ def foo; :r2; end
+ end
+ end
+
+ {}.foo
+ }
+ end
+
+ # [Bug #17806]
+ def test_redefining_refined_for_prepended_class
+ klass = Class.new { def foo; end }
+ _refinement = Module.new do
+ refine(klass) { def foo; :refined; end }
+ end
+ klass.prepend(Module.new)
+ klass.new.foo # cache foo
+ klass.define_method(:foo) { :second }
+ assert_equal(:second, klass.new.foo)
+ end
+
+ class Bug17822
+ module Ext
+ refine(Bug17822) do
+ def foo = :refined
+ end
+ end
+
+ private(def foo = :not_refined)
+
+ module Client
+ using Ext
+ def self.call_foo
+ Bug17822.new.foo
+ end
+ end
+ end
+
+ # [Bug #17822]
+ def test_privatizing_refined_method
+ assert_equal(:refined, Bug17822::Client.call_foo)
+ end
+
private
def eval_using(mod, s)
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb
index 495c9de919..679a013cf0 100644
--- a/test/ruby/test_regexp.rb
+++ b/test/ruby/test_regexp.rb
@@ -265,6 +265,27 @@ class TestRegexp < Test::Unit::TestCase
assert_equal(re, re.match("foo").regexp)
end
+ def test_match_lambda_multithread
+ bug17507 = "[ruby-core:101901]"
+ str = "a-x-foo-bar-baz-z-b"
+
+ worker = lambda do
+ m = /foo-([A-Za-z0-9_\.]+)-baz/.match(str)
+ assert_equal("bar", m[1], bug17507)
+
+ # These two lines are needed to trigger the bug
+ File.exist? "/tmp"
+ str.gsub(/foo-bar-baz/, "foo-abc-baz")
+ end
+
+ def self. threaded_test(worker)
+ 6.times.map {Thread.new {10_000.times {worker.call}}}.each(&:join)
+ end
+
+ # The bug only occurs in a method calling a block/proc/lambda
+ threaded_test(worker)
+ end
+
def test_source
bug5484 = '[ruby-core:40364]'
assert_equal('', //.source)
@@ -694,11 +715,16 @@ class TestRegexp < Test::Unit::TestCase
test = proc {|&blk| "abc".sub("a", ""); blk.call($~) }
bug10877 = '[ruby-core:68209] [Bug #10877]'
+ bug18160 = '[Bug #18160]'
test.call {|m| assert_raise_with_message(IndexError, /foo/, bug10877) {m["foo"]} }
key = "\u{3042}"
[Encoding::UTF_8, Encoding::Shift_JIS, Encoding::EUC_JP].each do |enc|
idx = key.encode(enc)
- test.call {|m| assert_raise_with_message(IndexError, /#{idx}/, bug10877) {m[idx]} }
+ pat = /#{idx}/
+ test.call {|m| assert_raise_with_message(IndexError, pat, bug10877) {m[idx]} }
+ test.call {|m| assert_raise_with_message(IndexError, pat, bug18160) {m.offset(idx)} }
+ test.call {|m| assert_raise_with_message(IndexError, pat, bug18160) {m.begin(idx)} }
+ test.call {|m| assert_raise_with_message(IndexError, pat, bug18160) {m.end(idx)} }
end
test.call {|m| assert_equal(/a/, m.regexp) }
test.call {|m| assert_equal("abc", m.string) }
@@ -1312,6 +1338,21 @@ class TestRegexp < Test::Unit::TestCase
assert_nil($1)
end
+ def test_backref_overrun
+ assert_raise_with_message(SyntaxError, /invalid backref number/) do
+ eval(%["".match(/(())(?<X>)((?(90000)))/)])
+ end
+ end
+
+ def test_invalid_group
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ assert_raise_with_message(RegexpError, /invalid conditional pattern/) do
+ Regexp.new("((?(1)x|x|)x)+")
+ end
+ end;
+ 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)
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index 30c07b9e5c..9b658286c9 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -371,15 +371,15 @@ class TestRequire < Test::Unit::TestCase
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
-
+ Dir.mktmpdir do |dir|
+ path = File.join(dir, base+".rb")
assert_raise_with_message(LoadError, /#{base}/) {
- load(File.join(File.dirname(path), base))
+ load(File.join(dir, base))
}
- t.puts "warn 'ok'"
- t.close
+ File.open(path, "w+b") do |t|
+ t.puts "warn 'ok'"
+ end
assert_include(path, base)
assert_warn("ok\n", bug) {
assert_nothing_raised(LoadError, bug) {
@@ -531,6 +531,28 @@ class TestRequire < Test::Unit::TestCase
$".replace(features)
end
+ def test_default_loaded_features_encoding
+ Dir.mktmpdir {|tmp|
+ Dir.mkdir("#{tmp}/1")
+ Dir.mkdir("#{tmp}/2")
+ File.write("#{tmp}/1/bug18191-1.rb", "")
+ File.write("#{tmp}/2/bug18191-2.rb", "")
+ assert_separately(%W[-Eutf-8 -I#{tmp}/1 -], "#{<<~"begin;"}\n#{<<~'end;'}")
+ tmp = #{tmp.dump}"/2"
+ begin;
+ $:.unshift(tmp)
+ require "bug18191-1"
+ require "bug18191-2"
+ encs = [Encoding::US_ASCII, Encoding.find("filesystem")]
+ message = -> {
+ require "pp"
+ {filesystem: encs[1], **$".group_by(&:encoding)}.pretty_inspect
+ }
+ assert($".all? {|n| encs.include?(n.encoding)}, message)
+ end;
+ }
+ end
+
def test_require_changed_current_dir
bug7158 = '[ruby-core:47970]'
Dir.mktmpdir {|tmp|
@@ -839,6 +861,23 @@ class TestRequire < Test::Unit::TestCase
}
end
+ def test_provide_in_required_file
+ paths, loaded = $:.dup, $".dup
+ Dir.mktmpdir do |tmp|
+ provide = File.realdirpath("provide.rb", tmp)
+ File.write(File.join(tmp, "target.rb"), "raise __FILE__\n")
+ File.write(provide, '$" << '"'target.rb'\n")
+ $:.replace([tmp])
+ assert(require("provide"))
+ assert(!require("target"))
+ assert_equal($".pop, provide)
+ assert_equal($".pop, "target.rb")
+ end
+ ensure
+ $:.replace(paths)
+ $".replace(loaded)
+ end
+
if defined?($LOAD_PATH.resolve_feature_path)
def test_resolve_feature_path
paths, loaded = $:.dup, $".dup
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index b7831948f0..0d0f4b4d7e 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -1075,6 +1075,11 @@ class TestRubyOptions < Test::Unit::TestCase
end
end
+ def test_rubylib_invalid_encoding
+ env = {"RUBYLIB"=>"\xFF", "LOCALE"=>"en_US.UTF-8", "LC_ALL"=>"en_US.UTF-8"}
+ assert_ruby_status([env, "-e;"])
+ end
+
def test_null_script
skip "#{IO::NULL} is not a character device" unless File.chardev?(IO::NULL)
assert_in_out_err([IO::NULL], success: true)
diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb
index 7821a221dc..2180c31d57 100644
--- a/test/ruby/test_settracefunc.rb
+++ b/test/ruby/test_settracefunc.rb
@@ -564,6 +564,16 @@ class TestSetTraceFunc < Test::Unit::TestCase
}
end
+ # Bug #18264
+ def test_tracpoint_memory_leak
+ assert_no_memory_leak([], <<-PREP, <<-CODE, rss: true)
+code = proc { TracePoint.new(:line) { } }
+1_000.times(&code)
+PREP
+1_000_000.times(&code)
+CODE
+ end
+
def trace_by_set_trace_func
events = []
trace = nil
@@ -2324,4 +2334,19 @@ class TestSetTraceFunc < Test::Unit::TestCase
EOS
assert_equal [:return, :unpack], event
end
+
+ def test_while_in_while
+ lines = []
+
+ TracePoint.new(:line){|tp|
+ next unless target_thread?
+ lines << tp.lineno
+ }.enable{
+ n = 3
+ while n > 0
+ n -= 1 while n > 0
+ end
+ }
+ assert_equal [__LINE__ - 5, __LINE__ - 4, __LINE__ - 3], lines, 'Bug #17868'
+ end
end
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index 4814872789..57e7dae18f 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -105,6 +105,16 @@ PREP
CODE
end
+ # Bug #18154
+ def test_initialize_nofree_memory_leak
+ assert_no_memory_leak([], <<-PREP, <<-CODE, rss: true)
+code = proc {0.to_s.__send__(:initialize, capacity: 10000)}
+1_000.times(&code)
+PREP
+100_000.times(&code)
+CODE
+ end
+
def test_AREF # '[]'
assert_equal("A", S("AooBar")[0])
assert_equal("B", S("FooBaB")[-1])
@@ -1852,6 +1862,7 @@ CODE
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(S("x"), S("\x00x\x00").strip)
assert_equal("0b0 ".force_encoding("UTF-16BE"),
"\x00 0b0 ".force_encoding("UTF-16BE").strip)
@@ -1870,6 +1881,10 @@ CODE
assert_equal(S("x"), a.strip!)
assert_equal(S("x"), a)
+ a = S("\x00x\x00")
+ assert_equal(S("x"), a.strip!)
+ assert_equal(S("x"), a)
+
a = S("x")
assert_nil(a.strip!)
assert_equal(S("x") ,a)
@@ -2703,6 +2718,7 @@ CODE
def test_rstrip
assert_equal(" hello", " hello ".rstrip)
assert_equal("\u3042", "\u3042 ".rstrip)
+ assert_equal("\u3042", "\u3042\u0000".rstrip)
assert_raise(Encoding::CompatibilityError) { "\u3042".encode("ISO-2022-JP").rstrip }
end
@@ -2723,12 +2739,17 @@ CODE
assert_equal(nil, s4.rstrip!)
assert_equal("\u3042", s4)
+ s5 = S("\u3042\u0000")
+ assert_equal("\u3042", s5.rstrip!)
+ assert_equal("\u3042", s5)
+
assert_raise(Encoding::CompatibilityError) { "\u3042".encode("ISO-2022-JP").rstrip! }
end
def test_lstrip
assert_equal("hello ", " hello ".lstrip)
assert_equal("\u3042", " \u3042".lstrip)
+ assert_equal("hello ", "\x00hello ".lstrip)
end
def test_lstrip_bang
@@ -2747,6 +2768,11 @@ CODE
s4 = S("\u3042")
assert_equal(nil, s4.lstrip!)
assert_equal("\u3042", s4)
+
+ s5 = S("\u0000\u3042")
+ assert_equal("\u3042", s5.lstrip!)
+ assert_equal("\u3042", s5)
+
end
def test_delete_prefix
diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb
index d94f4679d3..3afde9b0e3 100644
--- a/test/ruby/test_super.rb
+++ b/test/ruby/test_super.rb
@@ -521,6 +521,37 @@ class TestSuper < Test::Unit::TestCase
assert_equal(%w[B A], result, bug9721)
end
+ # [Bug #18329]
+ def test_super_missing_prepended_module
+ a = Module.new do
+ def probe(*methods)
+ prepend(probing_module(methods))
+ end
+
+ def probing_module(methods)
+ Module.new do
+ methods.each do |method|
+ define_method(method) do |*args, **kwargs, &block|
+ super(*args, **kwargs, &block)
+ end
+ end
+ end
+ end
+ end
+
+ b = Class.new do
+ extend a
+
+ probe :danger!, :missing
+
+ def danger!; end
+ end
+
+ o = b.new
+ o.danger!
+ 2.times { o.missing rescue NoMethodError }
+ end
+
def test_from_eval
bug10263 = '[ruby-core:65122] [Bug #10263a]'
a = Class.new do
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index e289eea2c2..a0cdb5b6fd 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -1730,6 +1730,21 @@ eom
assert_equal [[4, 1, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
end
+ def test_cdhash
+ assert_separately([], <<-RUBY)
+ n = case 1 when 2r then false else true end
+ assert_equal(n, true, '[ruby-core:103759] [Bug #17854]')
+ RUBY
+ assert_separately([], <<-RUBY)
+ n = case 3/2r when 1.5r then true else false end
+ assert_equal(n, true, '[ruby-core:103759] [Bug #17854]')
+ RUBY
+ assert_separately([], <<-RUBY)
+ n = case 1i when 1i then true else false end
+ assert_equal(n, true, '[ruby-core:103759] [Bug #17854]')
+ RUBY
+ end
+
private
def not_label(x) @result = x; @not_label ||= nil end
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
index 3852cb7020..9643a61cb6 100644
--- a/test/ruby/test_thread.rb
+++ b/test/ruby/test_thread.rb
@@ -1146,7 +1146,9 @@ q.pop
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)
+ out, err, status = EnvUtil.invoke_ruby([env, '-e', script], '', true, true)
+ assert_not_predicate(status, :signaled?, err)
+
use_length ? out.length : out
end
diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb
index dade0bff16..1502985200 100644
--- a/test/ruby/test_time_tz.rb
+++ b/test/ruby/test_time_tz.rb
@@ -7,9 +7,9 @@ class TestTimeTZ < Test::Unit::TestCase
has_lisbon_tz = true
force_tz_test = ENV["RUBY_FORCE_TIME_TZ_TEST"] == "yes"
case RUBY_PLATFORM
- when /linux/
+ when /darwin|linux/
force_tz_test = true
- when /darwin|freebsd|openbsd/
+ when /freebsd|openbsd/
has_lisbon_tz = false
force_tz_test = true
end
@@ -95,6 +95,9 @@ class TestTimeTZ < Test::Unit::TestCase
CORRECT_KIRITIMATI_SKIP_1994 = with_tz("Pacific/Kiritimati") {
Time.local(1994, 12, 31, 0, 0, 0).year == 1995
}
+ CORRECT_SINGAPORE_1982 = with_tz("Asia/Singapore") {
+ "2022g" if Time.local(1981, 12, 31, 23, 59, 59).utc_offset == 8*3600
+ }
def time_to_s(t)
t.to_s
@@ -140,9 +143,12 @@ class TestTimeTZ < Test::Unit::TestCase
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, "1981-12-31 23:29:59 +0730", :local, [1981,12,31,23,29,59])
+ if CORRECT_SINGAPORE_1982
+ assert_time_constructor(tz, "1982-01-01 00:00:00 +0800", :local, [1981,12,31,23,30,00])
+ assert_time_constructor(tz, "1982-01-01 00:00:00 +0800", :local, [1982,1,1,0,0,0])
+ assert_time_constructor(tz, "1982-01-01 00:29:59 +0800", :local, [1982,1,1,0,29,59])
+ end
assert_time_constructor(tz, "1982-01-01 00:30:00 +0800", :local, [1982,1,1,0,30,0])
}
end
@@ -196,7 +202,7 @@ class TestTimeTZ < Test::Unit::TestCase
def test_europe_lisbon
with_tz("Europe/Lisbon") {
- assert_equal("LMT", Time.new(-0x1_0000_0000_0000_0000).zone)
+ assert_include(%w"LMT CET", Time.new(-0x1_0000_0000_0000_0000).zone)
}
end if has_lisbon_tz
@@ -448,9 +454,12 @@ America/Managua Fri Jan 1 06:00:00 1993 UTC = Fri Jan 1 01:00:00 1993 EST isd
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 15:59:59 1981 UTC = Thu Dec 31 23:29: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
End
+ gen_zdump_test <<'End' if CORRECT_SINGAPORE_1982
+Asia/Singapore Thu Dec 31 16:00:00 1981 UTC = Fri Jan 1 00:00:00 1982 SGT isdst=0 gmtoff=28800
+End
gen_zdump_test CORRECT_TOKYO_DST_1951 ? <<'End' + (CORRECT_TOKYO_DST_1951 < "2018f" ? <<'2018e' : <<'2018f') : <<'End'
Asia/Tokyo Sat May 5 14:59:59 1951 UTC = Sat May 5 23:59:59 1951 JST isdst=0 gmtoff=32400
Asia/Tokyo Sat May 5 15:00:00 1951 UTC = Sun May 6 01:00:00 1951 JDT isdst=1 gmtoff=36000
diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb
index 04c8248697..c8b0034e06 100644
--- a/test/ruby/test_transcode.rb
+++ b/test/ruby/test_transcode.rb
@@ -126,6 +126,28 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("D\xFCrst".force_encoding('iso-8859-2'), "D\xFCrst".encode('iso-8859-2', 'iso-8859-1'))
end
+ def test_encode_xml_multibyte
+ encodings = %w'UTF-8 UTF-16LE UTF-16BE UTF-32LE UTF-32BE'
+ encodings.each do |src_enc|
+ encodings.each do |dst_enc|
+ escaped = "<>".encode(src_enc).encode(dst_enc, :xml=>:text)
+ assert_equal("&lt;&gt;", escaped.encode('UTF-8'), "failed encoding #{src_enc} to #{dst_enc} with xml: :text")
+
+ escaped = '<">'.encode(src_enc).encode(dst_enc, :xml=>:attr)
+ assert_equal('"&lt;&quot;&gt;"', escaped.encode('UTF-8'), "failed encoding #{src_enc} to #{dst_enc} with xml: :attr")
+
+ escaped = "<>".encode(src_enc).force_encoding("UTF-8").encode(dst_enc, src_enc, :xml=>:text)
+ assert_equal("&lt;&gt;", escaped.encode('UTF-8'), "failed encoding #{src_enc} to #{dst_enc} with xml: :text")
+
+ escaped = '<">'.encode(src_enc).force_encoding("UTF-8").encode(dst_enc, src_enc, :xml=>:attr)
+ assert_equal('"&lt;&quot;&gt;"', escaped.encode('UTF-8'), "failed encoding #{src_enc} to #{dst_enc} with xml: :attr")
+ end
+ end
+ # regression test; U+6E7F (湿) uses the same bytes in ISO-2022-JP as "<>"
+ assert_equal( "&lt;&gt;\u6E7F", "<>\u6E7F".encode("ISO-2022-JP").encode("ISO-2022-JP", :xml=>:text).encode("UTF-8"))
+ assert_equal("\"&lt;&gt;\u6E7F\"", "<>\u6E7F".encode("ISO-2022-JP").encode("ISO-2022-JP", :xml=>:attr).encode("UTF-8"))
+ end
+
def test_ascii_range
encodings = [
'US-ASCII', 'ASCII-8BIT',
diff --git a/test/ruby/test_weakmap.rb b/test/ruby/test_weakmap.rb
index 3b9eef770a..46d8b50c03 100644
--- a/test/ruby/test_weakmap.rb
+++ b/test/ruby/test_weakmap.rb
@@ -73,6 +73,15 @@ class TestWeakMap < Test::Unit::TestCase
@wm.inspect)
end
+ def test_inspect_garbage
+ 1000.times do |i|
+ @wm[i] = Object.new
+ @wm.inspect
+ end
+ assert_match(/\A\#<#{@wm.class.name}:[^:]++:(?:\s\d+\s=>\s\#<(?:Object|collected):[^:<>]*+>(?:,|>\z))+/,
+ @wm.inspect)
+ end
+
def test_each
m = __callee__[/test_(.*)/, 1]
x1 = Object.new
diff --git a/test/rubygems/data/null-type.gemspec.rz b/test/rubygems/data/null-type.gemspec.rz
index bad99289d1..2134fcde29 100644
--- a/test/rubygems/data/null-type.gemspec.rz
+++ b/test/rubygems/data/null-type.gemspec.rz
Binary files differ
diff --git a/test/rubygems/encrypted_private_key.pem b/test/rubygems/encrypted_private_key.pem
index 868f332b7c..d9667689a6 100644
--- a/test/rubygems/encrypted_private_key.pem
+++ b/test/rubygems/encrypted_private_key.pem
@@ -1,30 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-CBC,4E38D58B5A059DB6
+DEK-Info: AES-256-CBC,CB6FD0B173EF450C6EE21A01DD785C1D
-IgWLfnHVnkErKkhysrUMoE0ubkRDtJXZv9KR02jGGFk/kGqWyTqPk08uzhwVNM+l
-eOk0qfPykkJM3KZgqTsD6xfA1D5WqFp5mLoFXVVTn9I3acSZsqOY0FweCipwdVpI
-x+9Fl+v62kIW06dOjyWLE1abed9hHiXesGGsD87/RJSywy4OBxOcrhR1fJLK4ElR
-ya0UzI7rWnmZMChjaZBssfzT1DR79/dARXhon2m5EiIJDjMpc8BKGYlQy5RHCHwA
-cnrhUTTvsggZbQtmLZ/yVx8FSJ273XpYR0pmwbw4j1R+zeXQRK5MroBnCfOGcYa7
-rmpERmDW3VAuxXR20SUAGdo1XOMTDe1uLbaotn6e56pXghIaYROTPS+HsuOkAZGY
-OYWEkUoyog4l4n+h/C1umFfTFGvKNATLgDugONFvTw/PLbjvl+sWMy2QfqH0MlNB
-DIUPxhEVCFD9oB4nfB86WDAmPp1DH9/IBet/21kbQ2eTIzakTdG3XiC+xzAQRu68
-EOCTbasFWGxlCix66gt4xWMLksEg8UhWSpjS3/HsifrKyNMB8sfUFYmZmOYMW4mf
-NuEtpBL3AdHNObN8nQ75HfehukzNpbYVRsLzWrVgtxvXHVpnvoCCpCvQBMHeRZxK
-6m028mhH1m6yYE/uGFiRKLrN7BKAttbUiqnGgVIg/lQQilFWwylxQ6aXqJGmNgxa
-oihzWZRlXivIhhrM7VMnLoKAF/YfmWpP3zahGpBQGfObtPtm44R0ezXPdtsivnyu
-CmFOPGzRNMKZtH/lwVhuIIK3AFIGDsRRP9ySN4YfjQZnTdu2sRlxBnANP9m8W9T2
-p+C4zVkDYAbsuWq2HpHwsdL8gqIiXeptsHLqkNw+ulSSLyeBCgM9fpV3RsNGjwqu
-k8QLb1CYp2VX46CE8UKvOd/nyFnEsD+EAc3WangEwA41m2IaXcbs9Au7xsG9oacZ
-DrxlJVNxlxO9YyP9dNOTfP0fHIiygKQQY2aU3y3oRneu7ogYES5V2mUNH7cYUWVL
-CHPXAoUXJErvDQ/opW2DroA9Eqv9sST6WqBf6LXRcWU0ntfzcFUbEqgmCmB7Cbu2
-8udEn6iWilQahLyDoAShLkU7+Tk78Z1c6RuqjyY4VboZPzxrTYK8YIXzwX+jj9bG
-KIIGS5eghK185+AjlwtzJ7MBdoL323YIik6uOZluhnJHLaxjxUXGa1VqDgsyqGi7
-ISRMTpVTrbR+UtoEi4ZhMjobtFUr7lGkt24VkXwBKdoyryj4RPHGdp7Tf6XDJufQ
-+KKhqt8QrpOTPiMskFN2disOSF5/YZCmtT84nkhU7Hf1lkQ2kfx1zfNk0GqYYXOW
-zHOAczy8gWBRetDMnhRYohDzQGWn//b+2Wr2n1RD8D9kyjMRhpFMYfQGfRcuPGjW
-91k/T0XFcjcjeZPL9s+HITmrh7zg5WxbCfTEp91j3Oy1bns196SY77TE0BzUsqR2
-geJggcUMEfyvHiiCMtijmSSD9nf8tNIxLVL8Jaf1coA6e1CrlHnYAu2f/Q3GIcvU
-EEEmw+cZRwsk4fffYzh5psxxGdXKBv1KcQ/CeBhZL0WJsCp2y5oxwg==
+KqHn2Df8hSuwNE+W+60MnGtc6xpoXmF3iN25iVwcN67krYn+N6cBhjFeXwXccYwJ
+2gHSu4iEK9Qe32vK0yuv8N9h/fmsabZl0TotnEem/pqO5T8W4LxyK+Rw0s6RB30S
+C+mUisRADTanAxyBxsNU8xR8OAUNMAAxV1me6It0W2lfNE3t5jg/Kr0NWMoRUNRx
+dkE6WlD5D8jBeC3QdZ6OuE7QXOCEAWAjcFMc0d1WJq2t2r3TrLVfTH7EOoRyvL1H
+rrFRx/dEW1UJfM6P11wB5R0nhg3rDXF7oDFszjwO/3tzARke0NZuN37l301lYRl1
+aolO6sShJLa0Ml/TgNcJw0S6rc6a1Z52gTfQKztKcL1UX4HLZg75zKmn6qfatMBC
+iXn+pQRYNsOPQ5h4r7lBBqvuV+gBw+rN768tYpZ2/YVDaygxETHcZAFCdAw/JNbP
+d0XPIbP79NRrCgzSo58LKQGuOQf3Hh0vp1YS+MilMtm/eogoj1enSPM+ymStHRwG
+i+D00xCQ6blSOZ2eUUBJXt11YzP22GYnv+XTR/5kGKkTIvoRMfd+39bQyR32IEv2
+Z+yweAGQInD94eifT9ObbIayJ47y01KP0+Vj6hz4RCFsmJKsYiai5JiKlmf7lV9w
+7zH3TtCOx/xSyomesXVRkqvFkdyeguU72kXc5tiMPaDXGCOeV0GWyR1GU1DUX9/K
+60E7ym0Wx77WGMKk2fkirZzBdOeliyCRUXd7ccN2rBCjTwtjAUIk27lwzdUaTUv7
+EmjauDvSMFtir58c+zjlLmBaSQOzKcj0KXMp0Oucls9bD85WGGbGyzGhTa0AZ+/+
+cCEJt7RAwW0kTEO/uO+BAZe/zBoi9ek+QBn54FK3E7CXfS4Oi9Qbc3fwlVyTlVmz
+ZGrCncO0TIVGErFWK24Z7lX8rBnk8enfnamrPfKtwn4LG9aDfhSj8DtisjlRUVT5
+chDQ+CCi9rh3wXh28lyS+nXJ3yFidCzRgcsc3PpN/c4DNRggZc+C/KDw+J2FW+8Y
+p65OliBQHQcG0PnCa2xRyCGevytPG0rfNDgyaY33dPEo90mBLVcwLbzGiSGBHgFl
+pr8A/rqbnFpRO39NYbACeRFCqPpzyzfARCCcjcDoFrENdIaJui0fjlBkoV3B/KiK
+EVjDcgwt1HAtz8bV2YJ+OpQbhD7E90e2vTRMuXAH21Ygo32VOS0LRlCRc9ZyZW4z
+PTyO/6a+FbXZ1zhVJxu/0bmBERZ14WVmWq56oxQav8knpxYeYPgpEmIZnrHnJ1Ko
+UoXcc8Hy4NKtaBmDcaF8TCobNsRZTxO/htqpdyNsOrBSsnX2kP5D/O1l1vuVYi1/
+RYfUqL9dvGzvfsFuuDDjDlQ/fIA6pFzJV3fy4KJHlF1r33qaE/lNMdpKljBwvUII
+Vog4cGmzxssqK5q9kuogcuyeOuFODjBNW4qt0WylSi9bwwy3ZwaZLRqhngz6+tCV
+Jp45Gk881XiVe3aVU0l+4DmJJ9/5vwqjH5Vo/GJqFU6gzB+Zv/0plYeNkuE0Xo2z
+ecdxnGKVPl42q44lvczjDw2KX0ahxQrfrbcl48//zR295u9POzCL97d6zpioI2NR
-----END RSA PRIVATE KEY-----
diff --git a/lib/rubygems/test_case.rb b/test/rubygems/helper.rb
index e2763561c6..a09d0783f6 100644
--- a/lib/rubygems/test_case.rb
+++ b/test/rubygems/helper.rb
@@ -12,36 +12,17 @@ if File.exist?(bundler_gemspec)
end
begin
- gem 'minitest', '~> 5.13'
+ gem 'test-unit', '~> 3.0'
rescue Gem::LoadError
end
-begin
- require 'simplecov'
- SimpleCov.start do
- add_filter "/test/"
- add_filter "/bundler/"
- add_filter "/lib/rubygems/resolver/molinillo"
- end
-rescue LoadError
-end
-
if File.exist?(bundler_gemspec)
require_relative '../../bundler/lib/bundler'
else
require 'bundler'
end
-# Enable server plugin needed for bisection
-if ENV["RG_BISECT_SERVER_PLUGIN"]
- require ENV["RG_BISECT_SERVER_PLUGIN"]
-
- Minitest.extensions << "server"
-end
-
-ENV["MT_NO_PLUGINS"] = "true"
-
-require 'minitest/autorun'
+require 'test/unit'
ENV["JARS_SKIP"] = "true" if Gem.java_platform? # avoid unnecessary and noisy `jar-dependencies` post install hook
@@ -61,32 +42,28 @@ require 'rubygems/mock_gem_ui'
module Gem
##
- # Allows setting the gem path searcher. This method is available when
- # requiring 'rubygems/test_case'
+ # Allows setting the gem path searcher.
def self.searcher=(searcher)
@searcher = searcher
end
##
- # Allows toggling Windows behavior. This method is available when requiring
- # 'rubygems/test_case'
+ # Allows toggling Windows behavior.
def self.win_platform=(val)
@@win_platform = val
end
##
- # Allows setting path to Ruby. This method is available when requiring
- # 'rubygems/test_case'
+ # Allows setting path to Ruby.
def self.ruby=(ruby)
@ruby = ruby
end
##
- # When rubygems/test_case is required the default user interaction is a
- # MockGemUi.
+ # Sets the default user interaction to a MockGemUi.
module DefaultUserInteraction
@ui = Gem::MockGemUi.new
@@ -97,8 +74,7 @@ require "rubygems/command"
class Gem::Command
##
- # Allows resetting the hash of specific args per command. This method is
- # available when requiring 'rubygems/test_case'
+ # Allows resetting the hash of specific args per command.
def self.specific_extra_args_hash=(value)
@specific_extra_args_hash = value
@@ -111,7 +87,7 @@ end
# and uninstall gems, fetch remote gems through a stub fetcher and be assured
# your normal set of gems is not affected.
-class Gem::TestCase < Minitest::Test
+class Gem::TestCase < Test::Unit::TestCase
extend Gem::Deprecate
attr_accessor :fetcher # :nodoc:
@@ -120,8 +96,6 @@ class Gem::TestCase < Minitest::Test
attr_accessor :uri # :nodoc:
- TEST_PATH = ENV.fetch('RUBYGEMS_TEST_PATH', File.expand_path('../../../test/rubygems', __FILE__))
-
def assert_activate(expected, *specs)
specs.each do |spec|
case spec
@@ -140,11 +114,48 @@ class Gem::TestCase < Minitest::Test
end
def assert_directory_exists(path, msg = nil)
- msg = message(msg) { "Expected path '#{path}' to be a directory" }
- assert_path_exists path
+ msg = build_message(msg, "Expected path '#{path}' to be a directory")
+ assert_path_exist path
assert File.directory?(path), msg
end
+ # https://github.com/seattlerb/minitest/blob/21d9e804b63c619f602f3f4ece6c71b48974707a/lib/minitest/assertions.rb#L188
+ def _synchronize
+ yield
+ end
+
+ # https://github.com/seattlerb/minitest/blob/21d9e804b63c619f602f3f4ece6c71b48974707a/lib/minitest/assertions.rb#L546
+ def capture_subprocess_io
+ _synchronize do
+ begin
+ require "tempfile"
+
+ captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err")
+
+ orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
+ $stdout.reopen captured_stdout
+ $stderr.reopen captured_stderr
+
+ yield
+
+ $stdout.rewind
+ $stderr.rewind
+
+ return captured_stdout.read, captured_stderr.read
+ ensure
+ captured_stdout.unlink
+ captured_stderr.unlink
+ $stdout.reopen orig_stdout
+ $stderr.reopen orig_stderr
+
+ orig_stdout.close
+ orig_stderr.close
+ captured_stdout.close
+ captured_stderr.close
+ end
+ end
+ end
+
##
# Sets the ENABLE_SHARED entry in RbConfig::CONFIG to +value+ and restores
# the original value when the block ends
@@ -236,16 +247,14 @@ class Gem::TestCase < Minitest::Test
output.scan(/^#{Regexp.escape make_command}(?:[[:blank:]].*)?$/)
end
- def parse_make_command_line(line)
- command, *args = line.shellsplit
+ def parse_make_command_line_targets(line)
+ args = line.sub(/^#{Regexp.escape make_command}/, "").shellsplit
targets = []
- macros = {}
args.each do |arg|
case arg
when /\A(\w+)=/
- macros[$1] = $'
else
targets << arg
end
@@ -253,35 +262,30 @@ class Gem::TestCase < Minitest::Test
targets << '' if targets.empty?
- {
- :command => command,
- :targets => targets,
- :macros => macros,
- }
+ targets
end
def assert_contains_make_command(target, output, msg = nil)
if output.match(/\n/)
- msg = message(msg) do
+ msg = build_message(msg,
"Expected output containing make command \"%s\", but was \n\nBEGIN_OF_OUTPUT\n%sEND_OF_OUTPUT" % [
('%s %s' % [make_command, target]).rstrip,
output,
]
- end
+ )
else
- msg = message(msg) do
- 'Expected make command "%s": %s' % [
+ msg = build_message(msg,
+ 'Expected make command "%s", but was "%s"' % [
('%s %s' % [make_command, target]).rstrip,
output,
]
- end
+ )
end
assert scan_make_command_lines(output).any? {|line|
- make = parse_make_command_line(line)
+ targets = parse_make_command_line_targets(line)
- if make[:targets].include?(target)
- yield make, line if block_given?
+ if targets.include?(target)
true
else
false
@@ -377,6 +381,7 @@ class Gem::TestCase < Minitest::Test
ENV['GEM_PRIVATE_KEY_PASSPHRASE'] = PRIVATE_KEY_PASSPHRASE
+ Gem.instance_variable_set(:@default_specifications_dir, nil)
if Gem.java_platform?
@orig_default_gem_home = RbConfig::CONFIG['default_gem_home']
RbConfig::CONFIG['default_gem_home'] = @gemhome
@@ -387,6 +392,14 @@ class Gem::TestCase < Minitest::Test
@orig_bindir = RbConfig::CONFIG["bindir"]
RbConfig::CONFIG["bindir"] = File.join @gemhome, "bin"
+ @orig_sitelibdir = RbConfig::CONFIG["sitelibdir"]
+ new_sitelibdir = @orig_sitelibdir.sub(RbConfig::CONFIG["prefix"], @gemhome)
+ $LOAD_PATH.insert(Gem.load_path_insert_index, new_sitelibdir)
+ RbConfig::CONFIG["sitelibdir"] = new_sitelibdir
+
+ @orig_mandir = RbConfig::CONFIG["mandir"]
+ RbConfig::CONFIG["mandir"] = File.join @gemhome, "share", "man"
+
Gem::Specification.unresolved_deps.clear
Gem.use_paths(@gemhome)
@@ -458,15 +471,17 @@ class Gem::TestCase < Minitest::Test
Gem.ruby = @orig_ruby if @orig_ruby
+ RbConfig::CONFIG['mandir'] = @orig_mandir
+ RbConfig::CONFIG['sitelibdir'] = @orig_sitelibdir
RbConfig::CONFIG['bindir'] = @orig_bindir
+ Gem.instance_variable_set :@default_specifications_dir, nil
if Gem.java_platform?
RbConfig::CONFIG['default_gem_home'] = @orig_default_gem_home
else
Gem.instance_variable_set :@default_dir, nil
end
- Gem::Specification._clear_load_cache
Gem::Specification.unresolved_deps.clear
Gem::refresh
@@ -525,6 +540,10 @@ class Gem::TestCase < Minitest::Test
Gem.pre_uninstall_hooks.clear
end
+ def without_any_upwards_gemfiles
+ ENV["BUNDLE_GEMFILE"] = File.join(@tempdir, "Gemfile")
+ end
+
##
# A git_gem is used with a gem dependencies file. The gem created here
# has no files, just a gem specification for the given +name+ and +version+.
@@ -664,6 +683,28 @@ class Gem::TestCase < Minitest::Test
path
end
+ ##
+ # Load a YAML string, the psych 3 way
+
+ def load_yaml(yaml)
+ if YAML.respond_to?(:unsafe_load)
+ YAML.unsafe_load(yaml)
+ else
+ YAML.load(yaml)
+ end
+ end
+
+ ##
+ # Load a YAML file, the psych 3 way
+
+ def load_yaml_file(file)
+ if YAML.respond_to?(:unsafe_load_file)
+ YAML.unsafe_load_file(file)
+ else
+ YAML.load_file(file)
+ end
+ end
+
def all_spec_names
Gem::Specification.map(&:full_name)
end
@@ -778,16 +819,6 @@ class Gem::TestCase < Minitest::Test
Gem::Specification.unresolved_deps.values.map(&:to_s).sort
end
- def save_loaded_features
- old_loaded_features = $LOADED_FEATURES.dup
- yield
- ensure
- prefix = File.dirname(__FILE__) + "/"
- new_features = ($LOADED_FEATURES - old_loaded_features)
- old_loaded_features.concat(new_features.select {|f| f.rindex(prefix, 0) })
- $LOADED_FEATURES.replace old_loaded_features
- end
-
def new_default_spec(name, version, deps = nil, *files)
spec = util_spec name, version, deps
@@ -1045,19 +1076,23 @@ Also, a list:
@fetcher.data["#{@gem_repo}latest_specs.#{v}.gz"] = l_zip
@fetcher.data["#{@gem_repo}prerelease_specs.#{v}.gz"] = p_zip
- v = Gem.marshal_version
-
- all_specs.each do |spec|
- path = "#{@gem_repo}quick/Marshal.#{v}/#{spec.original_name}.gemspec.rz"
- data = Marshal.dump spec
- data_deflate = Zlib::Deflate.deflate data
- @fetcher.data[path] = data_deflate
- end
+ write_marshalled_gemspecs(*all_specs)
end
nil # force errors
end
+ def write_marshalled_gemspecs(*all_specs)
+ v = Gem.marshal_version
+
+ all_specs.each do |spec|
+ path = "#{@gem_repo}quick/Marshal.#{v}/#{spec.original_name}.gemspec.rz"
+ data = Marshal.dump spec
+ data_deflate = Zlib::Deflate.deflate data
+ @fetcher.data[path] = data_deflate
+ end
+ end
+
##
# Deflates +data+
@@ -1250,7 +1285,11 @@ Also, a list:
end
def ruby_with_rubygems_in_load_path
- [Gem.ruby, "-I", File.expand_path("..", __dir__)]
+ [Gem.ruby, "-I", rubygems_path]
+ end
+
+ def rubygems_path
+ $LOAD_PATH.find{|p| p == File.dirname($LOADED_FEATURES.find{|f| f.end_with?("/rubygems.rb") }) }
end
def with_clean_path_to_ruby
@@ -1281,8 +1320,8 @@ Also, a list:
end
end
- @@good_rake = "#{rubybin} #{escape_path(TEST_PATH, 'good_rake.rb')}"
- @@bad_rake = "#{rubybin} #{escape_path(TEST_PATH, 'bad_rake.rb')}"
+ @@good_rake = "#{rubybin} #{escape_path(__dir__, 'good_rake.rb')}"
+ @@bad_rake = "#{rubybin} #{escape_path(__dir__, 'bad_rake.rb')}"
##
# Construct a new Gem::Dependency.
@@ -1469,30 +1508,30 @@ Also, a list:
def self.cert_path(cert_name)
if 32 == (Time.at(2**32) rescue 32)
- cert_file = "#{TEST_PATH}/#{cert_name}_cert_32.pem"
+ cert_file = "#{__dir__}/#{cert_name}_cert_32.pem"
return cert_file if File.exist? cert_file
end
- "#{TEST_PATH}/#{cert_name}_cert.pem"
+ "#{__dir__}/#{cert_name}_cert.pem"
end
##
- # Loads an RSA private key named +key_name+ with +passphrase+ in <tt>test/rubygems/</tt>
+ # Loads a private key named +key_name+ with +passphrase+ in <tt>test/rubygems/</tt>
def self.load_key(key_name, passphrase = nil)
key_file = key_path key_name
key = File.read key_file
- OpenSSL::PKey::RSA.new key, passphrase
+ OpenSSL::PKey.read key, passphrase
end
##
# Returns the path to the key named +key_name+ from <tt>test/rubygems</tt>
def self.key_path(key_name)
- "#{TEST_PATH}/#{key_name}_key.pem"
+ "#{__dir__}/#{key_name}_key.pem"
end
# :stopdoc:
@@ -1519,4 +1558,38 @@ Also, a list:
end if Gem::HAVE_OPENSSL
end
-require 'rubygems/test_utilities'
+# https://github.com/seattlerb/minitest/blob/13c48a03d84a2a87855a4de0c959f96800100357/lib/minitest/mock.rb#L192
+class Object
+ def stub(name, val_or_callable, *block_args)
+ new_name = "__minitest_stub__#{name}"
+
+ metaclass = class << self; self; end
+
+ if respond_to? name and not methods.map(&:to_s).include? name.to_s
+ 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, &blk|
+ if val_or_callable.respond_to? :call
+ val_or_callable.call(*args, &blk)
+ else
+ blk.call(*block_args) if blk
+ val_or_callable
+ end
+ end
+
+ metaclass.send(:ruby2_keywords, name) if metaclass.respond_to?(:ruby2_keywords, true)
+
+ yield self
+ ensure
+ metaclass.send :undef_method, name
+ metaclass.send :alias_method, name, new_name
+ metaclass.send :undef_method, new_name
+ end
+end
+
+require_relative 'utilities'
diff --git a/lib/rubygems/installer_test_case.rb b/test/rubygems/installer_test_case.rb
index 416dac7be6..824ac53a82 100644
--- a/lib/rubygems/installer_test_case.rb
+++ b/test/rubygems/installer_test_case.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/installer'
class Gem::Installer
diff --git a/lib/rubygems/package/tar_test_case.rb b/test/rubygems/package/tar_test_case.rb
index 1161d0a5a8..99f503a23f 100644
--- a/lib/rubygems/package/tar_test_case.rb
+++ b/test/rubygems/package/tar_test_case.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative '../helper'
require 'rubygems/package'
##
diff --git a/test/rubygems/packages/ascii_binder-0.1.10.1.gem b/test/rubygems/packages/ascii_binder-0.1.10.1.gem
new file mode 100644
index 0000000000..19c505395e
--- /dev/null
+++ b/test/rubygems/packages/ascii_binder-0.1.10.1.gem
Binary files differ
diff --git a/test/rubygems/packages/ill-formatted-platform-1.0.0.10.gem b/test/rubygems/packages/ill-formatted-platform-1.0.0.10.gem
new file mode 100644
index 0000000000..58a13535c2
--- /dev/null
+++ b/test/rubygems/packages/ill-formatted-platform-1.0.0.10.gem
Binary files differ
diff --git a/test/rubygems/private_ec_key.pem b/test/rubygems/private_ec_key.pem
new file mode 100644
index 0000000000..5d855d0dfc
--- /dev/null
+++ b/test/rubygems/private_ec_key.pem
@@ -0,0 +1,9 @@
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,4107F98A374CB8EC18F1AA4EA4B6A0DB
+
+BRklFxJGcz7gqQYxek8TZkt8qbPhB0FSR6nyw3SYuio/2tlT9ohs74mlK3EbG9Lt
+Y4OquJbksBFmoB7fIoM4vnuIZ0Eoz2ooxn9tjhBtqJ3mVscYXwZmA3UDUWDMlviQ
+Fu37OpikQv4TFA1jlmUK0LM8xmUCfUeLl0kHD17lFsz2gkO2kwg8mn/YUMOIaDOu
+EnnmxbAwnZBpemQkQfpTt2mYL9gu3CcMt5gokBuGDxY=
+-----END EC PRIVATE KEY-----
diff --git a/test/rubygems/test_bundled_ca.rb b/test/rubygems/test_bundled_ca.rb
index 6973758c4c..fff5904aba 100644
--- a/test/rubygems/test_bundled_ca.rb
+++ b/test/rubygems/test_bundled_ca.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'net/http'
require 'rubygems/openssl'
@@ -26,14 +26,14 @@ class TestBundledCA < Gem::TestCase
end
def assert_https(host)
- self.assertions += 1
+ assert true
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, SocketError
- skip "#{host} seems offline, I can't tell whether ssl would work."
+ pend "#{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/
diff --git a/test/rubygems/test_config.rb b/test/rubygems/test_config.rb
index 015e2b1d8a..4d64f16fca 100644
--- a/test/rubygems/test_config.rb
+++ b/test/rubygems/test_config.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems'
require 'shellwords'
diff --git a/test/rubygems/test_deprecate.rb b/test/rubygems/test_deprecate.rb
index 5f8eef76cb..a619eccb37 100644
--- a/test/rubygems/test_deprecate.rb
+++ b/test/rubygems/test_deprecate.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/deprecate'
class TestDeprecate < Gem::TestCase
@@ -49,6 +49,22 @@ class TestDeprecate < Gem::TestCase
@message = "bar"
end
rubygems_deprecate :foo, :bar
+
+ def foo_arg(msg)
+ @message = "foo" + msg
+ end
+ def bar_arg(msg)
+ @message = "bar" + msg
+ end
+ rubygems_deprecate :foo_arg, :bar_arg
+
+ def foo_kwarg(message:)
+ @message = "foo" + message
+ end
+ def bar_kwarg(message:)
+ @message = "bar" + message
+ end
+ rubygems_deprecate :foo_kwarg, :bar_kwarg
end
class OtherThing
@@ -61,24 +77,48 @@ class TestDeprecate < Gem::TestCase
@message = "bar"
end
deprecate :foo, :bar, 2099, 3
+
+ def foo_arg(msg)
+ @message = "foo" + msg
+ end
+ def bar_arg(msg)
+ @message = "bar" + msg
+ end
+ deprecate :foo_arg, :bar_arg, 2099, 3
+
+ def foo_kwarg(message:)
+ @message = "foo" + message
+ end
+ def bar_kwarg(message:)
+ @message = "bar" + message
+ end
+ deprecate :foo_kwarg, :bar_kwarg, 2099, 3
end
def test_deprecated_method_calls_the_old_method
- capture_io do
+ capture_output do
thing = Thing.new
thing.foo
assert_equal "foo", thing.message
+ thing.foo_arg("msg")
+ assert_equal "foomsg", thing.message
+ thing.foo_kwarg(message: "msg")
+ assert_equal "foomsg", thing.message
end
end
def test_deprecated_method_outputs_a_warning
- out, err = capture_io do
+ out, err = capture_output do
thing = Thing.new
thing.foo
+ thing.foo_arg("msg")
+ thing.foo_kwarg(message: "msg")
end
assert_equal "", out
assert_match(/Thing#foo is deprecated; use bar instead\./, err)
+ assert_match(/Thing#foo_arg is deprecated; use bar_arg instead\./, err)
+ assert_match(/Thing#foo_kwarg is deprecated; use bar_kwarg instead\./, err)
assert_match(/in Rubygems [0-9]+/, err)
end
@@ -101,13 +141,17 @@ class TestDeprecate < Gem::TestCase
end
def test_deprecated_method_outputs_a_warning_old_way
- out, err = capture_io do
+ out, err = capture_output do
thing = OtherThing.new
thing.foo
+ thing.foo_arg("msg")
+ thing.foo_kwarg(message: "msg")
end
assert_equal "", out
- assert_match(/Thing#foo is deprecated; use bar instead\./, err)
- assert_match(/on or after 2099-03-01/, err)
+ assert_match(/OtherThing#foo is deprecated; use bar instead\./, err)
+ assert_match(/OtherThing#foo_arg is deprecated; use bar_arg instead\./, err)
+ assert_match(/OtherThing#foo_kwarg is deprecated; use bar_kwarg instead\./, err)
+ assert_match(/on or after 2099-03/, err)
end
end
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index 1c6d790b25..832701a173 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -1,5 +1,5 @@
# coding: US-ASCII
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems'
require 'rubygems/command'
require 'rubygems/installer'
@@ -19,79 +19,72 @@ class TestGem < Gem::TestCase
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 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 1"
- b2 = util_spec "b", "2", "c" => ">= 2"
- c1 = util_spec "c", "1"
- c2 = util_spec "c", "2"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
- install_specs c1, c2, b1, b2, a1
+ install_specs c1, c2, b1, b2, a1
- a1.activate
+ a1.activate
- assert_equal %w[a-1], loaded_spec_names
- assert_equal ["b (> 0)"], unresolved_names
+ assert_equal %w[a-1], loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
- Gem.finish_resolve
+ Gem.finish_resolve
- assert_equal %w[a-1 b-2 c-2], loaded_spec_names
- assert_equal [], unresolved_names
- end
+ assert_equal %w[a-1 b-2 c-2], loaded_spec_names
+ assert_equal [], unresolved_names
end
def test_self_finish_resolve_wtf
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
- b1 = util_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb" # this
- b2 = util_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb"
- c1 = util_spec "c", "1" # this
- c2 = util_spec "c", "2"
- d1 = util_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
- d2 = util_spec "d", "2", { "c" => "< 2" }, "lib/d.rb" # this
+ a1 = util_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
+ b1 = util_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb" # this
+ b2 = util_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb"
+ c1 = util_spec "c", "1" # this
+ c2 = util_spec "c", "2"
+ d1 = util_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
+ d2 = util_spec "d", "2", { "c" => "< 2" }, "lib/d.rb" # this
- install_specs c1, c2, b1, b2, d1, d2, a1
+ install_specs c1, c2, b1, b2, d1, d2, a1
- a1.activate
+ a1.activate
- assert_equal %w[a-1], loaded_spec_names
- assert_equal ["b (> 0)", "d (> 0)"], unresolved_names
+ assert_equal %w[a-1], loaded_spec_names
+ assert_equal ["b (> 0)", "d (> 0)"], unresolved_names
- Gem.finish_resolve
+ Gem.finish_resolve
- assert_equal %w[a-1 b-1 c-1 d-2], loaded_spec_names
- assert_equal [], unresolved_names
- end
+ assert_equal %w[a-1 b-1 c-1 d-2], loaded_spec_names
+ assert_equal [], unresolved_names
end
def test_self_finish_resolve_respects_loaded_specs
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 1"
- b2 = util_spec "b", "2", "c" => ">= 2"
- c1 = util_spec "c", "1"
- c2 = util_spec "c", "2"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
- install_specs c1, c2, b1, b2, a1
+ install_specs c1, c2, b1, b2, a1
- a1.activate
- c1.activate
+ a1.activate
+ c1.activate
- assert_equal %w[a-1 c-1], loaded_spec_names
- assert_equal ["b (> 0)"], unresolved_names
+ assert_equal %w[a-1 c-1], loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
- Gem.finish_resolve
+ Gem.finish_resolve
- assert_equal %w[a-1 b-1 c-1], loaded_spec_names
- assert_equal [], unresolved_names
- end
+ assert_equal %w[a-1 b-1 c-1], loaded_spec_names
+ assert_equal [], unresolved_names
end
def test_self_install
@@ -106,7 +99,7 @@ class TestGem < Gem::TestCase
assert_equal %w[a-1], installed.map {|spec| spec.full_name }
- assert_path_exists File.join(gemhome2, 'gems', 'a-1')
+ assert_path_exist File.join(gemhome2, 'gems', 'a-1')
end
def test_self_install_in_rescue
@@ -211,25 +204,21 @@ class TestGem < Gem::TestCase
end
def test_require_missing
- save_loaded_features do
- assert_raises ::LoadError do
- require "test_require_missing"
- end
+ assert_raise ::LoadError do
+ require "test_require_missing"
end
end
def test_require_does_not_glob
- save_loaded_features do
- a1 = util_spec "a", "1", nil, "lib/a1.rb"
-
- install_specs a1
+ a1 = util_spec "a", "1", nil, "lib/a1.rb"
- assert_raises ::LoadError do
- require "a*"
- end
+ install_specs a1
- assert_equal [], loaded_spec_names
+ assert_raise ::LoadError do
+ require "a*"
end
+
+ assert_equal [], loaded_spec_names
end
def test_self_bin_path_active
@@ -261,7 +250,7 @@ class TestGem < Gem::TestCase
end
def test_self_activate_bin_path_no_exec_name
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
Gem.activate_bin_path 'a'
end
@@ -342,7 +331,7 @@ class TestGem < Gem::TestCase
# c2 is missing, and b2 which has it as a dependency will be activated, so we should get an error about the orphaned dependency
- e = assert_raises Gem::UnsatisfiableDependencyError do
+ e = assert_raise Gem::UnsatisfiableDependencyError do
load Gem.activate_bin_path("a", "exec", ">= 0")
end
@@ -390,7 +379,7 @@ class TestGem < Gem::TestCase
File.open("Gemfile", "w") {|f| f.puts('source "https://rubygems.org"') }
- e = assert_raises Gem::GemNotFoundException do
+ e = assert_raise Gem::GemNotFoundException do
load Gem.activate_bin_path("bundler", "bundle", ">= 0.a")
end
@@ -487,7 +476,7 @@ class TestGem < Gem::TestCase
File.open("Gemfile", "w") {|f| f.puts('source "https://rubygems.org"') }
- e = assert_raises Gem::GemNotFoundException do
+ e = assert_raise Gem::GemNotFoundException do
load Gem.activate_bin_path("bundler", "bundle", "= 2.2.8")
end
@@ -495,7 +484,7 @@ class TestGem < Gem::TestCase
end
def test_self_bin_path_no_exec_name
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
Gem.bin_path 'a'
end
@@ -516,20 +505,20 @@ class TestGem < Gem::TestCase
util_spec 'a', '2' do |s|
s.executables = ['exec']
end
- assert_raises(Gem::GemNotFoundException) do
+ assert_raise(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
+ assert_raise(ArgumentError) do
Gem.bin_path('a', nil, '1')
end
end
def test_self_bin_path_not_found
- assert_raises(Gem::GemNotFoundException) do
+ assert_raise(Gem::GemNotFoundException) do
Gem.bin_path('non-existent', 'blah')
end
end
@@ -540,7 +529,6 @@ class TestGem < Gem::TestCase
s.executables = []
end
install_specs spec
- # Should not find a-10's non-abin (bug)
assert_equal @abin_path, Gem.bin_path('a', 'abin')
end
@@ -596,7 +584,7 @@ class TestGem < Gem::TestCase
end
def test_self_datadir_nonexistent_package
- assert_raises(Gem::MissingSpecError) do
+ assert_raise(Gem::MissingSpecError) do
Gem::Specification.find_by_name("xyzzy").datadir
end
end
@@ -664,22 +652,22 @@ class TestGem < Gem::TestCase
end
def test_self_use_gemdeps
- rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
+ with_rubygems_gemdeps('-') do
+ FileUtils.mkdir_p 'detect/a/b'
+ FileUtils.mkdir_p 'detect/a/Isolate'
- FileUtils.mkdir_p 'detect/a/b'
- FileUtils.mkdir_p 'detect/a/Isolate'
+ FileUtils.touch 'detect/Isolate'
- FileUtils.touch 'detect/Isolate'
+ begin
+ Dir.chdir 'detect/a/b'
- begin
- Dir.chdir 'detect/a/b'
+ Gem.use_gemdeps
- assert_equal add_bundler_full_name([]), Gem.use_gemdeps.map(&:full_name)
- ensure
- Dir.chdir @tempdir
+ assert_equal add_bundler_full_name([]), loaded_spec_names
+ ensure
+ Dir.chdir @tempdir
+ end
end
- ensure
- ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end
def test_self_dir
@@ -692,12 +680,12 @@ class TestGem < Gem::TestCase
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'
+ assert_path_exist File.join @gemhome, 'build_info'
+ assert_path_exist File.join @gemhome, 'cache'
+ assert_path_exist File.join @gemhome, 'doc'
+ assert_path_exist File.join @gemhome, 'extensions'
+ assert_path_exist File.join @gemhome, 'gems'
+ assert_path_exist File.join @gemhome, 'specifications'
end
def test_self_ensure_gem_directories_permissions
@@ -1058,7 +1046,7 @@ class TestGem < Gem::TestCase
assert_equal ["\xCF", "\x80"], Gem.read_binary('test').chars.to_a
- skip 'chmod not supported' if Gem.win_platform?
+ pend 'chmod not supported' if Gem.win_platform?
begin
File.chmod 0444, 'test'
@@ -1144,7 +1132,7 @@ class TestGem < Gem::TestCase
assert_equal Gem::Requirement.create('>= 1.2.3'), Gem.env_requirement('foo')
assert_equal Gem::Requirement.create('1.2.3'), Gem.env_requirement('bAr')
- assert_raises(Gem::Requirement::BadRequirementError) { Gem.env_requirement('baz') }
+ assert_raise(Gem::Requirement::BadRequirementError) { Gem.env_requirement('baz') }
assert_equal Gem::Requirement.default, Gem.env_requirement('qux')
end
@@ -1349,7 +1337,7 @@ class TestGem < Gem::TestCase
io.puts '# a_file.rb'
end
- e = assert_raises Gem::MissingSpecError do
+ e = assert_raise Gem::MissingSpecError do
Gem.try_activate 'a_file'
end
@@ -1370,7 +1358,7 @@ class TestGem < Gem::TestCase
io.puts '# a_file.rb'
end
- e = assert_raises Gem::MissingSpecError do
+ e = assert_raise Gem::MissingSpecError do
Gem.try_activate 'a_file'
end
@@ -1389,7 +1377,7 @@ class TestGem < Gem::TestCase
io.write spec.to_ruby_for_cache
end
- _, err = capture_io do
+ _, err = capture_output do
refute Gem.try_activate 'nonexistent'
end
@@ -1414,7 +1402,7 @@ class TestGem < Gem::TestCase
end
def test_setting_paths_does_not_warn_about_unknown_keys
- stdout, stderr = capture_io do
+ stdout, stderr = capture_output do
Gem.paths = { 'foo' => [],
'bar' => Object.new,
'GEM_HOME' => Gem.paths.home,
@@ -1432,7 +1420,7 @@ class TestGem < Gem::TestCase
end
def test_deprecated_paths=
- stdout, stderr = capture_io do
+ stdout, stderr = capture_output do
Gem.paths = { 'GEM_HOME' => Gem.paths.home,
'GEM_PATH' => [Gem.paths.home, 'foo'] }
end
@@ -1485,24 +1473,22 @@ class TestGem < Gem::TestCase
end
def test_self_needs_picks_up_unresolved_deps
- save_loaded_features do
- a = util_spec "a", "1"
- b = util_spec "b", "1", "c" => nil
- c = util_spec "c", "2"
- d = util_spec "d", "1", {'e' => '= 1'}, "lib/d#{$$}.rb"
- e = util_spec "e", "1"
-
- install_specs a, c, b, e, d
+ a = util_spec "a", "1"
+ b = util_spec "b", "1", "c" => nil
+ c = util_spec "c", "2"
+ d = util_spec "d", "1", {'e' => '= 1'}, "lib/d#{$$}.rb"
+ e = util_spec "e", "1"
- Gem.needs do |r|
- r.gem "a"
- r.gem "b", "= 1"
+ install_specs a, c, b, e, d
- require "d#{$$}"
- end
+ Gem.needs do |r|
+ r.gem "a"
+ r.gem "b", "= 1"
- assert_equal %w[a-1 b-1 c-2 d-1 e-1], loaded_spec_names
+ require "d#{$$}"
end
+
+ assert_equal %w[a-1 b-1 c-2 d-1 e-1], loaded_spec_names
end
def test_self_gunzip
@@ -1580,6 +1566,31 @@ class TestGem < Gem::TestCase
assert_equal %w[plugin], PLUGINS_LOADED
end
+ def test_load_user_installed_plugins
+ 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
+
+ foo = util_spec 'foo', '1' do |s|
+ s.files << plugin_path
+ end
+
+ install_gem_user foo
+ end
+
+ Gem.paths = { "GEM_PATH" => [Gem.dir, Gem.user_dir].join(File::PATH_SEPARATOR) }
+
+ 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
@@ -1689,11 +1700,11 @@ class TestGem < Gem::TestCase
f.puts "gem 'c'"
end
- ENV['RUBYGEMS_GEMDEPS'] = path
-
- Gem.use_gemdeps
+ with_rubygems_gemdeps(path) do
+ Gem.use_gemdeps
- assert_equal add_bundler_full_name(%W[a-1 b-1 c-1]), loaded_spec_names
+ assert_equal add_bundler_full_name(%W[a-1 b-1 c-1]), loaded_spec_names
+ end
end
def test_auto_activation_of_used_gemdeps_file
@@ -1711,10 +1722,13 @@ class TestGem < Gem::TestCase
f.puts "gem 'c'"
end
- ENV['RUBYGEMS_GEMDEPS'] = "-"
+ with_rubygems_gemdeps("-") do
+ expected_specs = [a, b, util_spec("bundler", Bundler::VERSION), c].compact.map(&:full_name)
- expected_specs = [a, b, util_spec("bundler", Bundler::VERSION), c].compact
- assert_equal expected_specs, Gem.use_gemdeps.sort_by {|s| s.name }
+ Gem.use_gemdeps
+
+ assert_equal expected_specs, loaded_spec_names
+ end
end
BUNDLER_LIB_PATH = File.expand_path $LOAD_PATH.find {|lp| File.file?(File.join(lp, "bundler.rb")) }
@@ -1726,10 +1740,18 @@ class TestGem < Gem::TestCase
names
end
- def test_looks_for_gemdeps_files_automatically_on_start
- skip "Requiring bundler messes things up" if Gem.java_platform?
+ def test_looks_for_gemdeps_files_automatically_from_binstubs
+ pend "Requiring bundler messes things up" if Gem.java_platform?
+
+ a = util_spec "a", "1" do |s|
+ s.executables = %w[foo]
+ s.bindir = "exe"
+ end
+
+ write_file File.join(@tempdir, 'exe', 'foo') do |fp|
+ fp.puts "puts Gem.loaded_specs.values.map(&:full_name).sort"
+ end
- a = util_spec "a", "1", nil, "lib/a.rb"
b = util_spec "b", "1", nil, "lib/b.rb"
c = util_spec "c", "1", nil, "lib/c.rb"
@@ -1741,31 +1763,44 @@ class TestGem < Gem::TestCase
install_gem c, :install_dir => path
ENV['GEM_PATH'] = path
- ENV['RUBYGEMS_GEMDEPS'] = "-"
- path = File.join @tempdir, "gem.deps.rb"
- cmd = [*ruby_with_rubygems_in_load_path,
- "-I#{BUNDLER_LIB_PATH}"]
- cmd << "-eputs Gem.loaded_specs.values.map(&:full_name).sort"
+ with_rubygems_gemdeps("-") do
+ new_PATH = [File.join(path, "bin"), ENV["PATH"]].join(File::PATH_SEPARATOR)
+ new_RUBYOPT = "-I#{rubygems_path} -I#{BUNDLER_LIB_PATH}"
- File.open path, "w" do |f|
- f.puts "gem 'a'"
- end
- out0 = IO.popen(cmd, &:read).split(/\n/)
+ path = File.join @tempdir, "gem.deps.rb"
- File.open path, "a" do |f|
- f.puts "gem 'b'"
- f.puts "gem 'c'"
- end
- out = IO.popen(cmd, &:read).split(/\n/)
+ File.open path, "w" do |f|
+ f.puts "gem 'a'"
+ end
+ out0 = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
+ IO.popen("foo", &:read).split(/\n/)
+ end
+
+ File.open path, "a" do |f|
+ f.puts "gem 'b'"
+ f.puts "gem 'c'"
+ end
+ out = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
+ IO.popen("foo", &:read).split(/\n/)
+ end
- assert_equal ["b-1", "c-1"], out - out0
+ assert_equal ["b-1", "c-1"], out - out0
+ end
end
- def test_looks_for_gemdeps_files_automatically_on_start_in_parent_dir
- skip "Requiring bundler messes things up" if Gem.java_platform?
+ def test_looks_for_gemdeps_files_automatically_from_binstubs_in_parent_dir
+ pend "Requiring bundler messes things up" if Gem.java_platform?
+
+ a = util_spec "a", "1" do |s|
+ s.executables = %w[foo]
+ s.bindir = "exe"
+ end
+
+ write_file File.join(@tempdir, 'exe', 'foo') do |fp|
+ fp.puts "puts Gem.loaded_specs.values.map(&:full_name).sort"
+ end
- a = util_spec "a", "1", nil, "lib/a.rb"
b = util_spec "b", "1", nil, "lib/b.rb"
c = util_spec "c", "1", nil, "lib/c.rb"
@@ -1777,29 +1812,34 @@ class TestGem < Gem::TestCase
install_gem c, :install_dir => path
ENV['GEM_PATH'] = path
- ENV['RUBYGEMS_GEMDEPS'] = "-"
- Dir.mkdir "sub1"
+ with_rubygems_gemdeps("-") do
+ Dir.mkdir "sub1"
- path = File.join @tempdir, "gem.deps.rb"
- cmd = [*ruby_with_rubygems_in_load_path, "-Csub1",
- "-I#{BUNDLER_LIB_PATH}"]
- cmd << "-eputs Gem.loaded_specs.values.map(&:full_name).sort"
+ new_PATH = [File.join(path, "bin"), ENV["PATH"]].join(File::PATH_SEPARATOR)
+ new_RUBYOPT = "-I#{rubygems_path} -I#{BUNDLER_LIB_PATH}"
- File.open path, "w" do |f|
- f.puts "gem 'a'"
- end
- out0 = IO.popen(cmd, &:read).split(/\n/)
+ path = File.join @tempdir, "gem.deps.rb"
- File.open path, "a" do |f|
- f.puts "gem 'b'"
- f.puts "gem 'c'"
- end
- out = IO.popen(cmd, &:read).split(/\n/)
+ File.open path, "w" do |f|
+ f.puts "gem 'a'"
+ end
+ out0 = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
+ IO.popen("foo", :chdir => "sub1", &:read).split(/\n/)
+ end
- Dir.rmdir "sub1"
+ File.open path, "a" do |f|
+ f.puts "gem 'b'"
+ f.puts "gem 'c'"
+ end
+ out = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
+ IO.popen("foo", :chdir => "sub1", &:read).split(/\n/)
+ end
+
+ Dir.rmdir "sub1"
- assert_equal ["b-1", "c-1"], out - out0
+ assert_equal ["b-1", "c-1"], out - out0
+ end
end
def test_register_default_spec
@@ -1863,25 +1903,23 @@ class TestGem < Gem::TestCase
end
def test_use_gemdeps_ENV
- rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil
-
- spec = util_spec 'a', 1
+ with_rubygems_gemdeps(nil) do
+ spec = util_spec 'a', 1
- refute spec.activated?
+ refute spec.activated?
- File.open 'gem.deps.rb', 'w' do |io|
- io.write 'gem "a"'
- end
+ File.open 'gem.deps.rb', 'w' do |io|
+ io.write 'gem "a"'
+ end
- Gem.use_gemdeps
+ Gem.use_gemdeps
- refute spec.activated?
- ensure
- ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ refute spec.activated?
+ end
end
def test_use_gemdeps_argument_missing
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
Gem.use_gemdeps 'gem.deps.rb'
end
@@ -1890,114 +1928,96 @@ class TestGem < Gem::TestCase
end
def test_use_gemdeps_argument_missing_match_ENV
- rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] =
- ENV['RUBYGEMS_GEMDEPS'], 'gem.deps.rb'
+ with_rubygems_gemdeps('gem.deps.rb') do
+ e = assert_raise ArgumentError do
+ Gem.use_gemdeps 'gem.deps.rb'
+ end
- e = assert_raises ArgumentError do
- Gem.use_gemdeps 'gem.deps.rb'
+ assert_equal 'Unable to find gem dependencies file at gem.deps.rb',
+ e.message
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
- rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
+ with_rubygems_gemdeps('-') do
+ spec = util_spec 'a', 1
+ install_specs spec
+ spec = Gem::Specification.find {|s| s == spec }
- spec = util_spec 'a', 1
- install_specs spec
- spec = Gem::Specification.find {|s| s == spec }
-
- refute spec.activated?
+ refute spec.activated?
- File.open 'Gemfile', 'w' do |io|
- io.write 'gem "a"'
- end
+ File.open 'Gemfile', 'w' do |io|
+ io.write 'gem "a"'
+ end
- Gem.use_gemdeps
+ Gem.use_gemdeps
- assert_equal add_bundler_full_name(%W[a-1]), loaded_spec_names
- ensure
- ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ assert_equal add_bundler_full_name(%W[a-1]), loaded_spec_names
+ end
end
def test_use_gemdeps_automatic_missing
- rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
+ with_rubygems_gemdeps('-') do
+ Gem.use_gemdeps
- Gem.use_gemdeps
-
- assert true # count
- ensure
- ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ assert true # count
+ end
end
def test_use_gemdeps_disabled
- rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], ''
-
- spec = util_spec 'a', 1
+ with_rubygems_gemdeps('') do
+ spec = util_spec 'a', 1
- refute spec.activated?
+ refute spec.activated?
- File.open 'gem.deps.rb', 'w' do |io|
- io.write 'gem "a"'
- end
+ File.open 'gem.deps.rb', 'w' do |io|
+ io.write 'gem "a"'
+ end
- Gem.use_gemdeps
+ Gem.use_gemdeps
- refute spec.activated?
- ensure
- ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ refute spec.activated?
+ end
end
def test_use_gemdeps_missing_gem
- rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], 'x'
-
- File.open 'x', 'w' do |io|
- io.write 'gem "a"'
- end
-
- platform = Bundler::GemHelpers.generic_local_platform
- if platform == Gem::Platform::RUBY
- platform = ''
- else
- platform = " #{platform}"
- end
+ with_rubygems_gemdeps('x') do
+ File.open 'x', 'w' do |io|
+ io.write 'gem "a"'
+ end
- expected = <<-EXPECTED
-Could not find gem 'a#{platform}' in any of the gem sources listed in your Gemfile.
-You may need to `gem install -g` to install missing gems
+ expected = <<-EXPECTED
+Could not find gem 'a' in locally installed gems.
+You may need to `bundle install` to install missing gems
- EXPECTED
+ EXPECTED
- Gem::Deprecate.skip_during do
- assert_output nil, expected do
- Gem.use_gemdeps
+ Gem::Deprecate.skip_during do
+ actual_stdout, actual_stderr = capture_output do
+ Gem.use_gemdeps
+ end
+ assert_empty actual_stdout
+ assert_equal(expected, actual_stderr)
end
end
- ensure
- ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end
def test_use_gemdeps_specific
- rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], 'x'
+ with_rubygems_gemdeps('x') do
+ spec = util_spec 'a', 1
+ install_specs spec
- spec = util_spec 'a', 1
- install_specs spec
+ spec = Gem::Specification.find {|s| s == spec }
+ refute spec.activated?
- spec = Gem::Specification.find {|s| s == spec }
- refute spec.activated?
-
- File.open 'x', 'w' do |io|
- io.write 'gem "a"'
- end
+ File.open 'x', 'w' do |io|
+ io.write 'gem "a"'
+ end
- Gem.use_gemdeps
+ Gem.use_gemdeps
- assert_equal add_bundler_full_name(%W[a-1]), loaded_spec_names
- ensure
- ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ assert_equal add_bundler_full_name(%W[a-1]), loaded_spec_names
+ end
end
def test_operating_system_defaults
@@ -2079,7 +2099,7 @@ You may need to `gem install -g` to install missing gems
refute_includes $LOAD_PATH, test_plugin_path
$LOAD_PATH.unshift test_plugin_path
- capture_io do
+ capture_output do
yield
end
ensure
@@ -2115,4 +2135,22 @@ You may need to `gem install -g` to install missing gems
def util_cache_dir
File.join Gem.dir, "cache"
end
+
+ def with_path_and_rubyopt(path_value, rubyopt_value)
+ path, ENV['PATH'] = ENV['PATH'], path_value
+ rubyopt, ENV['RUBYOPT'] = ENV['RUBYOPT'], rubyopt_value
+
+ yield
+ ensure
+ ENV['PATH'] = path
+ ENV['RUBYOPT'] = rubyopt
+ end
+
+ def with_rubygems_gemdeps(value)
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], value
+
+ yield
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
end
diff --git a/test/rubygems/test_gem_available_set.rb b/test/rubygems/test_gem_available_set.rb
index dd2816acc6..c4164d77f2 100644
--- a/test/rubygems/test_gem_available_set.rb
+++ b/test/rubygems/test_gem_available_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/available_set'
require 'rubygems/security'
diff --git a/test/rubygems/test_gem_bundler_version_finder.rb b/test/rubygems/test_gem_bundler_version_finder.rb
index 4372356db8..e971e6ae29 100644
--- a/test/rubygems/test_gem_bundler_version_finder.rb
+++ b/test/rubygems/test_gem_bundler_version_finder.rb
@@ -1,19 +1,17 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemBundlerVersionFinder < Gem::TestCase
def setup
super
@argv = ARGV.dup
- @env = ENV.to_hash.clone
- ENV.delete("BUNDLER_VERSION")
@dollar_0 = $0
+ without_any_upwards_gemfiles
end
def teardown
ARGV.replace @argv
- ENV.replace @env
$0 = @dollar_0
super
@@ -78,8 +76,8 @@ class TestGemBundlerVersionFinder < Gem::TestCase
end
def test_deleted_directory
- skip "Cannot perform this test on windows" if win_platform?
- skip "Cannot perform this test on Solaris" if /solaris/ =~ RUBY_PLATFORM
+ pend "Cannot perform this test on windows" if win_platform?
+ pend "Cannot perform this test on Solaris" if /solaris/ =~ RUBY_PLATFORM
require "tmpdir"
orig_dir = Dir.pwd
diff --git a/test/rubygems/test_gem_command.rb b/test/rubygems/test_gem_command.rb
index 2f87d9cc8d..65b9b040b7 100644
--- a/test/rubygems/test_gem_command.rb
+++ b/test/rubygems/test_gem_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/command'
class Gem::Command
@@ -118,7 +118,7 @@ class TestGemCommand < Gem::TestCase
use_ui @ui do
@cmd.when_invoked { true }
- ex = assert_raises OptionParser::InvalidOption do
+ ex = assert_raise Gem::OptionParser::InvalidOption do
@cmd.invoke('-zzz')
end
@@ -189,6 +189,18 @@ class TestGemCommand < Gem::TestCase
assert_match %r{Usage: gem doit}, @ui.output
end
+ def test_add_option
+ assert_nothing_raised RuntimeError do
+ @cmd.add_option('--force', 'skip validation of the spec') {|v,o| }
+ end
+ end
+
+ def test_add_option_with_empty
+ assert_raise RuntimeError, "Do not pass an empty string in opts" do
+ @cmd.add_option('', 'skip validation of the spec') {|v,o| }
+ end
+ end
+
def test_option_recognition
@cmd.add_option('-h', '--help [COMMAND]', 'Get help on COMMAND') do |value, options|
options[:help] = true
diff --git a/test/rubygems/test_gem_command_manager.rb b/test/rubygems/test_gem_command_manager.rb
index d4471b0b63..ee71fc7c18 100644
--- a/test/rubygems/test_gem_command_manager.rb
+++ b/test/rubygems/test_gem_command_manager.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/command_manager'
class TestGemCommandManager < Gem::TestCase
@@ -22,7 +22,7 @@ class TestGemCommandManager < Gem::TestCase
end
def test_find_command_ambiguous
- e = assert_raises Gem::CommandLineError do
+ e = assert_raise Gem::CommandLineError do
@command_manager.find_command 'u'
end
@@ -36,6 +36,18 @@ class TestGemCommandManager < Gem::TestCase
assert_kind_of Gem::Commands::InstallCommand, command
end
+ def test_find_login_alias_command
+ command = @command_manager.find_command 'login'
+
+ assert_kind_of Gem::Commands::SigninCommand, command
+ end
+
+ def test_find_logout_alias_comamnd
+ command = @command_manager.find_command 'logout'
+
+ assert_kind_of Gem::Commands::SignoutCommand, command
+ end
+
def test_find_command_ambiguous_exact
ins_command = Class.new
Gem::Commands.send :const_set, :InsCommand, ins_command
@@ -50,7 +62,7 @@ class TestGemCommandManager < Gem::TestCase
end
def test_find_command_unknown
- e = assert_raises Gem::CommandLineError do
+ e = assert_raise Gem::CommandLineError do
@command_manager.find_command 'xyz'
end
@@ -65,7 +77,7 @@ class TestGemCommandManager < Gem::TestCase
@command_manager.register_command :interrupt
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@command_manager.run %w[interrupt]
end
assert_equal '', ui.output
@@ -82,7 +94,7 @@ class TestGemCommandManager < Gem::TestCase
@command_manager.register_command :crash
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@command_manager.run %w[crash]
end
assert_equal '', ui.output
@@ -96,7 +108,7 @@ class TestGemCommandManager < Gem::TestCase
def test_process_args_bad_arg
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@command_manager.process_args %w[--bad-arg]
end
end
diff --git a/test/rubygems/test_gem_commands_build_command.rb b/test/rubygems/test_gem_commands_build_command.rb
index fe537780be..1304beb580 100644
--- a/test/rubygems/test_gem_commands_build_command.rb
+++ b/test/rubygems/test_gem_commands_build_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/build_command'
require 'rubygems/package'
@@ -126,7 +126,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
use_ui @ui do
Dir.chdir @tempdir do
- assert_raises Gem::InvalidSpecificationException do
+ assert_raise Gem::InvalidSpecificationException do
@cmd.execute
end
end
@@ -180,7 +180,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
use_ui @ui do
Dir.chdir @tempdir do
- assert_raises Gem::InvalidSpecificationException do
+ assert_raise Gem::InvalidSpecificationException do
@cmd.execute
end
end
@@ -208,8 +208,8 @@ class TestGemCommandsBuildCommand < Gem::TestCase
@cmd.options[:args] = [gemspec_file]
out, err = use_ui @ui do
- capture_io do
- assert_raises Gem::MockGemUi::TermError do
+ capture_output do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -225,7 +225,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
def test_execute_missing_file
@cmd.options[:args] = %w[some_gem]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -329,7 +329,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
@cmd.options[:args] = ["*.gemspec"]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -527,7 +527,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
use_ui @ui do
Dir.chdir(gemspec_dir) do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -582,7 +582,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
end
def test_build_signed_gem
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL && !java_platform?
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL && !java_platform?
trust_dir = Gem::Security.trust_dir
@@ -609,7 +609,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
end
def test_build_signed_gem_with_cert_expiration_length_days
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL && !java_platform?
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL && !java_platform?
gem_path = File.join Gem.user_home, ".gem"
Dir.mkdir gem_path
@@ -653,7 +653,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
end
def test_build_auto_resign_cert
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL && !java_platform?
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL && !java_platform?
gem_path = File.join Gem.user_home, ".gem"
Dir.mkdir gem_path
diff --git a/test/rubygems/test_gem_commands_cert_command.rb b/test/rubygems/test_gem_commands_cert_command.rb
index 19867bf37a..901bf5aed6 100644
--- a/test/rubygems/test_gem_commands_cert_command.rb
+++ b/test/rubygems/test_gem_commands_cert_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/cert_command'
unless Gem::HAVE_OPENSSL
@@ -14,9 +14,10 @@ class TestGemCommandsCertCommand < Gem::TestCase
ALTERNATE_CERT = load_cert 'alternate'
EXPIRED_PUBLIC_CERT = load_cert 'expired'
- ALTERNATE_KEY_FILE = key_path 'alternate'
- PRIVATE_KEY_FILE = key_path 'private'
- PUBLIC_KEY_FILE = key_path 'public'
+ ALTERNATE_KEY_FILE = key_path 'alternate'
+ PRIVATE_KEY_FILE = key_path 'private'
+ PRIVATE_EC_KEY_FILE = key_path 'private_ec'
+ PUBLIC_KEY_FILE = key_path 'public'
ALTERNATE_CERT_FILE = cert_path 'alternate'
CHILD_CERT_FILE = cert_path 'child'
@@ -54,7 +55,7 @@ class TestGemCommandsCertCommand < Gem::TestCase
assert_equal PUBLIC_CERT.to_pem, match.first.to_pem
assert_equal @trust_dir.cert_path(PUBLIC_CERT), match.last
- assert_raises StopIteration do
+ assert_raise StopIteration do
matches.next
end
end
@@ -69,7 +70,7 @@ class TestGemCommandsCertCommand < Gem::TestCase
assert_equal ALTERNATE_CERT.to_pem, match.first.to_pem
assert_equal @trust_dir.cert_path(ALTERNATE_CERT), match.last
- assert_raises StopIteration do
+ assert_raise StopIteration do
matches.next
end
end
@@ -83,7 +84,7 @@ class TestGemCommandsCertCommand < Gem::TestCase
cert_path = @trust_dir.cert_path PUBLIC_CERT
- assert_path_exists cert_path
+ assert_path_exist cert_path
assert_equal "Added '/CN=nobody/DC=example'\n", @ui.output
assert_empty @ui.error
@@ -138,8 +139,44 @@ Added '/CN=alternate/DC=example'
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')
+ assert_path_exist File.join(@tempdir, 'gem-private_key.pem')
+ assert_path_exist File.join(@tempdir, 'gem-public_cert.pem')
+ end
+
+ def test_execute_build_key_algorithm_ec_key
+ passphrase = 'Foo bar'
+
+ @cmd.handle_options %W[--build nobody@example.com --key-algorithm ec]
+
+ @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_exist File.join(@tempdir, 'gem-private_key.pem')
+
+ cert_path = File.join(@tempdir, 'gem-public_cert.pem')
+ assert_path_exist cert_path
+ cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
+ assert cert.public_key.is_a? OpenSSL::PKey::EC
end
def test_execute_build_bad_email_address
@@ -152,15 +189,15 @@ Added '/CN=alternate/DC=example'
use_ui @build_ui do
- e = assert_raises Gem::CommandLineError do
+ e = assert_raise Gem::CommandLineError do
@cmd.execute
end
assert_equal "Invalid email address #{email}",
e.message
- refute_path_exists File.join(@tempdir, 'gem-private_key.pem')
- refute_path_exists File.join(@tempdir, 'gem-public_cert.pem')
+ assert_path_not_exist File.join(@tempdir, 'gem-private_key.pem')
+ assert_path_not_exist File.join(@tempdir, 'gem-public_cert.pem')
end
end
@@ -195,8 +232,8 @@ Added '/CN=alternate/DC=example'
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')
+ assert_path_exist File.join(@tempdir, 'gem-private_key.pem')
+ assert_path_exist File.join(@tempdir, 'gem-public_cert.pem')
pem = File.read("#{@tempdir}/gem-public_cert.pem")
cert = OpenSSL::X509::Certificate.new(pem)
@@ -214,7 +251,7 @@ Added '/CN=alternate/DC=example'
@build_ui = Gem::MockGemUi.new "#{passphrase}\n#{passphrase_confirmation}"
use_ui @build_ui do
- e = assert_raises Gem::CommandLineError do
+ e = assert_raise Gem::CommandLineError do
@cmd.execute
end
@@ -232,8 +269,8 @@ Added '/CN=alternate/DC=example'
end
- refute_path_exists File.join(@tempdir, 'gem-private_key.pem')
- refute_path_exists File.join(@tempdir, 'gem-public_cert.pem')
+ assert_path_not_exist File.join(@tempdir, 'gem-private_key.pem')
+ assert_path_not_exist File.join(@tempdir, 'gem-public_cert.pem')
end
def test_execute_build_key
@@ -254,8 +291,8 @@ Added '/CN=alternate/DC=example'
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')
+ assert_path_exist File.join(@tempdir, 'gem-public_cert.pem')
+ assert_path_not_exist File.join(@tempdir, 'gem-private_key.pem')
end
def test_execute_build_encrypted_key
@@ -276,7 +313,29 @@ Added '/CN=alternate/DC=example'
assert_empty output
assert_empty @ui.error
- assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
+ assert_path_exist File.join(@tempdir, 'gem-public_cert.pem')
+ end
+
+ def test_execute_build_ec_key
+ @cmd.handle_options %W[
+ --build nobody@example.com
+ --private-key #{PRIVATE_EC_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_exist File.join(@tempdir, 'gem-public_cert.pem')
+ assert_path_not_exist File.join(@tempdir, 'gem-private_key.pem')
end
def test_execute_certificate
@@ -346,7 +405,7 @@ Added '/CN=alternate/DC=example'
cert_path = @trust_dir.cert_path PUBLIC_CERT
- assert_path_exists cert_path
+ assert_path_exist cert_path
@cmd.handle_options %W[--remove nobody]
@@ -357,7 +416,7 @@ Added '/CN=alternate/DC=example'
assert_equal "Removed '/CN=nobody/DC=example'\n", @ui.output
assert_equal '', @ui.error
- refute_path_exists cert_path
+ assert_path_not_exist cert_path
end
def test_execute_remove_multiple
@@ -367,8 +426,8 @@ Added '/CN=alternate/DC=example'
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
+ assert_path_exist public_path
+ assert_path_exist alternate_path
@cmd.handle_options %W[--remove example]
@@ -384,8 +443,8 @@ Removed '/CN=nobody/DC=example'
assert_equal expected, @ui.output
assert_equal '', @ui.error
- refute_path_exists public_path
- refute_path_exists alternate_path
+ assert_path_not_exist public_path
+ assert_path_not_exist alternate_path
end
def test_execute_remove_twice
@@ -395,8 +454,8 @@ Removed '/CN=nobody/DC=example'
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
+ assert_path_exist public_path
+ assert_path_exist alternate_path
@cmd.handle_options %W[--remove nobody --remove alternate]
@@ -412,8 +471,8 @@ Removed '/CN=alternate/DC=example'
assert_equal expected, @ui.output
assert_equal '', @ui.error
- refute_path_exists public_path
- refute_path_exists alternate_path
+ assert_path_not_exist public_path
+ assert_path_not_exist alternate_path
end
def test_execute_sign
@@ -552,7 +611,7 @@ Removed '/CN=alternate/DC=example'
@cmd.handle_options %W[--sign #{path}]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -580,7 +639,7 @@ ERROR: --certificate not specified and ~/.gem/gem-public_cert.pem does not exis
@cmd.handle_options %W[--sign #{path}]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -686,7 +745,7 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
def test_handle_options_add_bad
nonexistent = File.join @tempdir, 'nonexistent'
- e = assert_raises OptionParser::InvalidArgument do
+ e = assert_raise Gem::OptionParser::InvalidArgument do
@cmd.handle_options %W[--add #{nonexistent}]
end
@@ -696,7 +755,7 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
bad = File.join @tempdir, 'bad'
FileUtils.touch bad
- e = assert_raises OptionParser::InvalidArgument do
+ e = assert_raise Gem::OptionParser::InvalidArgument do
@cmd.handle_options %W[--add #{bad}]
end
@@ -706,7 +765,7 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
def test_handle_options_certificate
nonexistent = File.join @tempdir, 'nonexistent'
- e = assert_raises OptionParser::InvalidArgument do
+ e = assert_raise Gem::OptionParser::InvalidArgument do
@cmd.handle_options %W[--certificate #{nonexistent}]
end
@@ -716,7 +775,7 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
bad = File.join @tempdir, 'bad'
FileUtils.touch bad
- e = assert_raises OptionParser::InvalidArgument do
+ e = assert_raise Gem::OptionParser::InvalidArgument do
@cmd.handle_options %W[--certificate #{bad}]
end
@@ -727,7 +786,7 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
def test_handle_options_key_bad
nonexistent = File.join @tempdir, 'nonexistent'
- e = assert_raises OptionParser::InvalidArgument do
+ e = assert_raise Gem::OptionParser::InvalidArgument do
@cmd.handle_options %W[--private-key #{nonexistent}]
end
@@ -738,14 +797,14 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
bad = File.join @tempdir, 'bad'
FileUtils.touch bad
- e = assert_raises OptionParser::InvalidArgument do
+ e = assert_raise Gem::OptionParser::InvalidArgument do
@cmd.handle_options %W[--private-key #{bad}]
end
- assert_equal "invalid argument: --private-key #{bad}: invalid RSA key",
+ assert_equal "invalid argument: --private-key #{bad}: invalid RSA, DSA, or EC key",
e.message
- e = assert_raises OptionParser::InvalidArgument do
+ e = assert_raise Gem::OptionParser::InvalidArgument do
@cmd.handle_options %W[--private-key #{PUBLIC_KEY_FILE}]
end
@@ -792,7 +851,7 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
def test_handle_options_sign_nonexistent
nonexistent = File.join @tempdir, 'nonexistent'
- e = assert_raises OptionParser::InvalidArgument do
+ e = assert_raise Gem::OptionParser::InvalidArgument do
@cmd.handle_options %W[
--private-key #{ALTERNATE_KEY_FILE}
diff --git a/test/rubygems/test_gem_commands_check_command.rb b/test/rubygems/test_gem_commands_check_command.rb
index c922e40eab..f280b060c4 100644
--- a/test/rubygems/test_gem_commands_check_command.rb
+++ b/test/rubygems/test_gem_commands_check_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/check_command'
class TestGemCommandsCheckCommand < Gem::TestCase
@@ -50,18 +50,18 @@ class TestGemCommandsCheckCommand < Gem::TestCase
FileUtils.rm b.spec_file
- assert_path_exists b.gem_dir
- refute_path_exists b.spec_file
+ assert_path_exist b.gem_dir
+ assert_path_not_exist b.spec_file
Gem.use_paths @gemhome
- capture_io do
+ capture_output do
use_ui @ui do
@cmd.doctor
end
end
- refute_path_exists b.gem_dir
- refute_path_exists b.spec_file
+ assert_path_not_exist b.gem_dir
+ assert_path_not_exist b.spec_file
end
end
diff --git a/test/rubygems/test_gem_commands_cleanup_command.rb b/test/rubygems/test_gem_commands_cleanup_command.rb
index d937a5e549..fc98998012 100644
--- a/test/rubygems/test_gem_commands_cleanup_command.rb
+++ b/test/rubygems/test_gem_commands_cleanup_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/cleanup_command'
require 'rubygems/installer'
@@ -62,7 +62,7 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
@cmd.execute
- refute_path_exists @a_1.gem_dir
+ assert_path_not_exist @a_1.gem_dir
end
def test_execute_all_dependencies
@@ -81,8 +81,8 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
@cmd.execute
- refute_path_exists @a_1.gem_dir
- refute_path_exists @b_1.gem_dir
+ assert_path_not_exist @a_1.gem_dir
+ assert_path_not_exist @b_1.gem_dir
end
def test_execute_dev_dependencies
@@ -101,7 +101,7 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
@cmd.execute
- assert_path_exists @a_1.gem_dir
+ assert_path_exist @a_1.gem_dir
end
def test_execute_without_dev_dependencies
@@ -120,7 +120,7 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
@cmd.execute
- refute_path_exists @a_1.gem_dir
+ assert_path_not_exist @a_1.gem_dir
end
def test_execute_all
@@ -143,8 +143,8 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
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
+ assert_path_not_exist @a_1.gem_dir
+ assert_path_not_exist @b_1.gem_dir
end
def test_execute_all_user
@@ -153,15 +153,15 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
Gem::Specification.dirs = [Gem.dir, Gem.user_dir]
- assert_path_exists @a_1.gem_dir
- assert_path_exists @a_1_1.gem_dir
+ assert_path_exist @a_1.gem_dir
+ assert_path_exist @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
+ assert_path_not_exist @a_1.gem_dir
+ assert_path_not_exist @a_1_1.gem_dir
end
def test_execute_all_user_no_sudo
@@ -172,15 +172,15 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
Gem::Specification.dirs = [Gem.dir, Gem.user_dir]
- assert_path_exists @a_1.gem_dir
- assert_path_exists @a_1_1.gem_dir
+ assert_path_exist @a_1.gem_dir
+ assert_path_exist @a_1_1.gem_dir
@cmd.options[:args] = %w[a]
@cmd.execute
- assert_path_exists @a_1.gem_dir
- assert_path_exists @a_1_1.gem_dir
+ assert_path_exist @a_1.gem_dir
+ assert_path_exist @a_1_1.gem_dir
ensure
FileUtils.chmod 0755, @gemhome
end unless win_platform? || Process.uid.zero?
@@ -191,7 +191,7 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
@cmd.execute
- assert_path_exists @a_1.gem_dir
+ assert_path_exist @a_1.gem_dir
end
def test_execute_keeps_older_versions_with_deps
@@ -210,7 +210,7 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
@cmd.execute
- assert_path_exists @b_1.gem_dir
+ assert_path_exist @b_1.gem_dir
end
def test_execute_ignore_default_gem_verbose
@@ -257,9 +257,9 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
@cmd.execute
- assert_path_exists c_1.gem_dir
- refute_path_exists d_1.gem_dir
- refute_path_exists e_1.gem_dir
+ assert_path_exist c_1.gem_dir
+ assert_path_not_exist d_1.gem_dir
+ assert_path_not_exist e_1.gem_dir
end
def test_execute_user_install
@@ -282,10 +282,10 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
@cmd.execute
- refute_path_exists c_1.gem_dir
- assert_path_exists c_2.gem_dir
+ assert_path_not_exist c_1.gem_dir
+ assert_path_exist c_2.gem_dir
- assert_path_exists d_1.gem_dir
- assert_path_exists d_2.gem_dir
+ assert_path_exist d_1.gem_dir
+ assert_path_exist d_2.gem_dir
end
end
diff --git a/test/rubygems/test_gem_commands_contents_command.rb b/test/rubygems/test_gem_commands_contents_command.rb
index 7c89c67dd4..d79174717b 100644
--- a/test/rubygems/test_gem_commands_contents_command.rb
+++ b/test/rubygems/test_gem_commands_contents_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/contents_command'
class TestGemCommandsContentsCommand < Gem::TestCase
@@ -50,7 +50,7 @@ class TestGemCommandsContentsCommand < Gem::TestCase
def test_execute_bad_gem
@cmd.options[:args] = %w[foo]
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
@@ -94,7 +94,7 @@ class TestGemCommandsContentsCommand < Gem::TestCase
def test_execute_missing_single
@cmd.options[:args] = %w[foo]
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
@@ -110,7 +110,7 @@ class TestGemCommandsContentsCommand < Gem::TestCase
gem 'foo', 1
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
diff --git a/test/rubygems/test_gem_commands_dependency_command.rb b/test/rubygems/test_gem_commands_dependency_command.rb
index 11d7f8017a..13c7c065b5 100644
--- a/test/rubygems/test_gem_commands_dependency_command.rb
+++ b/test/rubygems/test_gem_commands_dependency_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/dependency_command'
class TestGemCommandsDependencyCommand < Gem::TestCase
@@ -64,7 +64,7 @@ Gem x-2
def test_execute_no_match
@cmd.options[:args] = %w[foo]
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @stub_ui do
@cmd.execute
end
@@ -155,7 +155,7 @@ Gem foo-2
@cmd.options[:reverse_dependencies] = true
@cmd.options[:domain] = :remote
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @stub_ui do
@cmd.execute
end
diff --git a/test/rubygems/test_gem_commands_environment_command.rb b/test/rubygems/test_gem_commands_environment_command.rb
index a3edeb69bd..2bf80d8d85 100644
--- a/test/rubygems/test_gem_commands_environment_command.rb
+++ b/test/rubygems/test_gem_commands_environment_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/environment_command'
class TestGemCommandsEnvironmentCommand < Gem::TestCase
@@ -109,7 +109,7 @@ class TestGemCommandsEnvironmentCommand < Gem::TestCase
def test_execute_unknown
@cmd.send :handle_options, %w[unknown]
- assert_raises Gem::CommandLineError do
+ assert_raise Gem::CommandLineError do
use_ui @ui do
@cmd.execute
end
diff --git a/test/rubygems/test_gem_commands_fetch_command.rb b/test/rubygems/test_gem_commands_fetch_command.rb
index dfe0d91726..c745648d56 100644
--- a/test/rubygems/test_gem_commands_fetch_command.rb
+++ b/test/rubygems/test_gem_commands_fetch_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/package'
require 'rubygems/security'
require 'rubygems/commands/fetch_command'
@@ -16,7 +16,7 @@ class TestGemCommandsFetchCommand < Gem::TestCase
fetcher.gem 'a', 2
end
- refute_path_exists File.join(@tempdir, 'cache'), 'sanity check'
+ assert_path_not_exist File.join(@tempdir, 'cache'), 'sanity check'
@cmd.options[:args] = %w[a]
@@ -28,9 +28,9 @@ class TestGemCommandsFetchCommand < Gem::TestCase
a2 = specs['a-2']
- assert_path_exists(File.join(@tempdir, a2.file_name),
+ assert_path_exist(File.join(@tempdir, a2.file_name),
"#{a2.full_name} not fetched")
- refute_path_exists File.join(@tempdir, 'cache'),
+ assert_path_not_exist File.join(@tempdir, 'cache'),
'gem repository directories must not be created'
end
@@ -40,7 +40,7 @@ class TestGemCommandsFetchCommand < Gem::TestCase
fetcher.gem 'a', 2
end
- refute_path_exists File.join(@tempdir, 'cache'), 'sanity check'
+ assert_path_not_exist File.join(@tempdir, 'cache'), 'sanity check'
@cmd.options[:args] = %w[a]
@cmd.options[:version] = req('>= 0.1')
@@ -52,9 +52,9 @@ class TestGemCommandsFetchCommand < Gem::TestCase
end
a2 = specs['a-2']
- assert_path_exists(File.join(@tempdir, a2.file_name),
+ assert_path_exist(File.join(@tempdir, a2.file_name),
"#{a2.full_name} not fetched")
- refute_path_exists File.join(@tempdir, 'cache'),
+ assert_path_not_exist File.join(@tempdir, 'cache'),
'gem repository directories must not be created'
end
@@ -75,10 +75,46 @@ class TestGemCommandsFetchCommand < Gem::TestCase
a2 = specs['a-2']
- assert_path_exists(File.join(@tempdir, a2.file_name),
+ assert_path_exist(File.join(@tempdir, a2.file_name),
"#{a2.full_name} not fetched")
end
+ def test_execute_platform
+ a2_spec, a2 = util_gem("a", "2")
+
+ a2_universal_darwin_spec, a2_universal_darwin = util_gem("a", "2") do |s|
+ s.platform = 'universal-darwin'
+ end
+
+ Gem::RemoteFetcher.fetcher = @fetcher = Gem::FakeFetcher.new
+
+ write_marshalled_gemspecs(a2_spec, a2_universal_darwin_spec)
+
+ @cmd.options[:args] = %w[a]
+
+ @fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] = util_gzip(Marshal.dump([
+ Gem::NameTuple.new(a2_spec.name, a2_spec.version, a2_spec.platform),
+ Gem::NameTuple.new(a2_universal_darwin_spec.name, a2_universal_darwin_spec.version, a2_universal_darwin_spec.platform),
+ ]))
+
+ @fetcher.data["#{@gem_repo}gems/#{a2_spec.file_name}"] = Gem.read_binary(a2)
+ FileUtils.cp a2, a2_spec.cache_file
+
+ @fetcher.data["#{@gem_repo}gems/#{a2_universal_darwin_spec.file_name}"] = Gem.read_binary(a2_universal_darwin)
+ FileUtils.cp a2_universal_darwin, a2_universal_darwin_spec.cache_file
+
+ util_set_arch 'arm64-darwin20' do
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+ end
+
+ assert_path_exist(File.join(@tempdir, a2_universal_darwin_spec.file_name),
+ "#{a2_universal_darwin_spec.full_name} not fetched")
+ end
+
def test_execute_specific_prerelease
specs = spec_fetcher do |fetcher|
fetcher.gem 'a', 2
@@ -97,7 +133,7 @@ class TestGemCommandsFetchCommand < Gem::TestCase
a2_pre = specs['a-2.a']
- assert_path_exists(File.join(@tempdir, a2_pre.file_name),
+ assert_path_exist(File.join(@tempdir, a2_pre.file_name),
"#{a2_pre.full_name} not fetched")
end
@@ -118,7 +154,7 @@ class TestGemCommandsFetchCommand < Gem::TestCase
a1 = specs['a-1']
- assert_path_exists(File.join(@tempdir, a1.file_name),
+ assert_path_exist(File.join(@tempdir, a1.file_name),
"#{a1.full_name} not fetched")
end
end
diff --git a/test/rubygems/test_gem_commands_generate_index_command.rb b/test/rubygems/test_gem_commands_generate_index_command.rb
index fc1317a49d..5b7b37a446 100644
--- a/test/rubygems/test_gem_commands_generate_index_command.rb
+++ b/test/rubygems/test_gem_commands_generate_index_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/indexer'
require 'rubygems/commands/generate_index_command'
diff --git a/test/rubygems/test_gem_commands_help_command.rb b/test/rubygems/test_gem_commands_help_command.rb
index 8d20563a60..a70dd770e1 100644
--- a/test/rubygems/test_gem_commands_help_command.rb
+++ b/test/rubygems/test_gem_commands_help_command.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
require "rubygems"
-require "rubygems/test_case"
+require_relative "helper"
require "rubygems/commands/help_command"
require "rubygems/package"
require "rubygems/command_manager"
@@ -35,6 +35,13 @@ class TestGemCommandsHelpCommand < Gem::TestCase
end
end
+ def test_gem_help_build
+ util_gem 'build' do |out, err|
+ assert_match(/-C PATH *Run as if gem build was started in <PATH>/, out)
+ assert_equal '', err
+ end
+ end
+
def test_gem_help_commands
mgr = Gem::CommandManager.new
@@ -48,7 +55,7 @@ class TestGemCommandsHelpCommand < Gem::TestCase
if Gem::HAVE_OPENSSL
assert_empty err
- refute_match 'No command found for ', out
+ refute_match %r{No command found for }, out
end
end
end
diff --git a/test/rubygems/test_gem_commands_info_command.rb b/test/rubygems/test_gem_commands_info_command.rb
index 6d67b567c7..462075f98c 100644
--- a/test/rubygems/test_gem_commands_info_command.rb
+++ b/test/rubygems/test_gem_commands_info_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/info_command'
class TestGemCommandsInfoCommand < Gem::TestCase
diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb
index c8015f9985..535180983b 100644
--- a/test/rubygems/test_gem_commands_install_command.rb
+++ b/test/rubygems/test_gem_commands_install_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/install_command'
require 'rubygems/request_set'
require 'rubygems/rdoc'
@@ -37,7 +37,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -59,7 +59,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
assert @cmd.options[:version].satisfied_by?(a2_pre.version)
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -82,7 +82,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
orig_dir = Dir.pwd
begin
Dir.chdir @tempdir
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
ensure
@@ -110,7 +110,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
orig_dir = Dir.pwd
begin
Dir.chdir @tempdir
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
assert_equal 2, e.exit_code
@@ -142,7 +142,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
orig_dir = Dir.pwd
begin
Dir.chdir orig_dir
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
ensure
@@ -173,7 +173,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
begin
Dir.chdir @tempdir
FileUtils.rm_r [@gemhome, "gems"]
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
ensure
@@ -187,8 +187,8 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
end
def test_execute_no_user_install
- skip 'skipped on MS Windows (chmod has no effect)' if win_platform?
- skip 'skipped in root privilege' if Process.uid.zero?
+ pend 'skipped on MS Windows (chmod has no effect)' if win_platform?
+ pend 'skipped in root privilege' if Process.uid.zero?
specs = spec_fetcher do |fetcher|
fetcher.gem 'a', 2
@@ -207,7 +207,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
FileUtils.chmod 0555, @gemhome
Dir.chdir @tempdir
- assert_raises Gem::FilePermissionError do
+ assert_raise Gem::FilePermissionError do
@cmd.execute
end
ensure
@@ -225,7 +225,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
@cmd.options[:args] = %w[no_such_gem]
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
assert_equal 2, e.exit_code
@@ -244,7 +244,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
@cmd.options[:args] = %w[no_such_gem]
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
assert_equal 2, e.exit_code
@@ -257,7 +257,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
def test_execute_no_gem
@cmd.options[:args] = %w[]
- assert_raises Gem::CommandLineError do
+ assert_raise Gem::CommandLineError do
@cmd.execute
end
end
@@ -268,7 +268,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
@cmd.options[:args] = %w[nonexistent]
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
assert_equal 2, e.exit_code
@@ -285,7 +285,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
@cmd.options[:args] = ['foo']
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
@@ -301,7 +301,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
def test_execute_http_proxy
use_ui @ui do
- e = assert_raises ArgumentError, @ui.error do
+ e = assert_raise ArgumentError, @ui.error do
@cmd.handle_options %w[-p=foo.bar.com]
end
@@ -327,7 +327,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
@cmd.options[:args] = %w[nonexistent]
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
assert_equal 2, e.exit_code
@@ -351,7 +351,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
@cmd.options[:suggest_alternate] = false
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
@@ -376,7 +376,7 @@ ERROR: Could not find a valid gem 'nonexistent_with_hint' (>= 0) in any reposit
@cmd.options[:args] = [misspelled]
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
@@ -401,7 +401,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = [misspelled]
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
@@ -423,7 +423,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:install_dir] = "whatever"
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -443,7 +443,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -461,7 +461,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -478,7 +478,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a:1]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -496,7 +496,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -526,7 +526,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -547,7 +547,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -565,7 +565,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -585,7 +585,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -605,7 +605,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -625,7 +625,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -657,7 +657,7 @@ ERROR: Possible alternatives: non_existent_with_hint
begin
Dir.chdir @tempdir
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
ensure
@@ -667,8 +667,8 @@ ERROR: Possible alternatives: non_existent_with_hint
wait_for_child_process_to_exit
- assert_path_exists File.join(a2.doc_dir, 'ri')
- assert_path_exists File.join(a2.doc_dir, 'rdoc')
+ assert_path_exist File.join(a2.doc_dir, 'ri')
+ assert_path_exist File.join(a2.doc_dir, 'rdoc')
end
def test_execute_rdoc_with_path
@@ -694,7 +694,7 @@ ERROR: Possible alternatives: non_existent_with_hint
begin
Dir.chdir @tempdir
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
ensure
@@ -704,7 +704,7 @@ ERROR: Possible alternatives: non_existent_with_hint
wait_for_child_process_to_exit
- assert_path_exists 'whatever/doc/a-2', 'documentation not installed'
+ assert_path_exist 'whatever/doc/a-2', 'documentation not installed'
end
def test_execute_saves_build_args
@@ -730,7 +730,7 @@ ERROR: Possible alternatives: non_existent_with_hint
begin
Dir.chdir @tempdir
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
ensure
@@ -739,7 +739,7 @@ ERROR: Possible alternatives: non_existent_with_hint
end
path = a2.build_info_file
- assert_path_exists path
+ assert_path_exist path
assert_equal args, a2.build_args
end
@@ -752,7 +752,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -772,7 +772,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -812,7 +812,7 @@ ERROR: Possible alternatives: non_existent_with_hint
use_ui @ui do
Dir.chdir @tempdir do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -844,7 +844,7 @@ ERROR: Possible alternatives: non_existent_with_hint
orig_dir = Dir.pwd
begin
Dir.chdir @tempdir
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
ensure
@@ -862,7 +862,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:version] = Gem::Requirement.new("> 1")
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
@@ -889,7 +889,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a:1 b:1]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -912,7 +912,7 @@ ERROR: Possible alternatives: non_existent_with_hint
orig_dir = Dir.pwd
begin
Dir.chdir @tempdir
- assert_raises Gem::MockGemUi::SystemExitException do
+ assert_raise Gem::MockGemUi::SystemExitException do
@cmd.execute
end
ensure
@@ -1007,7 +1007,7 @@ ERROR: Possible alternatives: non_existent_with_hint
orig_dir = Dir.pwd
begin
Dir.chdir @tempdir
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
ensure
@@ -1029,7 +1029,7 @@ ERROR: Possible alternatives: non_existent_with_hint
orig_dir = Dir.pwd
begin
Dir.chdir @tempdir
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
ensure
@@ -1052,7 +1052,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1067,6 +1067,31 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_equal x, e
end
+ def test_redact_credentials_from_uri_on_warning
+ spec_fetcher do |fetcher|
+ fetcher.download 'a', 2
+ end
+
+ Gem.sources << "http://username:SECURE_TOKEN@nonexistent.example"
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ assert_raise 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://username:REDACTED@nonexistent.example': no data for http://username:REDACTED@nonexistent.example/specs.4.8.gz (http://username:REDACTED@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
@@ -1079,7 +1104,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:gemdeps] = @gemdeps
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1103,7 +1128,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:gemdeps] = @gemdeps
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1128,7 +1153,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:gemdeps] = @gemdeps
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1150,7 +1175,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:gemdeps] = @gemdeps
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1173,7 +1198,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:gemdeps] = @gemdeps
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1200,7 +1225,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:gemdeps] = @gemdeps
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1227,7 +1252,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:gemdeps] = @gemdeps
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1259,7 +1284,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:gemdeps] = @gemdeps
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1294,7 +1319,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:gemdeps] = @gemdeps
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1379,7 +1404,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1406,7 +1431,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1435,7 +1460,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
@@ -1465,7 +1490,7 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.options[:args] = %w[a]
use_ui @ui do
- assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ assert_raise Gem::MockGemUi::SystemExitException, @ui.error do
@cmd.execute
end
end
diff --git a/test/rubygems/test_gem_commands_list_command.rb b/test/rubygems/test_gem_commands_list_command.rb
index 87da8dbd48..d8cffce7a3 100644
--- a/test/rubygems/test_gem_commands_list_command.rb
+++ b/test/rubygems/test_gem_commands_list_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/list_command'
class TestGemCommandsListCommand < Gem::TestCase
@@ -20,7 +20,7 @@ class TestGemCommandsListCommand < Gem::TestCase
def test_execute_installed
@cmd.handle_options %w[c --installed]
- assert_raises Gem::MockGemUi::SystemExitException do
+ assert_raise Gem::MockGemUi::SystemExitException do
use_ui @ui do
@cmd.execute
end
diff --git a/test/rubygems/test_gem_commands_lock_command.rb b/test/rubygems/test_gem_commands_lock_command.rb
index 3612778293..f8afca1e29 100644
--- a/test/rubygems/test_gem_commands_lock_command.rb
+++ b/test/rubygems/test_gem_commands_lock_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/lock_command'
class TestGemCommandsLockCommand < Gem::TestCase
@@ -55,7 +55,7 @@ gem 'd', '= 1'
def test_execute_strict
@cmd.handle_options %w[c-1 --strict]
- e = assert_raises Gem::Exception do
+ e = assert_raise Gem::Exception do
use_ui @ui do
@cmd.execute
end
diff --git a/test/rubygems/test_gem_commands_mirror.rb b/test/rubygems/test_gem_commands_mirror.rb
index 6b2b7d11bb..470f1c30fa 100644
--- a/test/rubygems/test_gem_commands_mirror.rb
+++ b/test/rubygems/test_gem_commands_mirror.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/mirror_command'
class TestGemCommandsMirrorCommand < Gem::TestCase
diff --git a/test/rubygems/test_gem_commands_open_command.rb b/test/rubygems/test_gem_commands_open_command.rb
index d3c665217f..8447f7ea35 100644
--- a/test/rubygems/test_gem_commands_open_command.rb
+++ b/test/rubygems/test_gem_commands_open_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/open_command'
class TestGemCommandsOpenCommand < Gem::TestCase
@@ -25,16 +25,15 @@ class TestGemCommandsOpenCommand < Gem::TestCase
gem 'foo', '1.0.0'
spec = gem 'foo', '1.0.1'
- mock = Minitest::Mock.new
- mock.expect(:call, true, [spec.full_gem_path])
- Dir.stub(:chdir, mock) do
- use_ui @ui do
- @cmd.execute
+ assert_nothing_raised Gem::MockGemUi::TermError do
+ Dir.stub(:chdir, spec.full_gem_path) do
+ use_ui @ui do
+ @cmd.execute
+ end
end
end
- assert mock.verify
assert_equal "", @ui.error
end
@@ -44,7 +43,7 @@ class TestGemCommandsOpenCommand < Gem::TestCase
gem "foo", "5.0"
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
@@ -57,7 +56,7 @@ class TestGemCommandsOpenCommand < Gem::TestCase
def test_execute_bad_gem
@cmd.options[:args] = %w[foo]
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
@@ -86,7 +85,7 @@ class TestGemCommandsOpenCommand < Gem::TestCase
gem("foo", "1.0")
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
diff --git a/test/rubygems/test_gem_commands_outdated_command.rb b/test/rubygems/test_gem_commands_outdated_command.rb
index c4af421f5d..dc5c40a782 100644
--- a/test/rubygems/test_gem_commands_outdated_command.rb
+++ b/test/rubygems/test_gem_commands_outdated_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/outdated_command'
class TestGemCommandsOutdatedCommand < Gem::TestCase
diff --git a/test/rubygems/test_gem_commands_owner_command.rb b/test/rubygems/test_gem_commands_owner_command.rb
index 4280fedff3..5b06b628c2 100644
--- a/test/rubygems/test_gem_commands_owner_command.rb
+++ b/test/rubygems/test_gem_commands_owner_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/owner_command'
class TestGemCommandsOwnerCommand < Gem::TestCase
@@ -53,7 +53,7 @@ EOF
end
def test_show_owners_dont_load_objects
- skip "testing a psych-only API" unless defined?(::Psych::DisallowedClass)
+ pend "testing a psych-only API" unless defined?(::Psych::DisallowedClass)
response = <<EOF
---
@@ -68,7 +68,7 @@ EOF
@stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
- assert_raises Psych::DisallowedClass do
+ assert_raise Psych::DisallowedClass do
use_ui @ui do
@cmd.show_owners("freewill")
end
@@ -109,7 +109,7 @@ EOF
response = "You don't have permission to push to this gem"
@stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 403, 'Forbidden']
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @stub_ui do
@cmd.show_owners("freewill")
end
diff --git a/test/rubygems/test_gem_commands_pristine_command.rb b/test/rubygems/test_gem_commands_pristine_command.rb
index 59f34af249..f4000f4657 100644
--- a/test/rubygems/test_gem_commands_pristine_command.rb
+++ b/test/rubygems/test_gem_commands_pristine_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/pristine_command'
class TestGemCommandsPristineCommand < Gem::TestCase
@@ -155,7 +155,7 @@ class TestGemCommandsPristineCommand < Gem::TestCase
@cmd.execute
end
- assert_path_exists gem_exec
+ assert_path_exist gem_exec
ruby_exec = sprintf Gem.default_exec_format, 'ruby'
@@ -356,10 +356,10 @@ class TestGemCommandsPristineCommand < Gem::TestCase
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')
+ assert_path_exist File.join(@gemhome, "gems", 'a-2')
+ assert_path_not_exist File.join(gemhome2, "gems", 'a-2')
+ assert_path_exist File.join(gemhome2, "gems", 'b-2')
+ assert_path_not_exist File.join(@gemhome, "gems", 'b-2')
end
def test_execute_missing_cache_gem
@@ -434,21 +434,21 @@ class TestGemCommandsPristineCommand < Gem::TestCase
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_exist File.join(@gemhome, "cache", 'a-1.gem')
+ assert_path_not_exist File.join(gemhome2, "cache", 'a-2.gem')
+ assert_path_exist File.join(@gemhome, "gems", 'a-1')
+ assert_path_not_exist 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')
+ assert_path_exist File.join(gemhome2, "cache", 'b-1.gem')
+ assert_path_not_exist File.join(@gemhome, "cache", 'b-2.gem')
+ assert_path_exist File.join(gemhome2, "gems", 'b-1')
+ assert_path_not_exist File.join(@gemhome, "gems", 'b-1')
end
def test_execute_no_gem
@cmd.options[:args] = %w[]
- e = assert_raises Gem::CommandLineError do
+ e = assert_raise Gem::CommandLineError do
use_ui @ui do
@cmd.execute
end
diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb
index 55dd51e6ad..fa3968ffce 100644
--- a/test/rubygems/test_gem_commands_push_command.rb
+++ b/test/rubygems/test_gem_commands_push_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/push_command'
class TestGemCommandsPushCommand < Gem::TestCase
@@ -123,7 +123,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
Gem.configuration.disable_default_gem_server = true
response = "You must specify a gem server"
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.send_gem(@path)
end
@@ -155,7 +155,6 @@ class TestGemCommandsPushCommand < Gem::TestCase
@host => @api_key,
}
- FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
@@ -190,7 +189,6 @@ class TestGemCommandsPushCommand < Gem::TestCase
@host => @api_key,
}
- FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
@@ -232,7 +230,6 @@ class TestGemCommandsPushCommand < Gem::TestCase
:rubygems_api_key => @api_key,
}
- FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
@@ -252,7 +249,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
response = %(ERROR: "#{@host}" is not allowed by the gemspec, which only allows "https://privategemserver.example")
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
send_battery
end
@@ -274,7 +271,6 @@ class TestGemCommandsPushCommand < Gem::TestCase
@host => @api_key,
}
- FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
@@ -284,7 +280,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
response = "ERROR: \"#{@host}\" is not allowed by the gemspec, which only allows \"#{push_host}\""
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
send_battery
end
@@ -305,7 +301,6 @@ class TestGemCommandsPushCommand < Gem::TestCase
host => api_key,
}
- FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
@@ -332,7 +327,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
def test_raises_error_with_no_arguments
def @cmd.sign_in(*); end
- assert_raises Gem::CommandLineError do
+ assert_raise Gem::CommandLineError do
@cmd.execute
end
end
@@ -342,7 +337,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
@fetcher.data["#{@host}/api/v1/gems"] = [response, 403, 'Forbidden']
@cmd.instance_variable_set :@host, @host
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.send_gem(@path)
end
@@ -392,7 +387,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
@fetcher.data["#{Gem.host}/api/v1/gems"] = [response, 401, 'Unauthorized']
@otp_ui = Gem::MockGemUi.new "111111\n"
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @otp_ui do
@cmd.send_gem(@path)
end
diff --git a/test/rubygems/test_gem_commands_query_command.rb b/test/rubygems/test_gem_commands_query_command.rb
index a21bc690fb..0cc88b1685 100644
--- a/test/rubygems/test_gem_commands_query_command.rb
+++ b/test/rubygems/test_gem_commands_query_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/query_command'
module TestGemCommandsQueryCommandSetup
@@ -213,7 +213,7 @@ pl (1)
def test_execute_installed
@cmd.handle_options %w[-n a --installed]
- assert_raises Gem::MockGemUi::SystemExitException do
+ assert_raise Gem::MockGemUi::SystemExitException do
use_ui @stub_ui do
@cmd.execute
end
@@ -226,7 +226,7 @@ pl (1)
def test_execute_installed_inverse
@cmd.handle_options %w[-n a --no-installed]
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
use_ui @stub_ui do
@cmd.execute
end
@@ -241,7 +241,7 @@ pl (1)
def test_execute_installed_inverse_not_installed
@cmd.handle_options %w[-n not_installed --no-installed]
- assert_raises Gem::MockGemUi::SystemExitException do
+ assert_raise Gem::MockGemUi::SystemExitException do
use_ui @stub_ui do
@cmd.execute
end
@@ -254,7 +254,7 @@ pl (1)
def test_execute_installed_no_name
@cmd.handle_options %w[--installed]
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
use_ui @stub_ui do
@cmd.execute
end
@@ -269,7 +269,7 @@ pl (1)
def test_execute_installed_not_installed
@cmd.handle_options %w[-n not_installed --installed]
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
use_ui @stub_ui do
@cmd.execute
end
@@ -284,7 +284,7 @@ pl (1)
def test_execute_installed_version
@cmd.handle_options %w[-n a --installed --version 2]
- assert_raises Gem::MockGemUi::SystemExitException do
+ assert_raise Gem::MockGemUi::SystemExitException do
use_ui @stub_ui do
@cmd.execute
end
@@ -297,7 +297,7 @@ pl (1)
def test_execute_installed_version_not_installed
@cmd.handle_options %w[-n c --installed --version 2]
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
use_ui @stub_ui do
@cmd.execute
end
diff --git a/test/rubygems/test_gem_commands_search_command.rb b/test/rubygems/test_gem_commands_search_command.rb
index 17693e6837..6397dbd4d4 100644
--- a/test/rubygems/test_gem_commands_search_command.rb
+++ b/test/rubygems/test_gem_commands_search_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/search_command'
class TestGemCommandsSearchCommand < Gem::TestCase
diff --git a/test/rubygems/test_gem_commands_server_command.rb b/test/rubygems/test_gem_commands_server_command.rb
index 89bdce05cd..d5cd4d13bd 100644
--- a/test/rubygems/test_gem_commands_server_command.rb
+++ b/test/rubygems/test_gem_commands_server_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/server_command'
class TestGemCommandsServerCommand < Gem::TestCase
@@ -40,18 +40,18 @@ class TestGemCommandsServerCommand < Gem::TestCase
begin
@cmd.send :handle_options, %w[-p discard]
assert_equal 9, @cmd.options[:port]
- rescue OptionParser::InvalidArgument
+ rescue Gem::OptionParser::InvalidArgument
# for container environment on GitHub Actions
end
- e = assert_raises OptionParser::InvalidArgument do
+ e = assert_raise Gem::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
+ e = assert_raise Gem::OptionParser::InvalidArgument do
@cmd.send :handle_options, %w[-p 65536]
end
diff --git a/test/rubygems/test_gem_commands_setup_command.rb b/test/rubygems/test_gem_commands_setup_command.rb
index 29850c9074..5cf94a1dc9 100644
--- a/test/rubygems/test_gem_commands_setup_command.rb
+++ b/test/rubygems/test_gem_commands_setup_command.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/setup_command'
class TestGemCommandsSetupCommand < Gem::TestCase
@@ -14,14 +14,12 @@ class TestGemCommandsSetupCommand < Gem::TestCase
def setup
super
- @install_dir = File.join @tempdir, 'install'
@cmd = Gem::Commands::SetupCommand.new
- @cmd.options[:prefix] = @install_dir
filelist = %w[
bin/gem
lib/rubygems.rb
- lib/rubygems/test_case.rb
+ lib/rubygems/requirement.rb
lib/rubygems/ssl_certs/rubygems.org/foo.pem
bundler/exe/bundle
bundler/exe/bundler
@@ -48,7 +46,7 @@ class TestGemCommandsSetupCommand < Gem::TestCase
io.puts gemspec.to_ruby
end
- open(File.join(Gem.default_specifications_dir, "bundler-1.15.4.gemspec"), 'w') do |io|
+ File.open(File.join(Gem.default_specifications_dir, "bundler-1.15.4.gemspec"), 'w') do |io|
gemspec.version = "1.15.4"
io.puts gemspec.to_ruby
end
@@ -158,8 +156,23 @@ class TestGemCommandsSetupCommand < Gem::TestCase
assert_match %r{\A#!\s*#{bin_env}#{ruby_exec}}, File.read(gem_bin_path)
end
+ def test_destdir_flag_does_not_try_to_write_to_the_default_gem_home
+ FileUtils.chmod "-w", File.join(@gemhome, "plugins")
+
+ destdir = File.join(@tempdir, 'foo')
+
+ @cmd.options[:destdir] = destdir
+ @cmd.execute
+
+ spec = Gem::Specification.load("bundler/bundler.gemspec")
+
+ spec.executables.each do |e|
+ assert_path_exist File.join destdir, @gemhome.gsub(/^[a-zA-Z]:/, ''), 'gems', spec.full_name, spec.bindir, e
+ end
+ end
+
def test_files_in
- assert_equal %w[rubygems.rb rubygems/ssl_certs/rubygems.org/foo.pem rubygems/test_case.rb],
+ assert_equal %w[rubygems.rb rubygems/requirement.rb rubygems/ssl_certs/rubygems.org/foo.pem],
@cmd.files_in('lib').sort
end
@@ -169,14 +182,14 @@ class TestGemCommandsSetupCommand < Gem::TestCase
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/rubygems.org/foo.pem')
+ assert_path_exist File.join(dir, 'rubygems.rb')
+ assert_path_exist File.join(dir, 'rubygems/ssl_certs/rubygems.org/foo.pem')
- assert_path_exists File.join(dir, 'bundler.rb')
- assert_path_exists File.join(dir, 'bundler/b.rb')
+ assert_path_exist File.join(dir, 'bundler.rb')
+ assert_path_exist File.join(dir, 'bundler/b.rb')
- assert_path_exists File.join(dir, 'bundler/templates/.circleci/config.yml') unless RUBY_ENGINE == "truffleruby" # https://github.com/oracle/truffleruby/issues/2116
- assert_path_exists File.join(dir, 'bundler/templates/.travis.yml')
+ assert_path_exist File.join(dir, 'bundler/templates/.circleci/config.yml')
+ assert_path_exist File.join(dir, 'bundler/templates/.travis.yml')
end
end
@@ -192,27 +205,27 @@ class TestGemCommandsSetupCommand < Gem::TestCase
spec.executables.each do |e|
if Gem.win_platform?
- assert_path_exists File.join(bin_dir, "#{e}.bat")
+ assert_path_exist File.join(bin_dir, "#{e}.bat")
end
- assert_path_exists File.join bin_dir, e
+ assert_path_exist File.join bin_dir, e
end
default_dir = Gem.default_specifications_dir
# expect to remove other versions of bundler gemspecs on default specification directory.
- refute_path_exists File.join(default_dir, "bundler-1.15.4.gemspec")
- assert_path_exists File.join(default_dir, "bundler-#{BUNDLER_VERS}.gemspec")
+ assert_path_not_exist File.join(default_dir, "bundler-1.15.4.gemspec")
+ assert_path_exist File.join(default_dir, "bundler-#{BUNDLER_VERS}.gemspec")
# expect to not remove bundler-* gemspecs.
- assert_path_exists File.join(Gem.dir, "specifications", "bundler-audit-1.0.0.gemspec")
+ assert_path_exist File.join(Gem.dir, "specifications", "bundler-audit-1.0.0.gemspec")
# expect to remove normal gem that was same version. because it's promoted default gems.
- refute_path_exists File.join(Gem.dir, "specifications", "bundler-#{BUNDLER_VERS}.gemspec")
+ assert_path_not_exist File.join(Gem.dir, "specifications", "bundler-#{BUNDLER_VERS}.gemspec")
- assert_path_exists "#{Gem.dir}/gems/bundler-#{BUNDLER_VERS}"
- assert_path_exists "#{Gem.dir}/gems/bundler-1.15.4"
- assert_path_exists "#{Gem.dir}/gems/bundler-audit-1.0.0"
+ assert_path_exist "#{Gem.dir}/gems/bundler-#{BUNDLER_VERS}"
+ assert_path_exist "#{Gem.dir}/gems/bundler-1.15.4"
+ assert_path_exist "#{Gem.dir}/gems/bundler-audit-1.0.0"
end
def test_install_default_bundler_gem_with_force_flag
@@ -227,27 +240,62 @@ class TestGemCommandsSetupCommand < Gem::TestCase
f.puts 'echo "hello"'
end
- bindir(bin_dir) do
- @cmd.options[:force] = true
-
- @cmd.install_default_bundler_gem bin_dir
+ @cmd.options[:force] = true
- bundler_spec = Gem::Specification.load("bundler/bundler.gemspec")
- default_spec_path = File.join(Gem.default_specifications_dir, "#{bundler_spec.full_name}.gemspec")
- spec = Gem::Specification.load(default_spec_path)
+ @cmd.install_default_bundler_gem bin_dir
- spec.executables.each do |e|
- if Gem.win_platform?
- assert_path_exists File.join(bin_dir, "#{e}.bat")
- end
+ bundler_spec = Gem::Specification.load("bundler/bundler.gemspec")
+ default_spec_path = File.join(Gem.default_specifications_dir, "#{bundler_spec.full_name}.gemspec")
+ spec = Gem::Specification.load(default_spec_path)
- assert_path_exists File.join bin_dir, e
+ spec.executables.each do |e|
+ if Gem.win_platform?
+ assert_path_exist File.join(bin_dir, "#{e}.bat")
end
+
+ assert_path_exist File.join bin_dir, e
+ end
+ end
+
+ def test_install_default_bundler_gem_with_destdir_flag
+ @cmd.extend FileUtils
+
+ FileUtils.chmod "-w", @gemhome
+
+ destdir = File.join(@tempdir, 'foo')
+ bin_dir = File.join(destdir, 'bin')
+
+ @cmd.options[:destdir] = destdir
+
+ @cmd.install_default_bundler_gem bin_dir
+
+ spec = Gem::Specification.load("bundler/bundler.gemspec")
+
+ spec.executables.each do |e|
+ assert_path_exist File.join destdir, @gemhome.gsub(/^[a-zA-Z]:/, ''), 'gems', spec.full_name, spec.bindir, e
+ end
+ end
+
+ def test_install_default_bundler_gem_with_destdir_and_prefix_flags
+ @cmd.extend FileUtils
+
+ destdir = File.join(@tempdir, 'foo')
+ bin_dir = File.join(destdir, 'bin')
+
+ @cmd.options[:destdir] = destdir
+ @cmd.options[:prefix] = "/"
+
+ @cmd.install_default_bundler_gem bin_dir
+
+ spec = Gem::Specification.load("bundler/bundler.gemspec")
+
+ spec.executables.each do |e|
+ assert_path_exist File.join destdir, 'gems', spec.full_name, spec.bindir, e
end
end
def test_remove_old_lib_files
- lib = File.join @install_dir, 'lib'
+ lib = RbConfig::CONFIG["sitelibdir"]
lib_rubygems = File.join lib, 'rubygems'
lib_bundler = File.join lib, 'bundler'
lib_rubygems_defaults = File.join lib_rubygems, 'defaults'
@@ -272,13 +320,13 @@ class TestGemCommandsSetupCommand < Gem::TestCase
@cmd.remove_old_lib_files lib
- files_that_go.each {|file| refute_path_exists(file) unless file == old_bundler_ci && RUBY_ENGINE == "truffleruby" } # https://github.com/oracle/truffleruby/issues/2116
+ files_that_go.each {|file| assert_path_not_exist(file) unless file == old_bundler_ci }
- files_that_stay.each {|file| assert_path_exists file }
+ files_that_stay.each {|file| assert_path_exist file }
end
def test_remove_old_man_files
- man = File.join @install_dir, 'man'
+ man = File.join RbConfig::CONFIG['mandir'], 'man'
ruby_1 = File.join man, 'man1', 'ruby.1'
bundle_b_1 = File.join man, 'man1', 'bundle-b.1'
@@ -295,9 +343,9 @@ class TestGemCommandsSetupCommand < Gem::TestCase
@cmd.remove_old_man_files man
- files_that_go.each {|file| refute_path_exists file }
+ files_that_go.each {|file| assert_path_not_exist file }
- files_that_stay.each {|file| assert_path_exists file }
+ files_that_stay.each {|file| assert_path_exist file }
end
def test_show_release_notes
@@ -384,14 +432,14 @@ class TestGemCommandsSetupCommand < Gem::TestCase
end
def default_gem_bin_path
- File.join @install_dir, 'bin', 'gem'
+ File.join RbConfig::CONFIG['bindir'], 'gem'
end
def default_bundle_bin_path
- File.join @install_dir, 'bin', 'bundle'
+ File.join RbConfig::CONFIG['bindir'], 'bundle'
end
def default_bundler_bin_path
- File.join @install_dir, 'bin', 'bundler'
+ File.join RbConfig::CONFIG['bindir'], 'bundler'
end
end unless Gem.java_platform?
diff --git a/test/rubygems/test_gem_commands_signin_command.rb b/test/rubygems/test_gem_commands_signin_command.rb
index f8262466b1..0f856a53ba 100644
--- a/test/rubygems/test_gem_commands_signin_command.rb
+++ b/test/rubygems/test_gem_commands_signin_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/signin_command'
require 'rubygems/installer'
@@ -26,14 +26,21 @@ class TestGemCommandsSigninCommand < Gem::TestCase
assert_match %r{Signed in.}, sign_in_ui.output
end
+ def test_execute_when_not_already_signed_in_and_not_preexisting_credentials_folder
+ FileUtils.rm Gem.configuration.credentials_path
+
+ sign_in_ui = util_capture { @cmd.execute }
+ assert_match %r{Signed in.}, sign_in_ui.output
+ end
+
def test_execute_when_already_signed_in_with_same_host
host = 'http://some-gemcutter-compatible-host.org'
util_capture(nil, host) { @cmd.execute }
- old_credentials = YAML.load_file Gem.configuration.credentials_path
+ old_credentials = load_yaml_file Gem.configuration.credentials_path
util_capture(nil, host) { @cmd.execute }
- new_credentials = YAML.load_file Gem.configuration.credentials_path
+ new_credentials = load_yaml_file Gem.configuration.credentials_path
assert_equal old_credentials[host], new_credentials[host]
end
@@ -45,7 +52,7 @@ class TestGemCommandsSigninCommand < Gem::TestCase
host = 'http://some-gemcutter-compatible-host.org'
util_capture(nil, host, api_key) { @cmd.execute }
- credentials = YAML.load_file Gem.configuration.credentials_path
+ credentials = load_yaml_file Gem.configuration.credentials_path
assert_equal credentials[:rubygems_api_key], api_key
@@ -60,7 +67,7 @@ class TestGemCommandsSigninCommand < Gem::TestCase
assert_match %r{Signed in.}, sign_in_ui.output
api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
- credentials = YAML.load_file Gem.configuration.credentials_path
+ credentials = load_yaml_file Gem.configuration.credentials_path
assert_equal api_key, credentials[host]
end
@@ -68,12 +75,12 @@ class TestGemCommandsSigninCommand < Gem::TestCase
util_capture { @cmd.execute }
api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
- credentials = YAML.load_file Gem.configuration.credentials_path
+ credentials = load_yaml_file Gem.configuration.credentials_path
assert_equal api_key, credentials[:rubygems_api_key]
end
- def test_excute_with_key_name_and_scope
+ def test_execute_with_key_name_and_scope
email = 'you@example.com'
password = 'secret'
api_key = '1234'
@@ -94,7 +101,7 @@ class TestGemCommandsSigninCommand < Gem::TestCase
assert_match "show_dashboard [y/N]", key_name_ui.output
assert_equal "name=test-key&push_rubygem=true", fetcher.last_request.body
- credentials = YAML.load_file Gem.configuration.credentials_path
+ credentials = load_yaml_file Gem.configuration.credentials_path
assert_equal api_key, credentials[:rubygems_api_key]
end
diff --git a/test/rubygems/test_gem_commands_signout_command.rb b/test/rubygems/test_gem_commands_signout_command.rb
index 20389d0537..aa6300b6ab 100644
--- a/test/rubygems/test_gem_commands_signout_command.rb
+++ b/test/rubygems/test_gem_commands_signout_command.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/signout_command'
require 'rubygems/installer'
diff --git a/test/rubygems/test_gem_commands_sources_command.rb b/test/rubygems/test_gem_commands_sources_command.rb
index 59acfb1ed6..7bca0f3803 100644
--- a/test/rubygems/test_gem_commands_sources_command.rb
+++ b/test/rubygems/test_gem_commands_sources_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/sources_command'
class TestGemCommandsSourcesCommand < Gem::TestCase
@@ -162,7 +162,7 @@ class TestGemCommandsSourcesCommand < Gem::TestCase
use_ui ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -188,7 +188,7 @@ class TestGemCommandsSourcesCommand < Gem::TestCase
@cmd.handle_options %w[--add http://beta-gems.example.com]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -299,7 +299,7 @@ source http://gems.example.com/ already present in the cache
ui = Gem::MockGemUi.new "n"
use_ui ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -367,7 +367,7 @@ source http://gems.example.com/ already present in the cache
ui = Gem::MockGemUi.new "n"
use_ui ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -385,7 +385,7 @@ source http://gems.example.com/ already present in the cache
@cmd.handle_options %w[--add beta-gems.example.com]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
diff --git a/test/rubygems/test_gem_commands_specification_command.rb b/test/rubygems/test_gem_commands_specification_command.rb
index 732278eb6f..c8cb7df32e 100644
--- a/test/rubygems/test_gem_commands_specification_command.rb
+++ b/test/rubygems/test_gem_commands_specification_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/specification_command'
class TestGemCommandsSpecificationCommand < Gem::TestCase
@@ -51,7 +51,7 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
@cmd.options[:all] = true
@cmd.options[:version] = "1"
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
@@ -64,7 +64,7 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
def test_execute_bad_name
@cmd.options[:args] = %w[foo]
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
@@ -78,7 +78,7 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
@cmd.options[:args] = %w[foo]
@cmd.options[:version] = "1.3.2"
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
@@ -114,7 +114,7 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
@cmd.execute
end
- assert_equal "foo", YAML.load(@ui.output)
+ assert_equal "foo", load_yaml(@ui.output)
end
def test_execute_file
@@ -230,7 +230,7 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
assert_match %r{\A--- !ruby/object:Gem::Specification}, @ui.output
assert_match %r{name: foo}, @ui.output
- spec = YAML.load @ui.output
+ spec = load_yaml @ui.output
assert_equal Gem::Version.new("2.0.0"), spec.version
end
@@ -252,7 +252,7 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
assert_match %r{\A--- !ruby/object:Gem::Specification}, @ui.output
assert_match %r{name: foo}, @ui.output
- spec = YAML.load @ui.output
+ spec = load_yaml @ui.output
assert_equal Gem::Version.new("2.0.1.pre"), spec.version
end
diff --git a/test/rubygems/test_gem_commands_stale_command.rb b/test/rubygems/test_gem_commands_stale_command.rb
index 0aa7f243e2..83bd3e5def 100644
--- a/test/rubygems/test_gem_commands_stale_command.rb
+++ b/test/rubygems/test_gem_commands_stale_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/stale_command'
class TestGemCommandsStaleCommand < Gem::TestCase
diff --git a/test/rubygems/test_gem_commands_uninstall_command.rb b/test/rubygems/test_gem_commands_uninstall_command.rb
index 03ce600cc4..5bd2c40d59 100644
--- a/test/rubygems/test_gem_commands_uninstall_command.rb
+++ b/test/rubygems/test_gem_commands_uninstall_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/installer_test_case'
+require_relative 'installer_test_case'
require 'rubygems/commands/uninstall_command'
class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
@@ -377,7 +377,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
@cmd.options[:args] = ['a:4']
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
use_ui ui do
@cmd.execute
end
@@ -420,7 +420,7 @@ WARNING: Use your OS package manager to uninstall vendor gems
@cmd.options[:version] = Gem::Requirement.new("> 1")
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
@@ -436,7 +436,7 @@ WARNING: Use your OS package manager to uninstall vendor gems
def test_handle_options_vendor_missing
vendordir(nil) do
- e = assert_raises OptionParser::InvalidOption do
+ e = assert_raise Gem::OptionParser::InvalidOption do
@cmd.handle_options %w[--vendor]
end
@@ -477,7 +477,7 @@ WARNING: Use your OS package manager to uninstall vendor gems
e = nil
@cmd.stub :uninstall, uninstall_exception do
use_ui @ui do
- e = assert_raises Gem::MockGemUi::TermError do
+ e = assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
diff --git a/test/rubygems/test_gem_commands_unpack_command.rb b/test/rubygems/test_gem_commands_unpack_command.rb
index e1fea0f0ff..55369f1eeb 100644
--- a/test/rubygems/test_gem_commands_unpack_command.rb
+++ b/test/rubygems/test_gem_commands_unpack_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/unpack_command'
class TestGemCommandsUnpackCommand < Gem::TestCase
@@ -151,7 +151,7 @@ class TestGemCommandsUnpackCommand < Gem::TestCase
end
def test_execute_sudo
- skip 'Cannot perform this test on windows (chmod)' if win_platform?
+ pend 'Cannot perform this test on windows (chmod)' if win_platform?
util_make_gems
@@ -210,7 +210,7 @@ class TestGemCommandsUnpackCommand < Gem::TestCase
end
end
- assert_path_exists File.join(@tempdir, foo_spec.full_name)
+ assert_path_exist File.join(@tempdir, foo_spec.full_name)
end
def test_handle_options_metadata
diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb
index 749e9bee20..a7ddddf2c1 100644
--- a/test/rubygems/test_gem_commands_update_command.rb
+++ b/test/rubygems/test_gem_commands_update_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/update_command'
class TestGemCommandsUpdateCommand < Gem::TestCase
@@ -95,7 +95,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
@cmd.options[:args] = []
@cmd.options[:system] = true
- assert_raises Gem::MockGemUi::SystemExitException do
+ assert_raise Gem::MockGemUi::SystemExitException do
use_ui @ui do
@cmd.execute
end
@@ -168,14 +168,23 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
@cmd.options[:args] = []
@cmd.options[:system] = "2.5.1"
- assert_raises Gem::MockGemUi::TermError do
+ oldest_version_mod = Module.new do
+ def oldest_supported_version
+ Gem::Version.new("2.5.2")
+ end
+ private :oldest_supported_version
+ end
+
+ @cmd.extend(oldest_version_mod)
+
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
end
assert_empty @ui.output
- assert_equal "ERROR: rubygems 2.5.1 is not supported. The oldest supported version is 2.5.2\n", @ui.error
+ assert_equal "ERROR: rubygems 2.5.1 is not supported on #{RUBY_VERSION}. The oldest version supported by this ruby is 2.5.2\n", @ui.error
end
def test_execute_system_specific_older_than_3_2_removes_plugins_dir
@@ -185,6 +194,15 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
end
+ oldest_version_mod = Module.new do
+ def oldest_supported_version
+ Gem::Version.new("2.5.2")
+ end
+ private :oldest_supported_version
+ end
+
+ @cmd.extend(oldest_version_mod)
+
@cmd.options[:args] = []
@cmd.options[:system] = "3.1"
@@ -193,7 +211,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
@cmd.execute
- refute_path_exists Gem.plugindir, "Plugins folder not removed when updating rubygems to pre-3.2"
+ assert_path_not_exist Gem.plugindir, "Plugins folder not removed when updating rubygems to pre-3.2"
end
def test_execute_system_specific_newer_than_or_equal_to_3_2_leaves_plugins_dir_alone
@@ -203,6 +221,15 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
end
+ oldest_version_mod = Module.new do
+ def oldest_supported_version
+ Gem::Version.new("2.5.2")
+ end
+ private :oldest_supported_version
+ end
+
+ @cmd.extend(oldest_version_mod)
+
@cmd.options[:args] = []
@cmd.options[:system] = "3.2.a"
@@ -212,8 +239,8 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
@cmd.execute
- assert_path_exists Gem.plugindir, "Plugin folder removed when updating rubygems to post-3.2"
- assert_path_exists plugin_file, "Plugin removed when updating rubygems to post-3.2"
+ assert_path_exist Gem.plugindir, "Plugin folder removed when updating rubygems to post-3.2"
+ assert_path_exist plugin_file, "Plugin removed when updating rubygems to post-3.2"
end
def test_execute_system_specifically_to_latest_version
@@ -246,7 +273,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
@cmd.options[:args] = %w[gem]
@cmd.options[:system] = true
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
@@ -264,7 +291,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
@cmd.options[:args] = []
@cmd.options[:system] = true
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
@@ -357,7 +384,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
a2 = @specs['a-2']
- assert_path_exists File.join(a2.doc_dir, 'rdoc')
+ assert_path_exist File.join(a2.doc_dir, 'rdoc')
end
def test_execute_named
@@ -503,7 +530,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
def test_fetch_remote_gems_error
Gem.sources.replace %w[http://nonexistent.example]
- assert_raises Gem::RemoteFetcher::FetchError do
+ assert_raise Gem::RemoteFetcher::FetchError do
@cmd.fetch_remote_gems @specs['a-1']
end
end
@@ -561,7 +588,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
def test_handle_options_system_non_version
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
@cmd.handle_options %w[--system non-version]
end
end
diff --git a/test/rubygems/test_gem_commands_which_command.rb b/test/rubygems/test_gem_commands_which_command.rb
index b67bf040d4..a398dc5708 100644
--- a/test/rubygems/test_gem_commands_which_command.rb
+++ b/test/rubygems/test_gem_commands_which_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/which_command'
class TestGemCommandsWhichCommand < Gem::TestCase
@@ -26,7 +26,7 @@ class TestGemCommandsWhichCommand < Gem::TestCase
@cmd.handle_options %w[directory]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -44,7 +44,7 @@ class TestGemCommandsWhichCommand < Gem::TestCase
@cmd.handle_options %w[foo_bar missinglib]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
@@ -58,7 +58,7 @@ class TestGemCommandsWhichCommand < Gem::TestCase
@cmd.handle_options %w[missinglib]
use_ui @ui do
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.execute
end
end
diff --git a/test/rubygems/test_gem_commands_yank_command.rb b/test/rubygems/test_gem_commands_yank_command.rb
index 3046655aa8..b798eb3689 100644
--- a/test/rubygems/test_gem_commands_yank_command.rb
+++ b/test/rubygems/test_gem_commands_yank_command.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/commands/yank_command'
class TestGemCommandsYankCommand < Gem::TestCase
@@ -35,7 +35,7 @@ class TestGemCommandsYankCommand < Gem::TestCase
def test_handle_options_missing_argument
%w[-v --version -p --platform].each do |option|
- assert_raises OptionParser::MissingArgument do
+ assert_raise Gem::OptionParser::MissingArgument do
@cmd.handle_options %W[a #{option}]
end
end
diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb
index ddc35a9594..32375e6936 100644
--- a/test/rubygems/test_gem_config_file.rb
+++ b/test/rubygems/test_gem_config_file.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/config_file'
class TestGemConfigFile < Gem::TestCase
@@ -41,6 +41,7 @@ class TestGemConfigFile < Gem::TestCase
assert_equal true, @cfg.verbose
assert_equal [@gem_repo], Gem.sources
assert_equal 365, @cfg.cert_expiration_length_days
+ assert_equal false, @cfg.ipv4_fallback_enabled
File.open @temp_conf, 'w' do |fp|
fp.puts ":backtrace: true"
@@ -56,6 +57,7 @@ class TestGemConfigFile < Gem::TestCase
fp.puts ":ssl_verify_mode: 0"
fp.puts ":ssl_ca_cert: /etc/ssl/certs"
fp.puts ":cert_expiration_length_days: 28"
+ fp.puts ":ipv4_fallback_enabled: true"
end
util_config_file
@@ -70,6 +72,14 @@ class TestGemConfigFile < Gem::TestCase
assert_equal 0, @cfg.ssl_verify_mode
assert_equal '/etc/ssl/certs', @cfg.ssl_ca_cert
assert_equal 28, @cfg.cert_expiration_length_days
+ assert_equal true, @cfg.ipv4_fallback_enabled
+ end
+
+ def test_initialize_ipv4_fallback_enabled_env
+ ENV['IPV4_FALLBACK_ENABLED'] = 'true'
+ util_config_file %W[--config-file #{@temp_conf}]
+
+ assert_equal true, @cfg.ipv4_fallback_enabled
end
def test_initialize_handle_arguments_config_file
@@ -185,14 +195,14 @@ class TestGemConfigFile < Gem::TestCase
end
def test_check_credentials_permissions
- skip 'chmod not supported' if win_platform?
+ pend '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
+ assert_raise Gem::MockGemUi::TermError do
@cfg.load_api_keys
end
end
@@ -243,7 +253,7 @@ if you believe they were disclosed to a third party.
args = %w[--debug]
- _, err = capture_io do
+ _, err = capture_output do
@cfg.handle_arguments args
end
@@ -312,13 +322,13 @@ if you believe they were disclosed to a third party.
end
def test_load_api_keys_bad_permission
- skip 'chmod not supported' if win_platform?
+ pend 'chmod not supported' if win_platform?
@cfg.rubygems_api_key = 'x'
File.chmod 0644, @cfg.credentials_path
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cfg.load_api_keys
end
end
@@ -344,7 +354,7 @@ if you believe they were disclosed to a third party.
:rubygems_api_key => 'x',
}
- assert_equal expected, YAML.load_file(@cfg.credentials_path)
+ assert_equal expected, load_yaml_file(@cfg.credentials_path)
unless win_platform?
stat = File.stat @cfg.credentials_path
@@ -354,13 +364,13 @@ if you believe they were disclosed to a third party.
end
def test_rubygems_api_key_equals_bad_permission
- skip 'chmod not supported' if win_platform?
+ pend 'chmod not supported' if win_platform?
@cfg.rubygems_api_key = 'x'
File.chmod 0644, @cfg.credentials_path
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cfg.rubygems_api_key = 'y'
end
@@ -368,7 +378,7 @@ if you believe they were disclosed to a third party.
:rubygems_api_key => 'x',
}
- assert_equal expected, YAML.load_file(@cfg.credentials_path)
+ assert_equal expected, load_yaml_file(@cfg.credentials_path)
stat = File.stat @cfg.credentials_path
diff --git a/test/rubygems/test_gem_dependency.rb b/test/rubygems/test_gem_dependency.rb
index 7ddeafedce..1ca0fc378c 100644
--- a/test/rubygems/test_gem_dependency.rb
+++ b/test/rubygems/test_gem_dependency.rb
@@ -1,8 +1,14 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/dependency'
class TestGemDependency < Gem::TestCase
+ def setup
+ super
+
+ without_any_upwards_gemfiles
+ end
+
def test_initialize
d = dep "pkg", "> 1.0"
@@ -11,7 +17,7 @@ class TestGemDependency < Gem::TestCase
end
def test_initialize_type_bad
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
Gem::Dependency.new 'monkey' => '1.0'
end
@@ -43,7 +49,7 @@ class TestGemDependency < Gem::TestCase
assert_equal :runtime, dep("pkg").type
assert_equal :development, dep("pkg", [], :development).type
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
dep "pkg", :sometimes
end
end
@@ -248,7 +254,7 @@ class TestGemDependency < Gem::TestCase
a = dep 'a'
b = dep 'b'
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
a.merge b
end
@@ -330,7 +336,7 @@ class TestGemDependency < Gem::TestCase
dep = Gem::Dependency.new "a", "= 2.0"
- e = assert_raises Gem::MissingSpecVersionError do
+ e = assert_raise Gem::MissingSpecVersionError do
dep.to_specs
end
@@ -353,7 +359,7 @@ class TestGemDependency < Gem::TestCase
assert_equal [b, b_1], dep.to_specs
Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["3.5", "reason"]) do
- e = assert_raises Gem::MissingSpecVersionError do
+ e = assert_raise Gem::MissingSpecVersionError do
dep.to_specs
end
@@ -377,7 +383,7 @@ class TestGemDependency < Gem::TestCase
dep = Gem::Dependency.new "b", "= 2.0"
- e = assert_raises Gem::MissingSpecError do
+ e = assert_raise Gem::MissingSpecError do
dep.to_specs
end
diff --git a/test/rubygems/test_gem_dependency_installer.rb b/test/rubygems/test_gem_dependency_installer.rb
index c62a3f355a..9cbdcefea4 100644
--- a/test/rubygems/test_gem_dependency_installer.rb
+++ b/test/rubygems/test_gem_dependency_installer.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/dependency_installer'
require 'rubygems/security'
@@ -113,7 +113,7 @@ class TestGemDependencyInstaller < Gem::TestCase
dep = Gem::Dependency.new "p"
inst = Gem::DependencyInstaller.new
- assert_raises Gem::UnsatisfiableDependencyError do
+ assert_raise Gem::UnsatisfiableDependencyError do
inst.install dep
end
@@ -392,7 +392,7 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal %w[f-1], inst.installed_gems.map {|s| s.full_name }
- assert_path_exists e1.extension_dir
+ assert_path_exist e1.extension_dir
end
def test_install_dependency_old
@@ -712,7 +712,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- e = assert_raises Gem::UnsatisfiableDependencyError do
+ e = assert_raise Gem::UnsatisfiableDependencyError do
inst = Gem::DependencyInstaller.new :domain => :local
inst.install 'b'
end
@@ -883,7 +883,7 @@ class TestGemDependencyInstaller < Gem::TestCase
policy = Gem::Security::HighSecurity
inst = Gem::DependencyInstaller.new :security_policy => policy
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
inst.install 'b'
end
diff --git a/test/rubygems/test_gem_dependency_list.rb b/test/rubygems/test_gem_dependency_list.rb
index 097e680596..15c50de199 100644
--- a/test/rubygems/test_gem_dependency_list.rb
+++ b/test/rubygems/test_gem_dependency_list.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/dependency_list'
class TestGemDependencyList < Gem::TestCase
diff --git a/test/rubygems/test_gem_dependency_resolution_error.rb b/test/rubygems/test_gem_dependency_resolution_error.rb
index 5321f7031c..0e4a2fe31a 100644
--- a/test/rubygems/test_gem_dependency_resolution_error.rb
+++ b/test/rubygems/test_gem_dependency_resolution_error.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemDependencyResolutionError < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_doctor.rb b/test/rubygems/test_gem_doctor.rb
index 1cef52234e..583c735dd3 100644
--- a/test/rubygems/test_gem_doctor.rb
+++ b/test/rubygems/test_gem_doctor.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/doctor'
class TestGemDoctor < Gem::TestCase
@@ -27,31 +27,31 @@ class TestGemDoctor < Gem::TestCase
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_exist File.join(a.gem_dir, 'Rakefile')
+ assert_path_exist File.join(a.gem_dir, 'lib', 'a.rb')
- assert_path_exists b.gem_dir
- refute_path_exists b.spec_file
+ assert_path_exist b.gem_dir
+ assert_path_not_exist b.spec_file
- assert_path_exists c.gem_dir
- assert_path_exists c.spec_file
+ assert_path_exist c.gem_dir
+ assert_path_exist c.spec_file
doctor = Gem::Doctor.new @gemhome
- capture_io do
+ capture_output 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_exist File.join(a.gem_dir, 'Rakefile')
+ assert_path_exist File.join(a.gem_dir, 'lib', 'a.rb')
- refute_path_exists b.gem_dir
- refute_path_exists b.spec_file
+ assert_path_not_exist b.gem_dir
+ assert_path_not_exist b.spec_file
- refute_path_exists c.gem_dir
- refute_path_exists c.spec_file
+ assert_path_not_exist c.gem_dir
+ assert_path_not_exist c.spec_file
expected = <<-OUTPUT
Checking #{@gemhome}
@@ -80,31 +80,31 @@ Removed directory gems/c-2
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_exist File.join(a.gem_dir, 'Rakefile')
+ assert_path_exist File.join(a.gem_dir, 'lib', 'a.rb')
- assert_path_exists b.gem_dir
- refute_path_exists b.spec_file
+ assert_path_exist b.gem_dir
+ assert_path_not_exist b.spec_file
- assert_path_exists c.gem_dir
- assert_path_exists c.spec_file
+ assert_path_exist c.gem_dir
+ assert_path_exist c.spec_file
doctor = Gem::Doctor.new @gemhome, true
- capture_io do
+ capture_output 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_exist File.join(a.gem_dir, 'Rakefile')
+ assert_path_exist File.join(a.gem_dir, 'lib', 'a.rb')
- assert_path_exists b.gem_dir
- refute_path_exists b.spec_file
+ assert_path_exist b.gem_dir
+ assert_path_not_exist b.spec_file
- assert_path_exists c.gem_dir
- assert_path_exists c.spec_file
+ assert_path_exist c.gem_dir
+ assert_path_exist c.spec_file
expected = <<-OUTPUT
Checking #{@gemhome}
@@ -127,13 +127,13 @@ Extra directory gems/c-2
doctor = Gem::Doctor.new @tempdir
- capture_io do
+ capture_output do
use_ui @ui do
doctor.doctor
end
end
- assert_path_exists other_dir
+ assert_path_exist other_dir
expected = <<-OUTPUT
Checking #{@tempdir}
@@ -163,13 +163,13 @@ This directory does not appear to be a RubyGems repository, skipping
doctor = Gem::Doctor.new @gemhome
- capture_io do
+ capture_output do
use_ui @ui do
doctor.doctor
end
end
- # refute_path_exists bad_plugin
+ # assert_path_not_exist bad_plugin
expected = <<-OUTPUT
Checking #{@gemhome}
diff --git a/test/rubygems/test_gem_ext_builder.rb b/test/rubygems/test_gem_ext_builder.rb
index 6bebfa7a03..165194510e 100644
--- a/test/rubygems/test_gem_ext_builder.rb
+++ b/test/rubygems/test_gem_ext_builder.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/ext'
require 'rubygems/installer'
@@ -106,6 +106,7 @@ install:
end
def test_build_extensions
+ pend if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning
@spec.extensions << 'ext/extconf.rb'
ext_dir = File.join @spec.gem_dir, 'ext'
@@ -132,15 +133,16 @@ install:
@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'
+ assert_path_exist @spec.extension_dir
+ assert_path_exist @spec.gem_build_complete_path
+ assert_path_exist File.join @spec.extension_dir, 'gem_make.out'
+ assert_path_exist File.join @spec.extension_dir, 'a.rb'
+ assert_path_exist File.join @spec.gem_dir, 'lib', 'a.rb'
+ assert_path_exist File.join @spec.gem_dir, 'lib', 'a', 'b.rb'
end
def test_build_extensions_with_gemhome_with_space
+ pend if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning
new_gemhome = File.join @tempdir, 'gem home'
File.rename(@gemhome, new_gemhome)
@gemhome = new_gemhome
@@ -161,6 +163,7 @@ install:
false
end
end
+ pend if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning
@spec.extensions << 'ext/extconf.rb'
@@ -188,12 +191,12 @@ install:
@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'
+ assert_path_exist @spec.extension_dir
+ assert_path_exist @spec.gem_build_complete_path
+ assert_path_exist File.join @spec.extension_dir, 'gem_make.out'
+ assert_path_exist File.join @spec.extension_dir, 'a.rb'
+ assert_path_not_exist File.join @spec.gem_dir, 'lib', 'a.rb'
+ assert_path_not_exist File.join @spec.gem_dir, 'lib', 'a', 'b.rb'
ensure
class << Gem
remove_method :install_extension_in_lib
@@ -210,7 +213,7 @@ install:
assert_equal '', @ui.output
assert_equal '', @ui.error
- refute_path_exists File.join @spec.extension_dir, 'gem_make.out'
+ assert_path_not_exist File.join @spec.extension_dir, 'gem_make.out'
end
def test_build_extensions_rebuild_failure
@@ -219,13 +222,13 @@ install:
@spec.extensions << nil
- assert_raises Gem::Ext::BuildError do
+ assert_raise Gem::Ext::BuildError do
use_ui @ui do
@builder.build_extensions
end
end
- refute_path_exists @spec.gem_build_complete_path
+ assert_path_not_exist @spec.gem_build_complete_path
end
def test_build_extensions_extconf_bad
@@ -235,7 +238,7 @@ install:
FileUtils.mkdir_p @spec.gem_dir
- e = assert_raises Gem::Ext::BuildError do
+ e = assert_raise Gem::Ext::BuildError do
use_ui @ui do
@builder.build_extensions
end
@@ -251,7 +254,7 @@ install:
assert_match %r{#{Regexp.escape Gem.ruby} .* extconf\.rb}, cmd_make_out
assert_match %r{: No such file}, cmd_make_out
- refute_path_exists @spec.gem_build_complete_path
+ assert_path_not_exist @spec.gem_build_complete_path
assert_equal cwd, Dir.pwd
end
@@ -261,7 +264,7 @@ install:
gem_make_out = File.join @spec.extension_dir, 'gem_make.out'
@spec.extensions << nil
- e = assert_raises Gem::Ext::BuildError do
+ e = assert_raise Gem::Ext::BuildError do
use_ui @ui do
@builder.build_extensions
end
@@ -273,7 +276,7 @@ install:
assert_equal "No builder for extension ''\n", File.read(gem_make_out)
- refute_path_exists @spec.gem_build_complete_path
+ assert_path_not_exist @spec.gem_build_complete_path
ensure
FileUtils.rm_f gem_make_out
end
@@ -308,7 +311,7 @@ install:
path = File.join @spec.gem_dir, "extconf_args"
assert_equal args.inspect, File.read(path).strip
- assert_path_exists @spec.extension_dir
+ assert_path_exist @spec.extension_dir
end
def test_initialize
diff --git a/test/rubygems/test_gem_ext_cmake_builder.rb b/test/rubygems/test_gem_ext_cmake_builder.rb
index dffe4a7fb3..5ab82c545f 100644
--- a/test/rubygems/test_gem_ext_cmake_builder.rb
+++ b/test/rubygems/test_gem_ext_cmake_builder.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/ext'
class TestGemExtCmakeBuilder < Gem::TestCase
@@ -7,13 +7,13 @@ class TestGemExtCmakeBuilder < Gem::TestCase
super
# Details: https://github.com/rubygems/rubygems/issues/1270#issuecomment-177368340
- skip "CmakeBuilder doesn't work on Windows." if Gem.win_platform?
+ pend "CmakeBuilder doesn't work on Windows." if Gem.win_platform?
begin
_, status = Open3.capture2e('cmake')
- skip 'cmake not present' unless status.success?
+ pend 'cmake not present' unless status.success?
rescue Errno::ENOENT
- skip 'cmake not present'
+ pend 'cmake not present'
end
@ext = File.join @tempdir, 'ext'
@@ -50,7 +50,7 @@ install (FILES test.txt DESTINATION bin)
def test_self_build_fail
output = []
- error = assert_raises Gem::InstallError do
+ error = assert_raise Gem::InstallError do
Gem::Ext::CmakeBuilder.build nil, @dest_path, output, [], nil, @ext
end
diff --git a/test/rubygems/test_gem_ext_configure_builder.rb b/test/rubygems/test_gem_ext_configure_builder.rb
index 6b11b0c2ab..76ccfe2dc4 100644
--- a/test/rubygems/test_gem_ext_configure_builder.rb
+++ b/test/rubygems/test_gem_ext_configure_builder.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/ext'
class TestGemExtConfigureBuilder < Gem::TestCase
@@ -17,7 +17,7 @@ class TestGemExtConfigureBuilder < Gem::TestCase
end
def test_self_build
- skip("test_self_build skipped on MS Windows (VC++)") if vc_windows?
+ pend("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"
@@ -42,10 +42,10 @@ class TestGemExtConfigureBuilder < Gem::TestCase
end
def test_self_build_fail
- skip("test_self_build_fail skipped on MS Windows (VC++)") if vc_windows?
+ pend("test_self_build_fail skipped on MS Windows (VC++)") if vc_windows?
output = []
- error = assert_raises Gem::InstallError do
+ error = assert_raise Gem::InstallError do
Gem::Ext::ConfigureBuilder.build nil, @dest_path, output, [], nil, @ext
end
@@ -62,7 +62,7 @@ class TestGemExtConfigureBuilder < Gem::TestCase
def test_self_build_has_makefile
if vc_windows? && !nmake_found?
- skip("test_self_build_has_makefile skipped - nmake not found")
+ pend("test_self_build_has_makefile skipped - nmake not found")
end
File.open File.join(@ext, 'Makefile'), 'w' do |makefile|
diff --git a/test/rubygems/test_gem_ext_ext_conf_builder.rb b/test/rubygems/test_gem_ext_ext_conf_builder.rb
index 21fe27166b..10a544cbbc 100644
--- a/test/rubygems/test_gem_ext_ext_conf_builder.rb
+++ b/test/rubygems/test_gem_ext_ext_conf_builder.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/ext'
class TestGemExtExtConfBuilder < Gem::TestCase
@@ -16,11 +16,11 @@ class TestGemExtExtConfBuilder < Gem::TestCase
def test_class_build
if java_platform?
- skip("failing on jruby")
+ pend("failing on jruby")
end
if vc_windows? && !nmake_found?
- skip("test_class_build skipped - nmake not found")
+ pend("test_class_build skipped - nmake not found")
end
File.open File.join(@ext, 'extconf.rb'), 'w' do |extconf|
@@ -41,11 +41,12 @@ class TestGemExtExtConfBuilder < Gem::TestCase
assert_contains_make_command '', output[7]
assert_contains_make_command 'install', output[10]
assert_empty Dir.glob(File.join(@ext, 'siteconf*.rb'))
+ assert_empty Dir.glob(File.join(@ext, '.gem.*'))
end
def test_class_build_rbconfig_make_prog
if java_platform?
- skip("failing on jruby")
+ pend("failing on jruby")
end
configure_args do
@@ -65,12 +66,15 @@ class TestGemExtExtConfBuilder < Gem::TestCase
end
end
- def test_class_build_env_make
- env_make = ENV.delete 'MAKE'
+ def test_class_build_env_MAKE
+ env_make = ENV.delete 'make'
+ ENV['make'] = nil
+
+ env_MAKE = ENV.delete 'MAKE'
ENV['MAKE'] = 'anothermake'
if java_platform?
- skip("failing on jruby")
+ pend("failing on jruby")
end
configure_args '' do
@@ -80,7 +84,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
output = []
- assert_raises Gem::InstallError do
+ assert_raise Gem::InstallError do
Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output, [], nil, @ext
end
@@ -88,12 +92,13 @@ class TestGemExtExtConfBuilder < Gem::TestCase
assert_contains_make_command 'clean', output[4]
end
ensure
- ENV['MAKE'] = env_make
+ ENV['MAKE'] = env_MAKE
+ 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")
+ pend("test_class_build_extconf_fail skipped - nmake not found")
end
File.open File.join(@ext, 'extconf.rb'), 'w' do |extconf|
@@ -104,7 +109,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
output = []
- error = assert_raises Gem::InstallError do
+ error = assert_raise Gem::InstallError do
Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output, [], nil, @ext
end
@@ -114,12 +119,12 @@ class TestGemExtExtConfBuilder < Gem::TestCase
assert_match(File.join(@dest_path, 'mkmf.log'), output[4])
assert_includes(output, "To see why this extension failed to compile, please check the mkmf.log which can be found here:\n")
- assert_path_exists File.join @dest_path, 'mkmf.log'
+ assert_path_exist File.join @dest_path, 'mkmf.log'
end
def test_class_build_extconf_success_without_warning
if vc_windows? && !nmake_found?
- skip("test_class_build_extconf_fail skipped - nmake not found")
+ pend("test_class_build_extconf_fail skipped - nmake not found")
end
File.open File.join(@ext, 'extconf.rb'), 'w' do |extconf|
@@ -134,12 +139,12 @@ class TestGemExtExtConfBuilder < Gem::TestCase
refute_includes(output, "To see why this extension failed to compile, please check the mkmf.log which can be found here:\n")
- assert_path_exists File.join @dest_path, 'mkmf.log'
+ assert_path_exist 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")
+ pend("test_class_build skipped - nmake not found")
end
File.open File.join(@ext, 'extconf.rb'), 'w' do |extconf|
@@ -180,7 +185,7 @@ end
def test_class_make
if vc_windows? && !nmake_found?
- skip("test_class_make skipped - nmake not found")
+ pend("test_class_make skipped - nmake not found")
end
output = []
@@ -202,7 +207,7 @@ end
end
def test_class_make_no_Makefile
- error = assert_raises Gem::InstallError do
+ error = assert_raise Gem::InstallError do
Gem::Ext::ExtConfBuilder.make @ext, ['output'], @ext
end
diff --git a/test/rubygems/test_gem_ext_rake_builder.rb b/test/rubygems/test_gem_ext_rake_builder.rb
index 094581890a..3d8922eed5 100644
--- a/test/rubygems/test_gem_ext_rake_builder.rb
+++ b/test/rubygems/test_gem_ext_rake_builder.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/ext'
class TestGemExtRakeBuilder < Gem::TestCase
@@ -48,6 +48,8 @@ class TestGemExtRakeBuilder < Gem::TestCase
end
def test_class_no_openssl_override
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
+
create_temp_mkrf_file('task :default')
rake = util_spec 'rake' do |s|
@@ -90,7 +92,7 @@ class TestGemExtRakeBuilder < Gem::TestCase
output = []
build_rake_in(false) do |rake|
- error = assert_raises Gem::InstallError do
+ error = assert_raise Gem::InstallError do
Gem::Ext::RakeBuilder.build "mkrf_conf.rb", @dest_path, output, [], nil, @ext
end
diff --git a/test/rubygems/test_gem_gem_runner.rb b/test/rubygems/test_gem_gem_runner.rb
index 8df11ecebc..6f5361cf2f 100644
--- a/test/rubygems/test_gem_gem_runner.rb
+++ b/test/rubygems/test_gem_gem_runner.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemGemRunner < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb
index 3290a3a908..0bcd1504e9 100644
--- a/test/rubygems/test_gem_gemcutter_utilities.rb
+++ b/test/rubygems/test_gem_gemcutter_utilities.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems'
require 'rubygems/command'
require 'rubygems/gemcutter_utilities'
@@ -14,6 +14,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
Gem.configuration.disable_default_gem_server = nil
ENV['RUBYGEMS_HOST'] = nil
+ ENV['GEM_HOST_OTP_CODE'] = nil
Gem.configuration.rubygems_api_key = nil
@cmd = Gem::Command.new '', 'summary'
@@ -22,6 +23,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
def teardown
ENV['RUBYGEMS_HOST'] = nil
+ ENV['GEM_HOST_OTP_CODE'] = nil
Gem.configuration.rubygems_api_key = nil
credential_teardown
@@ -35,8 +37,6 @@ class TestGemGemcutterUtilities < Gem::TestCase
"http://rubygems.engineyard.com" => "EYKEY",
}
- FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
-
File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
end
@@ -50,7 +50,6 @@ class TestGemGemcutterUtilities < Gem::TestCase
def test_api_key
keys = { :rubygems_api_key => 'KEY' }
- FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
@@ -63,7 +62,6 @@ class TestGemGemcutterUtilities < Gem::TestCase
def test_api_key_override
keys = { :rubygems_api_key => 'KEY', :other => 'OTHER' }
- FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
File.open Gem.configuration.credentials_path, 'w' do |f|
f.write keys.to_yaml
@@ -101,7 +99,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert @fetcher.last_request["authorization"]
assert_match %r{Signed in.}, @sign_in_ui.output
- credentials = YAML.load_file Gem.configuration.credentials_path
+ credentials = load_yaml_file Gem.configuration.credentials_path
assert_equal api_key, credentials[:rubygems_api_key]
end
@@ -115,7 +113,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert @fetcher.last_request["authorization"]
assert_match %r{Signed in.}, @sign_in_ui.output
- credentials = YAML.load_file Gem.configuration.credentials_path
+ credentials = load_yaml_file Gem.configuration.credentials_path
assert_equal api_key, credentials['http://example.com']
end
@@ -129,7 +127,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert @fetcher.last_request["authorization"]
assert_match %r{Signed in.}, @sign_in_ui.output
- credentials = YAML.load_file Gem.configuration.credentials_path
+ credentials = load_yaml_file Gem.configuration.credentials_path
assert_equal api_key, credentials[:rubygems_api_key]
end
@@ -142,7 +140,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert @fetcher.last_request["authorization"]
assert_match %r{Signed in.}, @sign_in_ui.output
- credentials = YAML.load_file Gem.configuration.credentials_path
+ credentials = load_yaml_file Gem.configuration.credentials_path
assert_equal api_key, credentials['http://example.com']
end
@@ -168,7 +166,6 @@ class TestGemGemcutterUtilities < Gem::TestCase
api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
other_api_key = 'f46dbb18bb6a9c97cdc61b5b85c186a17403cdcbf'
- FileUtils.mkdir_p File.dirname(Gem.configuration.credentials_path)
File.open Gem.configuration.credentials_path, 'w' do |f|
f.write Hash[:other_api_key, other_api_key].to_yaml
end
@@ -177,13 +174,13 @@ class TestGemGemcutterUtilities < Gem::TestCase
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
+ credentials = load_yaml_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
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
util_sign_in ['Access Denied.', 403, 'Forbidden']
end
@@ -191,6 +188,16 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert_match %r{Access Denied.}, @sign_in_ui.output
end
+ def test_signin_with_env_otp_code
+ ENV['GEM_HOST_OTP_CODE'] = '111111'
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+
+ util_sign_in [api_key, 200, 'OK']
+
+ assert_match 'Signed in with API key:', @sign_in_ui.output
+ assert_equal '111111', @fetcher.last_request['OTP']
+ end
+
def test_sign_in_with_correct_otp_code
api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
@@ -209,7 +216,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
def test_sign_in_with_incorrect_otp_code
response = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
util_sign_in [response, 401, 'Unauthorized'], nil, [], "111111\n"
end
@@ -246,7 +253,6 @@ class TestGemGemcutterUtilities < Gem::TestCase
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
@@ -257,7 +263,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
end
def test_verify_missing_api_key
- assert_raises Gem::MockGemUi::TermError do
+ assert_raise Gem::MockGemUi::TermError do
@cmd.verify_api_key :missing
end
end
diff --git a/test/rubygems/test_gem_impossible_dependencies_error.rb b/test/rubygems/test_gem_impossible_dependencies_error.rb
index e4fe6ef77c..971be151df 100644
--- a/test/rubygems/test_gem_impossible_dependencies_error.rb
+++ b/test/rubygems/test_gem_impossible_dependencies_error.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemImpossibleDependenciesError < Gem::TestCase
def test_message_conflict
diff --git a/test/rubygems/test_gem_indexer.rb b/test/rubygems/test_gem_indexer.rb
index adc83dd8fb..6653f29adf 100644
--- a/test/rubygems/test_gem_indexer.rb
+++ b/test/rubygems/test_gem_indexer.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/indexer'
class TestGemIndexer < Gem::TestCase
diff --git a/test/rubygems/test_gem_install_update_options.rb b/test/rubygems/test_gem_install_update_options.rb
index b4528dba17..a499c2be3b 100644
--- a/test/rubygems/test_gem_install_update_options.rb
+++ b/test/rubygems/test_gem_install_update_options.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/installer_test_case'
+require_relative 'installer_test_case'
require 'rubygems/install_update_options'
require 'rubygems/command'
require 'rubygems/dependency_installer'
@@ -92,7 +92,7 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
end
def test_security_policy
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
@cmd.handle_options %w[-P HighSecurity]
@@ -100,11 +100,11 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
end
def test_security_policy_unknown
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
@cmd.add_install_update_options
- e = assert_raises OptionParser::InvalidArgument do
+ e = assert_raise Gem::OptionParser::InvalidArgument do
@cmd.handle_options %w[-P UnknownSecurity]
end
assert_includes e.message, "UnknownSecurity"
@@ -124,8 +124,8 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
@installer = Gem::Installer.at @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)
+ assert_path_exist File.join(Gem.user_dir, 'gems')
+ assert_path_exist File.join(Gem.user_dir, 'gems', @spec.full_name)
end
def test_user_install_disabled_read_only
@@ -137,9 +137,9 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
@gem = @spec.cache_file
if win_platform?
- skip('test_user_install_disabled_read_only test skipped on MS Windows')
+ pend('test_user_install_disabled_read_only test skipped on MS Windows')
elsif Process.uid.zero?
- skip('test_user_install_disabled_read_only test skipped in root privilege')
+ pend('test_user_install_disabled_read_only test skipped in root privilege')
else
@cmd.handle_options %w[--no-user-install]
@@ -150,7 +150,7 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
Gem.use_paths @gemhome, @userhome
- assert_raises(Gem::FilePermissionError) do
+ assert_raise(Gem::FilePermissionError) do
Gem::Installer.at(@gem, @cmd.options).install
end
end
@@ -169,7 +169,7 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
def test_vendor_missing
vendordir(nil) do
- e = assert_raises OptionParser::InvalidOption do
+ e = assert_raise Gem::OptionParser::InvalidOption do
@cmd.handle_options %w[--vendor]
end
diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb
index 5652d86331..dae2b070d5 100644
--- a/test/rubygems/test_gem_installer.rb
+++ b/test/rubygems/test_gem_installer.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/installer_test_case'
+require_relative 'installer_test_case'
class TestGemInstaller < Gem::InstallerTestCase
def setup
@@ -33,6 +33,8 @@ class TestGemInstaller < Gem::InstallerTestCase
require 'rubygems'
+Gem.use_gemdeps
+
version = \">= 0.a\"
str = ARGV.first
@@ -76,7 +78,7 @@ end
installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
@@ -90,7 +92,7 @@ end
ui = Gem::MockGemUi.new "n\n"
use_ui ui do
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.generate_bin
end
@@ -134,7 +136,7 @@ gem 'other', version
installer.generate_bin # should not raise
installed_exec = File.join util_inst_bindir, 'foo-executable-bar'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
@@ -150,7 +152,7 @@ gem 'other', version
ui = Gem::MockGemUi.new "n\n"
use_ui ui do
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.generate_bin
end
@@ -169,7 +171,7 @@ gem 'other', version
installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
@@ -184,7 +186,7 @@ gem 'other', version
installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
@@ -222,7 +224,7 @@ gem 'other', version
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?
+ pend "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)
@@ -264,7 +266,7 @@ gem 'other', version
assert installer.ensure_dependency(@spec, dep)
dep = Gem::Dependency.new 'b', '> 2'
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.ensure_dependency @spec, dep
end
@@ -278,7 +280,7 @@ gem 'other', version
installer = Gem::Installer.at a_gem
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.ensure_loadable_spec
end
@@ -286,8 +288,35 @@ gem 'other', version
"(SyntaxError)", e.message
end
+ def test_ensure_no_race_conditions_between_installing_and_loading_gemspecs
+ a, a_gem = util_gem 'a', 2
+
+ Gem::Installer.at(a_gem).install
+
+ t1 = Thread.new do
+ 5.times do
+ Gem::Installer.at(a_gem).install
+ sleep 0.1
+ end
+ end
+
+ t2 = Thread.new do
+ _, err = capture_output do
+ 20.times do
+ Gem::Specification.load(a.spec_file)
+ Gem::Specification.send(:clear_load_cache)
+ end
+ end
+
+ assert_empty err
+ end
+
+ t1.join
+ t2.join
+ end
+
def test_ensure_loadable_spec_security_policy
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
_, a_gem = util_gem 'a', 2 do |s|
s.add_dependency 'garbage ~> 5'
@@ -296,7 +325,7 @@ gem 'other', version
policy = Gem::Security::HighSecurity
installer = Gem::Installer.at a_gem, :security_policy => policy
- assert_raises Gem::Security::Exception do
+ assert_raise Gem::Security::Exception do
installer.ensure_loadable_spec
end
end
@@ -306,7 +335,7 @@ gem 'other', version
installer.extract_files
- assert_path_exists File.join @spec.gem_dir, 'bin/executable'
+ assert_path_exist File.join @spec.gem_dir, 'bin/executable'
end
def test_generate_bin_bindir
@@ -329,7 +358,7 @@ gem 'other', version
assert_directory_exists util_inst_bindir
installed_exec = File.join(util_inst_bindir, 'executable')
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
assert_equal mask, File.stat(installed_exec).mode unless win_platform?
wrapper = File.read installed_exec
@@ -372,7 +401,7 @@ gem 'other', version
installer.generate_bin
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
assert_equal mask, File.stat(installed_exec).mode unless win_platform?
wrapper = File.read installed_exec
@@ -391,7 +420,7 @@ gem 'other', version
installer.generate_bin
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'foo-executable-bar'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
ensure
Gem::Installer.exec_format = nil
end
@@ -407,7 +436,7 @@ gem 'other', version
installer.generate_bin
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
ensure
Gem::Installer.exec_format = nil
end
@@ -431,7 +460,7 @@ gem 'other', version
installer.generate_bin
installed_exec = File.join("#{@gemhome}2", "bin", 'executable')
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
assert_equal mask, File.stat(installed_exec).mode unless win_platform?
wrapper = File.read installed_exec
@@ -446,7 +475,7 @@ gem 'other', version
installer.wrappers = true
installer.generate_bin
- refute_path_exists util_inst_bindir, 'bin dir was created when not needed'
+ assert_path_not_exist util_inst_bindir, 'bin dir was created when not needed'
end
def test_generate_bin_script_no_perms
@@ -458,13 +487,13 @@ gem 'other', version
Dir.mkdir util_inst_bindir
if win_platform?
- skip('test_generate_bin_script_no_perms skipped on MS Windows')
+ pend('test_generate_bin_script_no_perms skipped on MS Windows')
elsif Process.uid.zero?
- skip('test_generate_bin_script_no_perms skipped in root privilege')
+ pend('test_generate_bin_script_no_perms skipped in root privilege')
else
FileUtils.chmod 0000, util_inst_bindir
- assert_raises Gem::FilePermissionError do
+ assert_raise Gem::FilePermissionError do
installer.generate_bin
end
end
@@ -488,7 +517,7 @@ gem 'other', version
installer.generate_bin
installed_exec = File.join @gemhome, 'bin', 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
assert_equal mask, File.stat(installed_exec).mode unless win_platform?
wrapper = File.read installed_exec
@@ -515,7 +544,7 @@ gem 'other', version
installer.generate_bin
assert_directory_exists util_inst_bindir
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
assert_equal mask, File.stat(installed_exec).mode unless win_platform?
assert_match %r{generated by RubyGems}, File.read(installed_exec)
@@ -525,7 +554,7 @@ gem 'other', version
end
def test_generate_bin_symlink
- skip "Symlinks not supported or not enabled" unless symlink_supported?
+ pend "Symlinks not supported or not enabled" unless symlink_supported?
installer = setup_base_installer
@@ -549,7 +578,7 @@ gem 'other', version
installer.wrappers = false
installer.generate_bin
- refute_path_exists util_inst_bindir
+ assert_path_not_exist util_inst_bindir
end
def test_generate_bin_symlink_no_perms
@@ -562,13 +591,13 @@ gem 'other', version
Dir.mkdir util_inst_bindir
if win_platform?
- skip('test_generate_bin_symlink_no_perms skipped on MS Windows')
+ pend('test_generate_bin_symlink_no_perms skipped on MS Windows')
elsif Process.uid.zero?
- skip('test_user_install_disabled_read_only test skipped in root privilege')
+ pend('test_user_install_disabled_read_only test skipped in root privilege')
else
FileUtils.chmod 0000, util_inst_bindir
- assert_raises Gem::FilePermissionError do
+ assert_raise Gem::FilePermissionError do
installer.generate_bin
end
end
@@ -577,7 +606,7 @@ gem 'other', version
end
def test_generate_bin_symlink_update_newer
- skip "Symlinks not supported or not enabled" unless symlink_supported?
+ pend "Symlinks not supported or not enabled" unless symlink_supported?
installer = setup_base_installer
@@ -609,7 +638,7 @@ gem 'other', version
end
def test_generate_bin_symlink_update_older
- skip "Symlinks not supported or not enabled" unless symlink_supported?
+ pend "Symlinks not supported or not enabled" unless symlink_supported?
installer = setup_base_installer
@@ -647,7 +676,7 @@ gem 'other', version
end
def test_generate_bin_symlink_update_remove_wrapper
- skip "Symlinks not supported or not enabled" unless symlink_supported?
+ pend "Symlinks not supported or not enabled" unless symlink_supported?
installer = setup_base_installer
@@ -658,7 +687,7 @@ gem 'other', version
installer.generate_bin
installed_exec = File.join util_inst_bindir, 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
@spec = Gem::Specification.new do |s|
s.files = ['lib/code.rb']
@@ -701,7 +730,7 @@ gem 'other', version
assert_directory_exists util_inst_bindir
installed_exec = File.join(util_inst_bindir, 'executable')
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
if symlink_supported?
assert File.symlink?(installed_exec)
@@ -720,7 +749,7 @@ gem 'other', version
end
def test_generate_bin_uses_default_shebang
- skip "Symlinks not supported or not enabled" unless symlink_supported?
+ pend "Symlinks not supported or not enabled" unless symlink_supported?
installer = setup_base_installer
@@ -730,11 +759,30 @@ gem 'other', version
installer.generate_bin
default_shebang = Gem.ruby
- shebang_line = open("#{@gemhome}/bin/executable") {|f| f.readlines.first }
+ shebang_line = File.open("#{@gemhome}/bin/executable") {|f| f.readlines.first }
assert_match(/\A#!/, shebang_line)
assert_match(/#{default_shebang}/, shebang_line)
end
+ def test_generate_bin_with_dangling_symlink
+ gem_with_dangling_symlink = File.expand_path("packages/ascii_binder-0.1.10.1.gem", __dir__)
+
+ installer = Gem::Installer.at(
+ gem_with_dangling_symlink,
+ :user_install => false,
+ :force => true
+ )
+
+ build_rake_in do
+ use_ui @ui do
+ installer.install
+ end
+ end
+
+ assert_match %r{bin/ascii_binder` is dangling symlink pointing to `bin/asciibinder`}, @ui.error
+ assert_empty @ui.output
+ end
+
def test_generate_plugins
installer = util_setup_installer do |spec|
write_file File.join(@tempdir, 'lib', 'rubygems_plugin.rb') do |io|
@@ -926,22 +974,19 @@ gem 'other', version
spec_file = File.join @gemhome, 'specifications', @spec.spec_name
Gem.pre_install do
- refute_path_exists cache_file, 'cache file must not exist yet'
- refute_path_exists spec_file, 'spec file must not exist yet'
+ assert_path_not_exist cache_file, 'cache file must not exist yet'
true
end
Gem.post_build do
- 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'
+ assert_path_exist gemdir, 'gem install dir must exist'
+ assert_path_exist rakefile, 'gem executable must exist'
+ assert_path_not_exist stub_exe, 'gem executable must not exist'
true
end
Gem.post_install do
- assert_path_exists cache_file, 'cache file must exist'
- assert_path_exists spec_file, 'spec file must exist'
+ assert_path_exist cache_file, 'cache file must exist'
end
@newspec = nil
@@ -952,21 +997,21 @@ gem 'other', version
end
assert_equal @spec, @newspec
- assert_path_exists gemdir
- assert_path_exists stub_exe, 'gem executable must exist'
+ assert_path_exist gemdir
+ assert_path_exist stub_exe, 'gem executable must exist'
exe = File.join gemdir, 'bin', 'executable'
- assert_path_exists exe
+ assert_path_exist 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_exist File.join gemdir, 'lib', 'code.rb'
- assert_path_exists rakefile
+ assert_path_exist rakefile
assert_equal spec_file, @newspec.loaded_from
- assert_path_exists spec_file
+ assert_path_exist spec_file
assert_same installer, @post_build_hook_arg
assert_same installer, @post_install_hook_arg
@@ -989,7 +1034,7 @@ gem 'other', version
exe = File.join gemdir, 'bin', 'executable'
- e = assert_raises RuntimeError do
+ e = assert_raise RuntimeError do
instance_eval File.read(exe)
end
@@ -1035,7 +1080,7 @@ gem 'other', version
end
end
- e = assert_raises RuntimeError do
+ e = assert_raise RuntimeError do
instance_eval File.read(old_bin_file)
end
@@ -1063,7 +1108,7 @@ gem 'other', version
begin
Gem::Specification.reset
- e = assert_raises Gem::GemNotFoundException do
+ e = assert_raise Gem::GemNotFoundException do
instance_eval File.read(exe)
end
ensure
@@ -1080,7 +1125,7 @@ gem 'other', version
exe = File.join @gemhome, 'bin', 'executable'
- assert_path_exists exe, "default gem's executable not installed"
+ assert_path_exist exe, "default gem's executable not installed"
installer = util_setup_installer do |spec|
spec.name = 'default'
@@ -1098,7 +1143,7 @@ gem 'other', version
end
end
- e = assert_raises RuntimeError do
+ e = assert_raise RuntimeError do
instance_eval File.read(exe)
end
@@ -1125,7 +1170,7 @@ gem 'other', version
begin
Gem::Specification.reset
- e = assert_raises RuntimeError do
+ e = assert_raise RuntimeError do
instance_eval File.read(exe)
end
ensure
@@ -1145,7 +1190,7 @@ gem 'other', version
end
gemdir = File.join(@gemhome, 'gems', @spec.full_name)
- assert_path_exists File.join gemdir, 'lib', 'code.rb'
+ assert_path_exist File.join gemdir, 'lib', 'code.rb'
installer = util_setup_installer
@@ -1168,8 +1213,8 @@ gem 'other', version
end
end
- assert_path_exists File.join gemdir, 'lib', 'other.rb'
- refute_path_exists File.join gemdir, 'lib', 'code.rb',
+ assert_path_exist File.join gemdir, 'lib', 'other.rb'
+ assert_path_not_exist File.join gemdir, 'lib', 'code.rb',
"code.rb from prior install of same gem shouldn't remain here"
end
@@ -1184,7 +1229,7 @@ gem 'other', version
end
gem_dir = File.join(@gemhome, 'gems', 'missing_dep-1')
- assert_path_exists gem_dir
+ assert_path_exist gem_dir
end
def test_install_build_root
@@ -1211,19 +1256,23 @@ gem 'other', version
assert_directory_exists File.join(Gem.dir, 'doc')
assert_directory_exists 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
+ assert_path_exist File.join @gemhome, 'cache', @spec.file_name
+ assert_path_exist File.join @gemhome, 'specifications', @spec.spec_name
end
def test_install_post_build_false
- installer = setup_base_installer
+ @spec = util_spec 'a'
+
+ util_build_gem @spec
+
+ installer = util_installer @spec, @gemhome
Gem.post_build do
false
end
use_ui @ui do
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.install
end
@@ -1233,10 +1282,10 @@ gem 'other', version
end
spec_file = File.join @gemhome, 'specifications', @spec.spec_name
- refute_path_exists spec_file
+ assert_path_not_exist spec_file
gem_dir = File.join @gemhome, 'gems', @spec.full_name
- refute_path_exists gem_dir
+ assert_path_not_exist gem_dir
end
def test_install_post_build_nil
@@ -1251,21 +1300,25 @@ gem 'other', version
end
spec_file = File.join @gemhome, 'specifications', @spec.spec_name
- assert_path_exists spec_file
+ assert_path_exist spec_file
gem_dir = File.join @gemhome, 'gems', @spec.full_name
- assert_path_exists gem_dir
+ assert_path_exist gem_dir
end
def test_install_pre_install_false
- installer = setup_base_installer
+ @spec = util_spec 'a'
+
+ util_build_gem @spec
+
+ installer = util_installer @spec, @gemhome
Gem.pre_install do
false
end
use_ui @ui do
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.install
end
@@ -1275,7 +1328,7 @@ gem 'other', version
end
spec_file = File.join @gemhome, 'specifications', @spec.spec_name
- refute_path_exists spec_file
+ assert_path_not_exist spec_file
end
def test_install_pre_install_nil
@@ -1290,7 +1343,7 @@ gem 'other', version
end
spec_file = File.join @gemhome, 'specifications', @spec.spec_name
- assert_path_exists spec_file
+ assert_path_exist spec_file
end
def test_install_with_message
@@ -1344,7 +1397,7 @@ gem 'other', version
expected_makefile = File.join gemhome2, 'gems', @spec.full_name, 'Makefile'
- assert_path_exists expected_makefile
+ assert_path_exist expected_makefile
end
def test_install_extension_dir_is_removed_on_reinstall
@@ -1373,7 +1426,7 @@ gem 'other', version
write_file should_be_removed do |io|
io.write "DELETE ME ON REINSTALL"
end
- assert_path_exists should_be_removed
+ assert_path_exist should_be_removed
# reinstall the gem, this is also the same as pristine
use_ui @ui do
@@ -1381,7 +1434,7 @@ gem 'other', version
installer.install
end
- refute_path_exists should_be_removed
+ assert_path_not_exist should_be_removed
end
def test_install_user_extension_dir
@@ -1409,13 +1462,13 @@ gem 'other', version
expected_makefile = File.join Gem.user_dir, 'gems', @spec.full_name, 'Makefile'
- assert_path_exists expected_makefile
- assert_path_exists expected_extension_dir
- refute_path_exists File.join expected_extension_dir, 'gem_make.out'
+ assert_path_exist expected_makefile
+ assert_path_exist expected_extension_dir
+ assert_path_not_exist File.join expected_extension_dir, 'gem_make.out'
end
def test_find_lib_file_after_install
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
@spec = setup_base_spec
@spec.extensions << "extconf.rb"
@@ -1461,7 +1514,8 @@ gem 'other', version
end
def test_install_extension_and_script
- skip "Makefile creation crashes on jruby" if Gem.java_platform?
+ pend "Makefile creation crashes on jruby" if Gem.java_platform?
+ pend if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning
@spec = setup_base_spec
@spec.extensions << "extconf.rb"
@@ -1489,65 +1543,67 @@ gem 'other', version
RUBY
end
- refute_path_exists File.join @spec.gem_dir, rb
- refute_path_exists File.join @spec.gem_dir, rb2
+ assert_path_not_exist File.join @spec.gem_dir, rb
+ assert_path_not_exist File.join @spec.gem_dir, rb2
use_ui @ui do
path = Gem::Package.build @spec
installer = Gem::Installer.at path
installer.install
end
- assert_path_exists File.join @spec.gem_dir, rb
- assert_path_exists File.join @spec.gem_dir, rb2
+ assert_path_exist File.join @spec.gem_dir, rb
+ assert_path_exist File.join @spec.gem_dir, rb2
end
def test_install_extension_flat
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
- @spec = setup_base_spec
- @spec.require_paths = ["."]
+ begin
+ @spec = setup_base_spec
+ @spec.require_paths = ["."]
- @spec.extensions << "extconf.rb"
+ @spec.extensions << "extconf.rb"
- write_file File.join(@tempdir, "extconf.rb") do |io|
- io.write <<-RUBY
- require "mkmf"
+ write_file File.join(@tempdir, "extconf.rb") do |io|
+ io.write <<-RUBY
+ require "mkmf"
- CONFIG['CC'] = '$(TOUCH) $@ ||'
- CONFIG['LDSHARED'] = '$(TOUCH) $@ ||'
- $ruby = '#{Gem.ruby}'
+ CONFIG['CC'] = '$(TOUCH) $@ ||'
+ CONFIG['LDSHARED'] = '$(TOUCH) $@ ||'
+ $ruby = '#{Gem.ruby}'
- create_makefile("#{@spec.name}")
- RUBY
- end
+ create_makefile("#{@spec.name}")
+ RUBY
+ end
- # empty depend file for no auto dependencies
- @spec.files += %W[depend #{@spec.name}.c].each do |file|
- write_file File.join(@tempdir, file)
- end
+ # empty depend file for no auto dependencies
+ @spec.files += %W[depend #{@spec.name}.c].each do |file|
+ write_file File.join(@tempdir, file)
+ end
- so = File.join(@spec.gem_dir, "#{@spec.name}.#{RbConfig::CONFIG["DLEXT"]}")
- refute_path_exists so
- use_ui @ui do
- path = Gem::Package.build @spec
+ so = File.join(@spec.gem_dir, "#{@spec.name}.#{RbConfig::CONFIG["DLEXT"]}")
+ assert_path_not_exist so
+ use_ui @ui do
+ path = Gem::Package.build @spec
- installer = Gem::Installer.at path
- installer.install
- end
- assert_path_exists so
- rescue
- puts '-' * 78
- puts File.read File.join(@gemhome, 'gems', 'a-2', 'Makefile')
- puts '-' * 78
+ installer = Gem::Installer.at path
+ installer.install
+ end
+ assert_path_exist 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')
+ path = File.join(@gemhome, 'gems', 'a-2', 'gem_make.out')
- if File.exist?(path)
- puts File.read(path)
- puts '-' * 78
- end
+ if File.exist?(path)
+ puts File.read(path)
+ puts '-' * 78
+ end
- raise
+ raise
+ end
end
def test_installation_satisfies_dependency_eh
@@ -1580,7 +1636,7 @@ gem 'other', version
installer.force = false
use_ui @ui do
- assert_raises Gem::InstallError do
+ assert_raise Gem::InstallError do
installer.install
end
end
@@ -1639,7 +1695,7 @@ gem 'other', version
use_ui @ui do
installer = Gem::Installer.at gem
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal '#<Gem::Specification name=../malicious version=1> has an invalid name', e.message
@@ -1659,7 +1715,7 @@ gem 'other', version
use_ui @ui do
installer = Gem::Installer.at gem
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "#<Gem::Specification name=malicious\n::Object.const_set(:FROM_EVAL, true)# version=1> has an invalid name", e.message
@@ -1681,7 +1737,7 @@ gem 'other', version
use_ui @ui do
installer = Gem::Installer.at gem
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "#<Gem::Specification name=malicious version=1> has an invalid require_paths", e.message
@@ -1689,7 +1745,7 @@ gem 'other', version
end
def test_pre_install_checks_malicious_extensions_before_eval
- skip "mswin environment disallow to create file contained the carriage return code." if Gem.win_platform?
+ pend "mswin environment disallow to create file contained the carriage return code." if Gem.win_platform?
spec = util_spec "malicious", '1'
def spec.full_name # so the spec is buildable
@@ -1704,7 +1760,7 @@ gem 'other', version
use_ui @ui do
installer = Gem::Installer.at gem
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "#<Gem::Specification name=malicious version=1> has an invalid extensions", e.message
@@ -1725,7 +1781,7 @@ gem 'other', version
use_ui @ui do
installer = Gem::Installer.at gem
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "#<Gem::Specification name=malicious version=1> has an invalid specification_version", e.message
@@ -1747,13 +1803,33 @@ gem 'other', version
use_ui @ui do
installer = Gem::Installer.at gem
installer.ignore_dependencies = true
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
installer.pre_install_checks
end
assert_equal "#<Gem::Specification name=malicious version=1> has an invalid dependencies", e.message
end
end
+ def test_pre_install_checks_malicious_platform_before_eval
+ gem_with_ill_formated_platform = File.expand_path("packages/ill-formatted-platform-1.0.0.10.gem", __dir__)
+
+ installer = Gem::Installer.at(
+ gem_with_ill_formated_platform,
+ :install_dir => @gem_home,
+ :user_install => false,
+ :force => true
+ )
+
+ use_ui @ui do
+ e = assert_raise Gem::InstallError do
+ installer.pre_install_checks
+ end
+
+ assert_equal "x86-mswin32\n system('id > /tmp/nyangawa')# is an invalid platform", e.message
+ assert_empty @ui.output
+ end
+ end
+
def test_shebang
installer = setup_base_installer
@@ -1971,14 +2047,14 @@ gem 'other', version
installer.unpack dest
end
- assert_path_exists File.join dest, 'lib', 'code.rb'
- assert_path_exists File.join dest, 'bin', 'executable'
+ assert_path_exist File.join dest, 'lib', 'code.rb'
+ assert_path_exist File.join dest, 'bin', 'executable'
end
def test_write_build_info_file
installer = setup_base_installer
- refute_path_exists @spec.build_info_file
+ assert_path_not_exist @spec.build_info_file
installer.build_args = %w[
--with-libyaml-dir /usr/local/Cellar/libyaml/0.1.4
@@ -1986,7 +2062,7 @@ gem 'other', version
installer.write_build_info_file
- assert_path_exists @spec.build_info_file
+ assert_path_exist @spec.build_info_file
expected = "--with-libyaml-dir\n/usr/local/Cellar/libyaml/0.1.4\n"
@@ -1996,11 +2072,11 @@ gem 'other', version
def test_write_build_info_file_empty
installer = setup_base_installer
- refute_path_exists @spec.build_info_file
+ assert_path_not_exist @spec.build_info_file
installer.write_build_info_file
- refute_path_exists @spec.build_info_file
+ assert_path_not_exist @spec.build_info_file
end
def test_write_build_info_file_install_dir
@@ -2013,8 +2089,8 @@ gem 'other', version
installer.write_build_info_file
- refute_path_exists @spec.build_info_file
- assert_path_exists \
+ assert_path_not_exist @spec.build_info_file
+ assert_path_exist \
File.join("#{@gemhome}2", 'build_info', "#{@spec.full_name}.info")
end
@@ -2024,27 +2100,27 @@ gem 'other', version
gem = File.join @gemhome, @spec.file_name
FileUtils.mv cache_file, gem
- refute_path_exists cache_file
+ assert_path_not_exist cache_file
installer = Gem::Installer.at gem
installer.gem_home = @gemhome
installer.write_cache_file
- assert_path_exists cache_file
+ assert_path_exist cache_file
end
def test_write_spec
@spec = setup_base_spec
FileUtils.rm @spec.spec_file
- refute_path_exists @spec.spec_file
+ assert_path_not_exist @spec.spec_file
installer = Gem::Installer.for_spec @spec
installer.gem_home = @gemhome
installer.write_spec
- assert_path_exists @spec.spec_file
+ assert_path_exist @spec.spec_file
loaded = Gem::Specification.load @spec.spec_file
@@ -2056,7 +2132,7 @@ gem 'other', version
def test_write_spec_writes_cached_spec
@spec = setup_base_spec
FileUtils.rm @spec.spec_file
- refute_path_exists @spec.spec_file
+ assert_path_not_exist @spec.spec_file
@spec.files = %w[a.rb b.rb c.rb]
@@ -2099,7 +2175,7 @@ gem 'other', version
assert_directory_exists File.join(@spec.gem_dir, 'bin')
installed_exec = File.join @spec.gem_dir, 'bin', 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
assert_directory_exists File.join(Gem.default_dir, 'specifications')
assert_directory_exists File.join(Gem.default_dir, 'specifications', 'default')
@@ -2111,7 +2187,7 @@ gem 'other', version
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
wrapper = File.read installed_exec
@@ -2137,7 +2213,7 @@ gem 'other', version
assert_directory_exists util_inst_bindir
installed_exec = File.join util_inst_bindir, 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
wrapper = File.read installed_exec
assert_match %r{generated by RubyGems}, wrapper
@@ -2163,7 +2239,7 @@ gem 'other', version
assert_directory_exists File.join(@spec.gem_dir, 'exe')
installed_exec = File.join @spec.gem_dir, 'exe', 'executable'
- assert_path_exists installed_exec
+ assert_path_exist installed_exec
assert_directory_exists File.join(Gem.default_dir, 'specifications')
assert_directory_exists File.join(Gem.default_dir, 'specifications', 'default')
diff --git a/test/rubygems/test_gem_local_remote_options.rb b/test/rubygems/test_gem_local_remote_options.rb
index 93059ae731..948d1e3981 100644
--- a/test/rubygems/test_gem_local_remote_options.rb
+++ b/test/rubygems/test_gem_local_remote_options.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/local_remote_options'
require 'rubygems/command'
@@ -123,7 +123,7 @@ class TestGemLocalRemoteOptions < Gem::TestCase
s1 = 'htp://more-gems.example.com'
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
@cmd.handle_options %W[--source #{s1}]
end
diff --git a/test/rubygems/test_gem_name_tuple.rb b/test/rubygems/test_gem_name_tuple.rb
index 92cfe6c1d2..d87db9bc45 100644
--- a/test/rubygems/test_gem_name_tuple.rb
+++ b/test/rubygems/test_gem_name_tuple.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/name_tuple'
class TestGemNameTuple < Gem::TestCase
diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb
index fd28f9a2a5..48dcbee9f1 100644
--- a/test/rubygems/test_gem_package.rb
+++ b/test/rubygems/test_gem_package.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/package/tar_test_case'
-require 'digest'
+require_relative 'package/tar_test_case'
+require 'rubygems/openssl'
class TestGemPackage < Gem::Package::TarTestCase
def setup
@@ -22,7 +22,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_class_new_old_format
- skip "jruby can't require the simple_gem file" if Gem.java_platform?
+ pend "jruby can't require the simple_gem file" if Gem.java_platform?
require_relative "simple_gem"
File.open 'old_format.gem', 'wb' do |io|
io.write SIMPLE_GEM
@@ -84,21 +84,21 @@ class TestGemPackage < Gem::Package::TarTestCase
io.write spec.to_yaml
end
- metadata_sha256 = Digest::SHA256.hexdigest s.string
- metadata_sha512 = Digest::SHA512.hexdigest s.string
+ metadata_sha256 = OpenSSL::Digest::SHA256.hexdigest s.string
+ metadata_sha512 = OpenSSL::Digest::SHA512.hexdigest s.string
expected = {
'SHA512' => {
'metadata.gz' => metadata_sha512,
- 'data.tar.gz' => Digest::SHA512.hexdigest(tar),
+ 'data.tar.gz' => OpenSSL::Digest::SHA512.hexdigest(tar),
},
'SHA256' => {
'metadata.gz' => metadata_sha256,
- 'data.tar.gz' => Digest::SHA256.hexdigest(tar),
+ 'data.tar.gz' => OpenSSL::Digest::SHA256.hexdigest(tar),
},
}
- assert_equal expected, YAML.load(checksums)
+ assert_equal expected, load_yaml(checksums)
end
def test_build_time_uses_source_date_epoch
@@ -190,7 +190,7 @@ class TestGemPackage < Gem::Package::TarTestCase
File.symlink('../lib/code.rb', 'lib/code_sym2.rb')
rescue Errno::EACCES => e
if win_platform?
- skip "symlink - must be admin with no UAC on Windows"
+ pend "symlink - must be admin with no UAC on Windows"
else
raise e
end
@@ -218,7 +218,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
assert_equal %w[lib/code.rb], files
- assert_equal [{'lib/code_sym.rb' => 'lib/code.rb'}, {'lib/code_sym2.rb' => '../lib/code.rb'}], symlinks
+ assert_equal [{'lib/code_sym.rb' => 'code.rb'}, {'lib/code_sym2.rb' => '../lib/code.rb'}], symlinks
end
def test_build
@@ -240,7 +240,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package.build
assert_equal Gem::VERSION, spec.rubygems_version
- assert_path_exists spec.file_name
+ assert_path_exist spec.file_name
reader = Gem::Package.new spec.file_name
assert_equal spec, reader.spec
@@ -252,7 +252,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_build_auto_signed
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
@@ -279,7 +279,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package.build
assert_equal Gem::VERSION, spec.rubygems_version
- assert_path_exists spec.file_name
+ assert_path_exist spec.file_name
reader = Gem::Package.new spec.file_name
assert reader.verify
@@ -295,7 +295,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_build_auto_signed_encrypted_key
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
@@ -322,7 +322,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package.build
assert_equal Gem::VERSION, spec.rubygems_version
- assert_path_exists spec.file_name
+ assert_path_exist spec.file_name
reader = Gem::Package.new spec.file_name
assert reader.verify
@@ -343,7 +343,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package = Gem::Package.new spec.file_name
package.spec = spec
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
package.build
end
@@ -356,7 +356,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package = Gem::Package.new spec.file_name
package.spec = spec
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
package.build true, true
end
@@ -364,7 +364,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_build_signed
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
@@ -385,7 +385,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package.build
assert_equal Gem::VERSION, spec.rubygems_version
- assert_path_exists spec.file_name
+ assert_path_exist spec.file_name
reader = Gem::Package.new spec.file_name
assert reader.verify
@@ -401,7 +401,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_build_signed_encrypted_key
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
@@ -422,7 +422,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package.build
assert_equal Gem::VERSION, spec.rubygems_version
- assert_path_exists spec.file_name
+ assert_path_exist spec.file_name
reader = Gem::Package.new spec.file_name
assert reader.verify
@@ -476,7 +476,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package.extract_files @destination
extracted = File.join @destination, 'lib/code.rb'
- assert_path_exists extracted
+ assert_path_exist extracted
mask = 0100666 & (~File.umask)
@@ -507,7 +507,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package.extract_files @destination
- assert_path_exists @destination
+ assert_path_exist @destination
end
def test_extract_tar_gz_absolute
@@ -519,7 +519,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
end
- e = assert_raises Gem::Package::PathError do
+ e = assert_raise Gem::Package::PathError do
package.extract_tar_gz tgz_io, @destination
end
@@ -543,14 +543,14 @@ class TestGemPackage < Gem::Package::TarTestCase
package.extract_tar_gz tgz_io, @destination
rescue Errno::EACCES => e
if win_platform?
- skip "symlink - must be admin with no UAC on Windows"
+ pend "symlink - must be admin with no UAC on Windows"
else
raise e
end
end
extracted = File.join @destination, 'lib/foo.rb'
- assert_path_exists extracted
+ assert_path_exist extracted
assert_equal '../relative.rb',
File.readlink(extracted)
assert_equal 'hi',
@@ -574,18 +574,19 @@ class TestGemPackage < Gem::Package::TarTestCase
destination_subdir = File.join @destination, 'subdir'
FileUtils.mkdir_p destination_subdir
- e = assert_raises(Gem::Package::PathError, Errno::EACCES) do
+ expected_exceptions = win_platform? ? [Gem::Package::SymlinkError, Errno::EACCES] : [Gem::Package::SymlinkError]
+
+ e = assert_raise(*expected_exceptions) do
package.extract_tar_gz tgz_io, destination_subdir
end
- if Gem::Package::PathError === e
- assert_equal("installing into parent path lib/link/outside.txt of " +
- "#{destination_subdir} is not allowed", e.message)
- elsif win_platform?
- skip "symlink - must be admin with no UAC on Windows"
- else
- raise e
- end
+ pend "symlink - must be admin with no UAC on Windows" if Errno::EACCES === e
+
+ assert_equal("installing symlink 'lib/link' pointing to parent path #{@destination} of " +
+ "#{destination_subdir} is not allowed", e.message)
+
+ assert_path_not_exist File.join(@destination, "outside.txt")
+ assert_path_not_exist File.join(destination_subdir, "lib/link")
end
def test_extract_symlink_parent_doesnt_delete_user_dir
@@ -601,27 +602,27 @@ class TestGemPackage < Gem::Package::TarTestCase
destination_user_subdir = File.join destination_user_dir, 'dir'
FileUtils.mkdir_p destination_user_subdir
- skip "TMPDIR seems too long to add it as symlink into tar" if destination_user_dir.size > 90
+ pend "TMPDIR seems too long to add it as symlink into tar" if destination_user_dir.size > 90
tgz_io = util_tar_gz do |tar|
tar.add_symlink 'link', destination_user_dir, 16877
tar.add_symlink 'link/dir', '.', 16877
end
- e = assert_raises(Gem::Package::PathError, Errno::EACCES) do
+ expected_exceptions = win_platform? ? [Gem::Package::SymlinkError, Errno::EACCES] : [Gem::Package::SymlinkError]
+
+ e = assert_raise(*expected_exceptions) do
package.extract_tar_gz tgz_io, destination_subdir
end
- assert_path_exists destination_user_subdir
+ pend "symlink - must be admin with no UAC on Windows" if Errno::EACCES === e
- if Gem::Package::PathError === e
- assert_equal("installing into parent path #{destination_user_subdir} of " +
- "#{destination_subdir} is not allowed", e.message)
- elsif win_platform?
- skip "symlink - must be admin with no UAC on Windows"
- else
- raise e
- end
+ assert_equal("installing symlink 'link' pointing to parent path #{destination_user_dir} of " +
+ "#{destination_subdir} is not allowed", e.message)
+
+ assert_path_exist destination_user_subdir
+ assert_path_not_exist File.join(destination_subdir, "link/dir")
+ assert_path_not_exist File.join(destination_subdir, "link")
end
def test_extract_tar_gz_directory
@@ -638,10 +639,10 @@ class TestGemPackage < Gem::Package::TarTestCase
package.extract_tar_gz tgz_io, @destination
extracted = File.join @destination, 'lib/foo.rb'
- assert_path_exists extracted
+ assert_path_exist extracted
extracted = File.join @destination, 'lib/foo'
- assert_path_exists extracted
+ assert_path_exist extracted
end
def test_extract_tar_gz_dot_slash
@@ -656,7 +657,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package.extract_tar_gz tgz_io, @destination
extracted = File.join @destination, 'dot_slash.rb'
- assert_path_exists extracted
+ assert_path_exist extracted
end
def test_extract_tar_gz_dot_file
@@ -671,7 +672,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package.extract_tar_gz tgz_io, @destination
extracted = File.join @destination, '.dot_file.rb'
- assert_path_exists extracted
+ assert_path_exist extracted
end
if Gem.win_platform?
@@ -687,7 +688,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package.extract_tar_gz tgz_io, @destination.upcase
extracted = File.join @destination, 'foo/file.rb'
- assert_path_exists extracted
+ assert_path_exist extracted
end
end
@@ -706,7 +707,7 @@ class TestGemPackage < Gem::Package::TarTestCase
def test_install_location_absolute
package = Gem::Package.new @gem
- e = assert_raises Gem::Package::PathError do
+ e = assert_raise Gem::Package::PathError do
package.install_location '/absolute.rb', @destination
end
@@ -747,7 +748,7 @@ class TestGemPackage < Gem::Package::TarTestCase
def test_install_location_relative
package = Gem::Package.new @gem
- e = assert_raises Gem::Package::PathError do
+ e = assert_raise Gem::Package::PathError do
package.install_location '../relative.rb', @destination
end
@@ -762,7 +763,7 @@ class TestGemPackage < Gem::Package::TarTestCase
filename = "../#{File.basename(@destination)}suffix.rb"
- e = assert_raises Gem::Package::PathError do
+ e = assert_raise Gem::Package::PathError do
package.install_location filename, @destination
end
@@ -832,7 +833,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package = Gem::Package.new 'mismatch.gem'
- e = assert_raises Gem::Package::FormatError do
+ e = assert_raise Gem::Package::FormatError do
package.verify
end
@@ -856,7 +857,7 @@ class TestGemPackage < Gem::Package::TarTestCase
io.write metadata_gz
end
- digest = Digest::SHA1.new
+ digest = OpenSSL::Digest::SHA1.new
digest << metadata_gz
checksums = {
@@ -886,7 +887,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_corrupt
- skip "jruby strips the null byte and does not think it's corrupt" if Gem.java_platform?
+ pend "jruby strips the null byte and does not think it's corrupt" if Gem.java_platform?
tf = Tempfile.open 'corrupt' do |io|
data = Gem::Util.gzip 'a' * 10
io.write \
@@ -896,7 +897,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package = Gem::Package.new io.path
- e = assert_raises Gem::Package::FormatError do
+ e = assert_raise Gem::Package::FormatError do
package.verify
end
@@ -912,7 +913,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package = Gem::Package.new 'empty.gem'
- e = assert_raises Gem::Package::FormatError do
+ e = assert_raise Gem::Package::FormatError do
package.verify
end
@@ -922,7 +923,7 @@ class TestGemPackage < Gem::Package::TarTestCase
def test_verify_nonexistent
package = Gem::Package.new 'nonexistent.gem'
- e = assert_raises Gem::Package::FormatError do
+ e = assert_raise Gem::Package::FormatError do
package.verify
end
@@ -937,7 +938,7 @@ class TestGemPackage < Gem::Package::TarTestCase
build = Gem::Package.new @gem
build.spec = @spec
build.setup_signer
- open @gem, 'wb' do |gem_io|
+ File.open @gem, 'wb' do |gem_io|
Gem::Package::TarWriter.new gem_io do |gem|
build.add_metadata gem
build.add_contents gem
@@ -949,7 +950,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package = Gem::Package.new @gem
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
package.verify
end
@@ -957,12 +958,12 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_security_policy
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
package = Gem::Package.new @gem
package.security_policy = Gem::Security::HighSecurity
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
package.verify
end
@@ -974,7 +975,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_security_policy_low_security
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
@spec.cert_chain = [PUBLIC_CERT.to_pem]
@spec.signing_key = PRIVATE_KEY
@@ -994,7 +995,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_security_policy_checksum_missing
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
@spec.cert_chain = [PUBLIC_CERT.to_pem]
@spec.signing_key = PRIVATE_KEY
@@ -1015,7 +1016,7 @@ class TestGemPackage < Gem::Package::TarTestCase
bogus_data = Gem::Util.gzip 'hello'
fake_signer = Class.new do
def digest_name; 'SHA512'; end
- def digest_algorithm; Digest(:SHA512).new; end
+ def digest_algorithm; OpenSSL::Digest(:SHA512).new; end
def key; 'key'; end
def sign(*); 'fake_sig'; end
end
@@ -1032,7 +1033,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package = Gem::Package.new @gem
package.security_policy = Gem::Security::HighSecurity
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
package.verify
end
@@ -1049,7 +1050,7 @@ class TestGemPackage < Gem::Package::TarTestCase
package = Gem::Package.new 'bad.gem'
- e = assert_raises Gem::Package::FormatError do
+ e = assert_raise Gem::Package::FormatError do
package.verify
end
@@ -1068,8 +1069,8 @@ class TestGemPackage < Gem::Package::TarTestCase
_, err = use_ui @ui do
e = nil
- out_err = capture_io do
- e = assert_raises ArgumentError do
+ out_err = capture_output do
+ e = assert_raise ArgumentError do
package.verify_entry entry
end
end
@@ -1140,11 +1141,18 @@ class TestGemPackage < Gem::Package::TarTestCase
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
+ assert_raise(Gem::Package::Error) do
Gem::Package.new io
end
end
+ def test_contents_from_io
+ io = StringIO.new Gem.read_binary @gem
+ package = Gem::Package.new io
+
+ assert_equal %w[lib/code.rb], package.contents
+ end
+
def util_tar
tar_io = StringIO.new
diff --git a/test/rubygems/test_gem_package_old.rb b/test/rubygems/test_gem_package_old.rb
index 8c4c20006b..945340a96a 100644
--- a/test/rubygems/test_gem_package_old.rb
+++ b/test/rubygems/test_gem_package_old.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
unless Gem.java_platform? # jruby can't require the simple_gem file
require 'rubygems/simple_gem'
@@ -23,11 +23,11 @@ unless Gem.java_platform? # jruby can't require the simple_gem file
end
def test_contents_security_policy
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
@package.security_policy = Gem::Security::AlmostNoSecurity
- assert_raises Gem::Security::Exception do
+ assert_raise Gem::Security::Exception do
@package.contents
end
end
@@ -36,7 +36,7 @@ unless Gem.java_platform? # jruby can't require the simple_gem file
@package.extract_files @destination
extracted = File.join @destination, 'lib/foo.rb'
- assert_path_exists extracted
+ assert_path_exist extracted
mask = 0100644 & (~File.umask)
@@ -44,11 +44,11 @@ unless Gem.java_platform? # jruby can't require the simple_gem file
end
def test_extract_files_security_policy
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
@package.security_policy = Gem::Security::AlmostNoSecurity
- assert_raises Gem::Security::Exception do
+ assert_raise Gem::Security::Exception do
@package.extract_files @destination
end
end
@@ -58,17 +58,17 @@ unless Gem.java_platform? # jruby can't require the simple_gem file
end
def test_spec_security_policy
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
@package.security_policy = Gem::Security::AlmostNoSecurity
- assert_raises Gem::Security::Exception do
+ assert_raise Gem::Security::Exception do
@package.spec
end
end
def test_verify
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
assert @package.verify
@@ -78,7 +78,7 @@ unless Gem.java_platform? # jruby can't require the simple_gem file
@package.security_policy = Gem::Security::AlmostNoSecurity
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@package.verify
end
diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb
index da4f5506e9..0e72a72d6a 100644
--- a/test/rubygems/test_gem_package_tar_header.rb
+++ b/test/rubygems/test_gem_package_tar_header.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/package/tar_test_case'
+require_relative 'package/tar_test_case'
require 'rubygems/package'
class TestGemPackageTarHeader < Gem::Package::TarTestCase
@@ -57,19 +57,19 @@ class TestGemPackageTarHeader < Gem::Package::TarTestCase
end
def test_initialize_bad
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
Gem::Package::TarHeader.new :name => '', :size => '', :mode => ''
end
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
Gem::Package::TarHeader.new :name => '', :size => '', :prefix => ''
end
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
Gem::Package::TarHeader.new :name => '', :prefix => '', :mode => ''
end
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
Gem::Package::TarHeader.new :prefix => '', :size => '', :mode => ''
end
end
@@ -156,7 +156,7 @@ group\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
# overwrite the size field
header_s[124, 12] = val
io = TempIO.new header_s
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
Gem::Package::TarHeader.from io
end
io.close!
diff --git a/test/rubygems/test_gem_package_tar_reader.rb b/test/rubygems/test_gem_package_tar_reader.rb
index 05b3ac56a6..277b552f1b 100644
--- a/test/rubygems/test_gem_package_tar_reader.rb
+++ b/test/rubygems/test_gem_package_tar_reader.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/package/tar_test_case'
+require_relative 'package/tar_test_case'
require 'rubygems/package'
class TestGemPackageTarReader < Gem::Package::TarTestCase
diff --git a/test/rubygems/test_gem_package_tar_reader_entry.rb b/test/rubygems/test_gem_package_tar_reader_entry.rb
index 3003e51ac8..1be5870146 100644
--- a/test/rubygems/test_gem_package_tar_reader_entry.rb
+++ b/test/rubygems/test_gem_package_tar_reader_entry.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/package/tar_test_case'
+require_relative 'package/tar_test_case'
require 'rubygems/package'
class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
@@ -42,19 +42,19 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
assert @entry.bytes_read
- e = assert_raises(IOError) { @entry.eof? }
+ e = assert_raise(IOError) { @entry.eof? }
assert_equal 'closed Gem::Package::TarReader::Entry', e.message
- e = assert_raises(IOError) { @entry.getc }
+ e = assert_raise(IOError) { @entry.getc }
assert_equal 'closed Gem::Package::TarReader::Entry', e.message
- e = assert_raises(IOError) { @entry.pos }
+ e = assert_raise(IOError) { @entry.pos }
assert_equal 'closed Gem::Package::TarReader::Entry', e.message
- e = assert_raises(IOError) { @entry.read }
+ e = assert_raise(IOError) { @entry.read }
assert_equal 'closed Gem::Package::TarReader::Entry', e.message
- e = assert_raises(IOError) { @entry.rewind }
+ e = assert_raise(IOError) { @entry.rewind }
assert_equal 'closed Gem::Package::TarReader::Entry', e.message
end
@@ -75,10 +75,10 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
end
def test_full_name_null
- skip "jruby strips the null byte and does not think it's corrupt" if Gem.java_platform?
+ pend "jruby strips the null byte and does not think it's corrupt" if Gem.java_platform?
@entry.header.prefix << "\000"
- e = assert_raises Gem::Package::TarInvalidError do
+ e = assert_raise Gem::Package::TarInvalidError do
@entry.full_name
end
@@ -134,7 +134,7 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
end
def test_readpartial
- assert_raises(EOFError) do
+ assert_raise(EOFError) do
@entry.read(@contents.size)
@entry.readpartial(1)
end
diff --git a/test/rubygems/test_gem_package_tar_writer.rb b/test/rubygems/test_gem_package_tar_writer.rb
index 25dac5f148..31a91fa21a 100644
--- a/test/rubygems/test_gem_package_tar_writer.rb
+++ b/test/rubygems/test_gem_package_tar_writer.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/package/tar_test_case'
+require_relative 'package/tar_test_case'
require 'rubygems/package/tar_writer'
-require 'minitest/mock'
class TestGemPackageTarWriter < Gem::Package::TarTestCase
def setup
@@ -117,7 +116,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
def test_add_file_signer
- skip 'openssl is missing' unless Gem::HAVE_OPENSSL
+ pend 'openssl is missing' unless Gem::HAVE_OPENSSL
signer = Gem::Security::Signer.new PRIVATE_KEY, [PUBLIC_CERT]
@@ -150,13 +149,12 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
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])
+ @io.string[0, 512])
end
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
@@ -170,11 +168,11 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
- @io.string[0, 512])
- end
+ @io.string[0, 512])
- assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
- assert_equal 1024, @io.pos
+ assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
+ assert_equal 1024, @io.pos
+ end
end
def test_add_file_simple_source_date_epoch
@@ -185,7 +183,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc),
- @io.string[0, 512])
+ @io.string[0, 512])
end
end
@@ -194,7 +192,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
@tar_writer.add_file_simple 'x', 0, 100
assert_headers_equal tar_file_header('x', '', 0, 100, Time.now),
- @io.string[0, 512]
+ @io.string[0, 512]
end
assert_equal "\0" * 512, @io.string[512, 512]
@@ -209,7 +207,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
def test_add_file_simple_size
- assert_raises Gem::Package::TarWriter::FileOverflow do
+ assert_raise Gem::Package::TarWriter::FileOverflow do
@tar_writer.add_file_simple("lib/foo/bar", 0, 10) do |io|
io.write "1" * 11
end
@@ -221,27 +219,27 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
assert_equal "\0" * 1024, @io.string
- e = assert_raises IOError do
+ e = assert_raise IOError do
@tar_writer.close
end
assert_equal 'closed Gem::Package::TarWriter', e.message
- e = assert_raises IOError do
+ e = assert_raise IOError do
@tar_writer.flush
end
assert_equal 'closed Gem::Package::TarWriter', e.message
- e = assert_raises IOError do
+ e = assert_raise IOError do
@tar_writer.add_file 'x', 0
end
assert_equal 'closed Gem::Package::TarWriter', e.message
- e = assert_raises IOError do
+ e = assert_raise IOError do
@tar_writer.add_file_simple 'x', 0, 0
end
assert_equal 'closed Gem::Package::TarWriter', e.message
- e = assert_raises IOError do
+ e = assert_raise IOError do
@tar_writer.mkdir 'x', 0
end
assert_equal 'closed Gem::Package::TarWriter', e.message
@@ -297,7 +295,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
assert_equal ['b' * 100, 'a'], @tar_writer.split_name(name)
name = File.join 'a', 'b' * 101
- exception = assert_raises Gem::Package::TooLongFileName do
+ exception = assert_raise Gem::Package::TooLongFileName do
@tar_writer.split_name name
end
assert_includes exception.message, name
@@ -305,7 +303,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
# note, GNU tar 1.28 is unable to handle this case too,
# tested with "tar --format=ustar -cPf /tmp/foo.tartar -- /aaaaaa....a"
name = '/' + 'a' * 100
- exception = assert_raises Gem::Package::TooLongFileName do
+ exception = assert_raise Gem::Package::TooLongFileName do
@tar_writer.split_name name
end
assert_includes exception.message, name
@@ -316,7 +314,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
assert_equal ['b', 'a' * 155], @tar_writer.split_name(name)
name = File.join 'a' * 156, 'b'
- exception = assert_raises Gem::Package::TooLongFileName do
+ exception = assert_raise Gem::Package::TooLongFileName do
@tar_writer.split_name name
end
assert_includes exception.message, name
@@ -324,7 +322,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
def test_split_name_too_long_total
name = 'a' * 257
- exception = assert_raises Gem::Package::TooLongFileName do
+ exception = assert_raise Gem::Package::TooLongFileName do
@tar_writer.split_name name
end
assert_includes exception.message, name
diff --git a/test/rubygems/test_gem_package_task.rb b/test/rubygems/test_gem_package_task.rb
index ee9b8d44d4..0cedfe56eb 100644
--- a/test/rubygems/test_gem_package_task.rb
+++ b/test/rubygems/test_gem_package_task.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems'
begin
@@ -40,7 +40,7 @@ class TestGemPackageTask < Gem::TestCase
Rake.application['package'].invoke
- assert_path_exists 'pkg/pkgr-1.2.3.gem'
+ assert_path_exist 'pkg/pkgr-1.2.3.gem'
end
ensure
RakeFileUtils.verbose_flag = original_rake_fileutils_verbosity
@@ -56,7 +56,7 @@ class TestGemPackageTask < Gem::TestCase
g.summary = 'summary'
end
- _, err = capture_io do
+ _, err = capture_output do
Rake.application = Rake::Application.new
pkg = Gem::PackageTask.new(gem) do |p|
diff --git a/test/rubygems/test_gem_path_support.rb b/test/rubygems/test_gem_path_support.rb
index f24041a2d8..88a3cc29b9 100644
--- a/test/rubygems/test_gem_path_support.rb
+++ b/test/rubygems/test_gem_path_support.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems'
require 'fileutils'
@@ -130,7 +130,7 @@ class TestGemPathSupport < Gem::TestCase
begin
File.symlink(dir, symlink)
rescue NotImplementedError, SystemCallError
- skip 'symlinks not supported'
+ pend 'symlinks not supported'
end
not_existing = "#{@tempdir}/does_not_exist"
path = "#{symlink}#{File::PATH_SEPARATOR}#{not_existing}"
diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb
index 84754402ad..8029035db1 100644
--- a/test/rubygems/test_gem_platform.rb
+++ b/test/rubygems/test_gem_platform.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/platform'
require 'rbconfig'
@@ -122,6 +122,7 @@ class TestGemPlatform < Gem::TestCase
'i586-linux-gnu' => ['x86', 'linux', nil],
'i386-linux-gnu' => ['x86', 'linux', nil],
'i386-mingw32' => ['x86', 'mingw32', nil],
+ 'x64-mingw-ucrt' => ['x64', 'mingw', 'ucrt'],
'i386-mswin32' => ['x86', 'mswin32', nil],
'i386-mswin32_80' => ['x86', 'mswin32', '80'],
'i386-mswin32-80' => ['x86', 'mswin32', '80'],
diff --git a/test/rubygems/test_gem_rdoc.rb b/test/rubygems/test_gem_rdoc.rb
index 13fd4ba40b..7b8ad07b27 100644
--- a/test/rubygems/test_gem_rdoc.rb
+++ b/test/rubygems/test_gem_rdoc.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
require 'rubygems'
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/rdoc'
class TestGemRDoc < Gem::TestCase
@@ -24,7 +24,7 @@ class TestGemRDoc < Gem::TestCase
begin
Gem::RDoc.load_rdoc
rescue Gem::DocumentError => e
- skip e.message
+ pend e.message
end
Gem.configuration[:rdoc] = nil
@@ -84,16 +84,16 @@ class TestGemRDoc < Gem::TestCase
refute @hook.rdoc_installed?
refute @hook.ri_installed?
- assert_path_exists @a.doc_dir
+ assert_path_exist @a.doc_dir
end
def test_remove_unwritable
- skip 'chmod not supported' if Gem.win_platform?
- skip 'skipped in root privilege' if Process.uid.zero?
+ pend 'chmod not supported' if Gem.win_platform?
+ pend 'skipped in root privilege' if Process.uid.zero?
FileUtils.mkdir_p @a.base_dir
FileUtils.chmod 0, @a.base_dir
- e = assert_raises Gem::FilePermissionError do
+ e = assert_raise Gem::FilePermissionError do
@hook.remove
end
@@ -113,16 +113,16 @@ class TestGemRDoc < Gem::TestCase
def test_setup
@hook.setup
- assert_path_exists @a.doc_dir
+ assert_path_exist @a.doc_dir
end
def test_setup_unwritable
- skip 'chmod not supported' if Gem.win_platform?
- skip 'skipped in root privilege' if Process.uid.zero?
+ pend 'chmod not supported' if Gem.win_platform?
+ pend 'skipped in root privilege' if Process.uid.zero?
FileUtils.mkdir_p @a.doc_dir
FileUtils.chmod 0, @a.doc_dir
- e = assert_raises Gem::FilePermissionError do
+ e = assert_raise Gem::FilePermissionError do
@hook.setup
end
diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb
index 1c88e8d3e8..5ce420b91a 100644
--- a/test/rubygems/test_gem_remote_fetcher.rb
+++ b/test/rubygems/test_gem_remote_fetcher.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'webrick'
require 'webrick/https' if Gem::HAVE_OPENSSL
@@ -10,7 +10,6 @@ end
require 'rubygems/remote_fetcher'
require 'rubygems/package'
-require 'minitest/mock'
# = Testing Proxy Settings
#
@@ -146,7 +145,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
@fetcher.fetch_path("gems.example.com/yaml", nil, true)
end
@@ -184,7 +183,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
assert_equal 'hello', data
- refute_path_exists path
+ assert_path_not_exist path
end
def util_fuck_with_fetcher(data, blow = false)
@@ -242,6 +241,36 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
assert File.exist?(a1_cache_gem)
end
+ def test_download_with_token
+ 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://token@gems.example.com')
+ assert_equal("http://token@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_x_oauth_basic
+ 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://token:x-oauth-basic@gems.example.com')
+ assert_equal("http://token:x-oauth-basic@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|
@@ -390,7 +419,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_download_unsupported
inst = Gem::RemoteFetcher.fetcher
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
inst.download @a1, 'ftp://gems.rubyforge.org'
end
@@ -451,7 +480,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
url = 'http://example.com/uri'
- e = assert_raises Gem::RemoteFetcher::FetchError do
+ e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path url
end
@@ -469,7 +498,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
url = 'http://example.com/uri'
- e = assert_raises Gem::RemoteFetcher::FetchError do
+ e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path url
end
@@ -487,7 +516,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
url = 'http://example.com/uri'
- e = assert_raises Gem::RemoteFetcher::FetchError do
+ e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path url
end
@@ -496,6 +525,44 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
assert_equal url, e.uri
end
+ def test_fetch_path_timeout_error
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ def fetcher.fetch_http(uri, mtime = nil, head = nil)
+ raise Timeout::Error, 'timed out'
+ end
+
+ url = 'http://example.com/uri'
+
+ e = assert_raise Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path url
+ end
+
+ assert_match %r{Timeout::Error: timed out \(#{Regexp.escape url}\)\z},
+ e.message
+ assert_equal url, e.uri
+ end
+
+ def test_fetch_path_getaddrinfo_error
+ fetcher = Gem::RemoteFetcher.new nil
+ @fetcher = fetcher
+
+ def fetcher.fetch_http(uri, mtime = nil, head = nil)
+ raise SocketError, 'getaddrinfo: nodename nor servname provided'
+ end
+
+ url = 'http://example.com/uri'
+
+ e = assert_raise Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_path url
+ end
+
+ assert_match %r{SocketError: getaddrinfo: nodename nor servname provided \(#{Regexp.escape url}\)\z},
+ e.message
+ assert_equal url, e.uri
+ end
+
def test_fetch_path_openssl_ssl_sslerror
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
@@ -506,7 +573,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
url = 'http://example.com/uri'
- e = assert_raises Gem::RemoteFetcher::FetchError do
+ e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path url
end
@@ -596,7 +663,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
res
end
- e = assert_raises Gem::RemoteFetcher::FetchError do
+ e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_http URI.parse(url)
end
@@ -613,7 +680,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
res
end
- e = assert_raises Gem::RemoteFetcher::FetchError do
+ e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_http URI.parse(url)
end
@@ -798,7 +865,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher
- e = assert_raises Gem::RemoteFetcher::FetchError do
+ e = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_s3 URI.parse(url)
end
@@ -914,7 +981,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
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
+ assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
end
end
@@ -923,7 +990,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_do_not_allow_insecure_ssl_connection_by_default
ssl_server = start_ssl_server
with_configured_fetcher do |fetcher|
- assert_raises Gem::RemoteFetcher::FetchError do
+ assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
end
end
@@ -943,7 +1010,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
"redirecting to non-https resource: #{@server_uri} (https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri})"
with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher|
- err = assert_raises Gem::RemoteFetcher::FetchError do
+ err = assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri}")
end
@@ -956,7 +1023,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
temp_ca_cert = nil
with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher|
- assert_raises Gem::RemoteFetcher::FetchError do
+ assert_raise Gem::RemoteFetcher::FetchError do
fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}")
end
end
@@ -1046,7 +1113,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
end
def start_ssl_server(config = {})
- skip "starting this test server fails randomly on jruby" if Gem.java_platform?
+ pend "starting this test server fails randomly on jruby" if Gem.java_platform?
null_logger = NilLog.new
server = WEBrick::HTTPServer.new({
diff --git a/test/rubygems/test_gem_request.rb b/test/rubygems/test_gem_request.rb
index 47e5f97074..47654f6fa4 100644
--- a/test/rubygems/test_gem_request.rb
+++ b/test/rubygems/test_gem_request.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/request'
require 'ostruct'
require 'base64'
@@ -185,7 +185,7 @@ class TestGemRequest < Gem::TestCase
end
def test_fetch
- uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
+ uri = Gem::Uri.new(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)
@@ -197,31 +197,57 @@ class TestGemRequest < Gem::TestCase
end
def test_fetch_basic_auth
- uri = URI.parse "https://user:pass@example.rubygems/specs.#{Gem.marshal_version}"
+ Gem.configuration.verbose = :really
+ uri = Gem::Uri.new(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
+ use_ui @ui do
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request.fetch
+ end
c
end
auth_header = conn.payload['Authorization']
assert_equal "Basic #{Base64.encode64('user:pass')}".strip, auth_header
+ assert_includes @ui.output, "GET https://user:REDACTED@example.rubygems/specs.#{Gem.marshal_version}"
end
def test_fetch_basic_auth_encoded
- uri = URI.parse "https://user:%7BDEScede%7Dpass@example.rubygems/specs.#{Gem.marshal_version}"
+ Gem.configuration.verbose = :really
+ uri = Gem::Uri.new(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
+ use_ui @ui do
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request.fetch
+ end
c
end
auth_header = conn.payload['Authorization']
assert_equal "Basic #{Base64.encode64('user:{DEScede}pass')}".strip, auth_header
+ assert_includes @ui.output, "GET https://user:REDACTED@example.rubygems/specs.#{Gem.marshal_version}"
+ end
+
+ def test_fetch_basic_oauth_encoded
+ Gem.configuration.verbose = :really
+ uri = Gem::Uri.new(URI.parse "https://%7BDEScede%7Dpass:x-oauth-basic@example.rubygems/specs.#{Gem.marshal_version}")
+
+ conn = util_stub_net_http(:body => :junk, :code => 200) do |c|
+ use_ui @ui do
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request.fetch
+ end
+ c
+ end
+
+ auth_header = conn.payload['Authorization']
+ assert_equal "Basic #{Base64.encode64('{DEScede}pass:x-oauth-basic')}".strip, auth_header
+ assert_includes @ui.output, "GET https://REDACTED:x-oauth-basic@example.rubygems/specs.#{Gem.marshal_version}"
end
def test_fetch_head
- uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
+ uri = Gem::Uri.new(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
@@ -232,7 +258,7 @@ class TestGemRequest < Gem::TestCase
end
def test_fetch_unmodified
- uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
+ uri = Gem::Uri.new(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)
@@ -327,31 +353,37 @@ class TestGemRequest < Gem::TestCase
end
def test_verify_certificate
- skip if Gem.java_platform?
+ pend if Gem.java_platform?
+
+ error_number = OpenSSL::X509::V_ERR_OUT_OF_MEM
+
store = OpenSSL::X509::Store.new
context = OpenSSL::X509::StoreContext.new store
- context.error = OpenSSL::X509::V_ERR_OUT_OF_MEM
+ context.error = error_number
use_ui @ui do
Gem::Request.verify_certificate context
end
- assert_equal "ERROR: SSL verification error at depth 0: out of memory (17)\n",
+ assert_equal "ERROR: SSL verification error at depth 0: out of memory (#{error_number})\n",
@ui.error
end
def test_verify_certificate_extra_message
- skip if Gem.java_platform?
+ pend if Gem.java_platform?
+
+ error_number = OpenSSL::X509::V_ERR_INVALID_CA
+
store = OpenSSL::X509::Store.new
context = OpenSSL::X509::StoreContext.new store
- context.error = OpenSSL::X509::V_ERR_INVALID_CA
+ context.error = error_number
use_ui @ui do
Gem::Request.verify_certificate context
end
expected = <<-ERROR
-ERROR: SSL verification error at depth 0: invalid CA certificate (24)
+ERROR: SSL verification error at depth 0: invalid CA certificate (#{error_number})
ERROR: Certificate is an invalid CA certificate
ERROR
diff --git a/test/rubygems/test_gem_request_connection_pools.rb b/test/rubygems/test_gem_request_connection_pools.rb
index 2bd2d28469..c5e7cf2ed4 100644
--- a/test/rubygems/test_gem_request_connection_pools.rb
+++ b/test/rubygems/test_gem_request_connection_pools.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/request'
require 'timeout'
@@ -140,7 +140,7 @@ class TestGemRequestConnectionPool < Gem::TestCase
pool.checkout
Thread.new do
- assert_raises(Timeout::Error) do
+ assert_raise(Timeout::Error) do
Timeout.timeout(1) do
pool.checkout
end
diff --git a/test/rubygems/test_gem_request_set.rb b/test/rubygems/test_gem_request_set.rb
index 54ae7720c0..e2fe98b9bf 100644
--- a/test/rubygems/test_gem_request_set.rb
+++ b/test/rubygems/test_gem_request_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/request_set'
class TestGemRequestSet < Gem::TestCase
@@ -64,8 +64,8 @@ class TestGemRequestSet < Gem::TestCase
end
assert_includes installed, 'a-2'
- assert_path_exists File.join @gemhome, 'gems', 'a-2'
- assert_path_exists 'gem.deps.rb.lock'
+ assert_path_exist File.join @gemhome, 'gems', 'a-2'
+ assert_path_exist 'gem.deps.rb.lock'
assert rs.remote
refute done_installing_ran
@@ -87,9 +87,10 @@ Gems to install:
a-2
EXPECTED
- assert_output expected do
+ actual, _ = capture_output do
rs.install_from_gemdeps :gemdeps => io.path, :explain => true
end
+ assert_equal(expected, actual)
end
end
@@ -99,7 +100,7 @@ Gems to install:
end
util_clear_gems
- refute_path_exists File.join Gem.dir, 'gems', 'a-2'
+ assert_path_not_exist File.join Gem.dir, 'gems', 'a-2'
rs = Gem::RequestSet.new
installed = []
@@ -118,7 +119,7 @@ Gems to install:
end
assert_includes installed, 'a-2'
- refute_path_exists File.join Gem.dir, 'gems', 'a-2'
+ assert_path_not_exist File.join Gem.dir, 'gems', 'a-2'
end
def test_install_from_gemdeps_local
@@ -132,7 +133,7 @@ Gems to install:
io.puts 'gem "a"'
io.flush
- assert_raises Gem::UnsatisfiableDependencyError do
+ assert_raise Gem::UnsatisfiableDependencyError do
rs.install_from_gemdeps :gemdeps => io.path, :domain => :local
end
end
@@ -178,8 +179,8 @@ DEPENDENCIES
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'
+ assert_path_exist File.join @gemhome, 'specifications', 'a-1.gemspec'
+ assert_path_exist File.join @gemhome, 'specifications', 'b-1.gemspec'
end
def test_install_from_gemdeps_complex_dependencies
@@ -231,7 +232,7 @@ end
assert_includes installed, 'z-1.0.3'
- assert_path_exists File.join @gemhome, 'specifications', 'z-1.0.3.gemspec'
+ assert_path_exist File.join @gemhome, 'specifications', 'z-1.0.3.gemspec'
end
def test_install_from_gemdeps_version_mismatch
@@ -443,7 +444,7 @@ ruby "0"
set = StaticSet.new [a1, a2]
- assert_raises Gem::UnsatisfiableDependencyError do
+ assert_raise Gem::UnsatisfiableDependencyError do
rs.resolve set
end
end
@@ -524,8 +525,8 @@ ruby "0"
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_path_exist File.join @gemhome, 'specifications', 'a-1.gemspec'
+ assert_path_exist File.join @gemhome, 'specifications', 'b-1.gemspec'
assert_equal %w[b-1 a-1], installed.map {|s| s.full_name }
@@ -547,8 +548,8 @@ ruby "0"
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_path_exist File.join @tempdir, 'specifications', 'a-1.gemspec'
+ assert_path_exist File.join @tempdir, 'specifications', 'b-1.gemspec'
assert_equal %w[b-1 a-1], installed.map {|s| s.full_name }
end
diff --git a/test/rubygems/test_gem_request_set_gem_dependency_api.rb b/test/rubygems/test_gem_request_set_gem_dependency_api.rb
index 2a9663959c..cc4f99df91 100644
--- a/test/rubygems/test_gem_request_set_gem_dependency_api.rb
+++ b/test/rubygems/test_gem_request_set_gem_dependency_api.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/request_set'
class TestGemRequestSetGemDependencyAPI < Gem::TestCase
@@ -81,7 +81,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gem_duplicate
@gda.gem 'a'
- _, err = capture_io do
+ _, err = capture_output do
@gda.gem 'a'
end
@@ -129,7 +129,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_git_branch
- _, err = capture_io do
+ _, err = capture_output do
@gda.gem 'a', :git => 'git/a', :branch => 'other', :tag => 'v1'
end
expected = "Gem dependencies file gem.deps.rb includes git reference for both ref/branch and tag but only ref/branch is used."
@@ -150,7 +150,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_git_ref
- _, err = capture_io do
+ _, err = capture_output do
@gda.gem 'a', :git => 'git/a', :ref => 'abcd123', :branch => 'other'
end
expected = "Gem dependencies file gem.deps.rb includes git reference for both ref and branch but only ref is used."
@@ -388,7 +388,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gem_platforms_unknown
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
@gda.gem 'a', :platforms => :unknown
end
@@ -455,7 +455,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
gda = @GDA.new @set, nil
gda.gem name
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
gda.gem name, :path => directory
end
@@ -466,7 +466,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
gda.instance_variable_set :@vendor_set, @vendor_set
gda.gem name, :path => directory
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
gda.gem name
end
@@ -517,8 +517,8 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
def test_gemspec_bad
FileUtils.touch 'a.gemspec'
- e = assert_raises ArgumentError do
- capture_io do
+ e = assert_raise ArgumentError do
+ capture_output do
@gda.gemspec
end
end
@@ -550,7 +550,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
s.add_dependency 'c', 3
end
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
@gda.gemspec
end
@@ -582,7 +582,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
end
def test_gemspec_none
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
@gda.gemspec
end
@@ -658,14 +658,14 @@ end
gda.send :pin_gem_source, 'a'
gda.send :pin_gem_source, 'a'
- e = assert_raises ArgumentError do
+ e = assert_raise 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
+ e = assert_raise ArgumentError do
gda.send :pin_gem_source, 'a', :git, 'git://example/repo.git'
end
@@ -770,7 +770,7 @@ end
def test_ruby_engine_mismatch_engine
with_engine_version 'ruby', '2.0.0' do
- e = assert_raises Gem::RubyVersionMismatch do
+ e = assert_raise Gem::RubyVersionMismatch do
@gda.ruby RUBY_VERSION, :engine => 'jruby', :engine_version => '1.7.4'
end
@@ -781,7 +781,7 @@ end
def test_ruby_engine_mismatch_version
with_engine_version 'jruby', '1.7.6' do
- e = assert_raises Gem::RubyVersionMismatch do
+ e = assert_raise Gem::RubyVersionMismatch do
@gda.ruby RUBY_VERSION, :engine => 'jruby', :engine_version => '1.7.4'
end
@@ -791,7 +791,7 @@ end
end
def test_ruby_engine_no_engine_version
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
@gda.ruby RUBY_VERSION, :engine => 'jruby'
end
@@ -800,7 +800,7 @@ end
end
def test_ruby_mismatch
- e = assert_raises Gem::RubyVersionMismatch do
+ e = assert_raise Gem::RubyVersionMismatch do
@gda.ruby '1.8.0'
end
diff --git a/test/rubygems/test_gem_request_set_lockfile.rb b/test/rubygems/test_gem_request_set_lockfile.rb
index 44a47f2e00..690b632d9b 100644
--- a/test/rubygems/test_gem_request_set_lockfile.rb
+++ b/test/rubygems/test_gem_request_set_lockfile.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/request_set'
require 'rubygems/request_set/lockfile'
@@ -443,7 +443,7 @@ DEPENDENCIES
gem_deps_lock_file = "#{@gem_deps_file}.lock"
- assert_path_exists gem_deps_lock_file
+ assert_path_exist gem_deps_lock_file
refute_empty File.read gem_deps_lock_file
end
@@ -457,11 +457,11 @@ DEPENDENCIES
io.write 'hello'
end
- assert_raises Gem::UnsatisfiableDependencyError do
+ assert_raise Gem::UnsatisfiableDependencyError do
lockfile.write
end
- assert_path_exists gem_deps_lock_file
+ assert_path_exist gem_deps_lock_file
assert_equal 'hello', File.read(gem_deps_lock_file)
end
diff --git a/test/rubygems/test_gem_request_set_lockfile_parser.rb b/test/rubygems/test_gem_request_set_lockfile_parser.rb
index 9a42d81d0e..4007c3a69c 100644
--- a/test/rubygems/test_gem_request_set_lockfile_parser.rb
+++ b/test/rubygems/test_gem_request_set_lockfile_parser.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/request_set'
require 'rubygems/request_set/lockfile'
require 'rubygems/request_set/lockfile/tokenizer'
@@ -25,7 +25,7 @@ class TestGemRequestSetLockfileParser < Gem::TestCase
tokenizer = Gem::RequestSet::Lockfile::Tokenizer.new "foo", filename, 1, 0
parser = tokenizer.make_parser nil, nil
- e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ e = assert_raise Gem::RequestSet::Lockfile::ParseError do
parser.get :section
end
@@ -52,7 +52,7 @@ class TestGemRequestSetLockfileParser < Gem::TestCase
tokenizer = Gem::RequestSet::Lockfile::Tokenizer.new "x", filename, 1
parser = tokenizer.make_parser nil, nil
- e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ e = assert_raise Gem::RequestSet::Lockfile::ParseError do
parser.get :text, 'y'
end
@@ -518,7 +518,7 @@ DEPENDENCIES
end
def test_parse_missing
- assert_raises(Errno::ENOENT) do
+ assert_raise(Errno::ENOENT) do
parse_lockfile @set, []
end
diff --git a/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb b/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb
index b485b2c0b7..f779c33012 100644
--- a/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb
+++ b/test/rubygems/test_gem_request_set_lockfile_tokenizer.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/request_set'
require 'rubygems/request_set/lockfile'
require 'rubygems/request_set/lockfile/tokenizer'
@@ -193,7 +193,7 @@ DEPENDENCIES
def test_tokenize_conflict_markers
write_lockfile '<<<<<<<'
- e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ e = assert_raise Gem::RequestSet::Lockfile::ParseError do
tokenize_lockfile
end
@@ -202,7 +202,7 @@ DEPENDENCIES
write_lockfile '|||||||'
- e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ e = assert_raise Gem::RequestSet::Lockfile::ParseError do
tokenize_lockfile
end
@@ -211,7 +211,7 @@ DEPENDENCIES
write_lockfile '======='
- e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ e = assert_raise Gem::RequestSet::Lockfile::ParseError do
tokenize_lockfile
end
@@ -220,7 +220,7 @@ DEPENDENCIES
write_lockfile '>>>>>>>'
- e = assert_raises Gem::RequestSet::Lockfile::ParseError do
+ e = assert_raise Gem::RequestSet::Lockfile::ParseError do
tokenize_lockfile
end
diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb
index aa4c57ac27..b20b3cc0dc 100644
--- a/test/rubygems/test_gem_requirement.rb
+++ b/test/rubygems/test_gem_requirement.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require "rubygems/requirement"
class TestGemRequirement < Gem::TestCase
@@ -97,7 +97,7 @@ class TestGemRequirement < Gem::TestCase
'= junk',
'1..2',
].each do |bad|
- e = assert_raises Gem::Requirement::BadRequirementError do
+ e = assert_raise Gem::Requirement::BadRequirementError do
Gem::Requirement.parse bad
end
@@ -128,7 +128,7 @@ class TestGemRequirement < Gem::TestCase
refute_satisfied_by "1.2", r
assert_satisfied_by "1.3", r
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
assert_satisfied_by nil, r
end
end
@@ -140,7 +140,7 @@ class TestGemRequirement < Gem::TestCase
assert_satisfied_by "1.2", r
refute_satisfied_by "1.3", r
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
assert_satisfied_by nil, r
end
end
@@ -152,7 +152,7 @@ class TestGemRequirement < Gem::TestCase
assert_satisfied_by "1.2", r
refute_satisfied_by "1.3", r
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
assert_satisfied_by nil, r
end
end
@@ -164,7 +164,7 @@ class TestGemRequirement < Gem::TestCase
refute_satisfied_by "1.2", r
assert_satisfied_by "1.3", r
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
r.satisfied_by? nil
end
end
@@ -176,7 +176,7 @@ class TestGemRequirement < Gem::TestCase
assert_satisfied_by "1.2", r
assert_satisfied_by "1.3", r
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
r.satisfied_by? nil
end
end
@@ -188,7 +188,7 @@ class TestGemRequirement < Gem::TestCase
assert_satisfied_by "1.2", r
refute_satisfied_by "1.3", r
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
r.satisfied_by? nil
end
end
@@ -200,7 +200,7 @@ class TestGemRequirement < Gem::TestCase
refute_satisfied_by "1.2", r
refute_satisfied_by "1.3", r
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
r.satisfied_by? nil
end
end
@@ -212,7 +212,7 @@ class TestGemRequirement < Gem::TestCase
assert_satisfied_by "1.2", r
refute_satisfied_by "1.3", r
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
r.satisfied_by? nil
end
end
@@ -224,7 +224,7 @@ class TestGemRequirement < Gem::TestCase
assert_satisfied_by "1.2", r
assert_satisfied_by "1.3", r
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
r.satisfied_by? nil
end
end
@@ -282,18 +282,18 @@ class TestGemRequirement < Gem::TestCase
def test_illformed_requirements
[ ">>> 1.3.5", "> blah" ].each do |rq|
- assert_raises Gem::Requirement::BadRequirementError, "req [#{rq}] should fail" do
+ assert_raise 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
+ assert_raise ArgumentError do
req(">= 0").satisfied_by? Object.new
end
- assert_raises ArgumentError do
+ assert_raise ArgumentError do
req(">= 0").satisfied_by? Gem::Requirement.default
end
end
@@ -423,6 +423,40 @@ class TestGemRequirement < Gem::TestCase
assert_requirement_hash_equal "1", "1.0.0"
end
+ class Exploit < RuntimeError
+ end
+
+ def self.exploit(arg)
+ raise Exploit, "arg = #{arg}"
+ end
+
+ def test_marshal_load_attack
+ wa = Net::WriteAdapter.allocate
+ wa.instance_variable_set(:@socket, self.class)
+ wa.instance_variable_set(:@method_id, :exploit)
+ request_set = Gem::RequestSet.allocate
+ request_set.instance_variable_set(:@git_set, "id")
+ request_set.instance_variable_set(:@sets, wa)
+ wa = Net::WriteAdapter.allocate
+ wa.instance_variable_set(:@socket, request_set)
+ wa.instance_variable_set(:@method_id, :resolve)
+ ent = Gem::Package::TarReader::Entry.allocate
+ ent.instance_variable_set(:@read, 0)
+ ent.instance_variable_set(:@header, "aaa")
+ io = Net::BufferedIO.allocate
+ io.instance_variable_set(:@io, ent)
+ io.instance_variable_set(:@debug_output, wa)
+ reader = Gem::Package::TarReader.allocate
+ reader.instance_variable_set(:@io, io)
+ requirement = Gem::Requirement.allocate
+ requirement.instance_variable_set(:@requirements, reader)
+ m = [Gem::SpecFetcher, Gem::Installer, requirement]
+ e = assert_raise(TypeError) do
+ Marshal.load(Marshal.dump(m))
+ end
+ assert_equal(e.message, "wrong @requirements")
+ end
+
# Assert that two requirements are equal. Handles Gem::Requirements,
# strings, arrays, numbers, and versions.
diff --git a/test/rubygems/test_gem_resolver.rb b/test/rubygems/test_gem_resolver.rb
index 09feeac55b..ea9f9049ce 100644
--- a/test/rubygems/test_gem_resolver.rb
+++ b/test/rubygems/test_gem_resolver.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolver < Gem::TestCase
def setup
@@ -76,7 +76,7 @@ class TestGemResolver < Gem::TestCase
assert_same index_set, composed
- e = assert_raises ArgumentError do
+ e = assert_raise ArgumentError do
@DR.compose_sets nil
end
@@ -266,14 +266,14 @@ class TestGemResolver < Gem::TestCase
res = Gem::Resolver.new [a_dep], Gem::Resolver::IndexSet.new
- e = assert_raises Gem::UnsatisfiableDepedencyError do
+ e = assert_raise Gem::UnsatisfiableDependencyError do
res.resolve
end
refute_empty e.errors
end
- def test_no_overlap_specificly
+ def test_no_overlap_specifically
a = util_spec "a", '1'
b = util_spec "b", "1"
@@ -445,7 +445,7 @@ class TestGemResolver < Gem::TestCase
r = Gem::Resolver.new([ad, bd], s)
- e = assert_raises Gem::DependencyResolutionError do
+ e = assert_raise Gem::DependencyResolutionError do
r.resolve
end
@@ -469,7 +469,7 @@ class TestGemResolver < Gem::TestCase
r = Gem::Resolver.new([ad], set)
- e = assert_raises Gem::UnsatisfiableDepedencyError do
+ e = assert_raise Gem::UnsatisfiableDependencyError do
r.resolve
end
@@ -486,7 +486,7 @@ class TestGemResolver < Gem::TestCase
r = Gem::Resolver.new([ad], set(a1))
- e = assert_raises Gem::UnsatisfiableDepedencyError do
+ e = assert_raise Gem::UnsatisfiableDependencyError do
r.resolve
end
@@ -499,7 +499,7 @@ class TestGemResolver < Gem::TestCase
r = Gem::Resolver.new([ad], set(a1))
- e = assert_raises Gem::UnsatisfiableDepedencyError do
+ e = assert_raise Gem::UnsatisfiableDependencyError do
r.resolve
end
@@ -516,7 +516,7 @@ class TestGemResolver < Gem::TestCase
r = Gem::Resolver.new([ad], set(a1))
- e = assert_raises Gem::UnsatisfiableDepedencyError do
+ e = assert_raise Gem::UnsatisfiableDependencyError do
r.resolve
end
@@ -539,7 +539,7 @@ class TestGemResolver < Gem::TestCase
r = Gem::Resolver.new([ad, bd], s)
- e = assert_raises Gem::DependencyResolutionError do
+ e = assert_raise Gem::DependencyResolutionError do
r.resolve
end
@@ -611,7 +611,7 @@ class TestGemResolver < Gem::TestCase
r = Gem::Resolver.new([d1, d2, d3], s)
- assert_raises Gem::DependencyResolutionError do
+ assert_raise Gem::DependencyResolutionError do
r.resolve
end
end
@@ -629,7 +629,7 @@ class TestGemResolver < Gem::TestCase
r = Gem::Resolver.new [a_dep, b_dep], s
- assert_raises Gem::DependencyResolutionError do
+ assert_raise Gem::DependencyResolutionError do
r.resolve
end
end
@@ -781,7 +781,7 @@ class TestGemResolver < Gem::TestCase
r = Gem::Resolver.new([ad], set(a1))
- e = assert_raises Gem::UnsatisfiableDepedencyError do
+ e = assert_raise Gem::UnsatisfiableDependencyError do
r.resolve
end
diff --git a/test/rubygems/test_gem_resolver_activation_request.rb b/test/rubygems/test_gem_resolver_activation_request.rb
index f973c5956d..c7b726a230 100644
--- a/test/rubygems/test_gem_resolver_activation_request.rb
+++ b/test/rubygems/test_gem_resolver_activation_request.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverActivationRequest < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_resolver_api_set.rb b/test/rubygems/test_gem_resolver_api_set.rb
index aa17ec6f3a..c3db25d7aa 100644
--- a/test/rubygems/test_gem_resolver_api_set.rb
+++ b/test/rubygems/test_gem_resolver_api_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverAPISet < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_resolver_api_specification.rb b/test/rubygems/test_gem_resolver_api_specification.rb
index e9ba4ae481..3f9b81868f 100644
--- a/test/rubygems/test_gem_resolver_api_specification.rb
+++ b/test/rubygems/test_gem_resolver_api_specification.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverAPISpecification < Gem::TestCase
def test_initialize
diff --git a/test/rubygems/test_gem_resolver_best_set.rb b/test/rubygems/test_gem_resolver_best_set.rb
index 019ca70499..0e279d16a8 100644
--- a/test/rubygems/test_gem_resolver_best_set.rb
+++ b/test/rubygems/test_gem_resolver_best_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverBestSet < Gem::TestCase
def setup
@@ -126,7 +126,7 @@ class TestGemResolverBestSet < Gem::TestCase
error = Gem::RemoteFetcher::FetchError.new 'bogus', @gem_repo
- e = assert_raises Gem::RemoteFetcher::FetchError do
+ e = assert_raise Gem::RemoteFetcher::FetchError do
set.replace_failed_api_set error
end
diff --git a/test/rubygems/test_gem_resolver_composed_set.rb b/test/rubygems/test_gem_resolver_composed_set.rb
index 7c8ae004a3..405753a373 100644
--- a/test/rubygems/test_gem_resolver_composed_set.rb
+++ b/test/rubygems/test_gem_resolver_composed_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverComposedSet < Gem::TestCase
def test_errors
diff --git a/test/rubygems/test_gem_resolver_conflict.rb b/test/rubygems/test_gem_resolver_conflict.rb
index ff5fe9bae3..1d46e69c3f 100644
--- a/test/rubygems/test_gem_resolver_conflict.rb
+++ b/test/rubygems/test_gem_resolver_conflict.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverConflict < Gem::TestCase
def test_explanation
diff --git a/test/rubygems/test_gem_resolver_dependency_request.rb b/test/rubygems/test_gem_resolver_dependency_request.rb
index a8ddc8362b..cea0a7952d 100644
--- a/test/rubygems/test_gem_resolver_dependency_request.rb
+++ b/test/rubygems/test_gem_resolver_dependency_request.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverDependencyRequest < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_resolver_git_set.rb b/test/rubygems/test_gem_resolver_git_set.rb
index 6d048b3772..145cd6c7df 100644
--- a/test/rubygems/test_gem_resolver_git_set.rb
+++ b/test/rubygems/test_gem_resolver_git_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverGitSet < Gem::TestCase
def setup
@@ -51,7 +51,7 @@ class TestGemResolverGitSet < Gem::TestCase
assert @set.need_submodules[repository]
- refute_path_exists spec.source.repo_cache_dir
+ assert_path_not_exist spec.source.repo_cache_dir
end
def test_find_all
diff --git a/test/rubygems/test_gem_resolver_git_specification.rb b/test/rubygems/test_gem_resolver_git_specification.rb
index 4283e02765..857452c159 100644
--- a/test/rubygems/test_gem_resolver_git_specification.rb
+++ b/test/rubygems/test_gem_resolver_git_specification.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/installer'
class TestGemResolverGitSpecification < Gem::TestCase
@@ -62,7 +62,8 @@ class TestGemResolverGitSpecification < Gem::TestCase
# functional test for Gem::Ext::Builder
def test_install_extension
- skip if Gem.java_platform?
+ pend if Gem.java_platform?
+ pend if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning
name, _, repository, = git_gem 'a', 1 do |s|
s.extensions << 'ext/extconf.rb'
end
@@ -91,7 +92,7 @@ class TestGemResolverGitSpecification < Gem::TestCase
git_spec.install({})
- assert_path_exists File.join git_spec.spec.extension_dir, 'b.rb'
+ assert_path_exist File.join git_spec.spec.extension_dir, 'b.rb'
end
def test_install_installed
diff --git a/test/rubygems/test_gem_resolver_index_set.rb b/test/rubygems/test_gem_resolver_index_set.rb
index 2de766f60a..ddae0c2b82 100644
--- a/test/rubygems/test_gem_resolver_index_set.rb
+++ b/test/rubygems/test_gem_resolver_index_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverIndexSet < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_resolver_index_specification.rb b/test/rubygems/test_gem_resolver_index_specification.rb
index 702d26777b..ef9c17034e 100644
--- a/test/rubygems/test_gem_resolver_index_specification.rb
+++ b/test/rubygems/test_gem_resolver_index_specification.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/available_set'
class TestGemResolverIndexSpecification < Gem::TestCase
@@ -46,7 +46,7 @@ class TestGemResolverIndexSpecification < Gem::TestCase
called = installer
end
- assert_path_exists File.join @gemhome, 'specifications', 'a-2.gemspec'
+ assert_path_exist File.join @gemhome, 'specifications', 'a-2.gemspec'
assert_kind_of Gem::Installer, called
end
diff --git a/test/rubygems/test_gem_resolver_installed_specification.rb b/test/rubygems/test_gem_resolver_installed_specification.rb
index b102f98d00..0e5ea02fb9 100644
--- a/test/rubygems/test_gem_resolver_installed_specification.rb
+++ b/test/rubygems/test_gem_resolver_installed_specification.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverInstalledSpecification < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_resolver_installer_set.rb b/test/rubygems/test_gem_resolver_installer_set.rb
index 76c9c04a3c..928a16b9d3 100644
--- a/test/rubygems/test_gem_resolver_installer_set.rb
+++ b/test/rubygems/test_gem_resolver_installer_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverInstallerSet < Gem::TestCase
def test_add_always_install
@@ -16,7 +16,7 @@ class TestGemResolverInstallerSet < Gem::TestCase
assert_equal %w[a-2], set.always_install.map {|s| s.full_name }
- e = assert_raises Gem::UnsatisfiableDependencyError do
+ e = assert_raise Gem::UnsatisfiableDependencyError do
set.add_always_install dep('b')
end
@@ -29,7 +29,7 @@ class TestGemResolverInstallerSet < Gem::TestCase
set = Gem::Resolver::InstallerSet.new :both
- e = assert_raises Gem::UnsatisfiableDependencyError do
+ e = assert_raise Gem::UnsatisfiableDependencyError do
set.add_always_install dep 'a'
end
@@ -64,6 +64,24 @@ class TestGemResolverInstallerSet < Gem::TestCase
assert_equal %w[a-1], set.always_install.map {|s| s.full_name }
end
+ def test_add_always_install_prerelease_github_problem
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ end
+
+ # Github has an issue in which it will generate a misleading prerelease output in its RubyGems server API and
+ # returns a 0 version for the gem while it doesn't exist.
+ @fetcher.data["#{@gem_repo}prerelease_specs.#{Gem.marshal_version}.gz"] = util_gzip(Marshal.dump([
+ Gem::NameTuple.new('a', Gem::Version.new(0), 'ruby'),
+ ]))
+
+ 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'
@@ -71,7 +89,7 @@ class TestGemResolverInstallerSet < Gem::TestCase
set = Gem::Resolver::InstallerSet.new :both
- assert_raises Gem::UnsatisfiableDependencyError do
+ assert_raise Gem::UnsatisfiableDependencyError do
set.add_always_install dep('a')
end
end
@@ -192,7 +210,7 @@ class TestGemResolverInstallerSet < Gem::TestCase
def (set.remote_set).prefetch(_)
raise "called"
end
- assert_raises(RuntimeError){ set.prefetch(nil) }
+ assert_raise(RuntimeError){ set.prefetch(nil) }
set = Gem::Resolver::InstallerSet.new :local
def (set.remote_set).prefetch(_)
diff --git a/test/rubygems/test_gem_resolver_local_specification.rb b/test/rubygems/test_gem_resolver_local_specification.rb
index 0dcc436b29..c11f736128 100644
--- a/test/rubygems/test_gem_resolver_local_specification.rb
+++ b/test/rubygems/test_gem_resolver_local_specification.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/available_set'
class TestGemResolverLocalSpecification < Gem::TestCase
@@ -24,7 +24,7 @@ class TestGemResolverLocalSpecification < Gem::TestCase
called = installer
end
- assert_path_exists File.join @gemhome, 'specifications', 'a-2.gemspec'
+ assert_path_exist File.join @gemhome, 'specifications', 'a-2.gemspec'
assert_kind_of Gem::Installer, called
end
diff --git a/test/rubygems/test_gem_resolver_lock_set.rb b/test/rubygems/test_gem_resolver_lock_set.rb
index cb03e0b5f8..dc7767a3f8 100644
--- a/test/rubygems/test_gem_resolver_lock_set.rb
+++ b/test/rubygems/test_gem_resolver_lock_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverLockSet < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_resolver_lock_specification.rb b/test/rubygems/test_gem_resolver_lock_specification.rb
index 07654a9164..46c8e5edd6 100644
--- a/test/rubygems/test_gem_resolver_lock_specification.rb
+++ b/test/rubygems/test_gem_resolver_lock_specification.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/installer'
require 'rubygems/resolver'
diff --git a/test/rubygems/test_gem_resolver_requirement_list.rb b/test/rubygems/test_gem_resolver_requirement_list.rb
index af8a77ef4b..806c387669 100644
--- a/test/rubygems/test_gem_resolver_requirement_list.rb
+++ b/test/rubygems/test_gem_resolver_requirement_list.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverRequirementList < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_resolver_specification.rb b/test/rubygems/test_gem_resolver_specification.rb
index e395c473ab..9f28c1c0cf 100644
--- a/test/rubygems/test_gem_resolver_specification.rb
+++ b/test/rubygems/test_gem_resolver_specification.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverSpecification < Gem::TestCase
class TestSpec < Gem::Resolver::Specification
@@ -26,7 +26,7 @@ class TestGemResolverSpecification < Gem::TestCase
a_spec.install :install_dir => gemhome
- assert_path_exists File.join gemhome, 'gems', a.full_name
+ assert_path_exist File.join gemhome, 'gems', a.full_name
expected = File.join gemhome, 'specifications', a.spec_name
diff --git a/test/rubygems/test_gem_resolver_vendor_set.rb b/test/rubygems/test_gem_resolver_vendor_set.rb
index d14b877b45..e16d43ac1a 100644
--- a/test/rubygems/test_gem_resolver_vendor_set.rb
+++ b/test/rubygems/test_gem_resolver_vendor_set.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverVendorSet < Gem::TestCase
def setup
@@ -27,7 +27,7 @@ class TestGemResolverVendorSet < Gem::TestCase
FileUtils.rm_r directory
- e = assert_raises Gem::GemNotFoundException do
+ e = assert_raise Gem::GemNotFoundException do
@set.add_vendor_gem name, directory
end
@@ -74,7 +74,7 @@ class TestGemResolverVendorSet < Gem::TestCase
def test_load_spec
error = Object.const_defined?(:KeyError) ? KeyError : IndexError
- assert_raises error do
+ assert_raise error do
@set.load_spec 'b', v(1), Gem::Platform::RUBY, nil
end
end
diff --git a/test/rubygems/test_gem_resolver_vendor_specification.rb b/test/rubygems/test_gem_resolver_vendor_specification.rb
index dbf0c58e0b..93382ccd81 100644
--- a/test/rubygems/test_gem_resolver_vendor_specification.rb
+++ b/test/rubygems/test_gem_resolver_vendor_specification.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemResolverVendorSpecification < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_security.rb b/test/rubygems/test_gem_security.rb
index 92f9e55b21..d04bd4a8bd 100644
--- a/test/rubygems/test_gem_security.rb
+++ b/test/rubygems/test_gem_security.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/security'
unless Gem::HAVE_OPENSSL
@@ -12,6 +12,7 @@ end
class TestGemSecurity < Gem::TestCase
CHILD_KEY = load_key 'child'
+ EC_KEY = load_key 'private_ec', 'Foo bar'
ALTERNATE_CERT = load_cert 'child'
CHILD_CERT = load_cert 'child'
@@ -103,11 +104,38 @@ class TestGemSecurity < Gem::TestCase
end
def test_class_create_key
- key = @SEC.create_key 1024
+ key = @SEC.create_key 'rsa'
assert_kind_of OpenSSL::PKey::RSA, key
end
+ def test_class_create_key_downcases
+ key = @SEC.create_key 'DSA'
+
+ assert_kind_of OpenSSL::PKey::DSA, key
+ end
+
+ def test_class_create_key_raises_unknown_algorithm
+ e = assert_raise Gem::Security::Exception do
+ @SEC.create_key 'NOT_RSA'
+ end
+
+ assert_equal "NOT_RSA algorithm not found. RSA, DSA, and EC algorithms are supported.",
+ e.message
+ end
+
+ def test_class_get_public_key_rsa
+ pkey_pem = PRIVATE_KEY.public_key.to_pem
+
+ assert_equal pkey_pem, @SEC.get_public_key(PRIVATE_KEY).to_pem
+ end
+
+ def test_class_get_public_key_ec
+ pkey = @SEC.get_public_key(EC_KEY)
+
+ assert_respond_to pkey, :to_pem
+ end
+
def test_class_email_to_name
assert_equal '/CN=nobody/DC=example',
@SEC.email_to_name('nobody@example').to_s
@@ -135,7 +163,7 @@ class TestGemSecurity < Gem::TestCase
end
def test_class_re_sign_not_self_signed
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
Gem::Security.re_sign CHILD_CERT, CHILD_KEY
end
@@ -149,7 +177,7 @@ class TestGemSecurity < Gem::TestCase
end
def test_class_re_sign_wrong_key
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
Gem::Security.re_sign ALTERNATE_CERT, PRIVATE_KEY
end
@@ -259,13 +287,13 @@ class TestGemSecurity < Gem::TestCase
end
def test_class_write
- key = @SEC.create_key 1024
+ key = @SEC.create_key 'rsa'
path = File.join @tempdir, 'test-private_key.pem'
@SEC.write key, path
- assert_path_exists path
+ assert_path_exist path
key_from_file = File.read path
@@ -273,7 +301,7 @@ class TestGemSecurity < Gem::TestCase
end
def test_class_write_encrypted
- key = @SEC.create_key 1024
+ key = @SEC.create_key 'rsa'
path = File.join @tempdir, 'test-private_encrypted_key.pem'
@@ -281,7 +309,7 @@ class TestGemSecurity < Gem::TestCase
@SEC.write key, path, 0600, passphrase
- assert_path_exists path
+ assert_path_exist path
key_from_file = OpenSSL::PKey::RSA.new File.read(path), passphrase
@@ -289,7 +317,7 @@ class TestGemSecurity < Gem::TestCase
end
def test_class_write_encrypted_cipher
- key = @SEC.create_key 1024
+ key = @SEC.create_key 'rsa'
path = File.join @tempdir, 'test-private_encrypted__with_non_default_cipher_key.pem'
@@ -299,7 +327,7 @@ class TestGemSecurity < Gem::TestCase
@SEC.write key, path, 0600, passphrase, cipher
- assert_path_exists path
+ assert_path_exist path
key_file_contents = File.read(path)
diff --git a/test/rubygems/test_gem_security_policy.rb b/test/rubygems/test_gem_security_policy.rb
index 85e4590655..515b8ea00b 100644
--- a/test/rubygems/test_gem_security_policy.rb
+++ b/test/rubygems/test_gem_security_policy.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
unless Gem::HAVE_OPENSSL
warn 'Skipping Gem::Security::Policy tests. openssl not found.'
@@ -77,7 +77,7 @@ class TestGemSecurityPolicy < Gem::TestCase
invalid = digest 'hello!'
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@almost_no.check_data PUBLIC_KEY, @digest, signature, invalid
end
@@ -91,7 +91,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_chain_empty_chain
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@chain.check_chain [], Time.now
end
@@ -101,7 +101,7 @@ class TestGemSecurityPolicy < Gem::TestCase
def test_check_chain_invalid
chain = [PUBLIC_CERT, CHILD_CERT, INVALIDCHILD_CERT]
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@chain.check_chain chain, Time.now
end
@@ -111,7 +111,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_chain_no_chain
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@chain.check_chain nil, Time.now
end
@@ -123,7 +123,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_cert_expired
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@low.check_cert EXPIRED_CERT, nil, Time.now
end
@@ -133,7 +133,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_cert_future
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@low.check_cert FUTURE_CERT, nil, Time.now
end
@@ -143,7 +143,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_cert_invalid_issuer
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@low.check_cert INVALID_ISSUER_CERT, PUBLIC_CERT, Time.now
end
@@ -157,7 +157,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_cert_no_signer
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@high.check_cert(nil, nil, Time.now)
end
@@ -171,7 +171,7 @@ class TestGemSecurityPolicy < Gem::TestCase
def test_check_key_no_signer
assert @almost_no.check_key(nil, nil)
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@high.check_key(nil, nil)
end
@@ -179,7 +179,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_key_wrong_key
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@almost_no.check_key(PUBLIC_CERT, ALTERNATE_KEY)
end
@@ -194,7 +194,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_root_empty_chain
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@chain.check_root [], Time.now
end
@@ -204,7 +204,7 @@ class TestGemSecurityPolicy < Gem::TestCase
def test_check_root_invalid_signer
chain = [INVALID_SIGNER_CERT]
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@chain.check_root chain, Time.now
end
@@ -216,7 +216,7 @@ class TestGemSecurityPolicy < Gem::TestCase
def test_check_root_not_self_signed
chain = [INVALID_ISSUER_CERT]
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@chain.check_root chain, Time.now
end
@@ -226,7 +226,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_root_no_chain
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@chain.check_root nil, Time.now
end
@@ -246,7 +246,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_trust_empty_chain
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@chain.check_trust [], @digest, @trust_dir
end
@@ -256,7 +256,7 @@ class TestGemSecurityPolicy < Gem::TestCase
def test_check_trust_mismatch
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@high.check_trust [WRONG_KEY_CERT], @digest, @trust_dir
end
@@ -265,7 +265,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_trust_no_chain
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@chain.check_trust nil, @digest, @trust_dir
end
@@ -273,7 +273,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_trust_no_trust
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@high.check_trust [PUBLIC_CERT], @digest, @trust_dir
end
@@ -281,7 +281,7 @@ class TestGemSecurityPolicy < Gem::TestCase
end
def test_check_trust_no_trust_child
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@high.check_trust [PUBLIC_CERT, CHILD_CERT], @digest, @trust_dir
end
@@ -315,7 +315,7 @@ class TestGemSecurityPolicy < Gem::TestCase
_, signatures = dummy_signatures
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@almost_no.verify [PUBLIC_CERT], nil, {}, signatures
end
@@ -327,7 +327,7 @@ class TestGemSecurityPolicy < Gem::TestCase
_, signatures = dummy_signatures
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@no.verify [PUBLIC_CERT], nil, {}, signatures
end
@@ -345,7 +345,7 @@ class TestGemSecurityPolicy < Gem::TestCase
assert_match "WARNING: some_gem is not signed\n", @ui.error
- assert_raises Gem::Security::Exception do
+ assert_raise Gem::Security::Exception do
@high.verify [PUBLIC_CERT], nil, digests, {}
end
end
@@ -370,7 +370,7 @@ class TestGemSecurityPolicy < Gem::TestCase
signatures[1] = PRIVATE_KEY.sign @digest.new, data.digest
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@almost_no.verify [PUBLIC_CERT], nil, digests, signatures
end
@@ -387,7 +387,7 @@ class TestGemSecurityPolicy < Gem::TestCase
assert_equal "WARNING: email:nobody@example is not trusted for some_gem\n",
@ui.error
- assert_raises Gem::Security::Exception do
+ assert_raise Gem::Security::Exception do
@medium.verify [PUBLIC_CERT], nil, digests, signatures
end
end
@@ -402,7 +402,7 @@ class TestGemSecurityPolicy < Gem::TestCase
signature = PRIVATE_KEY.sign 'sha512', data.digest
signatures = { 0 => signature }
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@almost_no.verify [PUBLIC_CERT], nil, digests, signatures
end
@@ -486,7 +486,7 @@ class TestGemSecurityPolicy < Gem::TestCase
signatures['metadata.gz'] =
PRIVATE_KEY.sign @digest.new, metadata_gz_digest.digest
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@high.verify_signatures @spec, digests, signatures
end
@@ -509,7 +509,7 @@ class TestGemSecurityPolicy < Gem::TestCase
digests = package.digest s
digests[Gem::Security::DIGEST_NAME]['data.tar.gz'] = @digest.hexdigest 'hello'
- assert_raises Gem::Security::Exception do
+ assert_raise Gem::Security::Exception do
@high.verify_signatures @spec, digests, {}
end
end
diff --git a/test/rubygems/test_gem_security_signer.rb b/test/rubygems/test_gem_security_signer.rb
index 8a09f97f26..7bd6510b50 100644
--- a/test/rubygems/test_gem_security_signer.rb
+++ b/test/rubygems/test_gem_security_signer.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
unless Gem::HAVE_OPENSSL
warn 'Skipping Gem::Security::Signer tests. openssl not found.'
@@ -41,7 +41,7 @@ class TestGemSecuritySigner < Gem::TestCase
end
def test_initialize_cert_chain_invalid
- assert_raises OpenSSL::X509::CertificateError do
+ assert_raise OpenSSL::X509::CertificateError do
Gem::Security::Signer.new nil, ['garbage']
end
end
@@ -134,7 +134,7 @@ toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA==
def test_sign_expired
signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT]
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
signer.sign 'hello'
end
@@ -142,7 +142,7 @@ toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA==
end
def test_sign_expired_auto_update
- skip if Gem.java_platform?
+ pend if Gem.java_platform?
FileUtils.mkdir_p File.join(Gem.user_home, '.gem'), :mode => 0700
private_key_path = File.join(Gem.user_home, '.gem', 'gem-private_key.pem')
@@ -165,7 +165,7 @@ toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA==
expired_path =
File.join Gem.user_home, '.gem', "gem-public_cert.pem.expired.#{expiry}"
- assert_path_exists expired_path
+ assert_path_exist expired_path
assert_equal EXPIRED_CERT.to_pem, File.read(expired_path)
end
@@ -186,7 +186,7 @@ toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA==
signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT]
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
signer.sign 'hello'
end
@@ -202,7 +202,7 @@ toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA==
def test_sign_wrong_key
signer = Gem::Security::Signer.new ALTERNATE_KEY, [PUBLIC_CERT]
- assert_raises Gem::Security::Exception do
+ assert_raise Gem::Security::Exception do
signer.sign 'hello'
end
end
@@ -210,7 +210,7 @@ toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA==
def test_sign_no_certs
signer = Gem::Security::Signer.new ALTERNATE_KEY, []
- assert_raises Gem::Security::Exception do
+ assert_raise Gem::Security::Exception do
signer.sign 'hello'
end
end
diff --git a/test/rubygems/test_gem_security_trust_dir.rb b/test/rubygems/test_gem_security_trust_dir.rb
index 201de9d36b..fc88c84865 100644
--- a/test/rubygems/test_gem_security_trust_dir.rb
+++ b/test/rubygems/test_gem_security_trust_dir.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
unless Gem::HAVE_OPENSSL
warn 'Skipping Gem::Security::TrustDir tests. openssl not found.'
@@ -53,7 +53,7 @@ class TestGemSecurityTrustDir < Gem::TestCase
trusted = @trust_dir.cert_path PUBLIC_CERT
- assert_path_exists trusted
+ assert_path_exist trusted
mask = 0100600 & (~File.umask)
@@ -63,11 +63,11 @@ class TestGemSecurityTrustDir < Gem::TestCase
end
def test_verify
- refute_path_exists @dest_dir
+ assert_path_not_exist @dest_dir
@trust_dir.verify
- assert_path_exists @dest_dir
+ assert_path_exist @dest_dir
mask = 040700 & (~File.umask)
mask |= 0200000 if /aix/ =~ RUBY_PLATFORM
@@ -78,7 +78,7 @@ class TestGemSecurityTrustDir < Gem::TestCase
def test_verify_file
FileUtils.touch @dest_dir
- e = assert_raises Gem::Security::Exception do
+ e = assert_raise Gem::Security::Exception do
@trust_dir.verify
end
diff --git a/test/rubygems/test_gem_server.rb b/test/rubygems/test_gem_server.rb
index 0e283da5a4..f6aa99fb02 100644
--- a/test/rubygems/test_gem_server.rb
+++ b/test/rubygems/test_gem_server.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/server'
require 'stringio'
@@ -133,7 +133,7 @@ class TestGemServer < Gem::TestCase
def test_listen
util_listen
- capture_io do
+ capture_output do
@server.listen
end
@@ -143,7 +143,7 @@ class TestGemServer < Gem::TestCase
def test_listen_addresses
util_listen
- capture_io do
+ capture_output do
@server.listen %w[a b]
end
@@ -365,7 +365,7 @@ class TestGemServer < Gem::TestCase
specs_dir = File.join dir, 'specifications'
FileUtils.mkdir_p specs_dir
- open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ File.open File.join(specs_dir, spec.spec_name), 'w' do |io|
io.write spec.to_ruby
end
@@ -420,7 +420,7 @@ class TestGemServer < Gem::TestCase
specs_dir = File.join dir, 'specifications'
FileUtils.mkdir_p specs_dir
- open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ File.open File.join(specs_dir, spec.spec_name), 'w' do |io|
io.write spec.to_ruby
end
@@ -475,7 +475,7 @@ class TestGemServer < Gem::TestCase
specs_dir = File.join dir, 'specifications'
FileUtils.mkdir_p specs_dir
- open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ File.open File.join(specs_dir, spec.spec_name), 'w' do |io|
io.write spec.to_ruby
end
@@ -502,7 +502,7 @@ class TestGemServer < Gem::TestCase
specs_dir = File.join dir, 'specifications'
FileUtils.mkdir_p specs_dir
- open File.join(specs_dir, spec.spec_name), 'w' do |io|
+ File.open File.join(specs_dir, spec.spec_name), 'w' do |io|
io.write spec.to_ruby
end
diff --git a/test/rubygems/test_gem_silent_ui.rb b/test/rubygems/test_gem_silent_ui.rb
index 3c4811aad5..355255fb48 100644
--- a/test/rubygems/test_gem_silent_ui.rb
+++ b/test/rubygems/test_gem_silent_ui.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/user_interaction'
require 'timeout'
@@ -16,7 +16,7 @@ class TestGemSilentUI < Gem::TestCase
def test_ask
value = nil
- out, err = capture_io do
+ out, err = capture_output do
use_ui @sui do
value = @sui.ask 'Problem?'
end
@@ -30,7 +30,7 @@ class TestGemSilentUI < Gem::TestCase
def test_ask_for_password
value = nil
- out, err = capture_io do
+ out, err = capture_output do
use_ui @sui do
value = @sui.ask_for_password 'Problem?'
end
@@ -44,9 +44,9 @@ class TestGemSilentUI < Gem::TestCase
def test_ask_yes_no
value = nil
- out, err = capture_io do
+ out, err = capture_output do
use_ui @sui do
- assert_raises(Gem::OperationNotSupportedError) do
+ assert_raise(Gem::OperationNotSupportedError) do
@sui.ask_yes_no 'Problem?'
end
end
@@ -55,7 +55,7 @@ class TestGemSilentUI < Gem::TestCase
assert_empty out, 'No output'
assert_empty err, 'No output'
- out, err = capture_io do
+ out, err = capture_output do
use_ui @sui do
value = @sui.ask_yes_no 'Problem?', true
end
@@ -66,7 +66,7 @@ class TestGemSilentUI < Gem::TestCase
assert value, 'Value is true'
- out, err = capture_io do
+ out, err = capture_output do
use_ui @sui do
value = @sui.ask_yes_no 'Problem?', false
end
@@ -80,7 +80,7 @@ class TestGemSilentUI < Gem::TestCase
def test_choose_from_list
value = nil
- out, err = capture_io do
+ out, err = capture_output do
use_ui @sui do
value = @sui.choose_from_list 'Problem?', %w[yes no]
end
@@ -93,7 +93,7 @@ class TestGemSilentUI < Gem::TestCase
end
def test_progress_reporter
- out, err = capture_io do
+ out, err = capture_output do
use_ui @sui do
@sui.progress_reporter 10, 'hi'
end
@@ -104,7 +104,7 @@ class TestGemSilentUI < Gem::TestCase
end
def test_download_reporter
- out, err = capture_io do
+ out, err = capture_output do
use_ui @sui do
@sui.download_reporter.fetch 'a.gem', 1024
end
diff --git a/test/rubygems/test_gem_source.rb b/test/rubygems/test_gem_source.rb
index d6c2ffa051..24312626f4 100644
--- a/test/rubygems/test_gem_source.rb
+++ b/test/rubygems/test_gem_source.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/source'
require 'rubygems/indexer'
@@ -22,7 +22,7 @@ class TestGemSource < Gem::TestCase
end
def test_initialize_invalid_uri
- assert_raises URI::InvalidURIError do
+ assert_raise URI::InvalidURIError do
Gem::Source.new 'git@example:a.git'
end
end
@@ -185,7 +185,7 @@ class TestGemSource < Gem::TestCase
def test_load_specs_from_unavailable_uri
src = Gem::Source.new("http://not-there.nothing")
- assert_raises Gem::RemoteFetcher::FetchError do
+ assert_raise Gem::RemoteFetcher::FetchError do
src.load_specs :latest
end
end
diff --git a/test/rubygems/test_gem_source_fetch_problem.rb b/test/rubygems/test_gem_source_fetch_problem.rb
index 1a0545a893..816407781d 100644
--- a/test/rubygems/test_gem_source_fetch_problem.rb
+++ b/test/rubygems/test_gem_source_fetch_problem.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemSourceFetchProblem < Gem::TestCase
def test_exception
@@ -8,7 +8,7 @@ class TestGemSourceFetchProblem < Gem::TestCase
sf = Gem::SourceFetchProblem.new source, error
- e = assert_raises RuntimeError do
+ e = assert_raise RuntimeError do
raise sf
end
@@ -23,4 +23,14 @@ class TestGemSourceFetchProblem < Gem::TestCase
refute_match sf.wordy, 'secret'
end
+
+ def test_source_password_no_redacted
+ source = Gem::Source.new 'https://username:secret@gemsource.com'
+ error = RuntimeError.new 'test'
+
+ sf = Gem::SourceFetchProblem.new source, error
+ sf.wordy
+
+ assert_match 'secret', source.uri.to_s
+ end
end
diff --git a/test/rubygems/test_gem_source_git.rb b/test/rubygems/test_gem_source_git.rb
index f5406d2215..36e904732b 100644
--- a/test/rubygems/test_gem_source_git.rb
+++ b/test/rubygems/test_gem_source_git.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/source'
class TestGemSourceGit < Gem::TestCase
@@ -24,7 +24,7 @@ class TestGemSourceGit < Gem::TestCase
def test_checkout
@source.checkout
- assert_path_exists File.join @source.install_dir, 'a.gemspec'
+ assert_path_exist File.join @source.install_dir, 'a.gemspec'
end
def test_checkout_master
@@ -39,7 +39,7 @@ class TestGemSourceGit < Gem::TestCase
@source.checkout
- assert_path_exists File.join @source.install_dir, 'b.gemspec'
+ assert_path_exist File.join @source.install_dir, 'b.gemspec'
end
def test_checkout_local
@@ -49,7 +49,7 @@ class TestGemSourceGit < Gem::TestCase
install_dir = File.join Gem.dir, 'bundler', 'gems', "a-#{@head[0..11]}"
- refute_path_exists File.join install_dir, 'a.gemspec'
+ assert_path_not_exist File.join install_dir, 'a.gemspec'
end
def test_checkout_local_cached
@@ -59,10 +59,15 @@ class TestGemSourceGit < Gem::TestCase
@source.checkout
- assert_path_exists File.join @source.install_dir, 'a.gemspec'
+ assert_path_exist File.join @source.install_dir, 'a.gemspec'
end
def test_checkout_submodules
+ # We need to allow to checkout submodules with file:// protocol
+ # CVE-2022-39253
+ # https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/
+ system(@git, *%W[config --global protocol.file.allow always])
+
source = Gem::Source::Git.new @name, @repository, 'master', true
git_gem 'b'
@@ -76,14 +81,14 @@ class TestGemSourceGit < Gem::TestCase
source.checkout
- assert_path_exists File.join source.install_dir, 'a.gemspec'
- assert_path_exists File.join source.install_dir, 'b/b.gemspec'
+ assert_path_exist File.join source.install_dir, 'a.gemspec'
+ assert_path_exist File.join source.install_dir, 'b/b.gemspec'
end
def test_cache
assert @source.cache
- assert_path_exists @source.repo_cache_dir
+ assert_path_exist @source.repo_cache_dir
Dir.chdir @source.repo_cache_dir do
assert_equal @head, Gem::Util.popen(@git, 'rev-parse', 'master').strip
@@ -95,7 +100,7 @@ class TestGemSourceGit < Gem::TestCase
@source.cache
- refute_path_exists @source.repo_cache_dir
+ assert_path_not_exist @source.repo_cache_dir
end
def test_dir_shortref
@@ -186,7 +191,7 @@ class TestGemSourceGit < Gem::TestCase
source.cache
- e = assert_raises Gem::Exception do
+ e = assert_raise Gem::Exception do
capture_subprocess_io { source.rev_parse }
end
@@ -240,7 +245,7 @@ class TestGemSourceGit < Gem::TestCase
specs = nil
- capture_io do
+ capture_output do
specs = source.specs
end
@@ -275,7 +280,7 @@ class TestGemSourceGit < Gem::TestCase
source = Gem::Source::Git.new @name, @repository, 'master', true
source.remote = false
- capture_io do
+ capture_output do
assert_empty source.specs
end
end
diff --git a/test/rubygems/test_gem_source_installed.rb b/test/rubygems/test_gem_source_installed.rb
index 93aa4eb039..ef9b63e611 100644
--- a/test/rubygems/test_gem_source_installed.rb
+++ b/test/rubygems/test_gem_source_installed.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/source'
class TestGemSourceInstalled < Gem::TestCase
diff --git a/test/rubygems/test_gem_source_list.rb b/test/rubygems/test_gem_source_list.rb
index 7c60af3ff8..6ac5dbb2a6 100644
--- a/test/rubygems/test_gem_source_list.rb
+++ b/test/rubygems/test_gem_source_list.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'rubygems'
require 'rubygems/source_list'
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemSourceList < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_source_local.rb b/test/rubygems/test_gem_source_local.rb
index 7417f8d111..2d4ddbc3a4 100644
--- a/test/rubygems/test_gem_source_local.rb
+++ b/test/rubygems/test_gem_source_local.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/source'
require 'fileutils'
diff --git a/test/rubygems/test_gem_source_lock.rb b/test/rubygems/test_gem_source_lock.rb
index 8c55926d94..5f916cdf7f 100644
--- a/test/rubygems/test_gem_source_lock.rb
+++ b/test/rubygems/test_gem_source_lock.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemSourceLock < Gem::TestCase
def test_fetch_spec
diff --git a/test/rubygems/test_gem_source_specific_file.rb b/test/rubygems/test_gem_source_specific_file.rb
index 003fbec81d..72ed993a88 100644
--- a/test/rubygems/test_gem_source_specific_file.rb
+++ b/test/rubygems/test_gem_source_specific_file.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/source'
class TestGemSourceSpecificFile < Gem::TestCase
@@ -27,7 +27,7 @@ class TestGemSourceSpecificFile < Gem::TestCase
end
def test_fetch_spec_fails_on_unknown_name
- assert_raises Gem::Exception do
+ assert_raise Gem::Exception do
@sf.fetch_spec(nil)
end
end
diff --git a/test/rubygems/test_gem_source_subpath_problem.rb b/test/rubygems/test_gem_source_subpath_problem.rb
index b2289ea625..c37df39f0c 100644
--- a/test/rubygems/test_gem_source_subpath_problem.rb
+++ b/test/rubygems/test_gem_source_subpath_problem.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/source'
class TestGemSourceSubpathProblem < Gem::TestCase
diff --git a/test/rubygems/test_gem_source_vendor.rb b/test/rubygems/test_gem_source_vendor.rb
index 18a3f47f45..a5ffb0f223 100644
--- a/test/rubygems/test_gem_source_vendor.rb
+++ b/test/rubygems/test_gem_source_vendor.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/source'
class TestGemSourceVendor < Gem::TestCase
diff --git a/test/rubygems/test_gem_spec_fetcher.rb b/test/rubygems/test_gem_spec_fetcher.rb
index 9c756aacf3..afae46e120 100644
--- a/test/rubygems/test_gem_spec_fetcher.rb
+++ b/test/rubygems/test_gem_spec_fetcher.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/spec_fetcher'
class TestGemSpecFetcher < Gem::TestCase
@@ -38,7 +38,7 @@ class TestGemSpecFetcher < Gem::TestCase
end
def test_initialize_unwritable_home_dir
- skip 'chmod not supported' if Gem.win_platform?
+ pend 'chmod not supported' if Gem.win_platform?
FileUtils.chmod 0000, Gem.user_home
diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb
index 29a3e74bfc..bfd0db347e 100644
--- a/test/rubygems/test_gem_specification.rb
+++ b/test/rubygems/test_gem_specification.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
require 'benchmark'
-require 'rubygems/test_case'
+require_relative 'helper'
require 'date'
require 'pathname'
require 'stringio'
@@ -107,8 +107,6 @@ end
end
@current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
-
- load 'rubygems/syck_hack.rb'
end
def test_self_find_active_stub_by_path
@@ -129,265 +127,241 @@ end
end
def test_self_activate_ambiguous_direct
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec("b", "1", { "c" => ">= 1" }, "lib/d#{$$}.rb")
- b2 = util_spec("b", "2", { "c" => ">= 2" }, "lib/d#{$$}.rb")
- c1 = util_spec "c", "1"
- c2 = util_spec "c", "2"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec("b", "1", { "c" => ">= 1" }, "lib/d#{$$}.rb")
+ b2 = util_spec("b", "2", { "c" => ">= 2" }, "lib/d#{$$}.rb")
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
- Gem::Specification.reset
- install_specs c1, c2, b1, b2, a1
+ Gem::Specification.reset
+ install_specs c1, c2, b1, b2, a1
- a1.activate
- assert_equal %w[a-1], loaded_spec_names
- assert_equal ["b (> 0)"], unresolved_names
+ a1.activate
+ assert_equal %w[a-1], loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
- require "d#{$$}"
+ require "d#{$$}"
- assert_equal %w[a-1 b-2 c-2], loaded_spec_names
- assert_equal [], unresolved_names
- end
+ assert_equal %w[a-1 b-2 c-2], loaded_spec_names
+ assert_equal [], unresolved_names
end
def test_find_in_unresolved_tree_is_not_exponentiental
- save_loaded_features do
- num_of_pkg = 7
- num_of_version_per_pkg = 3
- packages = (0..num_of_pkg).map do |pkgi|
- (0..num_of_version_per_pkg).map do |pkg_version|
- deps = Hash[((pkgi + 1)..num_of_pkg).map do |deppkgi|
- ["pkg#{deppkgi}", ">= 0"]
- end]
- util_spec "pkg#{pkgi}", pkg_version.to_s, deps
- end
+ num_of_pkg = 7
+ num_of_version_per_pkg = 3
+ packages = (0..num_of_pkg).map do |pkgi|
+ (0..num_of_version_per_pkg).map do |pkg_version|
+ deps = Hash[((pkgi + 1)..num_of_pkg).map do |deppkgi|
+ ["pkg#{deppkgi}", ">= 0"]
+ end]
+ util_spec "pkg#{pkgi}", pkg_version.to_s, deps
end
- base = util_spec "pkg_base", "1", {"pkg0" => ">= 0"}
+ end
+ base = util_spec "pkg_base", "1", {"pkg0" => ">= 0"}
- Gem::Specification.reset
- install_specs(*packages.flatten.reverse)
- install_specs base
- base.activate
+ Gem::Specification.reset
+ install_specs(*packages.flatten.reverse)
+ install_specs base
+ base.activate
- tms = Benchmark.measure do
- assert_raises(LoadError) { require 'no_such_file_foo' }
- end
- assert_operator tms.total, :<=, 10
+ tms = Benchmark.measure do
+ assert_raise(LoadError) { require 'no_such_file_foo' }
end
+ assert_operator tms.total, :<=, 10
end
def test_self_activate_ambiguous_indirect
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 1"
- b2 = util_spec "b", "2", "c" => ">= 2"
- c1 = util_spec "c", "1", nil, "lib/d#{$$}.rb"
- c2 = util_spec "c", "2", nil, "lib/d#{$$}.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1", nil, "lib/d#{$$}.rb"
+ c2 = util_spec "c", "2", nil, "lib/d#{$$}.rb"
- install_specs c1, c2, b1, b2, a1
+ install_specs c1, c2, b1, b2, a1
- a1.activate
- assert_equal %w[a-1], loaded_spec_names
- assert_equal ["b (> 0)"], unresolved_names
+ a1.activate
+ assert_equal %w[a-1], loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
- require "d#{$$}"
+ require "d#{$$}"
- assert_equal %w[a-1 b-2 c-2], loaded_spec_names
- assert_equal [], unresolved_names
- end
+ assert_equal %w[a-1 b-2 c-2], loaded_spec_names
+ assert_equal [], unresolved_names
end
def test_self_activate_ambiguous_indirect_conflict
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- a2 = util_spec "a", "2", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 1"
- b2 = util_spec "b", "2", "c" => ">= 2"
- c1 = util_spec "c", "1", nil, "lib/d#{$$}.rb"
- c2 = util_spec("c", "2", { "a" => "1" }, "lib/d#{$$}.rb") # conflicts with a-2
+ a1 = util_spec "a", "1", "b" => "> 0"
+ a2 = util_spec "a", "2", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1", nil, "lib/d#{$$}.rb"
+ c2 = util_spec("c", "2", { "a" => "1" }, "lib/d#{$$}.rb") # conflicts with a-2
- install_specs c1, b1, a1, a2, c2, b2
+ install_specs c1, b1, a1, a2, c2, b2
- a2.activate
- assert_equal %w[a-2], loaded_spec_names
- assert_equal ["b (> 0)"], unresolved_names
+ a2.activate
+ assert_equal %w[a-2], loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
- require "d#{$$}"
+ require "d#{$$}"
- assert_equal %w[a-2 b-1 c-1], loaded_spec_names
- assert_equal [], unresolved_names
- end
+ assert_equal %w[a-2 b-1 c-1], loaded_spec_names
+ assert_equal [], unresolved_names
end
def test_self_activate_ambiguous_unrelated
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 1"
- b2 = util_spec "b", "2", "c" => ">= 2"
- c1 = util_spec "c", "1"
- c2 = util_spec "c", "2"
- d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2"
+ d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb"
- install_specs d1, c1, c2, b1, b2, a1
+ install_specs d1, c1, c2, b1, b2, a1
- a1.activate
- assert_equal %w[a-1], loaded_spec_names
- assert_equal ["b (> 0)"], unresolved_names
+ a1.activate
+ assert_equal %w[a-1], loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
- require "d#{$$}"
+ require "d#{$$}"
- assert_equal %w[a-1 d-1], loaded_spec_names
- assert_equal ["b (> 0)"], unresolved_names
- end
+ assert_equal %w[a-1 d-1], loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
end
def test_require_should_prefer_latest_gem_level1
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
- b2 = util_spec "b", "2", "c" => ">= 0"
- c1 = util_spec "c", "1", nil, "lib/c#{$$}.rb" # 1st level
- c2 = util_spec "c", "2", nil, "lib/c#{$$}.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
+ b2 = util_spec "b", "2", "c" => ">= 0"
+ c1 = util_spec "c", "1", nil, "lib/c#{$$}.rb" # 1st level
+ c2 = util_spec "c", "2", nil, "lib/c#{$$}.rb"
- install_specs c1, c2, b1, b2, a1
+ install_specs c1, c2, b1, b2, a1
- a1.activate
+ a1.activate
- require "c#{$$}"
+ require "c#{$$}"
- assert_equal %w[a-1 b-2 c-2], loaded_spec_names
- end
+ assert_equal %w[a-1 b-2 c-2], loaded_spec_names
end
def test_require_should_prefer_latest_gem_level2
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
- b2 = util_spec "b", "2", "c" => ">= 0"
- c1 = util_spec "c", "1", "d" => ">= 0" # 1st level
- c2 = util_spec "c", "2", "d" => ">= 0"
- d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb" # 2nd level
- d2 = util_spec "d", "2", nil, "lib/d#{$$}.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
+ b2 = util_spec "b", "2", "c" => ">= 0"
+ c1 = util_spec "c", "1", "d" => ">= 0" # 1st level
+ c2 = util_spec "c", "2", "d" => ">= 0"
+ d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb" # 2nd level
+ d2 = util_spec "d", "2", nil, "lib/d#{$$}.rb"
- install_specs d1, d2, c1, c2, b1, b2, a1
+ install_specs d1, d2, c1, c2, b1, b2, a1
- a1.activate
+ a1.activate
- require "d#{$$}"
+ require "d#{$$}"
- assert_equal %w[a-1 b-2 c-2 d-2], loaded_spec_names
- end
+ assert_equal %w[a-1 b-2 c-2 d-2], loaded_spec_names
end
def test_require_finds_in_2nd_level_indirect
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
- b2 = util_spec "b", "2", "c" => ">= 0"
- c1 = util_spec "c", "1", "d" => "<= 2" # 1st level
- c2 = util_spec "c", "2", "d" => "<= 2"
- d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb" # 2nd level
- d2 = util_spec "d", "2", nil, "lib/d#{$$}.rb"
- d3 = util_spec "d", "3", nil, "lib/d#{$$}.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
+ b2 = util_spec "b", "2", "c" => ">= 0"
+ c1 = util_spec "c", "1", "d" => "<= 2" # 1st level
+ c2 = util_spec "c", "2", "d" => "<= 2"
+ d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb" # 2nd level
+ d2 = util_spec "d", "2", nil, "lib/d#{$$}.rb"
+ d3 = util_spec "d", "3", nil, "lib/d#{$$}.rb"
- install_specs d1, d2, d3, c1, c2, b1, b2, a1
+ install_specs d1, d2, d3, c1, c2, b1, b2, a1
- a1.activate
+ a1.activate
- require "d#{$$}"
+ require "d#{$$}"
- assert_equal %w[a-1 b-2 c-2 d-2], loaded_spec_names
- end
+ assert_equal %w[a-1 b-2 c-2 d-2], loaded_spec_names
end
def test_require_should_prefer_reachable_gems
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
- b2 = util_spec "b", "2", "c" => ">= 0"
- c1 = util_spec "c", "1", "d" => "<= 2" # 1st level
- c2 = util_spec "c", "2", "d" => "<= 2"
- d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb" # 2nd level
- d2 = util_spec "d", "2", nil, "lib/d#{$$}.rb"
- d3 = util_spec "d", "3", nil, "lib/d#{$$}.rb"
- e = util_spec "anti_d", "1", nil, "lib/d#{$$}.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 0" # unresolved
+ b2 = util_spec "b", "2", "c" => ">= 0"
+ c1 = util_spec "c", "1", "d" => "<= 2" # 1st level
+ c2 = util_spec "c", "2", "d" => "<= 2"
+ d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb" # 2nd level
+ d2 = util_spec "d", "2", nil, "lib/d#{$$}.rb"
+ d3 = util_spec "d", "3", nil, "lib/d#{$$}.rb"
+ e = util_spec "anti_d", "1", nil, "lib/d#{$$}.rb"
+
+ install_specs d1, d2, d3, e, c1, c2, b1, b2, a1
- install_specs d1, d2, d3, e, c1, c2, b1, b2, a1
-
- a1.activate
+ a1.activate
- require "d#{$$}"
+ require "d#{$$}"
- assert_equal %w[a-1 b-2 c-2 d-2], loaded_spec_names
- end
+ assert_equal %w[a-1 b-2 c-2 d-2], loaded_spec_names
end
def test_require_should_not_conflict
- save_loaded_features do
- base = util_spec "0", "1", "A" => ">= 1"
- a1 = util_spec "A", "1", {"c" => ">= 2", "b" => "> 0"}, "lib/a.rb"
- a2 = util_spec "A", "2", {"c" => ">= 2", "b" => "> 0"}, "lib/a.rb"
- b1 = util_spec "b", "1", {"c" => "= 1"}, "lib/d#{$$}.rb"
- b2 = util_spec "b", "2", {"c" => "= 2"}, "lib/d#{$$}.rb"
- c1 = util_spec "c", "1", {}, "lib/c.rb"
- c2 = util_spec "c", "2", {}, "lib/c.rb"
- c3 = util_spec "c", "3", {}, "lib/c.rb"
-
- install_specs c1, c2, c3, b1, b2, a1, a2, base
-
- base.activate
- assert_equal %w[0-1], loaded_spec_names
- assert_equal ["A (>= 1)"], unresolved_names
+ base = util_spec "0", "1", "A" => ">= 1"
+ a1 = util_spec "A", "1", {"c" => ">= 2", "b" => "> 0"}, "lib/a.rb"
+ a2 = util_spec "A", "2", {"c" => ">= 2", "b" => "> 0"}, "lib/a.rb"
+ b1 = util_spec "b", "1", {"c" => "= 1"}, "lib/d#{$$}.rb"
+ b2 = util_spec "b", "2", {"c" => "= 2"}, "lib/d#{$$}.rb"
+ c1 = util_spec "c", "1", {}, "lib/c.rb"
+ c2 = util_spec "c", "2", {}, "lib/c.rb"
+ c3 = util_spec "c", "3", {}, "lib/c.rb"
- require "d#{$$}"
+ install_specs c1, c2, c3, b1, b2, a1, a2, base
- assert_equal %w[0-1 A-2 b-2 c-2], loaded_spec_names
- assert_equal [], unresolved_names
- end
+ base.activate
+ assert_equal %w[0-1], loaded_spec_names
+ assert_equal ["A (>= 1)"], unresolved_names
+
+ require "d#{$$}"
+
+ assert_equal %w[0-1 A-2 b-2 c-2], loaded_spec_names
+ assert_equal [], unresolved_names
end
def test_inner_clonflict_in_indirect_gems
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 1" # unresolved
- b2 = util_spec "b", "2", "c" => ">= 1", "d" => "< 3"
- c1 = util_spec "c", "1", "d" => "<= 2" # 1st level
- c2 = util_spec "c", "2", "d" => "<= 2"
- c3 = util_spec "c", "3", "d" => "<= 3"
- d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb" # 2nd level
- d2 = util_spec "d", "2", nil, "lib/d#{$$}.rb"
- d3 = util_spec "d", "3", nil, "lib/d#{$$}.rb"
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1" # unresolved
+ b2 = util_spec "b", "2", "c" => ">= 1", "d" => "< 3"
+ c1 = util_spec "c", "1", "d" => "<= 2" # 1st level
+ c2 = util_spec "c", "2", "d" => "<= 2"
+ c3 = util_spec "c", "3", "d" => "<= 3"
+ d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb" # 2nd level
+ d2 = util_spec "d", "2", nil, "lib/d#{$$}.rb"
+ d3 = util_spec "d", "3", nil, "lib/d#{$$}.rb"
+
+ install_specs d1, d2, d3, c1, c2, c3, b1, b2, a1
- install_specs d1, d2, d3, c1, c2, c3, b1, b2, a1
+ a1.activate
- a1.activate
+ require "d#{$$}"
- require "d#{$$}"
-
- assert_includes [%w[a-1 b-2 c-3 d-2],%w[a-1 b-2 d-2]], loaded_spec_names
- end
+ assert_includes [%w[a-1 b-2 c-3 d-2],%w[a-1 b-2 d-2]], loaded_spec_names
end
def test_inner_clonflict_in_indirect_gems_reversed
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- b1 = util_spec "b", "1", "xc" => ">= 1" # unresolved
- b2 = util_spec "b", "2", "xc" => ">= 1", "d" => "< 3"
- c1 = util_spec "xc", "1", "d" => "<= 3" # 1st level
- c2 = util_spec "xc", "2", "d" => "<= 2"
- c3 = util_spec "xc", "3", "d" => "<= 3"
- d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb" # 2nd level
- d2 = util_spec "d", "2", nil, "lib/d#{$$}.rb"
- d3 = util_spec "d", "3", nil, "lib/d#{$$}.rb"
-
- install_specs d1, d2, d3, c1, c2, c3, b1, b2, a1
+ a1 = util_spec "a", "1", "b" => "> 0"
+ b1 = util_spec "b", "1", "xc" => ">= 1" # unresolved
+ b2 = util_spec "b", "2", "xc" => ">= 1", "d" => "< 3"
+ c1 = util_spec "xc", "1", "d" => "<= 3" # 1st level
+ c2 = util_spec "xc", "2", "d" => "<= 2"
+ c3 = util_spec "xc", "3", "d" => "<= 3"
+ d1 = util_spec "d", "1", nil, "lib/d#{$$}.rb" # 2nd level
+ d2 = util_spec "d", "2", nil, "lib/d#{$$}.rb"
+ d3 = util_spec "d", "3", nil, "lib/d#{$$}.rb"
+
+ install_specs d1, d2, d3, c1, c2, c3, b1, b2, a1
- a1.activate
+ a1.activate
- require "d#{$$}"
+ require "d#{$$}"
- assert_includes [%w[a-1 b-2 d-2 xc-3], %w[a-1 b-2 d-2]], loaded_spec_names
- end
+ assert_includes [%w[a-1 b-2 d-2 xc-3], %w[a-1 b-2 d-2]], loaded_spec_names
end
##
@@ -406,7 +380,7 @@ end
c = util_spec 'c', '1.0', 'b' => '= 2.0'
install_specs b1, b2, c, a
- e = assert_raises Gem::LoadError do
+ e = assert_raise Gem::LoadError do
assert_activate nil, a, c, "b"
end
@@ -428,7 +402,7 @@ end
install_specs b1, b2, c, a
- e = assert_raises Gem::ConflictError do
+ e = assert_raise Gem::ConflictError do
assert_activate nil, a, c, "b"
end
@@ -511,41 +485,37 @@ end
install_specs b1, b2, a1
a1.activate
- save_loaded_features do
- require "b/c"
- end
+ require "b/c"
assert_equal %w[a-1 b-1], loaded_spec_names
end
def test_self_activate_via_require_wtf
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
- b1 = util_spec "b", "1", { "c" => ">= 1" }, "lib/b#{$$}.rb"
- b2 = util_spec "b", "2", { "c" => ">= 2" }, "lib/b#{$$}.rb" # this
- c1 = util_spec "c", "1"
- c2 = util_spec "c", "2" # this
- d1 = util_spec "d", "1", { "c" => "< 2" }, "lib/d#{$$}.rb"
- d2 = util_spec "d", "2", { "c" => "< 2" }, "lib/d#{$$}.rb" # this
+ a1 = util_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
+ b1 = util_spec "b", "1", { "c" => ">= 1" }, "lib/b#{$$}.rb"
+ b2 = util_spec "b", "2", { "c" => ">= 2" }, "lib/b#{$$}.rb" # this
+ c1 = util_spec "c", "1"
+ c2 = util_spec "c", "2" # this
+ d1 = util_spec "d", "1", { "c" => "< 2" }, "lib/d#{$$}.rb"
+ d2 = util_spec "d", "2", { "c" => "< 2" }, "lib/d#{$$}.rb" # this
- install_specs c1, c2, b1, b2, d1, d2, a1
+ install_specs c1, c2, b1, b2, d1, d2, a1
- a1.activate
+ a1.activate
- assert_equal %w[a-1], loaded_spec_names
- assert_equal ["b (> 0)", "d (> 0)"], unresolved_names
+ assert_equal %w[a-1], loaded_spec_names
+ assert_equal ["b (> 0)", "d (> 0)"], unresolved_names
- require "b#{$$}"
+ require "b#{$$}"
- e = assert_raises Gem::LoadError do
- require "d#{$$}"
- end
+ e = assert_raise Gem::LoadError do
+ require "d#{$$}"
+ end
- assert_equal "unable to find a version of 'd' to activate", e.message
+ 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
+ assert_equal %w[a-1 b-2 c-2], loaded_spec_names
+ assert_equal ["d (> 0)"], unresolved_names
end
def test_self_activate_deep_unambiguous
@@ -673,7 +643,7 @@ end
gem "b", "= 1.0"
- assert_raises Gem::LoadError do
+ assert_raise Gem::LoadError do
gem "b", "= 2.0"
end
end
@@ -747,125 +717,6 @@ end
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")
-
- spec = with_syck do
- Gem::Specification.from_yaml yaml
- end
-
- assert_kind_of Time, @a1.date
- assert_kind_of Time, 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
-
- spec = with_syck do
- Gem::Specification.from_yaml yaml
- end
-
- op = spec.dependencies.first.requirement.requirements.first.first
- refute_kind_of YAML::Syck::DefaultKey, op
-
- refute_match %r{DefaultKey}, 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
-
- spec = Gem::Specification.from_yaml yaml
-
- op = spec.dependencies.first.requirement.requirements.first.first
- refute_kind_of YAML::Syck::DefaultKey, op
-
- refute_match %r{DefaultKey}, 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
-
- spec = Gem::Specification.from_yaml yaml
-
- op = spec.dependencies.first.requirement.requirements.first.first
- refute_kind_of YAML::Syck::DefaultKey, op
-
- refute_match %r{DefaultKey}, spec.to_ruby
- end
-
def test_self_from_yaml_cleans_up_Date_objects
yaml = <<-YAML
--- !ruby/object:Gem::Specification
@@ -973,7 +824,7 @@ dependencies: []
io.write @a2.to_ruby_for_cache
end
rescue Errno::EINVAL
- skip "cannot create '#{full_path}' on this platform"
+ pend "cannot create '#{full_path}' on this platform"
end
spec = Gem::Specification.load full_path
@@ -992,7 +843,7 @@ dependencies: []
io.write @a2.to_ruby_for_cache
end
rescue Errno::EINVAL
- skip "cannot create '#{full_path}' on this platform"
+ pend "cannot create '#{full_path}' on this platform"
end
spec = Gem::Specification.load full_path
@@ -1011,7 +862,7 @@ dependencies: []
io.write @a2.to_ruby_for_cache
end
rescue Errno::EINVAL
- skip "cannot create '#{full_path}' on this platform"
+ pend "cannot create '#{full_path}' on this platform"
end
spec = Gem::Specification.load full_path
@@ -1345,7 +1196,7 @@ dependencies: []
spec.instance_variable_set :@licenses, (class << (Object.new);self;end)
spec.loaded_from = '/path/to/file'
- e = assert_raises Gem::FormatException do
+ e = assert_raise Gem::FormatException do
spec.dup
end
@@ -1451,7 +1302,7 @@ dependencies: []
end
def test_build_args
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
ext_spec
assert_empty @ext.build_args
@@ -1470,10 +1321,10 @@ dependencies: []
end
def test_build_extensions
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
ext_spec
- refute_path_exists @ext.extension_dir, 'sanity check'
+ assert_path_not_exist @ext.extension_dir, 'sanity check'
refute_empty @ext.extensions, 'sanity check'
extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
@@ -1491,7 +1342,7 @@ dependencies: []
@ext.build_extensions
- assert_path_exists @ext.extension_dir
+ assert_path_exist @ext.extension_dir
end
def test_default_spec_stub_is_marked_default
@@ -1506,7 +1357,7 @@ dependencies: []
end
def test_build_extensions_built
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
ext_spec
refute_empty @ext.extensions, 'sanity check'
@@ -1520,7 +1371,7 @@ dependencies: []
@ext.build_extensions
gem_make_out = File.join @ext.extension_dir, 'gem_make.out'
- refute_path_exists gem_make_out
+ assert_path_not_exist gem_make_out
end
def test_build_extensions_default_gem
@@ -1541,25 +1392,25 @@ dependencies: []
spec.build_extensions
- refute_path_exists spec.extension_dir
+ assert_path_not_exist spec.extension_dir
end
def test_build_extensions_error
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
ext_spec
refute_empty @ext.extensions, 'sanity check'
- assert_raises Gem::Ext::BuildError do
+ assert_raise Gem::Ext::BuildError do
@ext.build_extensions
end
end
def test_build_extensions_extensions_dir_unwritable
- skip 'chmod not supported' if Gem.win_platform?
- skip 'skipped in root privilege' if Process.uid.zero?
+ pend 'chmod not supported' if Gem.win_platform?
+ pend 'skipped in root privilege' if Process.uid.zero?
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
ext_spec
refute_empty @ext.extensions, 'sanity check'
@@ -1582,7 +1433,7 @@ dependencies: []
FileUtils.chmod 0555, File.join(@ext.base_dir, 'extensions')
@ext.build_extensions
- refute_path_exists @ext.extension_dir
+ assert_path_not_exist @ext.extension_dir
ensure
unless ($DEBUG or win_platform? or Process.uid.zero? or Gem.java_platform?)
FileUtils.chmod 0755, File.join(@ext.base_dir, 'extensions')
@@ -1591,8 +1442,8 @@ dependencies: []
end
def test_build_extensions_no_extensions_dir_unwritable
- skip 'chmod not supported' if Gem.win_platform?
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend 'chmod not supported' if Gem.win_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
ext_spec
refute_empty @ext.extensions, 'sanity check'
@@ -1616,36 +1467,22 @@ dependencies: []
@ext.build_extensions
gem_make_out = File.join @ext.extension_dir, 'gem_make.out'
- refute_path_exists gem_make_out
+ assert_path_not_exist gem_make_out
ensure
FileUtils.chmod 0755, @gemhome
end
def test_build_extensions_none
- refute_path_exists @a1.extension_dir, 'sanity check'
+ assert_path_not_exist @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
- skip "extensions don't quite work on jruby" if Gem.java_platform?
- 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
+ assert_path_not_exist @a1.extension_dir
end
def test_build_extensions_preview
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
ext_spec
extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
@@ -1668,7 +1505,7 @@ dependencies: []
@ext.build_extensions
gem_make_out = File.join @ext.extension_dir, 'gem_make.out'
- assert_path_exists gem_make_out
+ assert_path_exist gem_make_out
end
def test_contains_requirable_file_eh
@@ -1680,10 +1517,10 @@ dependencies: []
end
def test_contains_requirable_file_eh_extension
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
ext_spec
- _, err = capture_io do
+ _, err = capture_output do
refute @ext.contains_requirable_file? 'nonexistent'
end
@@ -1696,7 +1533,7 @@ dependencies: []
def test_contains_requirable_file_eh_extension_java_platform
ext_spec(platform: Gem::Platform.new("java"))
- _, err = capture_io do
+ _, err = capture_output do
refute @ext.contains_requirable_file? 'nonexistent'
end
@@ -1718,7 +1555,7 @@ dependencies: []
end
def test_date_equals_string_bad
- assert_raises Gem::InvalidSpecificationException do
+ assert_raise Gem::InvalidSpecificationException do
@a1.date = '9/11/2003'
end
end
@@ -2281,43 +2118,39 @@ dependencies: []
end
def test_require_already_activated
- save_loaded_features do
- a1 = util_spec "a", "1", nil, "lib/d#{$$}.rb"
+ a1 = util_spec "a", "1", nil, "lib/d#{$$}.rb"
- install_specs a1 # , a2, b1, b2, c1, c2
+ install_specs a1 # , a2, b1, b2, c1, c2
- a1.activate
- assert_equal %w[a-1], loaded_spec_names
- assert_equal [], unresolved_names
+ a1.activate
+ assert_equal %w[a-1], loaded_spec_names
+ assert_equal [], unresolved_names
- assert require "d#{$$}"
+ assert require "d#{$$}"
- assert_equal %w[a-1], loaded_spec_names
- assert_equal [], unresolved_names
- end
+ assert_equal %w[a-1], loaded_spec_names
+ assert_equal [], unresolved_names
end
def test_require_already_activated_indirect_conflict
- save_loaded_features do
- a1 = util_spec "a", "1", "b" => "> 0"
- a2 = util_spec "a", "2", "b" => "> 0"
- b1 = util_spec "b", "1", "c" => ">= 1"
- b2 = util_spec "b", "2", "c" => ">= 2"
- c1 = util_spec "c", "1", nil, "lib/d#{$$}.rb"
- c2 = util_spec("c", "2", { "a" => "1" }, "lib/d#{$$}.rb") # conflicts with a-2
+ a1 = util_spec "a", "1", "b" => "> 0"
+ a2 = util_spec "a", "2", "b" => "> 0"
+ b1 = util_spec "b", "1", "c" => ">= 1"
+ b2 = util_spec "b", "2", "c" => ">= 2"
+ c1 = util_spec "c", "1", nil, "lib/d#{$$}.rb"
+ c2 = util_spec("c", "2", { "a" => "1" }, "lib/d#{$$}.rb") # conflicts with a-2
- install_specs c1, b1, a1, a2, c2, b2
+ install_specs c1, b1, a1, a2, c2, b2
- a1.activate
- c1.activate
- assert_equal %w[a-1 c-1], loaded_spec_names
- assert_equal ["b (> 0)"], unresolved_names
+ a1.activate
+ c1.activate
+ assert_equal %w[a-1 c-1], loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
- assert require "d#{$$}"
+ assert require "d#{$$}"
- assert_equal %w[a-1 c-1], loaded_spec_names
- assert_equal ["b (> 0)"], unresolved_names
- end
+ assert_equal %w[a-1 c-1], loaded_spec_names
+ assert_equal ["b (> 0)"], unresolved_names
end
def test_requirements
@@ -2449,7 +2282,7 @@ end
def test_to_ruby_with_rsa_key
require 'rubygems/openssl'
- skip 'openssl is missing' unless defined?(OpenSSL::PKey::RSA)
+ pend 'openssl is missing' unless defined?(OpenSSL::PKey::RSA)
rsa_key = OpenSSL::PKey::RSA.new(2048)
@a2.signing_key = rsa_key
@@ -2633,7 +2466,7 @@ end
def test_to_yaml
yaml_str = @a1.to_yaml
- refute_match '!!null', yaml_str
+ refute_match %r{!!null}, yaml_str
same_spec = Gem::Specification.from_yaml(yaml_str)
@@ -2663,7 +2496,7 @@ end
yaml_str = @a1.to_yaml
- same_spec = YAML.load yaml_str
+ same_spec = load_yaml yaml_str
assert_equal Gem::Platform.new('powerpc-darwin7'), same_spec.platform
assert_equal 'powerpc-darwin7.9.0', same_spec.original_platform
@@ -2704,7 +2537,7 @@ end
assert_equal [], @a1.authors
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -2712,7 +2545,7 @@ end
@a1.authors = ["#{f} (who is writing this software)"]
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -2720,7 +2553,7 @@ end
@a1.authors = ["#{t} (who is writing this software)"]
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -2799,7 +2632,7 @@ end
@a1.add_development_dependency 'c', '>= 1.2.3'
use_ui @ui do
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -2903,7 +2736,7 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
@a1.description = "#{f} (describe your package)"
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -2911,11 +2744,39 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
@a1.description = "#{t} (describe your package)"
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
assert_equal %("#{f}" or "#{t}" is not a description), e.message
+
+ # Adding #{f} anywhere after the start of the description should be fine.
+ @a1.description = "(some description) #{f}"
+
+ assert_nothing_raised do
+ @a1.validate
+ end
+
+ # Adding #{t} anywhere after the start of the description should be fine.
+ @a1.description = "(some description) #{t}"
+
+ assert_nothing_raised do
+ @a1.validate
+ end
+
+ # Adding #{f} at the start of the second or later line should be fine.
+ @a1.description = "(some description)\n#{f}"
+
+ assert_nothing_raised do
+ @a1.validate
+ end
+
+ # Adding #{t} at the start of the second or later line should be fine.
+ @a1.description = "(some description)\n#{t}"
+
+ assert_nothing_raised do
+ @a1.validate
+ end
end
end
@@ -2925,7 +2786,7 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
Dir.chdir @tempdir do
@a1.email = "FIxxxXME (your e-mail)".sub(/xxx/, "")
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -2933,7 +2794,7 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
@a1.email = "#{t} (your e-mail)"
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -2942,7 +2803,7 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
end
def test_validate_empty
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
Gem::Specification.new.validate
end
@@ -2950,7 +2811,7 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
end
def test_validate_error
- assert_raises Gem::InvalidSpecificationException do
+ assert_raise Gem::InvalidSpecificationException do
use_ui @ui do
Gem::Specification.new.validate
end
@@ -2980,12 +2841,12 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
def test_validate_empty_require_paths
if win_platform?
- skip 'test_validate_empty_require_paths skipped on MS Windows (symlink)'
+ pend 'test_validate_empty_require_paths skipped on MS Windows (symlink)'
else
util_setup_validate
@a1.require_paths = []
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -2995,7 +2856,7 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
end
def test_validate_files
- skip 'test_validate_files skipped on MS Windows (symlink)' if win_platform?
+ pend 'test_validate_files skipped on MS Windows (symlink)' if win_platform?
util_setup_validate
@a1.files += ['lib', 'lib2']
@@ -3035,9 +2896,11 @@ WARN: Clearing out unresolved specs. Try 'gem cleanup <gem>'
Please report a bug if this causes problems.
EXPECTED
- assert_output nil, expected do
+ actual_stdout, actual_stderr = capture_output do
specification.reset
end
+ assert_empty actual_stdout
+ assert_equal(expected, actual_stderr)
end
def test_unresolved_specs_with_versions
@@ -3066,16 +2929,20 @@ WARN: Clearing out unresolved specs. Try 'gem cleanup <gem>'
Please report a bug if this causes problems.
EXPECTED
- assert_output nil, expected do
+ actual_stdout, actual_stderr = capture_output do
specification.reset
end
+ assert_empty actual_stdout
+ assert_equal(expected, actual_stderr)
end
def test_duplicate_runtime_dependency
expected = "WARNING: duplicated b dependency [\"~> 3.0\", \"~> 3.0\"]\n"
- assert_output nil, expected do
+ out, err = capture_output do
@a1.add_runtime_dependency "b", "~> 3.0", "~> 3.0"
end
+ assert_empty out
+ assert_equal(expected, err)
end
def set_orig(cls)
@@ -3090,7 +2957,7 @@ Please report a bug if this causes problems.
@a1.files = [@a1.file_name]
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -3122,7 +2989,7 @@ Please report a bug if this causes problems.
@a1.homepage = 'over at my cool site'
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -3130,7 +2997,7 @@ Please report a bug if this causes problems.
@a1.homepage = 'ftp://rubygems.org'
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -3199,6 +3066,17 @@ http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
WARNING
end
+ def test_validate_license_ref
+ util_setup_validate
+
+ use_ui @ui do
+ @a1.licenses = ['LicenseRef-LICENSE.md']
+ @a1.validate
+ end
+
+ assert_empty @ui.error
+ end
+
def test_validate_license_values_plus
util_setup_validate
@@ -3319,7 +3197,7 @@ Did you mean 'Ruby'?
def test_validate_name
util_setup_validate
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.name = :json
@a1.validate
end
@@ -3327,31 +3205,31 @@ Did you mean 'Ruby'?
assert_equal 'invalid value for attribute name: ":json" must be a string', e.message
@a1.name = []
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
assert_equal "invalid value for attribute name: \"[]\" must be a string", e.message
@a1.name = ""
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
assert_equal "invalid value for attribute name: \"\" must include at least one letter", e.message
@a1.name = "12345"
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
assert_equal "invalid value for attribute name: \"12345\" must include at least one letter", e.message
@a1.name = "../malicious"
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
assert_equal "invalid value for attribute name: \"../malicious\" can only include letters, numbers, dashes, and underscores", e.message
@a1.name = "\ba\t"
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
assert_equal "invalid value for attribute name: \"\\ba\\t\" can only include letters, numbers, dashes, and underscores", e.message
@@ -3368,7 +3246,7 @@ Did you mean 'Ruby'?
spec = @a1.dup
spec.instance_variable_set "@#{name}", nil
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
spec.validate
end
@@ -3378,7 +3256,7 @@ Did you mean 'Ruby'?
end
def test_validate_permissions
- skip 'chmod not supported' if Gem.win_platform?
+ pend 'chmod not supported' if Gem.win_platform?
util_setup_validate
@@ -3397,7 +3275,7 @@ Did you mean 'Ruby'?
end
def test_validate_permissions_of_missing_file_non_packaging
- skip 'chmod not supported' if Gem.win_platform?
+ pend 'chmod not supported' if Gem.win_platform?
util_setup_validate
@@ -3429,7 +3307,7 @@ Did you mean 'Ruby'?
util_setup_validate
@a1.rubygems_version = "3"
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -3443,7 +3321,7 @@ Did you mean 'Ruby'?
Dir.chdir @tempdir do
@a1.specification_version = '1.0'
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
use_ui @ui do
@a1.validate
end
@@ -3468,7 +3346,7 @@ Did you mean 'Ruby'?
@a1.summary = "#{f} (describe your package)"
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -3476,7 +3354,7 @@ Did you mean 'Ruby'?
@a1.summary = "#{t} (describe your package)"
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@a1.validate
end
@@ -3529,7 +3407,7 @@ Did you mean 'Ruby'?
specfile.write "raise 'boom'"
specfile.close
begin
- capture_io do
+ capture_output do
Gem::Specification.load(specfile.path)
end
rescue => e
@@ -3599,7 +3477,7 @@ Did you mean 'Ruby'?
s.metadata = { 1 => "fail" }
end
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@m2.validate
end
@@ -3616,11 +3494,11 @@ Did you mean 'Ruby'?
s.metadata = { ("x" * 129) => "fail" }
end
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@m2.validate
end
- assert_equal "metadata key too large (129 > 128)", e.message
+ assert_equal "metadata key is too large (129 > 128)", e.message
end
end
@@ -3633,11 +3511,11 @@ Did you mean 'Ruby'?
s.metadata = { 'fail' => [] }
end
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@m2.validate
end
- assert_equal "metadata values must be a String", e.message
+ assert_equal "metadata['fail'] value must be a String", e.message
end
end
@@ -3650,11 +3528,11 @@ Did you mean 'Ruby'?
s.metadata = { 'fail' => ("x" * 1025) }
end
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@m2.validate
end
- assert_equal "metadata value too large (1025 > 1024)", e.message
+ assert_equal "metadata['fail'] value is too large (1025 > 1024)", e.message
end
end
@@ -3667,7 +3545,7 @@ Did you mean 'Ruby'?
s.metadata = { 'homepage_uri' => 'http:/example.com' }
end
- e = assert_raises Gem::InvalidSpecificationException do
+ e = assert_raise Gem::InvalidSpecificationException do
@m2.validate
end
@@ -3707,7 +3585,7 @@ end
end
def test_missing_extensions_eh
- skip "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
ext_spec
assert @ext.missing_extensions?
@@ -3737,18 +3615,6 @@ end
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
@@ -3786,7 +3652,7 @@ end
assert Gem::Specification.find_by_name "a", "1"
assert Gem::Specification.find_by_name "a", ">1"
- assert_raises Gem::MissingSpecError do
+ assert_raise Gem::MissingSpecError do
Gem::Specification.find_by_name "monkeys"
end
end
@@ -3807,7 +3673,7 @@ end
assert Gem::Specification.find_by_name "b"
- assert_raises Gem::MissingSpecVersionError do
+ assert_raise Gem::MissingSpecVersionError do
Gem::Specification.find_by_name "b", "1"
end
@@ -3880,49 +3746,6 @@ 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
diff --git a/test/rubygems/test_gem_stream_ui.rb b/test/rubygems/test_gem_stream_ui.rb
index a62e9ea0cf..dc245c342a 100644
--- a/test/rubygems/test_gem_stream_ui.rb
+++ b/test/rubygems/test_gem_stream_ui.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/user_interaction'
require 'timeout'
@@ -90,7 +90,7 @@ class TestGemStreamUI < Gem::TestCase
@in.tty = false
Timeout.timeout(SHORT_TIMEOUT) do
- assert_raises(Gem::OperationNotSupportedError) do
+ assert_raise(Gem::OperationNotSupportedError) do
@sui.ask_yes_no("do coconuts migrate?")
end
end
diff --git a/test/rubygems/test_gem_stub_specification.rb b/test/rubygems/test_gem_stub_specification.rb
index 2ee94dcf8d..e008391ef7 100644
--- a/test/rubygems/test_gem_stub_specification.rb
+++ b/test/rubygems/test_gem_stub_specification.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require "rubygems/test_case"
+require_relative "helper"
require "rubygems/stub_specification"
class TestStubSpecification < Gem::TestCase
@@ -65,9 +65,9 @@ class TestStubSpecification < Gem::TestCase
end
def test_contains_requirable_file_eh_extension
- skip "I guess making the stub match the running platform should work" if Gem.java_platform?
+ pend "I guess making the stub match the running platform should work" if Gem.java_platform?
stub_with_extension do |stub|
- _, err = capture_io do
+ _, err = capture_output do
refute stub.contains_requirable_file? 'nonexistent'
end
@@ -122,7 +122,7 @@ class TestStubSpecification < Gem::TestCase
end
def test_missing_extensions_eh
- skip "I guess making the stub match the running platform should work" if Gem.java_platform?
+ pend "I guess making the stub match the running platform should work" if Gem.java_platform?
stub = stub_with_extension do |s|
extconf_rb = File.join s.gem_dir, s.extensions.first
FileUtils.mkdir_p File.dirname extconf_rb
@@ -189,7 +189,7 @@ class TestStubSpecification < Gem::TestCase
def test_to_spec_missing_extensions
stub = stub_with_extension
- capture_io do
+ capture_output do
stub.contains_requirable_file? 'nonexistent'
end
diff --git a/test/rubygems/test_gem_text.rb b/test/rubygems/test_gem_text.rb
index d8e4b75f3a..069cfdc4e6 100644
--- a/test/rubygems/test_gem_text.rb
+++ b/test/rubygems/test_gem_text.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require "rubygems/text"
class TestGemText < Gem::TestCase
diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb
index 1ca4991d1e..9e18972864 100644
--- a/test/rubygems/test_gem_uninstaller.rb
+++ b/test/rubygems/test_gem_uninstaller.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/installer_test_case'
+require_relative 'installer_test_case'
require 'rubygems/uninstaller'
class TestGemUninstaller < Gem::InstallerTestCase
@@ -54,7 +54,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
uninstaller.remove_all [@spec]
end
- refute_path_exists @spec.gem_dir
+ assert_path_not_exist @spec.gem_dir
end
def test_remove_executables_force_keep
@@ -138,7 +138,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
Dir.mkdir "#{@gemhome}2"
uninstaller = Gem::Uninstaller.new nil, :install_dir => "#{@gemhome}2"
- e = assert_raises Gem::GemNotInHomeException do
+ e = assert_raise Gem::GemNotInHomeException do
use_ui ui do
uninstaller.remove @spec
end
@@ -149,11 +149,11 @@ class TestGemUninstaller < Gem::InstallerTestCase
assert_equal expected, e.message
- assert_path_exists @spec.gem_dir
+ assert_path_exist @spec.gem_dir
end
def test_remove_symlinked_gem_home
- skip "Symlinks not supported or not enabled" unless symlink_supported?
+ pend "Symlinks not supported or not enabled" unless symlink_supported?
Dir.mktmpdir("gem_home") do |dir|
symlinked_gem_home = "#{dir}/#{File.basename(@gemhome)}"
@@ -166,7 +166,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
uninstaller.remove @spec
end
- refute_path_exists @spec.gem_dir
+ assert_path_not_exist @spec.gem_dir
end
end
@@ -295,9 +295,16 @@ class TestGemUninstaller < Gem::InstallerTestCase
uninstaller = Gem::Uninstaller.new spec.name, :executables => true
- uninstaller.uninstall
-
- refute_path_exists spec.gem_dir
+ ui = Gem::MockGemUi.new "1\ny\n"
+ use_ui ui do
+ uninstaller.uninstall
+ end
+ expected = "Successfully uninstalled default-2\n" \
+ "There was both a regular copy and a default copy of default-2. The " \
+ "regular copy was successfully uninstalled, but the default copy " \
+ "was left around because default gems can't be removed.\n"
+ assert_equal expected, ui.output
+ assert_path_not_exist spec.gem_dir
end
def test_uninstall_extension
@@ -318,18 +325,18 @@ create_makefile '#{@spec.name}'
installer.install
end
- assert_path_exists @spec.extension_dir, 'sanity check'
+ assert_path_exist @spec.extension_dir, 'sanity check'
uninstaller = Gem::Uninstaller.new @spec.name, :executables => true
uninstaller.uninstall
- refute_path_exists @spec.extension_dir
+ assert_path_not_exist @spec.extension_dir
end
def test_uninstall_nonexistent
uninstaller = Gem::Uninstaller.new 'bogus', :executables => true
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
uninstaller.uninstall
end
@@ -351,7 +358,7 @@ create_makefile '#{@spec.name}'
ui = Gem::MockGemUi.new "n\n"
- assert_raises Gem::DependencyRemovalException do
+ assert_raise Gem::DependencyRemovalException do
use_ui ui do
uninstaller.uninstall
end
@@ -371,16 +378,16 @@ create_makefile '#{@spec.name}'
gem_dir = File.join @user_spec.gem_dir
Gem.pre_uninstall do
- assert_path_exists gem_dir
+ assert_path_exist gem_dir
end
Gem.post_uninstall do
- refute_path_exists gem_dir
+ assert_path_not_exist gem_dir
end
uninstaller.uninstall
- refute_path_exists gem_dir
+ assert_path_not_exist gem_dir
assert_same uninstaller, @pre_uninstall_hook_arg
assert_same uninstaller, @post_uninstall_hook_arg
@@ -392,7 +399,7 @@ create_makefile '#{@spec.name}'
uninstaller = Gem::Uninstaller.new @spec.name, :executables => true
- e = assert_raises Gem::InstallError do
+ e = assert_raise Gem::InstallError do
uninstaller.uninstall
end
@@ -546,7 +553,7 @@ create_makefile '#{@spec.name}'
un = Gem::Uninstaller.new('q', :abort_on_dependent => true)
ui = Gem::MockGemUi.new("y\n")
- assert_raises Gem::DependencyRemovalException do
+ assert_raise Gem::DependencyRemovalException do
use_ui ui do
un.uninstall
end
@@ -619,7 +626,7 @@ create_makefile '#{@spec.name}'
end
FileUtils.stub :rm_r, stub_rm_r do
- assert_raises Gem::UninstallError do
+ assert_raise Gem::UninstallError do
uninstaller.uninstall
end
end
diff --git a/test/rubygems/test_gem_unsatisfiable_dependency_error.rb b/test/rubygems/test_gem_unsatisfiable_dependency_error.rb
index 7950efb2a9..f9d30123f0 100644
--- a/test/rubygems/test_gem_unsatisfiable_dependency_error.rb
+++ b/test/rubygems/test_gem_unsatisfiable_dependency_error.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestGemUnsatisfiableDependencyError < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_uri.rb b/test/rubygems/test_gem_uri.rb
new file mode 100644
index 0000000000..7fe572518b
--- /dev/null
+++ b/test/rubygems/test_gem_uri.rb
@@ -0,0 +1,39 @@
+require_relative 'helper'
+require 'rubygems/uri'
+
+class TestUri < Gem::TestCase
+ def test_to_s_not_string
+ assert_equal "not_a_uri", Gem::Uri.new(:not_a_uri).to_s
+ end
+
+ def test_to_s_invalid_uri
+ assert_equal "https://www.example.com:80index", Gem::Uri.new("https://www.example.com:80index").to_s
+ end
+
+ def test_redacted_with_user_pass
+ assert_equal "https://user:REDACTED@example.com", Gem::Uri.new("https://user:pass@example.com").redacted.to_s
+ end
+
+ def test_redacted_with_token
+ assert_equal "https://REDACTED@example.com", Gem::Uri.new("https://token@example.com").redacted.to_s
+ end
+
+ def test_redacted_with_user_x_oauth_basic
+ assert_equal "https://REDACTED:x-oauth-basic@example.com", Gem::Uri.new("https://token:x-oauth-basic@example.com").redacted.to_s
+ end
+
+ def test_redacted_without_credential
+ assert_equal "https://www.example.com", Gem::Uri.new("https://www.example.com").redacted.to_s
+ end
+
+ def test_redacted_with_invalid_uri
+ assert_equal "https://www.example.com:80index", Gem::Uri.new("https://www.example.com:80index").redacted.to_s
+ end
+
+ def test_redacted_does_not_modify_uri
+ url = 'https://user:password@example.com'
+ uri = Gem::Uri.new(url)
+ assert_equal 'https://user:REDACTED@example.com', uri.redacted.to_s
+ assert_equal url, uri.to_s
+ end
+end
diff --git a/test/rubygems/test_gem_uri_formatter.rb b/test/rubygems/test_gem_uri_formatter.rb
index debc7739cb..a41c9238ad 100644
--- a/test/rubygems/test_gem_uri_formatter.rb
+++ b/test/rubygems/test_gem_uri_formatter.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/uri_formatter'
class TestGemUriFormatter < Gem::TestCase
diff --git a/test/rubygems/test_gem_util.rb b/test/rubygems/test_gem_util.rb
index 7197f664e2..e0db5c283d 100644
--- a/test/rubygems/test_gem_util.rb
+++ b/test/rubygems/test_gem_util.rb
@@ -1,23 +1,25 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/util'
class TestGemUtil < Gem::TestCase
def test_class_popen
- skip "popen with a block does not behave well on jruby" if Gem.java_platform?
+ pend "popen with a block does not behave well on jruby" if Gem.java_platform?
assert_equal "0\n", Gem::Util.popen(*ruby_with_rubygems_in_load_path, '-e', 'p 0')
- assert_raises Errno::ECHILD do
+ assert_raise Errno::ECHILD do
Process.wait(-1)
end
end
def test_silent_system
- skip if Gem.java_platform?
+ pend if Gem.java_platform?
Gem::Deprecate.skip_during do
- assert_silent do
+ out, err = capture_output do
Gem::Util.silent_system(*ruby_with_rubygems_in_load_path, '-e', 'puts "hello"; warn "hello"')
end
+ assert_empty out
+ assert_empty err
end
end
@@ -33,14 +35,14 @@ class TestGemUtil < Gem::TestCase
end
def test_traverse_parents_does_not_crash_on_permissions_error
- skip 'skipped on MS Windows (chmod has no effect)' if win_platform? || java_platform?
+ pend 'skipped on MS Windows (chmod has no effect)' if win_platform? || java_platform?
FileUtils.mkdir_p 'd/e/f'
# remove 'execute' permission from "e" directory and make it
# impossible to cd into it and its children
FileUtils.chmod(0666, 'd/e')
- skip 'skipped in root privilege' if Process.uid.zero?
+ pend 'skipped in root privilege' if Process.uid.zero?
paths = Gem::Util.traverse_parents('d/e/f').to_a
diff --git a/test/rubygems/test_gem_validator.rb b/test/rubygems/test_gem_validator.rb
index 5158543fa9..8090776b4a 100644
--- a/test/rubygems/test_gem_validator.rb
+++ b/test/rubygems/test_gem_validator.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "rubygems/test_case"
+require_relative "helper"
require "rubygems/validator"
class TestGemValidator < Gem::TestCase
diff --git a/test/rubygems/test_gem_version.rb b/test/rubygems/test_gem_version.rb
index 7b382809c9..422e1ee86c 100644
--- a/test/rubygems/test_gem_version.rb
+++ b/test/rubygems/test_gem_version.rb
@@ -1,9 +1,7 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require "rubygems/version"
-require "minitest/benchmark"
-
class TestGemVersion < Gem::TestCase
class V < ::Gem::Version
end
@@ -47,9 +45,11 @@ class TestGemVersion < Gem::TestCase
assert_equal false, Gem::Version.correct?("an incorrect version")
expected = "nil versions are discouraged and will be deprecated in Rubygems 4\n"
- assert_output nil, expected do
+ actual_stdout, actual_stderr = capture_output do
Gem::Version.correct?(nil)
end
+ assert_empty actual_stdout
+ assert_equal(expected, actual_stderr)
end
def test_class_new_subclass
@@ -100,7 +100,7 @@ class TestGemVersion < Gem::TestCase
invalid_versions << "2.3422222.222.222222222.22222.ads0as.dasd0.ddd2222.2.qd3e."
invalid_versions.each do |invalid|
- e = assert_raises ArgumentError, invalid do
+ e = assert_raise ArgumentError, invalid do
Gem::Version.new invalid
end
@@ -108,15 +108,6 @@ class TestGemVersion < Gem::TestCase
end
end
- def bench_anchored_version_pattern
- assert_performance_linear 0.5 do |count|
- version_string = count.times.map {|i| "0" * i.succ }.join(".") << "."
- version_string =~ Gem::Version::ANCHORED_VERSION_PATTERN
- end
- rescue RegexpError
- skip "It fails to allocate the memory for regex pattern of Gem::Version::ANCHORED_VERSION_PATTERN"
- end
-
def test_empty_version
["", " ", " "].each do |empty|
assert_equal "0", Gem::Version.new(empty).version
diff --git a/test/rubygems/test_gem_version_option.rb b/test/rubygems/test_gem_version_option.rb
index 49a8513dbe..74d7979e3a 100644
--- a/test/rubygems/test_gem_version_option.rb
+++ b/test/rubygems/test_gem_version_option.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems/command'
require 'rubygems/version_option'
diff --git a/test/rubygems/test_kernel.rb b/test/rubygems/test_kernel.rb
index 0fa36a0dec..dee36d05ee 100644
--- a/test/rubygems/test_kernel.rb
+++ b/test/rubygems/test_kernel.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestKernel < Gem::TestCase
def setup
@@ -8,6 +8,8 @@ class TestKernel < Gem::TestCase
@old_path = $:.dup
util_make_gems
+
+ without_any_upwards_gemfiles
end
def teardown
@@ -38,7 +40,7 @@ class TestKernel < Gem::TestCase
def test_gem_re_gem_mismatch
assert gem('a', '=1')
- assert_raises Gem::LoadError do
+ assert_raise Gem::LoadError do
gem('a', '= 2')
end
@@ -65,7 +67,7 @@ class TestKernel < Gem::TestCase
def test_gem_env_req
ENV["GEM_REQUIREMENT_A"] = '~> 2.0'
- assert_raises(Gem::MissingSpecVersionError) { gem('a', '= 1') }
+ assert_raise(Gem::MissingSpecVersionError) { gem('a', '= 1') }
assert gem('a', '> 1')
assert_equal @a2, Gem.loaded_specs['a']
end
@@ -73,7 +75,7 @@ class TestKernel < Gem::TestCase
def test_gem_conflicting
assert gem('a', '= 1'), "Should load"
- ex = assert_raises Gem::LoadError do
+ ex = assert_raise Gem::LoadError do
gem 'a', '= 2'
end
@@ -120,7 +122,7 @@ class TestKernel < Gem::TestCase
quick_gem 'bundler', '1'
quick_gem 'bundler', '2.a'
- e = assert_raises Gem::MissingSpecVersionError do
+ e = assert_raise Gem::MissingSpecVersionError do
gem('bundler')
end
assert_match "Could not find 'bundler' (55) required by reason.", e.message
diff --git a/test/rubygems/test_project_sanity.rb b/test/rubygems/test_project_sanity.rb
index 831a2c00aa..e9e3bfd1ba 100644
--- a/test/rubygems/test_project_sanity.rb
+++ b/test/rubygems/test_project_sanity.rb
@@ -1,11 +1,11 @@
# frozen_string_literal: true
-require "rubygems/test_case"
+require_relative "helper"
require "open3"
class TestProjectSanity < Gem::TestCase
def test_manifest_is_up_to_date
- skip unless File.exist?(File.expand_path("../../../Rakefile", __FILE__))
+ pend unless File.exist?(File.expand_path("../../../Rakefile", __FILE__))
_, status = Open3.capture2e("rake check_manifest")
diff --git a/test/rubygems/test_remote_fetch_error.rb b/test/rubygems/test_remote_fetch_error.rb
index 29aaaa8adb..b9e58389d3 100644
--- a/test/rubygems/test_remote_fetch_error.rb
+++ b/test/rubygems/test_remote_fetch_error.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
class TestRemoteFetchError < Gem::TestCase
def test_password_redacted
error = Gem::RemoteFetcher::FetchError.new('There was an error fetching', 'https://user:secret@gemsource.org')
- refute_match 'secret', error.to_s
+ refute_match %r{secret}, error.to_s
end
def test_invalid_url
diff --git a/test/rubygems/test_require.rb b/test/rubygems/test_require.rb
index 8c7d06edc3..56774116a9 100644
--- a/test/rubygems/test_require.rb
+++ b/test/rubygems/test_require.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'rubygems/test_case'
+require_relative 'helper'
require 'rubygems'
class TestGemRequire < Gem::TestCase
@@ -24,16 +24,6 @@ class TestGemRequire < Gem::TestCase
end
end
- def setup
- super
-
- @old_loaded_features = $LOADED_FEATURES.dup
- assert_raises LoadError do
- require 'test_gem_require_a'
- end
- $LOADED_FEATURES.replace @old_loaded_features
- end
-
def assert_require(path)
assert require(path), "'#{path}' was already required"
end
@@ -88,8 +78,6 @@ class TestGemRequire < Gem::TestCase
FileUtils.mkdir_p File.dirname c_rb
File.open(c_rb, 'w') {|f| f.write "class Object; HELLO = 'world' end" }
- lp = $LOAD_PATH.dup
-
# Pretend to provide a commandline argument that overrides a file in gem b
$LOAD_PATH.unshift dash_i_arg
@@ -98,7 +86,6 @@ class TestGemRequire < Gem::TestCase
assert_equal "world", ::Object::HELLO
assert_equal %w[a-1 b-1], loaded_spec_names
ensure
- $LOAD_PATH.replace lp
Object.send :remove_const, :HELLO if Object.const_defined? :HELLO
end
@@ -132,8 +119,6 @@ class TestGemRequire < Gem::TestCase
assert_require 'test_gem_require_a'
- lp = $LOAD_PATH.dup
-
# Pretend to provide a commandline argument that overrides a file in gem b
$LOAD_PATH.unshift dash_i_arg
@@ -142,27 +127,20 @@ class TestGemRequire < Gem::TestCase
assert_equal "world", ::Object::HELLO
assert_equal %w[a-1 b-1], loaded_spec_names
ensure
- $LOAD_PATH.replace lp
Object.send :remove_const, :HELLO if Object.const_defined? :HELLO
end
def test_dash_i_respects_default_library_extension_priority
- skip "extensions don't quite work on jruby" if Gem.java_platform?
- skip "not installed yet" unless RbConfig::TOPDIR
+ pend "extensions don't quite work on jruby" if Gem.java_platform?
+ pend "not installed yet" unless RbConfig::TOPDIR
dash_i_ext_arg = util_install_extension_file('a')
dash_i_lib_arg = util_install_ruby_file('a')
- lp = $LOAD_PATH.dup
-
- begin
- $LOAD_PATH.unshift dash_i_lib_arg
- $LOAD_PATH.unshift dash_i_ext_arg
- assert_require 'a'
- assert_match(/a\.rb$/, $LOADED_FEATURES.last)
- ensure
- $LOAD_PATH.replace lp
- end
+ $LOAD_PATH.unshift dash_i_lib_arg
+ $LOAD_PATH.unshift dash_i_ext_arg
+ assert_require 'a'
+ assert_match(/a\.rb$/, $LOADED_FEATURES.last)
end
def test_concurrent_require
@@ -245,12 +223,12 @@ class TestGemRequire < Gem::TestCase
end
def test_activate_via_require_respects_loaded_files
- skip "Not sure what's going on. If another spec creates a 'a' gem before
+ pend "Not sure what's going on. If another spec creates a 'a' gem before
this test, somehow require will load the benchmark in b, and ignore that the
stdlib one is already in $LOADED_FEATURES?. Reproducible by running the
spaceship_specific_file test before this one" if java_platform?
- skip "not installed yet" unless RbConfig::TOPDIR
+ pend "not installed yet" unless RbConfig::TOPDIR
lib_dir = File.expand_path("../../lib", File.dirname(__FILE__))
rubylibdir = File.realdirpath(RbConfig::CONFIG["rubylibdir"])
@@ -359,7 +337,7 @@ class TestGemRequire < Gem::TestCase
assert_equal %w[a-1 c-1], loaded_spec_names
assert_equal ["b (> 0)", "x (> 0)"], unresolved_names
- e = assert_raises(Gem::LoadError) do
+ e = assert_raise(Gem::LoadError) do
require("ib")
end
@@ -382,7 +360,7 @@ class TestGemRequire < Gem::TestCase
assert_equal %w[a-1 c-1], loaded_spec_names
assert_equal ["b (> 0)"], unresolved_names
- e = assert_raises(Gem::LoadError) do
+ e = assert_raise(Gem::LoadError) do
require("ib")
end
@@ -466,8 +444,7 @@ class TestGemRequire < Gem::TestCase
end
def test_realworld_default_gem
- testing_ruby_repo = !ENV["GEM_COMMAND"].nil?
- skip "this test can't work under ruby-core setup" if testing_ruby_repo || java_platform?
+ omit "this test can't work under ruby-core setup" if testing_ruby_repo?
cmd = <<-RUBY
$stderr = $stdout
@@ -480,8 +457,7 @@ class TestGemRequire < Gem::TestCase
end
def test_realworld_upgraded_default_gem
- testing_ruby_repo = !ENV["GEM_COMMAND"].nil?
- skip "this test can't work under ruby-core setup" if testing_ruby_repo
+ omit "this test can't work under ruby-core setup" if testing_ruby_repo?
newer_json = util_spec("json", "999.99.9", nil, ["lib/json.rb"])
install_gem newer_json
@@ -626,7 +602,7 @@ class TestGemRequire < Gem::TestCase
b2a = util_spec('bundler', '2.a', nil, "lib/bundler/setup.rb")
install_specs b1, b2a
- e = assert_raises Gem::MissingSpecVersionError do
+ e = assert_raise Gem::MissingSpecVersionError do
gem('bundler')
end
assert_match "Could not find 'bundler' (55) required by reason.", e.message
@@ -679,8 +655,6 @@ class TestGemRequire < Gem::TestCase
end
def test_no_crash_when_overriding_warn_with_warning_module
- skip "https://github.com/oracle/truffleruby/issues/2109" if RUBY_ENGINE == "truffleruby"
-
Dir.mktmpdir("warn_test") do |dir|
File.write(dir + "/main.rb", "module Warning; def warn(str); super; end; end; warn 'Foo Bar'")
_, err = capture_subprocess_io do
@@ -722,6 +696,10 @@ class TestGemRequire < Gem::TestCase
private
+ def testing_ruby_repo?
+ !ENV["GEM_COMMAND"].nil?
+ end
+
def silence_warnings
old_verbose, $VERBOSE = $VERBOSE, false
yield
@@ -753,12 +731,12 @@ class TestGemRequire < Gem::TestCase
spec.files += ["extconf.rb", "depend", "#{name}.c"]
so = File.join(spec.gem_dir, "#{name}.#{RbConfig::CONFIG["DLEXT"]}")
- refute_path_exists so
+ assert_path_not_exist so
path = Gem::Package.build spec
installer = Gem::Installer.at path
installer.install
- assert_path_exists so
+ assert_path_exist so
spec.gem_dir
end
diff --git a/test/rubygems/test_rubygems.rb b/test/rubygems/test_rubygems.rb
new file mode 100644
index 0000000000..493b9fdf4a
--- /dev/null
+++ b/test/rubygems/test_rubygems.rb
@@ -0,0 +1,44 @@
+require_relative 'helper'
+
+class GemTest < Gem::TestCase
+ def test_rubygems_normal_behaviour
+ _ = Gem::Util.popen(*ruby_with_rubygems_in_load_path, '-e', "'require \"rubygems\"'", {:err => [:child, :out]}).strip
+ assert $?.success?
+ end
+
+ def test_operating_system_other_exceptions
+ pend "does not apply to truffleruby" if RUBY_ENGINE == 'truffleruby'
+
+ path = util_install_operating_system_rb <<-RUBY
+ intentionally_not_implemented_method
+ RUBY
+
+ output = Gem::Util.popen(*ruby_with_rubygems_and_fake_operating_system_in_load_path(path), '-e', "'require \"rubygems\"'", {:err => [:child, :out]}).strip
+ assert !$?.success?
+ assert_includes output, "undefined local variable or method `intentionally_not_implemented_method'"
+ assert_includes output, "Loading the rubygems/defaults/operating_system.rb file caused an error. " \
+ "This file is owned by your OS, not by rubygems upstream. " \
+ "Please find out which OS package this file belongs to and follow the guidelines from your OS to report " \
+ "the problem and ask for help."
+ end
+
+ private
+
+ def util_install_operating_system_rb(content)
+ dir_lib = Dir.mktmpdir("test_operating_system_lib", @tempdir)
+ dir_lib_arg = File.join dir_lib
+
+ dir_lib_rubygems_defaults_arg = File.join dir_lib_arg, "lib", "rubygems", "defaults"
+ FileUtils.mkdir_p dir_lib_rubygems_defaults_arg
+
+ operating_system_rb = File.join dir_lib_rubygems_defaults_arg, "operating_system.rb"
+
+ File.open(operating_system_rb, 'w') {|f| f.write content }
+
+ File.join dir_lib_arg, "lib"
+ end
+
+ def ruby_with_rubygems_and_fake_operating_system_in_load_path(operating_system_path)
+ [Gem.ruby, "-I", operating_system_path, "-I" , $LOAD_PATH.find{|p| p == File.dirname($LOADED_FEATURES.find{|f| f.end_with?("/rubygems.rb") }) }]
+ end
+end
diff --git a/lib/rubygems/test_utilities.rb b/test/rubygems/utilities.rb
index 1371ae9b14..20416fe70b 100644
--- a/lib/rubygems/test_utilities.rb
+++ b/test/rubygems/utilities.rb
@@ -76,7 +76,7 @@ class Gem::FakeFetcher
def cache_update_path(uri, path = nil, update = true)
if data = fetch_path(uri)
- open(path, 'wb') {|io| io.write data } if path and update
+ File.open(path, 'wb') {|io| io.write data } if path and update
data
else
Gem.read_binary(path) if path
@@ -348,8 +348,6 @@ end
# A StringIO duck-typed class that uses Tempfile instead of String as the
# backing store.
#
-# This is available when rubygems/test_utilities is required.
-#--
# This class was added to flush out problems in Rubinius' IO implementation.
class TempIO < Tempfile
diff --git a/test/socket/test_tcp.rb b/test/socket/test_tcp.rb
index a9e2a417a5..9aa716f7ec 100644
--- a/test/socket/test_tcp.rb
+++ b/test/socket/test_tcp.rb
@@ -115,4 +115,29 @@ class TestSocket_TCPSocket < Test::Unit::TestCase
assert_raise(IO::WaitReadable) { svr.accept_nonblock(exception: true) }
}
end
+
+ def test_accept_multithread
+ attempts_count = 5
+ server_threads_count = 3
+ client_threads_count = 3
+
+ attempts_count.times do
+ server_threads = Array.new(server_threads_count) do
+ Thread.new do
+ TCPServer.open("localhost", 0) do |server|
+ accept_threads = Array.new(client_threads_count) do
+ Thread.new { server.accept.close }
+ end
+ client_threads = Array.new(client_threads_count) do
+ Thread.new { TCPSocket.open(server.addr[3], server.addr[1]) {} }
+ end
+ client_threads.each(&:join)
+ accept_threads.each(&:join)
+ end
+ end
+ end
+
+ server_threads.each(&:join)
+ end
+ end
end if defined?(TCPSocket)
diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb
index a49326119f..144a9f4292 100644
--- a/test/stringio/test_stringio.rb
+++ b/test/stringio/test_stringio.rb
@@ -446,6 +446,15 @@ class TestStringIO < Test::Unit::TestCase
f.close unless f.closed?
end
+ def test_each_byte_closed
+ f = StringIO.new("1234")
+ assert_equal("1".ord, f.each_byte {|c| f.close; break c })
+ f = StringIO.new("1234")
+ assert_raise(IOError) do
+ f.each_byte { f.close }
+ end
+ end
+
def test_getbyte
f = StringIO.new("1234")
assert_equal("1".ord, f.getbyte)
@@ -520,11 +529,29 @@ class TestStringIO < Test::Unit::TestCase
assert_equal(%w(1 2 3 4), f.each_char.to_a)
end
+ def test_each_char_closed
+ f = StringIO.new("1234")
+ assert_equal("1", f.each_char {|c| f.close; break c })
+ f = StringIO.new("1234")
+ assert_raise(IOError) do
+ f.each_char { f.close }
+ end
+ end
+
def test_each_codepoint
f = StringIO.new("1234")
assert_equal([49, 50, 51, 52], f.each_codepoint.to_a)
end
+ def test_each_codepoint_closed
+ f = StringIO.new("1234")
+ assert_equal("1".ord, f.each_codepoint {|c| f.close; break c })
+ f = StringIO.new("1234")
+ assert_raise(IOError) do
+ f.each_codepoint { f.close }
+ end
+ end
+
def test_each_codepoint_enumerator
io = StringIO.new('你好поÑтроить')
@@ -730,6 +757,15 @@ class TestStringIO < Test::Unit::TestCase
assert_equal("b""\0""a", s.string)
end
+ def test_ungetc_fill
+ count = 100
+ s = StringIO.new
+ s.print 'a' * count
+ s.ungetc('b' * (count * 5))
+ assert_equal((count * 5), s.string.size)
+ assert_match(/\Ab+\z/, s.string)
+ end
+
def test_ungetbyte_pos
b = '\\b00010001 \\B00010001 \\b1 \\B1 \\b000100011'
s = StringIO.new( b )
@@ -755,6 +791,15 @@ class TestStringIO < Test::Unit::TestCase
assert_equal("b""\0""a", s.string)
end
+ def test_ungetbyte_fill
+ count = 100
+ s = StringIO.new
+ s.print 'a' * count
+ s.ungetbyte('b' * (count * 5))
+ assert_equal((count * 5), s.string.size)
+ assert_match(/\Ab+\z/, s.string)
+ end
+
def test_frozen
s = StringIO.new
s.freeze
@@ -798,18 +843,17 @@ class TestStringIO < Test::Unit::TestCase
end
def test_overflow
- skip if RbConfig::SIZEOF["void*"] > RbConfig::SIZEOF["long"]
+ return if RbConfig::SIZEOF["void*"] > RbConfig::SIZEOF["long"]
limit = RbConfig::LIMITS["INTPTR_MAX"] - 0x10
assert_separately(%w[-rstringio], "#{<<-"begin;"}\n#{<<-"end;"}")
begin;
limit = #{limit}
ary = []
- while true
+ begin
x = "a"*0x100000
break if [x].pack("p").unpack("i!")[0] < 0
ary << x
- skip if ary.size > 100
- end
+ end while ary.size <= 100
s = StringIO.new(x)
s.gets("xxx", limit)
assert_equal(0x100000, s.pos)
diff --git a/test/strscan/test_ractor.rb b/test/strscan/test_ractor.rb
index 0d44242304..480c1ae8a6 100644
--- a/test/strscan/test_ractor.rb
+++ b/test/strscan/test_ractor.rb
@@ -3,7 +3,7 @@ require 'test/unit'
class TestStringScannerRactor < Test::Unit::TestCase
def setup
- skip unless defined? Ractor
+ pend unless defined? Ractor
end
def test_ractor
diff --git a/test/strscan/test_stringscanner.rb b/test/strscan/test_stringscanner.rb
index 4b001b317e..6e30be1f7d 100644
--- a/test/strscan/test_stringscanner.rb
+++ b/test/strscan/test_stringscanner.rb
@@ -206,6 +206,23 @@ class TestStringScanner < Test::Unit::TestCase
assert_equal 11, s.charpos
end
+ def test_charpos_not_use_string_methods
+ string = +'abcädeföghi'
+ scanner = create_string_scanner(string)
+
+ class << string
+ EnvUtil.suppress_warning do
+ undef_method(*instance_methods)
+ end
+ end
+
+ assert_equal 0, scanner.charpos
+ assert_equal "abcä", scanner.scan_until(/ä/)
+ assert_equal 4, scanner.charpos
+ assert_equal "defö", scanner.scan_until(/ö/)
+ assert_equal 8, scanner.charpos
+ end
+
def test_concat
s = create_string_scanner('a'.dup)
s.scan(/a/)
diff --git a/test/test_time.rb b/test/test_time.rb
index ca20788aac..4f11048596 100644
--- a/test/test_time.rb
+++ b/test/test_time.rb
@@ -62,6 +62,15 @@ class TestTimeExtension < Test::Unit::TestCase # :nodoc:
assert_equal(true, t.utc?)
end
+ def test_rfc2822_nonlinear
+ pre = ->(n) {"0 Feb 00 00 :00" + " " * n}
+ assert_linear_performance([100, 500, 5000, 50_000], pre: pre) do |s|
+ assert_raise(ArgumentError) do
+ Time.rfc2822(s)
+ end
+ end
+ end
+
def test_encode_rfc2822
t = Time.utc(1)
assert_equal("Mon, 01 Jan 0001 00:00:00 -0000", t.rfc2822)
diff --git a/test/test_tmpdir.rb b/test/test_tmpdir.rb
index c56fd5f401..7ef9f59b54 100644
--- a/test/test_tmpdir.rb
+++ b/test/test_tmpdir.rb
@@ -97,8 +97,10 @@ class TestTmpdir < Test::Unit::TestCase
target = target.chomp('/') + '/'
traversal_path = target.sub(/\A\w:/, '') # for DOSISH
traversal_path = Array.new(target.count('/')-2, '..').join('/') + traversal_path
- actual = yield traversal_path
- assert_not_send([File.absolute_path(actual), :start_with?, target])
+ [File::SEPARATOR, File::ALT_SEPARATOR].compact.each do |separator|
+ actual = yield traversal_path.tr('/', separator)
+ assert_not_send([File.absolute_path(actual), :start_with?, target])
+ end
end
end
end
diff --git a/test/uri/test_common.rb b/test/uri/test_common.rb
index 1afa35f93d..2b877c10d7 100644
--- a/test/uri/test_common.rb
+++ b/test/uri/test_common.rb
@@ -56,6 +56,17 @@ class TestCommon < Test::Unit::TestCase
assert_raise(NoMethodError) { Object.new.URI("http://www.ruby-lang.org/") }
end
+ def test_parse_timeout
+ pre = ->(n) {
+ 'https://example.com/dir/' + 'a' * (n * 100) + '/##.jpg'
+ }
+ assert_linear_performance((1..10).map {|i| i * 100}, pre: pre) do |uri|
+ assert_raise(URI::InvalidURIError) do
+ URI.parse(uri)
+ end
+ end
+ 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",
diff --git a/test/uri/test_parser.rb b/test/uri/test_parser.rb
index b13a26ca84..e2f50cf2b4 100644
--- a/test/uri/test_parser.rb
+++ b/test/uri/test_parser.rb
@@ -58,4 +58,33 @@ class URI::TestParser < Test::Unit::TestCase
assert_equal("\u3042", p1.unescape('%e3%81%82'.force_encoding(Encoding::US_ASCII)))
assert_equal("\xe3\x83\x90\xe3\x83\x90", p1.unescape("\xe3\x83\x90%e3%83%90"))
end
+
+ def test_split
+ assert_equal(["http", nil, "example.com", nil, nil, "", nil, nil, nil], URI.split("http://example.com"))
+ assert_equal(["http", nil, "[0::0]", nil, nil, "", nil, nil, nil], URI.split("http://[0::0]"))
+ assert_equal([nil, nil, "example.com", nil, nil, "", nil, nil, nil], URI.split("//example.com"))
+ assert_equal([nil, nil, "[0::0]", nil, nil, "", nil, nil, nil], URI.split("//[0::0]"))
+ end
+
+ def test_rfc2822_parse_relative_uri
+ pre = ->(length) {
+ " " * length + "\0"
+ }
+ parser = URI::RFC2396_Parser.new
+ assert_linear_performance((1..5).map {|i| 10**i}, pre: pre) do |uri|
+ assert_raise(URI::InvalidURIError) do
+ parser.split(uri)
+ end
+ end
+ end
+
+ def test_rfc3986_port_check
+ pre = ->(length) {"\t" * length + "a"}
+ uri = URI.parse("http://my.example.com")
+ assert_linear_performance((1..5).map {|i| 10**i}, pre: pre) do |port|
+ assert_raise(URI::InvalidComponentError) do
+ uri.port = port
+ end
+ end
+ end
end
diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb
index addd4270e1..c469d24dfc 100644
--- a/test/zlib/test_zlib.rb
+++ b/test/zlib/test_zlib.rb
@@ -3,6 +3,8 @@
require 'test/unit'
require 'stringio'
require 'tempfile'
+require 'tmpdir'
+require 'securerandom'
begin
require 'zlib'
@@ -502,6 +504,72 @@ if defined? Zlib
assert_raise(Zlib::StreamError) { z.set_dictionary("foo") }
z.close
end
+
+ def test_multithread_deflate
+ zd = Zlib::Deflate.new
+
+ s = "x" * 10000
+ (0...10).map do |x|
+ Thread.new do
+ 1000.times { zd.deflate(s) }
+ end
+ end.each do |th|
+ th.join
+ end
+ ensure
+ zd&.finish
+ zd&.close
+ end
+
+ def test_multithread_inflate
+ zi = Zlib::Inflate.new
+
+ s = Zlib.deflate("x" * 10000)
+ (0...10).map do |x|
+ Thread.new do
+ 1000.times { zi.inflate(s) }
+ end
+ end.each do |th|
+ th.join
+ end
+ ensure
+ zi&.finish
+ zi&.close
+ end
+
+ def test_recursive_deflate
+ original_gc_stress = GC.stress
+ GC.stress = true
+ zd = Zlib::Deflate.new
+
+ s = SecureRandom.random_bytes(1024**2)
+ assert_raise(Zlib::InProgressError) do
+ zd.deflate(s) do
+ zd.deflate(s)
+ end
+ end
+ ensure
+ GC.stress = original_gc_stress
+ zd&.finish
+ zd&.close
+ end
+
+ def test_recursive_inflate
+ original_gc_stress = GC.stress
+ GC.stress = true
+ zi = Zlib::Inflate.new
+
+ s = Zlib.deflate(SecureRandom.random_bytes(1024**2))
+
+ assert_raise(Zlib::InProgressError) do
+ zi.inflate(s) do
+ zi.inflate(s)
+ end
+ end
+ ensure
+ GC.stress = original_gc_stress
+ zi&.close
+ end
end
class TestZlibGzipFile < Test::Unit::TestCase
@@ -720,6 +788,26 @@ if defined? Zlib
gz.close
}
end
+
+ if defined? File::TMPFILE
+ def test_path_tmpfile
+ sio = StringIO.new("".dup, 'w')
+ gz = Zlib::GzipWriter.new(sio)
+ gz.write "hi"
+ gz.close
+
+ File.open(Dir.mktmpdir, File::RDWR | File::TMPFILE) do |io|
+ io.write sio.string
+ io.rewind
+
+ gz = Zlib::GzipWriter.new(io)
+ assert_raise(NoMethodError) { gz.path }
+
+ gz = Zlib::GzipReader.new(io)
+ assert_raise(NoMethodError) { gz.path }
+ end
+ end
+ end
end
class TestZlibGzipReader < Test::Unit::TestCase
diff --git a/thread.c b/thread.c
index ddb4621ee2..59279ef8ff 100644
--- a/thread.c
+++ b/thread.c
@@ -424,13 +424,6 @@ rb_vm_gvl_destroy(rb_global_vm_lock_t *gvl)
{
gvl_release(gvl);
gvl_destroy(gvl);
-
- if (0) {
- rb_vm_t *vm = GET_VM();
- /* may be held by running threads */
- rb_native_mutex_destroy(&vm->waitpid_lock);
- rb_native_mutex_destroy(&vm->workqueue_lock);
- }
}
void
@@ -546,12 +539,15 @@ terminate_all(rb_ractor_t *r, const rb_thread_t *main_thread)
static void
rb_threadptr_join_list_wakeup(rb_thread_t *thread)
{
- struct rb_waiting_list *join_list = thread->join_list;
+ while (thread->join_list) {
+ struct rb_waiting_list *join_list = thread->join_list;
+
+ // Consume the entry from the join list:
+ thread->join_list = join_list->next;
- while (join_list) {
rb_thread_t *target_thread = join_list->thread;
- if (target_thread->scheduler != Qnil) {
+ if (target_thread->scheduler != Qnil && rb_fiberptr_blocking(join_list->fiber) == 0) {
rb_scheduler_unblock(target_thread->scheduler, target_thread->self, rb_fiberptr_self(join_list->fiber));
} else {
rb_threadptr_interrupt(target_thread);
@@ -564,25 +560,20 @@ rb_threadptr_join_list_wakeup(rb_thread_t *thread)
break;
}
}
-
- join_list = join_list->next;
}
}
void
rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th)
{
- const char *err;
- rb_mutex_t *mutex;
- rb_mutex_t *mutexes = th->keeping_mutexes;
+ while (th->keeping_mutexes) {
+ rb_mutex_t *mutex = th->keeping_mutexes;
+ th->keeping_mutexes = mutex->next_mutex;
+
+ /* rb_warn("mutex #<%p> remains to be locked by terminated thread", (void *)mutexes); */
- while (mutexes) {
- mutex = mutexes;
- /* rb_warn("mutex #<%p> remains to be locked by terminated thread",
- (void *)mutexes); */
- mutexes = mutex->next_mutex;
- err = rb_mutex_unlock_th(mutex, th, mutex->fiber);
- if (err) rb_bug("invalid keeping_mutexes: %s", err);
+ const char *error_message = rb_mutex_unlock_th(mutex, th, mutex->fiber);
+ if (error_message) rb_bug("invalid keeping_mutexes: %s", error_message);
}
}
@@ -640,6 +631,7 @@ thread_cleanup_func_before_exec(void *th_ptr)
{
rb_thread_t *th = th_ptr;
th->status = THREAD_KILLED;
+
// The thread stack doesn't exist in the forked process:
th->ec->machine.stack_start = th->ec->machine.stack_end = NULL;
@@ -696,7 +688,7 @@ rb_vm_proc_local_ep(VALUE proc)
VALUE rb_vm_invoke_proc_with_self(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler);
-static void
+static VALUE
thread_do_start_proc(rb_thread_t *th)
{
VALUE args = th->invoke_arg.proc.args;
@@ -710,7 +702,6 @@ thread_do_start_proc(rb_thread_t *th)
th->ec->root_lep = rb_vm_proc_local_ep(procval);
th->ec->root_svar = Qfalse;
- EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef);
vm_check_ints_blocking(th->ec);
if (th->invoke_type == thread_invoke_type_ractor_proc) {
@@ -721,11 +712,12 @@ thread_do_start_proc(rb_thread_t *th)
rb_ractor_receive_parameters(th->ec, th->ractor, args_len, (VALUE *)args_ptr);
vm_check_ints_blocking(th->ec);
- // kick thread
- th->value = rb_vm_invoke_proc_with_self(th->ec, proc, self,
- args_len, args_ptr,
- th->invoke_arg.proc.kw_splat,
- VM_BLOCK_HANDLER_NONE);
+ return rb_vm_invoke_proc_with_self(
+ th->ec, proc, self,
+ args_len, args_ptr,
+ th->invoke_arg.proc.kw_splat,
+ VM_BLOCK_HANDLER_NONE
+ );
}
else {
args_len = RARRAY_LENINT(args);
@@ -741,17 +733,12 @@ thread_do_start_proc(rb_thread_t *th)
vm_check_ints_blocking(th->ec);
- // kick thread
- th->value = rb_vm_invoke_proc(th->ec, proc,
- args_len, args_ptr,
- th->invoke_arg.proc.kw_splat,
- VM_BLOCK_HANDLER_NONE);
- }
-
- EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
-
- if (th->invoke_type == thread_invoke_type_ractor_proc) {
- rb_ractor_atexit(th->ec, th->value);
+ return rb_vm_invoke_proc(
+ th->ec, proc,
+ args_len, args_ptr,
+ th->invoke_arg.proc.kw_splat,
+ VM_BLOCK_HANDLER_NONE
+ );
}
}
@@ -759,24 +746,42 @@ static void
thread_do_start(rb_thread_t *th)
{
native_set_thread_name(th);
+ VALUE result = Qundef;
+
+ EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef);
switch (th->invoke_type) {
case thread_invoke_type_proc:
+ result = thread_do_start_proc(th);
+ break;
+
case thread_invoke_type_ractor_proc:
- thread_do_start_proc(th);
+ result = thread_do_start_proc(th);
+ rb_ractor_atexit(th->ec, result);
break;
+
case thread_invoke_type_func:
- th->value = (*th->invoke_arg.func.func)(th->invoke_arg.func.arg);
+ result = (*th->invoke_arg.func.func)(th->invoke_arg.func.arg);
break;
+
case thread_invoke_type_none:
rb_bug("unreachable");
}
rb_scheduler_set(Qnil);
+
+ th->value = result;
+
+ EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
}
void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
+// io.c
+VALUE rb_io_prep_stdin(void);
+VALUE rb_io_prep_stdout(void);
+VALUE rb_io_prep_stderr(void);
+
static int
thread_start_func_2(rb_thread_t *th, VALUE *stack_start)
{
@@ -799,6 +804,10 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start)
RB_VM_LOCK();
{
rb_vm_ractor_blocking_cnt_dec(th->vm, th->ractor, __FILE__, __LINE__);
+ rb_ractor_t *r = th->ractor;
+ r->r_stdin = rb_io_prep_stdin();
+ r->r_stdout = rb_io_prep_stdout();
+ r->r_stderr = rb_io_prep_stderr();
}
RB_VM_UNLOCK();
}
@@ -814,87 +823,96 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start)
th->ec->machine.stack_start = STACK_DIR_UPPER(vm_stack + size, vm_stack);
th->ec->machine.stack_maxsize -= size * sizeof(VALUE);
- {
- thread_debug("thread start (get lock): %p\n", (void *)th);
+ thread_debug("thread start (get lock): %p\n", (void *)th);
- EC_PUSH_TAG(th->ec);
- if ((state = EC_EXEC_TAG()) == TAG_NONE) {
- SAVE_ROOT_JMPBUF(th, thread_do_start(th));
- }
- else {
- errinfo = th->ec->errinfo;
+ // Ensure that we are not joinable.
+ VM_ASSERT(th->value == Qundef);
- if (state == TAG_FATAL) {
- if (th->invoke_type == thread_invoke_type_ractor_proc) {
- rb_ractor_atexit(th->ec, Qnil);
- }
- /* fatal error within this thread, need to stop whole script */
- }
- else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
- /* exit on main_thread. */
- }
- else {
- if (th->report_on_exception) {
- VALUE mesg = rb_thread_to_s(th->self);
- rb_str_cat_cstr(mesg, " terminated with exception (report_on_exception is true):\n");
- rb_write_error_str(mesg);
- rb_ec_error_print(th->ec, errinfo);
- }
+ EC_PUSH_TAG(th->ec);
- if (th->invoke_type == thread_invoke_type_ractor_proc) {
- rb_ractor_atexit_exception(th->ec);
- }
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ SAVE_ROOT_JMPBUF(th, thread_do_start(th));
+ } else {
+ errinfo = th->ec->errinfo;
- if (th->vm->thread_abort_on_exception ||
- th->abort_on_exception || RTEST(ruby_debug)) {
- /* exit on main_thread */
- }
- else {
- errinfo = Qnil;
- }
- }
- th->value = Qnil;
- }
+ VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
+ if (!NIL_P(exc)) errinfo = exc;
- if (th->invoke_type == thread_invoke_type_ractor_proc) {
- rb_thread_terminate_all(th);
- rb_ractor_teardown(th->ec);
+ if (state == TAG_FATAL) {
+ if (th->invoke_type == thread_invoke_type_ractor_proc) {
+ rb_ractor_atexit(th->ec, Qnil);
+ }
+ /* fatal error within this thread, need to stop whole script */
}
+ else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
+ /* exit on main_thread. */
+ }
+ else {
+ if (th->report_on_exception) {
+ VALUE mesg = rb_thread_to_s(th->self);
+ rb_str_cat_cstr(mesg, " terminated with exception (report_on_exception is true):\n");
+ rb_write_error_str(mesg);
+ rb_ec_error_print(th->ec, errinfo);
+ }
- th->status = THREAD_KILLED;
- thread_debug("thread end: %p\n", (void *)th);
+ if (th->invoke_type == thread_invoke_type_ractor_proc) {
+ rb_ractor_atexit_exception(th->ec);
+ }
- if (th->vm->ractor.main_thread == th) {
- ruby_stop(0);
- }
+ if (th->vm->thread_abort_on_exception ||
+ th->abort_on_exception || RTEST(ruby_debug)) {
+ /* exit on main_thread */
+ }
+ else {
+ errinfo = Qnil;
+ }
+ }
+ th->value = Qnil;
+ }
- if (RB_TYPE_P(errinfo, T_OBJECT)) {
- /* treat with normal error object */
- rb_threadptr_raise(ractor_main_th, 1, &errinfo);
- }
- EC_POP_TAG();
+ // The thread is effectively finished and can be joined.
+ VM_ASSERT(th->value != Qundef);
- rb_ec_clear_current_thread_trace_func(th->ec);
+ rb_threadptr_join_list_wakeup(th);
+ rb_threadptr_unlock_all_locking_mutexes(th);
- /* locking_mutex must be Qfalse */
- if (th->locking_mutex != Qfalse) {
- rb_bug("thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE")",
- (void *)th, th->locking_mutex);
- }
+ if (th->invoke_type == thread_invoke_type_ractor_proc) {
+ rb_thread_terminate_all(th);
+ rb_ractor_teardown(th->ec);
+ }
- if (ractor_main_th->status == THREAD_KILLED &&
- th->ractor->threads.cnt <= 2 /* main thread and this thread */) {
- /* I'm last thread. wake up main thread from rb_thread_terminate_all */
- rb_threadptr_interrupt(ractor_main_th);
- }
+ th->status = THREAD_KILLED;
+ thread_debug("thread end: %p\n", (void *)th);
+
+ if (th->vm->ractor.main_thread == th) {
+ ruby_stop(0);
+ }
+
+ if (RB_TYPE_P(errinfo, T_OBJECT)) {
+ /* treat with normal error object */
+ rb_threadptr_raise(ractor_main_th, 1, &errinfo);
+ }
- rb_threadptr_join_list_wakeup(th);
- rb_threadptr_unlock_all_locking_mutexes(th);
- rb_check_deadlock(th->ractor);
+ EC_POP_TAG();
+
+ rb_ec_clear_current_thread_trace_func(th->ec);
- rb_fiber_close(th->ec->fiber_ptr);
+ /* locking_mutex must be Qfalse */
+ if (th->locking_mutex != Qfalse) {
+ rb_bug("thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE")",
+ (void *)th, th->locking_mutex);
}
+ if (ractor_main_th->status == THREAD_KILLED &&
+ th->ractor->threads.cnt <= 2 /* main thread and this thread */) {
+ /* I'm last thread. wake up main thread from rb_thread_terminate_all */
+ rb_threadptr_interrupt(ractor_main_th);
+ }
+
+ rb_check_deadlock(th->ractor);
+
+ rb_fiber_close(th->ec->fiber_ptr);
+
thread_cleanup_func(th, FALSE);
VM_ASSERT(th->ec->vm_stack == NULL);
@@ -1151,6 +1169,12 @@ remove_from_join_list(VALUE arg)
static rb_hrtime_t *double2hrtime(rb_hrtime_t *, double);
+static int
+thread_finished(rb_thread_t *th)
+{
+ return th->status == THREAD_KILLED || th->value != Qundef;
+}
+
static VALUE
thread_join_sleep(VALUE arg)
{
@@ -1177,7 +1201,7 @@ thread_join_sleep(VALUE arg)
end = rb_hrtime_add(*limit, rb_hrtime_now());
}
- while (target_th->status != THREAD_KILLED) {
+ while (!thread_finished(target_th)) {
VALUE scheduler = rb_scheduler_current();
if (scheduler != Qnil) {
@@ -3318,11 +3342,11 @@ rb_thread_status(VALUE thread)
static VALUE
rb_thread_alive_p(VALUE thread)
{
- if (rb_threadptr_dead(rb_thread_ptr(thread))) {
- return Qfalse;
+ if (thread_finished(rb_thread_ptr(thread))) {
+ return Qfalse;
}
else {
- return Qtrue;
+ return Qtrue;
}
}
diff --git a/thread_pthread.c b/thread_pthread.c
index 97879f559a..134d1875cb 100644
--- a/thread_pthread.c
+++ b/thread_pthread.c
@@ -1759,6 +1759,7 @@ ubf_timer_disarm(void)
#if UBF_TIMER == UBF_TIMER_POSIX
rb_atomic_t prev;
+ if (timer_posix.owner && timer_posix.owner != getpid()) return;
prev = ATOMIC_CAS(timer_posix.state, RTIMER_ARMED, RTIMER_DISARM);
switch (prev) {
case RTIMER_DISARM: return; /* likely */
diff --git a/thread_sync.c b/thread_sync.c
index 8c999e2164..26d5e6b686 100644
--- a/thread_sync.c
+++ b/thread_sync.c
@@ -32,7 +32,7 @@ sync_wakeup(struct list_head *head, long max)
if (cur->th->status != THREAD_KILLED) {
- if (cur->th->scheduler != Qnil) {
+ if (cur->th->scheduler != Qnil && rb_fiberptr_blocking(cur->fiber) == 0) {
rb_scheduler_unblock(cur->th->scheduler, cur->self, rb_fiberptr_self(cur->fiber));
} else {
rb_threadptr_interrupt(cur->th);
@@ -437,7 +437,7 @@ rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t *th, rb_fiber_t *fiber)
list_for_each_safe(&mutex->waitq, cur, next, node) {
list_del_init(&cur->node);
- if (cur->th->scheduler != Qnil) {
+ if (cur->th->scheduler != Qnil && rb_fiberptr_blocking(cur->fiber) == 0) {
rb_scheduler_unblock(cur->th->scheduler, cur->self, rb_fiberptr_self(cur->fiber));
goto found;
} else {
@@ -560,7 +560,7 @@ rb_mutex_sleep(VALUE self, VALUE timeout)
RUBY_VM_CHECK_INTS_BLOCKING(GET_EC());
time_t end = time(0) - beg;
- return INT2FIX(end);
+ return TIMET2NUM(end);
}
/*
diff --git a/tool/bundler/rubocop_gems.rb b/tool/bundler/rubocop_gems.rb
new file mode 100644
index 0000000000..84cb226330
--- /dev/null
+++ b/tool/bundler/rubocop_gems.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gem "rubocop", "~> 1.7"
+
+gem "minitest"
+gem "rake"
+gem "rake-compiler"
+gem "rspec"
+gem "test-unit"
diff --git a/tool/bundler/rubocop_gems.rb.lock b/tool/bundler/rubocop_gems.rb.lock
new file mode 100644
index 0000000000..fdbbdbdcc3
--- /dev/null
+++ b/tool/bundler/rubocop_gems.rb.lock
@@ -0,0 +1,63 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ ast (2.4.2)
+ diff-lcs (1.4.4)
+ minitest (5.14.4)
+ parallel (1.21.0)
+ parser (3.0.2.0)
+ ast (~> 2.4.1)
+ power_assert (2.0.1)
+ rainbow (3.0.0)
+ rake (13.0.6)
+ rake-compiler (1.1.1)
+ rake
+ regexp_parser (2.1.1)
+ rexml (3.2.5)
+ rspec (3.10.0)
+ rspec-core (~> 3.10.0)
+ rspec-expectations (~> 3.10.0)
+ rspec-mocks (~> 3.10.0)
+ rspec-core (3.10.1)
+ rspec-support (~> 3.10.0)
+ rspec-expectations (3.10.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.10.0)
+ rspec-mocks (3.10.2)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.10.0)
+ rspec-support (3.10.3)
+ rubocop (1.23.0)
+ parallel (~> 1.10)
+ parser (>= 3.0.0.0)
+ rainbow (>= 2.2.2, < 4.0)
+ regexp_parser (>= 1.8, < 3.0)
+ rexml
+ rubocop-ast (>= 1.12.0, < 2.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (>= 1.4.0, < 3.0)
+ rubocop-ast (1.13.0)
+ parser (>= 3.0.1.1)
+ ruby-progressbar (1.11.0)
+ test-unit (3.5.1)
+ power_assert
+ unicode-display_width (2.1.0)
+
+PLATFORMS
+ arm64-darwin-20
+ arm64-darwin-21
+ universal-java-11
+ x86_64-darwin-19
+ x86_64-darwin-20
+ x86_64-linux
+
+DEPENDENCIES
+ minitest
+ rake
+ rake-compiler
+ rspec
+ rubocop (~> 1.7)
+ test-unit
+
+BUNDLED WITH
+ 2.2.33
diff --git a/tool/bundler/standard_gems.rb b/tool/bundler/standard_gems.rb
new file mode 100644
index 0000000000..1cd189742d
--- /dev/null
+++ b/tool/bundler/standard_gems.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gem "standard", "~> 1.0"
+
+gem "minitest"
+gem "rake"
+gem "rake-compiler"
+gem "rspec"
+gem "test-unit"
diff --git a/tool/bundler/standard_gems.rb.lock b/tool/bundler/standard_gems.rb.lock
new file mode 100644
index 0000000000..af22080659
--- /dev/null
+++ b/tool/bundler/standard_gems.rb.lock
@@ -0,0 +1,69 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ ast (2.4.2)
+ diff-lcs (1.4.4)
+ minitest (5.14.4)
+ parallel (1.21.0)
+ parser (3.0.2.0)
+ ast (~> 2.4.1)
+ power_assert (2.0.1)
+ rainbow (3.0.0)
+ rake (13.0.6)
+ rake-compiler (1.1.1)
+ rake
+ regexp_parser (2.1.1)
+ rexml (3.2.5)
+ rspec (3.10.0)
+ rspec-core (~> 3.10.0)
+ rspec-expectations (~> 3.10.0)
+ rspec-mocks (~> 3.10.0)
+ rspec-core (3.10.1)
+ rspec-support (~> 3.10.0)
+ rspec-expectations (3.10.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.10.0)
+ rspec-mocks (3.10.2)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.10.0)
+ rspec-support (3.10.3)
+ rubocop (1.22.3)
+ parallel (~> 1.10)
+ parser (>= 3.0.0.0)
+ rainbow (>= 2.2.2, < 4.0)
+ regexp_parser (>= 1.8, < 3.0)
+ rexml
+ rubocop-ast (>= 1.12.0, < 2.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (>= 1.4.0, < 3.0)
+ rubocop-ast (1.13.0)
+ parser (>= 3.0.1.1)
+ rubocop-performance (1.11.5)
+ rubocop (>= 1.7.0, < 2.0)
+ rubocop-ast (>= 0.4.0)
+ ruby-progressbar (1.11.0)
+ standard (1.4.0)
+ rubocop (= 1.22.3)
+ rubocop-performance (= 1.11.5)
+ test-unit (3.5.1)
+ power_assert
+ unicode-display_width (2.1.0)
+
+PLATFORMS
+ arm64-darwin-20
+ arm64-darwin-21
+ universal-java-11
+ x86_64-darwin-19
+ x86_64-darwin-20
+ x86_64-linux
+
+DEPENDENCIES
+ minitest
+ rake
+ rake-compiler
+ rspec
+ standard (~> 1.0)
+ test-unit
+
+BUNDLED WITH
+ 2.3.0.dev
diff --git a/tool/bundler/test_gems.rb b/tool/bundler/test_gems.rb
index ef40fc8d0e..215d23183e 100644
--- a/tool/bundler/test_gems.rb
+++ b/tool/bundler/test_gems.rb
@@ -3,11 +3,10 @@
source "https://rubygems.org"
gem "rack", "2.0.8"
+gem "webrick", "1.7.0"
gem "rack-test", "~> 1.1"
gem "artifice", "~> 0.6.0"
gem "compact_index", "~> 0.13.0"
gem "sinatra", "~> 2.0"
gem "rake", "13.0.1"
gem "builder", "~> 3.2"
-# ruby-graphviz is used by the viz tests
-gem "ruby-graphviz", "1.2.4" # for >= Ruby 2.3
diff --git a/tool/bundler/test_gems.rb.lock b/tool/bundler/test_gems.rb.lock
index 095eac9f57..7c65d17837 100644
--- a/tool/bundler/test_gems.rb.lock
+++ b/tool/bundler/test_gems.rb.lock
@@ -13,18 +13,22 @@ GEM
rack-test (1.1.0)
rack (>= 1.0, < 3)
rake (13.0.1)
- ruby-graphviz (1.2.4)
- ruby2_keywords (0.0.2)
+ ruby2_keywords (0.0.5)
sinatra (2.0.8.1)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.8.1)
tilt (~> 2.0)
tilt (2.0.10)
+ webrick (1.7.0)
PLATFORMS
java
ruby
+ universal-java-11
+ x64-mingw32
+ x86_64-darwin-20
+ x86_64-linux
DEPENDENCIES
artifice (~> 0.6.0)
@@ -33,8 +37,8 @@ DEPENDENCIES
rack (= 2.0.8)
rack-test (~> 1.1)
rake (= 13.0.1)
- ruby-graphviz (= 1.2.4)
sinatra (~> 2.0)
+ webrick (= 1.7.0)
BUNDLED WITH
- 2.2.0.dev
+ 2.2.33
diff --git a/tool/fake.rb b/tool/fake.rb
index 42174052e2..4946fd6234 100644
--- a/tool/fake.rb
+++ b/tool/fake.rb
@@ -43,6 +43,7 @@ prehook = proc do |extmk|
$extout_prefix = '$(extout)$(target_prefix)/'
config = RbConfig::CONFIG
mkconfig = RbConfig::MAKEFILE_CONFIG
+ $builtruby ||= File.join(builddir, config['RUBY_INSTALL_NAME'] + config['EXEEXT'])
RbConfig.fire_update!("builddir", builddir)
RbConfig.fire_update!("buildlibdir", builddir)
RbConfig.fire_update!("libdir", builddir)
diff --git a/tool/leaked-globals b/tool/leaked-globals
index 0fb9e657c9..083f658fb1 100755
--- a/tool/leaked-globals
+++ b/tool/leaked-globals
@@ -7,6 +7,8 @@ until ARGV.empty?
SYMBOL_PREFIX = $1
when /\ANM=(.*)/ # may be multiple words
NM = $1
+ when /\APLATFORM=(.+)?/
+ platform = $1
else
break
end
@@ -16,15 +18,39 @@ end
config = ARGV.shift
count = 0
col = Colorize.new
-REPLACE = File.read(config).scan(/\bAC_(?:REPLACE|CHECK)_FUNCS?\(\K\w+/)
+config_code = File.read(config)
+REPLACE = config_code.scan(/\bAC_(?:REPLACE|CHECK)_FUNCS?\((\w+)/).flatten
+# REPLACE << 'memcmp' if /\bAC_FUNC_MEMCMP\b/ =~ config_code
+REPLACE.push('main', 'DllMain')
+if platform and !platform.empty?
+ begin
+ h = File.read(platform)
+ rescue Errno::ENOENT
+ else
+ REPLACE.concat(
+ h .gsub(%r[/\*.*?\*/]m, " ") # delete block comments
+ .gsub(%r[//.*], " ") # delete oneline comments
+ .gsub(/^\s*#.*(?:\\\n.*)*/, "") # delete preprocessor directives
+ .gsub(/(?:\A|;)\K\s*typedef\s.*?;/m, "")
+ .scan(/\b((?!rb_|DEPRECATED|_)\w+)\s*\(.*\);/)
+ .flatten)
+ end
+end
+missing = File.dirname(config) + "/missing/"
+ARGV.reject! do |n|
+ unless (src = Dir.glob(missing + File.basename(n, ".*") + ".[cS]")).empty?
+ puts "Ignore #{n} because of #{src.map {|s| File.basename(s)}.join(', ')} under missing"
+ true
+ end
+end
print "Checking leaked global symbols..."
STDOUT.flush
-IO.foreach("|#{NM} -Pgp #{ARGV.join(' ')}") do |line|
+IO.foreach("|#{NM} #{ARGV.join(' ')}") do |line|
n, t, = line.split
next unless /[A-TV-Z]/ =~ t
next unless n.sub!(/^#{SYMBOL_PREFIX}/o, "")
next if n.include?(".")
- next if /\A(?:Init_|InitVM_|RUBY_|ruby_|rb_|[Oo]nig|dln_|mjit_|coroutine_|nu(?:comp|rat)_)/ =~ n
+ next if /\A(?:Init_|InitVM_|RUBY_|ruby_|rb_|[Oo]nig|dln_|mjit_|coroutine_)/ =~ n
next if REPLACE.include?(n)
puts col.fail("leaked") if count.zero?
count += 1
diff --git a/tool/lib/minitest/unit.rb b/tool/lib/minitest/unit.rb
index f70762e5d5..9f07313e1b 100644
--- a/tool/lib/minitest/unit.rb
+++ b/tool/lib/minitest/unit.rb
@@ -452,11 +452,15 @@ module MiniTest
msg = message(msg) { "Expected path '#{path}' to exist" }
assert File.exist?(path), msg
end
+ alias assert_path_exist assert_path_exists
+ alias refute_path_not_exist assert_path_exists
def refute_path_exists(path, msg = nil)
msg = message(msg) { "Expected path '#{path}' to not exist" }
refute File.exist?(path), msg
end
+ alias refute_path_exist refute_path_exists
+ alias assert_path_not_exist refute_path_exists
##
# Captures $stdout and $stderr into strings:
diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb
index d76353cf0f..38369a1040 100644
--- a/tool/lib/test/unit.rb
+++ b/tool/lib/test/unit.rb
@@ -117,6 +117,9 @@ module Test
filter = nil
elsif negative.empty? and positive.size == 1 and pos_pat !~ positive[0]
filter = positive[0]
+ unless /\A[A-Z]\w*(?:::[A-Z]\w*)*#/ =~ filter
+ filter = /##{Regexp.quote(filter)}\z/
+ end
else
filter = Regexp.union(*positive.map! {|s| Regexp.new(s[pos_pat, 1] || "\\A#{Regexp.quote(s)}\\z")})
end
diff --git a/tool/lib/test/unit/core_assertions.rb b/tool/lib/test/unit/core_assertions.rb
index abd0e45035..29d0763132 100644
--- a/tool/lib/test/unit/core_assertions.rb
+++ b/tool/lib/test/unit/core_assertions.rb
@@ -743,6 +743,39 @@ eom
end
end
+ # Expect +seq+ to respond to +first+ and +each+ methods, e.g.,
+ # Array, Range, Enumerator::ArithmeticSequence and other
+ # Enumerable-s, and each elements should be size factors.
+ #
+ # :yield: each elements of +seq+.
+ def assert_linear_performance(seq, rehearsal: nil, pre: ->(n) {n})
+ first = seq.first
+ *arg = pre.call(first)
+ times = (0..(rehearsal || (2 * first))).map do
+ st = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ yield(*arg)
+ t = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - st)
+ assert_operator 0, :<=, t
+ t.nonzero?
+ end
+ times.compact!
+ tmin, tmax = times.minmax
+ tmax *= tmax / tmin
+ tmax = 10**Math.log10(tmax).ceil
+
+ seq.each do |i|
+ next if i == first
+ t = tmax * i.fdiv(first)
+ *arg = pre.call(i)
+ message = "[#{i}]: in #{t}s"
+ Timeout.timeout(t, Timeout::Error, message) do
+ st = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ yield(*arg)
+ assert_operator (Process.clock_gettime(Process::CLOCK_MONOTONIC) - st), :<=, t, message
+ end
+ end
+ end
+
def diff(exp, act)
require 'pp'
q = PP.new(+"")
diff --git a/tool/lib/vcs.rb b/tool/lib/vcs.rb
index 0c01abb0ac..c6d6cddded 100644
--- a/tool/lib/vcs.rb
+++ b/tool/lib/vcs.rb
@@ -52,9 +52,6 @@ module DebugSystem
ret
end
end
-module Kernel
- prepend(DebugSystem)
-end
class VCS
prepend(DebugSystem) if defined?(DebugSystem)
diff --git a/tool/m4/ruby_check_builtin_setjmp.m4 b/tool/m4/ruby_check_builtin_setjmp.m4
index a4289e2e9d..008fd45911 100644
--- a/tool/m4/ruby_check_builtin_setjmp.m4
+++ b/tool/m4/ruby_check_builtin_setjmp.m4
@@ -8,18 +8,18 @@ AC_CACHE_CHECK(for __builtin_setjmp, ac_cv_func___builtin_setjmp,
ac_cv_func___builtin_setjmp=no
for cast in "" "(void **)"; do
RUBY_WERROR_FLAG(
- [AC_TRY_LINK([@%:@include <setjmp.h>
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[@%:@include <setjmp.h>
@%:@include <stdio.h>
jmp_buf jb;
@%:@ifdef NORETURN
NORETURN(void t(void));
@%:@endif
void t(void) {__builtin_longjmp($cast jb, 1);}
- int jump(void) {(void)(__builtin_setjmp($cast jb) ? 1 : 0); return 0;}],
- [
+ int jump(void) {(void)(__builtin_setjmp($cast jb) ? 1 : 0); return 0;}]],
+ [[
void (*volatile f)(void) = t;
if (!jump()) printf("%d\n", f != 0);
- ],
+ ]])],
[ac_cv_func___builtin_setjmp="yes with cast ($cast)"])
])
test "$ac_cv_func___builtin_setjmp" = no || break
diff --git a/tool/m4/ruby_check_printf_prefix.m4 b/tool/m4/ruby_check_printf_prefix.m4
index 9007c18c0a..0415f9fa92 100644
--- a/tool/m4/ruby_check_printf_prefix.m4
+++ b/tool/m4/ruby_check_printf_prefix.m4
@@ -4,8 +4,7 @@ AC_CACHE_CHECK([for printf prefix for $1], [rb_cv_pri_prefix_]AS_TR_SH($1),[
[rb_cv_pri_prefix_]AS_TR_SH($1)=[NONE]
RUBY_WERROR_FLAG(RUBY_APPEND_OPTIONS(CFLAGS, $rb_cv_wsuppress_flags)
for pri in $2; do
- AC_TRY_COMPILE(
- [@%:@include <stdio.h>
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <stdio.h>
@%:@include <stddef.h>
@%:@ifdef __GNUC__
@%:@if defined __MINGW_PRINTF_FORMAT
@@ -18,9 +17,9 @@ AC_CACHE_CHECK([for printf prefix for $1], [rb_cv_pri_prefix_]AS_TR_SH($1),[
@%:@else
@%:@define PRINTF_ARGS(decl, string_index, first_to_check) decl
@%:@endif
- PRINTF_ARGS(void test_sprintf(const char*, ...), 1, 2);],
- [printf("%]${pri}[d", (]$1[)42);
- test_sprintf("%]${pri}[d", (]$1[)42);],
+ PRINTF_ARGS(void test_sprintf(const char*, ...), 1, 2);]],
+ [[printf("%]${pri}[d", (]$1[)42);
+ test_sprintf("%]${pri}[d", (]$1[)42);]])],
[rb_cv_pri_prefix_]AS_TR_SH($1)[=[$pri]; break])
done)])
AS_IF([test "[$rb_cv_pri_prefix_]AS_TR_SH($1)" != NONE], [
diff --git a/tool/m4/ruby_check_setjmp.m4 b/tool/m4/ruby_check_setjmp.m4
index 59f38581b8..66652984ea 100644
--- a/tool/m4/ruby_check_setjmp.m4
+++ b/tool/m4/ruby_check_setjmp.m4
@@ -2,14 +2,14 @@
# used for AC_ARG_WITH(setjmp-type)
AC_DEFUN([RUBY_CHECK_SETJMP], [
AC_CACHE_CHECK([for ]$1[ as a macro or function], ac_cv_func_$1,
- [AC_TRY_COMPILE([
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@include <setjmp.h>
]AC_INCLUDES_DEFAULT([$3])[
@%:@define JMPARGS_1 env
@%:@define JMPARGS_2 env,1
@%:@define JMPARGS JMPARGS_]m4_ifval($2,2,1)[
-],
- m4_ifval($2,$2,jmp_buf)[ env; $1(JMPARGS);],
+]],
+ [m4_ifval($2,$2,jmp_buf)[ env; $1(JMPARGS);]])],
ac_cv_func_$1=yes,
ac_cv_func_$1=no)]
)
diff --git a/tool/m4/ruby_check_sysconf.m4 b/tool/m4/ruby_check_sysconf.m4
index f6b247a16f..8324be6764 100644
--- a/tool/m4/ruby_check_sysconf.m4
+++ b/tool/m4/ruby_check_sysconf.m4
@@ -1,9 +1,9 @@
# -*- Autoconf -*-
AC_DEFUN([RUBY_CHECK_SYSCONF], [dnl
AC_CACHE_CHECK([whether _SC_$1 is supported], rb_cv_have_sc_[]m4_tolower($1),
- [AC_TRY_COMPILE([#include <unistd.h>
- ],
- [_SC_$1 >= 0],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>
+ ]],
+ [[_SC_$1 >= 0]])],
rb_cv_have_sc_[]m4_tolower($1)=yes,
rb_cv_have_sc_[]m4_tolower($1)=no)
])
diff --git a/tool/m4/ruby_cppoutfile.m4 b/tool/m4/ruby_cppoutfile.m4
index 7c81c4f354..495ae0aae4 100644
--- a/tool/m4/ruby_cppoutfile.m4
+++ b/tool/m4/ruby_cppoutfile.m4
@@ -4,8 +4,8 @@ AC_DEFUN([RUBY_CPPOUTFILE],
[save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS='-o conftest-1.i'
rb_cv_cppoutfile=no
-AC_TRY_CPP([test-for-cppout],
- [grep test-for-cppout conftest-1.i > /dev/null && rb_cv_cppoutfile=yes])
+AC_PREPROC_IFELSE([AC_LANG_SOURCE([[test-for-cppout]])],
+ [grep test-for-cppout conftest-1.i > /dev/null && rb_cv_cppoutfile=yes])
CPPFLAGS="$save_CPPFLAGS"
rm -f conftest*])
AS_IF([test "$rb_cv_cppoutfile" = yes], [
diff --git a/tool/m4/ruby_decl_attribute.m4 b/tool/m4/ruby_decl_attribute.m4
index 3187b9be60..22358a079a 100644
--- a/tool/m4/ruby_decl_attribute.m4
+++ b/tool/m4/ruby_decl_attribute.m4
@@ -21,7 +21,7 @@ for mac in \
"__declspec(attrib_code) x" \
x; do
m4_ifval([$4],mac="$mac"${rbcv_cond+" /* only if $rbcv_cond */"})
- AC_TRY_COMPILE(
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
m4_ifval([$4],${rbcv_cond+[@%:@if ]$rbcv_cond})
[@%:@define ]attrib[](attrib_params)[ $mac]
m4_ifval([$4],${rbcv_cond+[@%:@else]}
@@ -30,7 +30,7 @@ ${rbcv_cond+[@%:@endif]})
$6
@%:@define mesg ("")
@%:@define san "address"
- attrib[](attrib_params)[;], [],
+ attrib[](attrib_params)[;]], [[]])],
[rbcv="$mac"; break])
done
])])
diff --git a/tool/m4/ruby_dtrace_available.m4 b/tool/m4/ruby_dtrace_available.m4
index 79586d152c..babffaffac 100644
--- a/tool/m4/ruby_dtrace_available.m4
+++ b/tool/m4/ruby_dtrace_available.m4
@@ -7,7 +7,7 @@ AC_DEFUN([RUBY_DTRACE_AVAILABLE],
AS_FOR(opt, rb_dtrace_opt, ["-xnolibs" ""], [dnl
AS_IF([$DTRACE opt -h -o conftest_provider.h -s conftest_provider.d >/dev/null 2>/dev/null],
[], [continue])
- AC_TRY_COMPILE([@%:@include "conftest_provider.h"], [CONFTEST_FIRE();],
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include "conftest_provider.h"]], [[CONFTEST_FIRE();]])],
[], [continue])
# DTrace is available on the system
rb_cv_dtrace_available=yes${rb_dtrace_opt:+"(opt)"}
diff --git a/tool/m4/ruby_dtrace_postprocess.m4 b/tool/m4/ruby_dtrace_postprocess.m4
index 9ef088b3f8..1cb651b481 100644
--- a/tool/m4/ruby_dtrace_postprocess.m4
+++ b/tool/m4/ruby_dtrace_postprocess.m4
@@ -12,7 +12,7 @@ _PROBES
$DTRACE ${DTRACE_OPT} -h -o conftest_provider.h -s conftest_provider.d >/dev/null 2>/dev/null &&
:
}], [
- AC_TRY_COMPILE([@%:@include "conftest_provider.h"], [CONFTEST_FIRE();], [
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include "conftest_provider.h"]], [[CONFTEST_FIRE();]])],[
AS_IF([{
cp -p conftest.${ac_objext} conftest.${ac_objext}.save &&
$DTRACE ${DTRACE_OPT} -G -s conftest_provider.d conftest.${ac_objext} 2>/dev/null &&
diff --git a/tool/m4/ruby_mingw32.m4 b/tool/m4/ruby_mingw32.m4
index f44fe5575c..76b95f02a8 100644
--- a/tool/m4/ruby_mingw32.m4
+++ b/tool/m4/ruby_mingw32.m4
@@ -3,11 +3,11 @@ AC_DEFUN([RUBY_MINGW32],
[AS_CASE(["$host_os"],
[cygwin*], [
AC_CACHE_CHECK(for mingw32 environment, rb_cv_mingw32,
-[AC_TRY_CPP([
+[AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
#ifndef __MINGW32__
# error
#endif
-], rb_cv_mingw32=yes,rb_cv_mingw32=no)
+]])],[rb_cv_mingw32=yes],[rb_cv_mingw32=no])
rm -f conftest*])
AS_IF([test "$rb_cv_mingw32" = yes], [
target_os="mingw32"
diff --git a/tool/m4/ruby_stack_grow_direction.m4 b/tool/m4/ruby_stack_grow_direction.m4
index 74ec219322..f5f93579a4 100644
--- a/tool/m4/ruby_stack_grow_direction.m4
+++ b/tool/m4/ruby_stack_grow_direction.m4
@@ -6,7 +6,7 @@ AS_CASE(["$1"],
[m68*|x86*|x64|i?86|ppc*|sparc*|alpha*], [ $2=-1],
[hppa*], [ $2=+1],
[
- AC_TRY_RUN([
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
/* recurse to get rid of inlining */
static int
stack_growup_p(addr, n)
@@ -23,7 +23,7 @@ int main()
int x;
return stack_growup_p(&x, 10);
}
-], $2=-1, $2=+1, $2=0)
+]])],[$2=-1],[$2=+1],[$2=0])
])
eval stack_grow_dir=\$$2])
eval $2=\$stack_grow_dir
diff --git a/tool/m4/ruby_try_cflags.m4 b/tool/m4/ruby_try_cflags.m4
index 86ab80e1e6..8c9f22d50c 100644
--- a/tool/m4/ruby_try_cflags.m4
+++ b/tool/m4/ruby_try_cflags.m4
@@ -3,7 +3,7 @@ AC_DEFUN([RUBY_TRY_CFLAGS], [
AC_MSG_CHECKING([whether ]$1[ is accepted as CFLAGS])
RUBY_WERROR_FLAG([
CFLAGS="[$]CFLAGS $1"
- AC_TRY_COMPILE([$4], [$5],
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[$4]], [[$5]])],
[$2
AC_MSG_RESULT(yes)],
[$3
diff --git a/tool/m4/ruby_try_cxxflags.m4 b/tool/m4/ruby_try_cxxflags.m4
index 5f84140670..265f79a450 100644
--- a/tool/m4/ruby_try_cxxflags.m4
+++ b/tool/m4/ruby_try_cxxflags.m4
@@ -5,7 +5,7 @@ AC_DEFUN([RUBY_TRY_CXXFLAGS], [
AC_MSG_CHECKING([whether ]$1[ is accepted as CXXFLAGS])
RUBY_WERROR_FLAG([
AC_LANG_PUSH([C++])
- AC_TRY_LINK([$4], [$5],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[$4]], [[$5]])],
[$2
AC_MSG_RESULT(yes)],
[$3
diff --git a/tool/m4/ruby_try_ldflags.m4 b/tool/m4/ruby_try_ldflags.m4
index b275107ed9..d27940c7b2 100644
--- a/tool/m4/ruby_try_ldflags.m4
+++ b/tool/m4/ruby_try_ldflags.m4
@@ -4,7 +4,7 @@ AC_DEFUN([RUBY_TRY_LDFLAGS], [
LDFLAGS="[$]LDFLAGS $1"
AC_MSG_CHECKING([whether $1 is accepted as LDFLAGS])
RUBY_WERROR_FLAG([
- AC_TRY_LINK([$4], [$5],
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[$4]], [[$5]])],
[$2
AC_MSG_RESULT(yes)],
[$3
diff --git a/tool/rbinstall.rb b/tool/rbinstall.rb
index b49d91fc3f..7232865c9e 100755
--- a/tool/rbinstall.rb
+++ b/tool/rbinstall.rb
@@ -20,6 +20,7 @@ require 'fileutils'
require 'shellwords'
require 'optparse'
require 'optparse/shellwords'
+require 'pathname'
require 'rubygems'
begin
require "zlib"
@@ -701,6 +702,10 @@ module RbInstall
when "lib"
base = @base_dir
prefix = base.sub(/lib\/.*?\z/, "") + "lib/"
+ # for lib/net/net-smtp.gemspec
+ if m = Pathname.new(@gemspec).basename(".gemspec").to_s.match(/.*\-(.*)\z/)
+ base = "#{@base_dir}/#{m[1]}" unless remove_prefix(prefix, @base_dir).include?(m[1])
+ end
end
if base
diff --git a/tool/ruby_vm/views/_mjit_compile_getinlinecache.erb b/tool/ruby_vm/views/_mjit_compile_getinlinecache.erb
index 1b636bceb6..1acfdb7f0b 100644
--- a/tool/ruby_vm/views/_mjit_compile_getinlinecache.erb
+++ b/tool/ruby_vm/views/_mjit_compile_getinlinecache.erb
@@ -13,17 +13,10 @@
% # compiler: Capture IC values, locking getinlinecache
struct iseq_inline_constant_cache_entry *ice = ic->entry;
- if (ice == NULL) {
- goto getinlinecache_cancel;
- }
- rb_serial_t ic_serial = ice->ic_serial;
- const rb_cref_t *ic_cref = ice->ic_cref;
- VALUE ic_value = ice->value;
-
- if (ic_serial && !status->compile_info->disable_const_cache) {
+ if (ice != NULL && ice->ic_serial && !status->compile_info->disable_const_cache) {
% # JIT: Inline everything in IC, and cancel the slow path
- fprintf(f, " if (vm_ic_hit_p((rb_serial_t)%"PRI_SERIALT_PREFIX"u, (const rb_cref_t *)0x%"PRIxVALUE", reg_cfp->ep)) {", ic_serial, (VALUE)ic_cref);
- fprintf(f, " stack[%d] = 0x%"PRIxVALUE";\n", b->stack_size, ic_value);
+ fprintf(f, " if (vm_inlined_ic_hit_p(0x%"PRIxVALUE", 0x%"PRIxVALUE", (const rb_cref_t *)0x%"PRIxVALUE", %"PRI_SERIALT_PREFIX"u, reg_cfp->ep)) {", ice->flags, ice->value, (VALUE)ice->ic_cref, ice->ic_serial);
+ fprintf(f, " stack[%d] = 0x%"PRIxVALUE";\n", b->stack_size, ice->value);
fprintf(f, " goto label_%d;\n", pos + insn_len(insn) + (int)dst);
fprintf(f, " }");
fprintf(f, " else {");
@@ -36,4 +29,3 @@
b->stack_size += <%= insn.call_attribute('sp_inc') %>;
break;
}
- getinlinecache_cancel:;
diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb
index 620215067d..77854a6a48 100644
--- a/tool/sync_default_gems.rb
+++ b/tool/sync_default_gems.rb
@@ -94,11 +94,14 @@ def sync_default_gems(gem)
cp_r(Dir.glob("#{upstream}/lib/rubygems*"), "lib")
cp_r("#{upstream}/test/rubygems", "test")
when "bundler"
- rm_rf(%w[lib/bundler lib/bundler.rb libexec/bundler libexec/bundle spec/bundler] + Dir.glob("man/{bundle*,gemfile*}"))
+ rm_rf(%w[lib/bundler lib/bundler.rb libexec/bundler libexec/bundle spec/bundler tool/bundler/*] + Dir.glob("man/{bundle*,gemfile*}"))
cp_r(Dir.glob("#{upstream}/bundler/lib/bundler*"), "lib")
cp_r(Dir.glob("#{upstream}/bundler/exe/bundle*"), "libexec")
cp_r("#{upstream}/bundler/bundler.gemspec", "lib/bundler")
cp_r("#{upstream}/bundler/spec", "spec/bundler")
+ cp_r(Dir.glob("#{upstream}/bundler/tool/bundler/test_gems*"), "tool/bundler")
+ cp_r(Dir.glob("#{upstream}/bundler/tool/bundler/rubocop_gems*"), "tool/bundler")
+ cp_r(Dir.glob("#{upstream}/bundler/tool/bundler/standard_gems*"), "tool/bundler")
cp_r(Dir.glob("#{upstream}/bundler/man/*.{1,5,1\.txt,5\.txt,ronn}"), "man")
rm_rf(%w[spec/bundler/support/artifice/vcr_cassettes])
when "rdoc"
diff --git a/tool/test-bundled-gems.rb b/tool/test-bundled-gems.rb
index b9a1e559d4..48be1bef24 100644
--- a/tool/test-bundled-gems.rb
+++ b/tool/test-bundled-gems.rb
@@ -25,6 +25,8 @@ File.foreach("#{gem_dir}/bundled_gems") do |line|
test_command << " stdlib_test validate"
first_timeout *= 3
+ elsif gem == "test-unit"
+ test_command = "#{ruby} -C #{gem_dir}/src/#{gem} test/run-test.rb"
end
puts test_command
diff --git a/transcode.c b/transcode.c
index a72afdc44b..d2abd9e0e5 100644
--- a/transcode.c
+++ b/transcode.c
@@ -2719,6 +2719,12 @@ str_transcode0(int argc, VALUE *argv, VALUE *self, int ecflags, VALUE ecopts)
}
}
else {
+ if (senc && denc && !rb_enc_asciicompat(senc) && !rb_enc_asciicompat(denc)) {
+ rb_encoding *utf8 = rb_utf8_encoding();
+ str = rb_str_conv_enc(str, senc, utf8);
+ senc = utf8;
+ sname = "UTF-8";
+ }
if (encoding_equal(sname, dname)) {
sname = "";
dname = "";
diff --git a/util.c b/util.c
index 6db8ddbfbe..8ec0cd60e5 100644
--- a/util.c
+++ b/util.c
@@ -29,6 +29,7 @@
#include "internal/sanitizers.h"
#include "internal/util.h"
#include "ruby/util.h"
+#include "ruby_atomic.h"
const char ruby_hexdigits[] = "0123456789abcdef0123456789ABCDEF";
#define hexdigit ruby_hexdigits
diff --git a/version.h b/version.h
index 8a90b78e40..6ba76ddc6b 100644
--- a/version.h
+++ b/version.h
@@ -10,13 +10,13 @@
*/
# define RUBY_VERSION_MAJOR RUBY_API_VERSION_MAJOR
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
-#define RUBY_VERSION_TEENY 0
+#define RUBY_VERSION_TEENY 7
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
-#define RUBY_PATCHLEVEL 42
+#define RUBY_PATCHLEVEL 220
-#define RUBY_RELEASE_YEAR 2021
-#define RUBY_RELEASE_MONTH 2
-#define RUBY_RELEASE_DAY 16
+#define RUBY_RELEASE_YEAR 2024
+#define RUBY_RELEASE_MONTH 4
+#define RUBY_RELEASE_DAY 23
#include "ruby/version.h"
diff --git a/vm.c b/vm.c
index 696cffb202..0259d9c9e7 100644
--- a/vm.c
+++ b/vm.c
@@ -1005,7 +1005,7 @@ env_copy(const VALUE *src_ep, VALUE read_only_variables)
volatile VALUE prev_env = Qnil;
if (read_only_variables) {
- for (int i=0; i<RARRAY_LENINT(read_only_variables); i++) {
+ for (int i=RARRAY_LENINT(read_only_variables)-1; i>=0; i--) {
ID id = SYM2ID(rb_str_intern(RARRAY_AREF(read_only_variables, i)));
for (unsigned int j=0; j<src_env->iseq->body->local_table_size; j++) {
@@ -1796,7 +1796,7 @@ rb_iter_break_value(VALUE val)
/* optimization: redefine management */
-static st_table *vm_opt_method_table = 0;
+static st_table *vm_opt_method_def_table = 0;
static st_table *vm_opt_mid_table = 0;
static int
@@ -1850,9 +1850,8 @@ rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VALUE klass)
klass = RBASIC_CLASS(klass);
}
if (vm_redefinition_check_method_type(me->def)) {
- if (st_lookup(vm_opt_method_table, (st_data_t)me, &bop)) {
- int flag = vm_redefinition_check_flag(klass);
-
+ if (st_lookup(vm_opt_method_def_table, (st_data_t)me->def, &bop)) {
+ int flag = vm_redefinition_check_flag(klass);
ruby_vm_redefined_flag[bop] |= flag;
}
}
@@ -1883,7 +1882,7 @@ add_opt_method(VALUE klass, ID mid, VALUE bop)
const rb_method_entry_t *me = rb_method_entry_at(klass, mid);
if (me && vm_redefinition_check_method_type(me->def)) {
- st_insert(vm_opt_method_table, (st_data_t)me, (st_data_t)bop);
+ st_insert(vm_opt_method_def_table, (st_data_t)me->def, (st_data_t)bop);
st_insert(vm_opt_mid_table, (st_data_t)mid, (st_data_t)Qtrue);
}
else {
@@ -1897,7 +1896,7 @@ vm_init_redefined_flag(void)
ID mid;
VALUE bop;
- vm_opt_method_table = st_init_numtable();
+ vm_opt_method_def_table = st_init_numtable();
vm_opt_mid_table = st_init_numtable();
#define OP(mid_, bop_) (mid = id##mid_, bop = BOP_##bop_, ruby_vm_redefined_flag[bop] = 0)
@@ -2589,6 +2588,18 @@ rb_vm_mark(void *ptr)
rb_gc_mark_values(RUBY_NSIG, vm->trap_list.cmd);
rb_id_table_foreach_values(vm->negative_cme_table, vm_mark_negative_cme, NULL);
+ for (i=0; i<VM_GLOBAL_CC_CACHE_TABLE_SIZE; i++) {
+ const struct rb_callcache *cc = vm->global_cc_cache_table[i];
+
+ if (cc != NULL) {
+ if (!vm_cc_invalidated_p(cc)) {
+ rb_gc_mark((VALUE)cc);
+ }
+ else {
+ vm->global_cc_cache_table[i] = NULL;
+ }
+ }
+ }
mjit_mark();
}
@@ -2653,6 +2664,8 @@ ruby_vm_destruct(rb_vm_t *vm)
if (objspace) {
rb_objspace_free(objspace);
}
+ rb_native_mutex_destroy(&vm->waitpid_lock);
+ rb_native_mutex_destroy(&vm->workqueue_lock);
/* after freeing objspace, you *can't* use ruby_xfree() */
ruby_mimfree(vm);
ruby_current_vm_ptr = NULL;
@@ -2795,6 +2808,11 @@ rb_execution_context_update(const rb_execution_context_t *ec)
cfp->block_code = (void *)rb_gc_location((VALUE)cfp->block_code);
if (!VM_ENV_LOCAL_P(ep)) {
+ const VALUE *prev_ep = VM_ENV_PREV_EP(ep);
+ if (VM_ENV_FLAGS(prev_ep, VM_ENV_FLAG_ESCAPED)) {
+ VM_FORCE_WRITE(&prev_ep[VM_ENV_DATA_INDEX_ENV], rb_gc_location(prev_ep[VM_ENV_DATA_INDEX_ENV]));
+ }
+
if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED)) {
VM_FORCE_WRITE(&ep[VM_ENV_DATA_INDEX_ENV], rb_gc_location(ep[VM_ENV_DATA_INDEX_ENV]));
VM_FORCE_WRITE(&ep[VM_ENV_DATA_INDEX_ME_CREF], rb_gc_location(ep[VM_ENV_DATA_INDEX_ME_CREF]));
@@ -2835,6 +2853,11 @@ rb_execution_context_mark(const rb_execution_context_t *ec)
rb_gc_mark_movable((VALUE)cfp->block_code);
if (!VM_ENV_LOCAL_P(ep)) {
+ const VALUE *prev_ep = VM_ENV_PREV_EP(ep);
+ if (VM_ENV_FLAGS(prev_ep, VM_ENV_FLAG_ESCAPED)) {
+ rb_gc_mark_movable(prev_ep[VM_ENV_DATA_INDEX_ENV]);
+ }
+
if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED)) {
rb_gc_mark_movable(ep[VM_ENV_DATA_INDEX_ENV]);
rb_gc_mark(ep[VM_ENV_DATA_INDEX_ME_CREF]);
@@ -3059,6 +3082,8 @@ th_init(rb_thread_t *th, VALUE self)
th->thread_id_string[0] = '\0';
#endif
+ th->value = Qundef;
+
#if OPT_CALL_THREADED_CODE
th->retval = Qundef;
#endif
@@ -3071,16 +3096,17 @@ static VALUE
ruby_thread_init(VALUE self)
{
rb_thread_t *th = GET_THREAD();
- rb_thread_t *targe_th = rb_thread_ptr(self);
+ rb_thread_t *target_th = rb_thread_ptr(self);
rb_vm_t *vm = th->vm;
- targe_th->vm = vm;
- th_init(targe_th, self);
+ target_th->vm = vm;
+ th_init(target_th, self);
+
+ target_th->top_wrapper = 0;
+ target_th->top_self = rb_vm_top_self();
+ target_th->ec->root_svar = Qfalse;
+ target_th->ractor = th->ractor;
- targe_th->top_wrapper = 0;
- targe_th->top_self = rb_vm_top_self();
- targe_th->ec->root_svar = Qfalse;
- targe_th->ractor = th->ractor;
return self;
}
@@ -3645,6 +3671,8 @@ Init_VM(void)
* The Binding of the top level scope
*/
rb_define_global_const("TOPLEVEL_BINDING", rb_binding_new());
+
+ rb_objspace_gc_enable(vm->objspace);
}
vm_init_redefined_flag();
@@ -3710,8 +3738,6 @@ Init_vm_objects(void)
vm->mark_object_ary = rb_ary_tmp_new(128);
vm->loading_table = st_init_strtable();
vm->frozen_strings = st_init_table_with_size(&rb_fstring_hash_type, 10000);
-
- rb_objspace_gc_enable(vm->objspace);
}
/* top self */
diff --git a/vm_args.c b/vm_args.c
index ac39b31078..9ee2832ad1 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -710,8 +710,6 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
return opt_pc;
}
-void rb_backtrace_use_iseq_first_lineno_for_last_location(VALUE self); /* vm_backtrace.c */
-
static void
raise_argument_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const VALUE exc)
{
diff --git a/vm_backtrace.c b/vm_backtrace.c
index f2cc21294c..70c2ab7b8a 100644
--- a/vm_backtrace.c
+++ b/vm_backtrace.c
@@ -502,6 +502,9 @@ backtrace_size(const rb_execution_context_t *ec)
return start_cfp - last_cfp + 1;
}
+static bool is_internal_location(const rb_control_frame_t *cfp);
+static void bt_iter_skip_skip_internal(void *ptr, const rb_control_frame_t *cfp);
+
static int
backtrace_each(const rb_execution_context_t *ec,
ptrdiff_t from_last,
@@ -515,7 +518,7 @@ backtrace_each(const rb_execution_context_t *ec,
const rb_control_frame_t *last_cfp = ec->cfp;
const rb_control_frame_t *start_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
const rb_control_frame_t *cfp;
- ptrdiff_t size, i, last, start = 0;
+ ptrdiff_t size, real_size, i, j, last, start = 0;
int ret = 0;
// In the case the thread vm_stack or cfp is not initialized, there is no backtrace.
@@ -539,10 +542,18 @@ backtrace_each(const rb_execution_context_t *ec,
RUBY_VM_NEXT_CONTROL_FRAME(start_cfp)); /* skip top frames */
if (start_cfp < last_cfp) {
- size = last = 0;
+ real_size = size = last = 0;
}
else {
- size = start_cfp - last_cfp + 1;
+ /* Ensure we don't look at frames beyond the ones requested */
+ for(; from_last > 0 && start_cfp >= last_cfp; from_last--) {
+ if (last_cfp->iseq && !last_cfp->pc) {
+ from_last++;
+ }
+ last_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(last_cfp);
+ }
+
+ real_size = size = start_cfp - last_cfp + 1;
if (from_last > size) {
size = last = 0;
@@ -567,9 +578,45 @@ backtrace_each(const rb_execution_context_t *ec,
init(arg, size);
- /* SDR(); */
- for (i=0, cfp = start_cfp; i<last; i++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
- if (i < start) {
+ /* If a limited number of frames is requested, scan the VM stack for
+ * from the current frame (after skipping the number of frames requested above)
+ * towards the earliest frame (start_cfp). Track the total number of frames
+ * and the number of frames that will be part of the backtrace. Start the
+ * scan at the oldest frame that should still be part of the backtrace.
+ *
+ * If the last frame in the backtrace is a cfunc frame, continue scanning
+ * till earliest frame to find the first iseq frame with pc, so that the
+ * location can be used for the trailing cfunc frames in the backtrace.
+ */
+ if (start > 0 && num_frames >= 0 && num_frames < real_size) {
+ int found_frames = 0, total_frames = 0;
+ bool last_frame_cfunc = FALSE;
+ const rb_control_frame_t *new_start_cfp;
+
+ for (cfp = last_cfp; found_frames < num_frames && start_cfp >= cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp), total_frames++) {
+ if ((cfp->iseq && cfp->pc) || RUBYVM_CFUNC_FRAME_P(cfp)) {
+ last_frame_cfunc = RUBYVM_CFUNC_FRAME_P(cfp);
+ found_frames++;
+ }
+ }
+ new_start_cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp);
+ if (iter_skip && (last_frame_cfunc || iter_skip == bt_iter_skip_skip_internal)) {
+ for (; start_cfp >= cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
+ if (cfp->iseq && cfp->pc && (iter_skip != bt_iter_skip_skip_internal || !is_internal_location(cfp))) {
+ iter_skip(arg, cfp);
+ break;
+ }
+ }
+ }
+
+ last = found_frames;
+ real_size = total_frames;
+ start = 0;
+ start_cfp = new_start_cfp;
+ }
+
+ for (i=0, j=0, cfp = start_cfp; i<last && j<real_size; i++, j++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
+ if (j < start) {
if (iter_skip) {
iter_skip(arg, cfp);
}
@@ -578,9 +625,11 @@ backtrace_each(const rb_execution_context_t *ec,
/* fprintf(stderr, "cfp: %d\n", (rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp); */
if (cfp->iseq) {
- if (cfp->pc) {
- iter_iseq(arg, cfp);
- }
+ if (cfp->pc) {
+ iter_iseq(arg, cfp);
+ } else {
+ i--;
+ }
}
else if (RUBYVM_CFUNC_FRAME_P(cfp)) {
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
diff --git a/vm_callinfo.h b/vm_callinfo.h
index 4ee6fa72e6..081b67d2b9 100644
--- a/vm_callinfo.h
+++ b/vm_callinfo.h
@@ -357,6 +357,17 @@ vm_cc_markable(const struct rb_callcache *cc)
return FL_TEST_RAW((VALUE)cc, VM_CALLCACHE_UNMARKABLE) == 0;
}
+static inline bool
+vm_cc_invalidated_p(const struct rb_callcache *cc)
+{
+ if (cc->klass && METHOD_ENTRY_INVALIDATED(vm_cc_cme(cc))) {
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
// For MJIT. cc_cme is supposed to have inlined `vm_cc_cme(cc)`.
static inline bool
vm_cc_valid_p(const struct rb_callcache *cc, const rb_callable_method_entry_t *cc_cme, VALUE klass)
@@ -376,18 +387,6 @@ extern const struct rb_callcache *rb_vm_empty_cc(void);
/* callcache: mutate */
static inline void
-vm_cc_cme_set(const struct rb_callcache *cc, const struct rb_callable_method_entry_struct *cme)
-{
- VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache));
- VM_ASSERT(cc != vm_cc_empty());
- VM_ASSERT(vm_cc_cme(cc) != NULL);
- VM_ASSERT(vm_cc_cme(cc)->called_id == cme->called_id);
- VM_ASSERT(!vm_cc_markable(cc)); // only used for vm_eval.c
-
- *((const struct rb_callable_method_entry_struct **)&cc->cme_) = cme;
-}
-
-static inline void
vm_cc_call_set(const struct rb_callcache *cc, vm_call_handler call)
{
VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache));
diff --git a/vm_core.h b/vm_core.h
index 5f8d4ab876..6ab3608edd 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -136,7 +136,7 @@
void *rb_allocate_sigaltstack(void);
void *rb_register_sigaltstack(void *);
# define RB_ALTSTACK_INIT(var, altstack) var = rb_register_sigaltstack(altstack)
-# define RB_ALTSTACK_FREE(var) xfree(var)
+# define RB_ALTSTACK_FREE(var) free(var)
# define RB_ALTSTACK(var) var
#else /* noop */
# define RB_ALTSTACK_INIT(var, altstack)
@@ -658,6 +658,11 @@ typedef struct rb_vm_struct {
struct rb_id_table *negative_cme_table;
+#ifndef VM_GLOBAL_CC_CACHE_TABLE_SIZE
+#define VM_GLOBAL_CC_CACHE_TABLE_SIZE 1023
+#endif
+ const struct rb_callcache *global_cc_cache_table[VM_GLOBAL_CC_CACHE_TABLE_SIZE]; // vm_eval.c
+
#if USE_VM_CLOCK
uint32_t clock;
#endif
@@ -1755,7 +1760,7 @@ RUBY_SYMBOL_EXPORT_END
#define GET_VM() rb_current_vm()
#define GET_RACTOR() rb_current_ractor()
#define GET_THREAD() rb_current_thread()
-#define GET_EC() rb_current_execution_context()
+#define GET_EC() rb_current_execution_context(true)
static inline rb_thread_t *
rb_ec_thread_ptr(const rb_execution_context_t *ec)
@@ -1789,7 +1794,7 @@ rb_ec_vm_ptr(const rb_execution_context_t *ec)
}
static inline rb_execution_context_t *
-rb_current_execution_context(void)
+rb_current_execution_context(bool expect_ec)
{
#ifdef RB_THREAD_LOCAL_SPECIFIER
#if __APPLE__
@@ -1800,7 +1805,7 @@ rb_current_execution_context(void)
#else
rb_execution_context_t *ec = native_tls_get(ruby_current_ec_key);
#endif
- VM_ASSERT(ec != NULL);
+ VM_ASSERT(!expect_ec || ec != NULL);
return ec;
}
diff --git a/vm_dump.c b/vm_dump.c
index 043c07ccd9..47fc9f93a6 100644
--- a/vm_dump.c
+++ b/vm_dump.c
@@ -982,7 +982,7 @@ rb_vm_bugreport(const void *ctx)
enum {other_runtime_info = 0};
#endif
const rb_vm_t *const vm = GET_VM();
- const rb_execution_context_t *ec = GET_EC();
+ const rb_execution_context_t *ec = rb_current_execution_context(false);
if (vm && ec) {
SDR();
@@ -1019,36 +1019,38 @@ rb_vm_bugreport(const void *ctx)
LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
fprintf(stderr, "\n");
}
- fprintf(stderr, "* Loaded features:\n\n");
- for (i=0; i<RARRAY_LEN(vm->loaded_features); i++) {
- name = RARRAY_AREF(vm->loaded_features, i);
- if (RB_TYPE_P(name, T_STRING)) {
- fprintf(stderr, " %4d %.*s\n", i,
- LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
- }
- else if (RB_TYPE_P(name, T_CLASS) || RB_TYPE_P(name, T_MODULE)) {
- const char *const type = RB_TYPE_P(name, T_CLASS) ?
- "class" : "module";
- name = rb_search_class_path(rb_class_real(name));
- if (!RB_TYPE_P(name, T_STRING)) {
- fprintf(stderr, " %4d %s:<unnamed>\n", i, type);
- continue;
- }
- fprintf(stderr, " %4d %s:%.*s\n", i, type,
- LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
- }
- else {
- VALUE klass = rb_search_class_path(rb_obj_class(name));
- if (!RB_TYPE_P(klass, T_STRING)) {
- fprintf(stderr, " %4d #<%p:%p>\n", i,
- (void *)CLASS_OF(name), (void *)name);
- continue;
- }
- fprintf(stderr, " %4d #<%.*s:%p>\n", i,
- LIMITED_NAME_LENGTH(klass), RSTRING_PTR(klass),
- (void *)name);
- }
- }
+ if (vm->loaded_features) {
+ fprintf(stderr, "* Loaded features:\n\n");
+ for (i=0; i<RARRAY_LEN(vm->loaded_features); i++) {
+ name = RARRAY_AREF(vm->loaded_features, i);
+ if (RB_TYPE_P(name, T_STRING)) {
+ fprintf(stderr, " %4d %.*s\n", i,
+ LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
+ }
+ else if (RB_TYPE_P(name, T_CLASS) || RB_TYPE_P(name, T_MODULE)) {
+ const char *const type = RB_TYPE_P(name, T_CLASS) ?
+ "class" : "module";
+ name = rb_search_class_path(rb_class_real(name));
+ if (!RB_TYPE_P(name, T_STRING)) {
+ fprintf(stderr, " %4d %s:<unnamed>\n", i, type);
+ continue;
+ }
+ fprintf(stderr, " %4d %s:%.*s\n", i, type,
+ LIMITED_NAME_LENGTH(name), RSTRING_PTR(name));
+ }
+ else {
+ VALUE klass = rb_search_class_path(rb_obj_class(name));
+ if (!RB_TYPE_P(klass, T_STRING)) {
+ fprintf(stderr, " %4d #<%p:%p>\n", i,
+ (void *)CLASS_OF(name), (void *)name);
+ continue;
+ }
+ fprintf(stderr, " %4d #<%.*s:%p>\n", i,
+ LIMITED_NAME_LENGTH(klass), RSTRING_PTR(klass),
+ (void *)name);
+ }
+ }
+ }
fprintf(stderr, "\n");
}
diff --git a/vm_eval.c b/vm_eval.c
index 94f3baceab..59d5614dd2 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -48,7 +48,7 @@ rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE
struct rb_calling_info calling = {
.ci = &VM_CI_ON_STACK(id, kw_splat ? VM_CALL_KW_SPLAT : 0, argc, NULL),
.cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, { 0 }, cme),
- .block_handler = VM_BLOCK_HANDLER_NONE,
+ .block_handler = vm_passed_block_handler(ec),
.recv = recv,
.argc = argc,
.kw_splat = kw_splat,
@@ -57,13 +57,53 @@ rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE
return vm_call0_body(ec, &calling, argv);
}
+static inline VALUE
+vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const struct rb_callcache *cc, int kw_splat)
+{
+ struct rb_calling_info calling = {
+ .ci = &VM_CI_ON_STACK(id, kw_splat ? VM_CALL_KW_SPLAT : 0, argc, NULL),
+ .cc = cc,
+ .block_handler = vm_passed_block_handler(ec),
+ .recv = recv,
+ .argc = argc,
+ .kw_splat = kw_splat,
+ };
+
+ return vm_call0_body(ec, &calling, argv);
+}
+
+static VALUE
+vm_call0_cme(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, const rb_callable_method_entry_t *cme)
+{
+ calling->cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, { 0 }, cme);
+ return vm_call0_body(ec, calling, argv);
+}
+
+static VALUE
+vm_call0_super(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, VALUE klass, enum method_missing_reason ex)
+{
+ ID mid = vm_ci_mid(calling->ci);
+ klass = RCLASS_SUPER(klass);
+
+ if (klass) {
+ const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid);
+
+ if (cme) {
+ RUBY_VM_CHECK_INTS(ec);
+ return vm_call0_cme(ec, calling, argv, cme);
+ }
+ }
+
+ vm_passed_block_handler_set(ec, calling->block_handler);
+ return method_missing(ec, calling->recv, mid, calling->argc, argv, ex, calling->kw_splat);
+}
+
static VALUE
vm_call0_cfunc_with_frame(rb_execution_context_t* ec, struct rb_calling_info *calling, const VALUE *argv)
{
const struct rb_callinfo *ci = calling->ci;
- const struct rb_callcache *cc = calling->cc;
VALUE val;
- const rb_callable_method_entry_t *me = vm_cc_cme(cc);
+ const rb_callable_method_entry_t *me = vm_cc_cme(calling->cc);
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(me->def, body.cfunc);
int len = cfunc->argc;
VALUE recv = calling->recv;
@@ -115,12 +155,8 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const
{
const struct rb_callinfo *ci = calling->ci;
const struct rb_callcache *cc = calling->cc;
-
VALUE ret;
- calling->block_handler = vm_passed_block_handler(ec);
-
- again:
switch (vm_cc_cme(cc)->def->type) {
case VM_METHOD_TYPE_ISEQ:
{
@@ -169,38 +205,27 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const
ret = vm_call_bmethod_body(ec, calling, argv);
goto success;
case VM_METHOD_TYPE_ZSUPER:
+ {
+ VALUE klass = RCLASS_ORIGIN(vm_cc_cme(cc)->defined_class);
+ return vm_call0_super(ec, calling, argv, klass, MISSING_SUPER);
+ }
case VM_METHOD_TYPE_REFINED:
{
- const rb_method_type_t type = vm_cc_cme(cc)->def->type;
- VALUE super_class = vm_cc_cme(cc)->defined_class;
+ const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
- if (type == VM_METHOD_TYPE_ZSUPER) {
- super_class = RCLASS_ORIGIN(super_class);
- }
- else if (vm_cc_cme(cc)->def->body.refined.orig_me) {
- vm_cc_cme_set(cc, refined_method_callable_without_refinement(vm_cc_cme(cc)));
- goto again;
- }
-
- super_class = RCLASS_SUPER(super_class);
- if (super_class) {
- vm_cc_cme_set(cc, rb_callable_method_entry(super_class, vm_ci_mid(ci)));
- if (vm_cc_cme(cc)) {
- RUBY_VM_CHECK_INTS(ec);
- goto again;
- }
+ if (cme->def->body.refined.orig_me) {
+ const rb_callable_method_entry_t *orig_cme = refined_method_callable_without_refinement(cme);
+ return vm_call0_cme(ec, calling, argv, orig_cme);
}
- enum method_missing_reason ex = (type == VM_METHOD_TYPE_ZSUPER) ? MISSING_SUPER : 0;
- ret = method_missing(ec, calling->recv, vm_ci_mid(ci), calling->argc, argv, ex, calling->kw_splat);
- goto success;
+ VALUE klass = cme->defined_class;
+ return vm_call0_super(ec, calling, argv, klass, 0);
}
case VM_METHOD_TYPE_ALIAS:
- vm_cc_cme_set(cc, aliased_callable_method_entry(vm_cc_cme(cc)));
- goto again;
+ return vm_call0_cme(ec, calling, argv, aliased_callable_method_entry(vm_cc_cme(cc)));
case VM_METHOD_TYPE_MISSING:
{
- vm_passed_block_handler_set(ec, calling->block_handler);
+ vm_passed_block_handler_set(ec, calling->block_handler);
return method_missing(ec, calling->recv, vm_ci_mid(ci), calling->argc,
argv, MISSING_NOENTRY, calling->kw_splat);
}
@@ -302,9 +327,116 @@ stack_check(rb_execution_context_t *ec)
#ifndef MJIT_HEADER
+void
+rb_check_stack_overflow(void)
+{
+#ifndef RB_THREAD_LOCAL_SPECIFIER
+ if (!ruby_current_ec_key) return;
+#endif
+ rb_execution_context_t *ec = GET_EC();
+ if (ec) stack_check(ec);
+}
+
+NORETURN(static void uncallable_object(VALUE recv, ID mid));
static inline const rb_callable_method_entry_t *rb_search_method_entry(VALUE recv, ID mid);
static inline enum method_missing_reason rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry_t *me, call_type scope, VALUE self);
+static const struct rb_callcache *
+cc_new(VALUE klass, ID mid, int argc, const rb_callable_method_entry_t *cme)
+{
+ const struct rb_callcache *cc;
+
+ RB_VM_LOCK_ENTER();
+ {
+ struct rb_class_cc_entries *ccs;
+ struct rb_id_table *cc_tbl = RCLASS_CC_TBL(klass);
+
+ if (rb_id_table_lookup(cc_tbl, mid, (VALUE*)&ccs)) {
+ // ok
+ }
+ else {
+ ccs = vm_ccs_create(klass, cme);
+ rb_id_table_insert(cc_tbl, mid, (VALUE)ccs);
+ }
+
+ if (ccs->len > 0) {
+ cc = ccs->entries[0].cc;
+ }
+ else {
+ const struct rb_callinfo *ci = vm_ci_new(mid, 0, argc, false); // TODO: proper ci
+ cc = vm_cc_new(klass, cme, vm_call_general);
+ METHOD_ENTRY_CACHED_SET((struct rb_callable_method_entry_struct *)cme);
+ vm_ccs_push(klass, ccs, ci, cc);
+ }
+ }
+ RB_VM_LOCK_LEAVE();
+
+ return cc;
+}
+
+static VALUE
+gccct_hash(VALUE klass, ID mid)
+{
+ return (klass >> 3) ^ (VALUE)mid;
+}
+
+NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index));
+
+static const struct rb_callcache *
+gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index)
+{
+ const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid);
+ const struct rb_callcache *cc;
+
+ if (cme != NULL) {
+ cc = cc_new(klass, mid, argc, cme);
+ }
+ else {
+ cc = NULL;
+ }
+
+ return vm->global_cc_cache_table[index] = cc;
+}
+
+static inline const struct rb_callcache *
+gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc)
+{
+ VALUE klass;
+
+ if (!SPECIAL_CONST_P(recv)) {
+ klass = RBASIC_CLASS(recv);
+ if (UNLIKELY(!klass)) uncallable_object(recv, mid);
+ }
+ else {
+ klass = CLASS_OF(recv);
+ }
+
+ // search global method cache
+ unsigned int index = (unsigned int)(gccct_hash(klass, mid) % VM_GLOBAL_CC_CACHE_TABLE_SIZE);
+ rb_vm_t *vm = rb_ec_vm_ptr(ec);
+ const struct rb_callcache *cc = vm->global_cc_cache_table[index];
+
+ if (LIKELY(cc)) {
+ if (LIKELY(vm_cc_class_check(cc, klass))) {
+ const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
+ if (LIKELY(!METHOD_ENTRY_INVALIDATED(cme) &&
+ cme->called_id == mid)) {
+
+ VM_ASSERT(vm_cc_cme(cc) == rb_callable_method_entry(klass, mid));
+ RB_DEBUG_COUNTER_INC(gccct_hit);
+
+ return cc;
+ }
+ }
+ }
+ else {
+ RB_DEBUG_COUNTER_INC(gccct_null);
+ }
+
+ RB_DEBUG_COUNTER_INC(gccct_miss);
+ return gccct_method_search_slowpath(vm, klass, mid, argc, index);
+}
+
/*!
* \internal
* calls the specified method.
@@ -326,7 +458,6 @@ rb_call0(rb_execution_context_t *ec,
VALUE recv, ID mid, int argc, const VALUE *argv,
call_type call_scope, VALUE self)
{
- const rb_callable_method_entry_t *me;
enum method_missing_reason call_status;
call_type scope = call_scope;
int kw_splat = RB_NO_KEYWORDS;
@@ -344,21 +475,34 @@ rb_call0(rb_execution_context_t *ec,
break;
}
+ const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc);
+
if (scope == CALL_PUBLIC) {
RB_DEBUG_COUNTER_INC(call0_public);
- me = rb_callable_method_entry_with_refinements(CLASS_OF(recv), mid, NULL);
+
+ const rb_callable_method_entry_t *cc_cme = cc ? vm_cc_cme(cc) : NULL;
+ const rb_callable_method_entry_t *cme = callable_method_entry_refeinements0(CLASS_OF(recv), mid, NULL, true, cc_cme);
+ call_status = rb_method_call_status(ec, cme, scope, self);
+
+ if (UNLIKELY(call_status != MISSING_NONE)) {
+ return method_missing(ec, recv, mid, argc, argv, call_status, kw_splat);
+ }
+ else if (UNLIKELY(cc_cme != cme)) { // refinement is solved
+ stack_check(ec);
+ return rb_vm_call_kw(ec, recv, mid, argc, argv, cme, kw_splat);
+ }
}
else {
RB_DEBUG_COUNTER_INC(call0_other);
- me = rb_search_method_entry(recv, mid);
- }
- call_status = rb_method_call_status(ec, me, scope, self);
+ call_status = rb_method_call_status(ec, cc ? vm_cc_cme(cc) : NULL, scope, self);
- if (call_status != MISSING_NONE) {
- return method_missing(ec, recv, mid, argc, argv, call_status, kw_splat);
+ if (UNLIKELY(call_status != MISSING_NONE)) {
+ return method_missing(ec, recv, mid, argc, argv, call_status, kw_splat);
+ }
}
+
stack_check(ec);
- return rb_vm_call_kw(ec, recv, mid, argc, argv, me, kw_splat);
+ return vm_call0_cc(ec, recv, mid, argc, argv, cc, kw_splat);
}
struct rescue_funcall_args {
@@ -576,7 +720,6 @@ rb_type_str(enum ruby_value_type type)
return NULL;
}
-NORETURN(static void uncallable_object(VALUE recv, ID mid));
static void
uncallable_object(VALUE recv, ID mid)
{
@@ -850,6 +993,45 @@ method_missing(rb_execution_context_t *ec, VALUE obj, ID id, int argc, const VAL
#ifndef MJIT_HEADER
+static inline VALUE
+rb_funcallv_scope(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope)
+{
+ rb_execution_context_t *ec = GET_EC();
+ const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc);
+ VALUE self = ec->cfp->self;
+
+ if (LIKELY(cc) &&
+ LIKELY(rb_method_call_status(ec, vm_cc_cme(cc), scope, self) == MISSING_NONE)) {
+ // fastpath
+ return vm_call0_cc(ec, recv, mid, argc, argv, cc, false);
+ }
+ else {
+ return rb_call0(ec, recv, mid, argc, argv, scope, self);
+ }
+}
+
+#ifdef rb_funcallv
+#undef rb_funcallv
+#endif
+/*!
+ * Calls a method
+ * \param recv receiver of the method
+ * \param mid an ID that represents the name of the method
+ * \param argc the number of arguments
+ * \param argv pointer to an array of method arguments
+ */
+VALUE
+rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)
+{
+ return rb_funcallv_scope(recv, mid, argc, argv, CALL_FCALL);
+}
+
+VALUE
+rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
+{
+ return rb_call(recv, mid, argc, argv, kw_splat ? CALL_FCALL_KW : CALL_FCALL);
+}
+
/*!
* Calls a method
* \param recv receiver of the method
@@ -875,7 +1057,8 @@ rb_apply(VALUE recv, ID mid, VALUE args)
}
argv = ALLOCA_N(VALUE, argc);
MEMCPY(argv, RARRAY_CONST_PTR_TRANSIENT(args), VALUE, argc);
- return rb_call(recv, mid, argc, argv, CALL_FCALL);
+
+ return rb_funcallv(recv, mid, argc, argv);
}
#ifdef rb_funcall
@@ -911,29 +1094,7 @@ rb_funcall(VALUE recv, ID mid, int n, ...)
else {
argv = 0;
}
- return rb_call(recv, mid, n, argv, CALL_FCALL);
-}
-
-#ifdef rb_funcallv
-#undef rb_funcallv
-#endif
-/*!
- * Calls a method
- * \param recv receiver of the method
- * \param mid an ID that represents the name of the method
- * \param argc the number of arguments
- * \param argv pointer to an array of method arguments
- */
-VALUE
-rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)
-{
- return rb_call(recv, mid, argc, argv, CALL_FCALL);
-}
-
-VALUE
-rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
-{
- return rb_call(recv, mid, argc, argv, kw_splat ? CALL_FCALL_KW : CALL_FCALL);
+ return rb_funcallv(recv, mid, n, argv);
}
/*!
@@ -963,7 +1124,6 @@ rb_check_funcall_basic_kw(VALUE recv, ID mid, VALUE ancestor, int argc, const VA
return Qundef;
}
-
/*!
* Calls a method.
*
@@ -976,7 +1136,7 @@ rb_check_funcall_basic_kw(VALUE recv, ID mid, VALUE ancestor, int argc, const VA
VALUE
rb_funcallv_public(VALUE recv, ID mid, int argc, const VALUE *argv)
{
- return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
+ return rb_funcallv_scope(recv, mid, argc, argv, CALL_PUBLIC);
}
VALUE
@@ -989,7 +1149,7 @@ VALUE
rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv)
{
PASS_PASSED_BLOCK_HANDLER();
- return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
+ return rb_funcallv_public(recv, mid, argc, argv);
}
VALUE
@@ -1006,7 +1166,7 @@ rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE pas
vm_passed_block_handler_set(GET_EC(), passed_procval);
}
- return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
+ return rb_funcallv_public(recv, mid, argc, argv);
}
VALUE
@@ -1181,10 +1341,10 @@ VALUE
rb_yield(VALUE val)
{
if (val == Qundef) {
- return rb_yield_0(0, 0);
+ return rb_yield_0(0, NULL);
}
else {
- return rb_yield_1(val);
+ return rb_yield_0(1, &val);
}
}
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 6bbb4bca9b..757e75d66f 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -237,7 +237,7 @@ static bool vm_stack_canary_was_born = false;
#ifndef MJIT_HEADER
MJIT_FUNC_EXPORTED void
-vm_check_canary(const rb_execution_context_t *ec, VALUE *sp)
+rb_vm_check_canary(const rb_execution_context_t *ec, VALUE *sp)
{
const struct rb_control_frame_struct *reg_cfp = ec->cfp;
const struct rb_iseq_struct *iseq;
@@ -284,6 +284,7 @@ vm_check_canary(const rb_execution_context_t *ec, VALUE *sp)
rb_bug("see above.");
}
#endif
+#define vm_check_canary(ec, sp) rb_vm_check_canary(ec, sp)
#else
#define vm_check_canary(ec, sp)
@@ -1805,7 +1806,8 @@ vm_search_method_fastpath(VALUE cd_owner, struct rb_call_data *cd, VALUE klass)
#if OPT_INLINE_METHOD_CACHE
if (LIKELY(vm_cc_class_check(cc, klass))) {
- if (LIKELY(!METHOD_ENTRY_INVALIDATED(vm_cc_cme(cc)))) {
+ const struct rb_callable_method_entry_struct *cme = vm_cc_cme(cc);
+ if (LIKELY(cme && !METHOD_ENTRY_INVALIDATED(cme))) {
VM_ASSERT(callable_method_entry_p(vm_cc_cme(cc)));
RB_DEBUG_COUNTER_INC(mc_inline_hit);
VM_ASSERT(vm_cc_cme(cc) == NULL || // not found
@@ -1856,6 +1858,7 @@ check_cfunc(const rb_callable_method_entry_t *me, VALUE (*func)())
static inline int
vm_method_cfunc_is(const rb_iseq_t *iseq, CALL_DATA cd, VALUE recv, VALUE (*func)())
{
+ VM_ASSERT(iseq != NULL);
const struct rb_callcache *cc = vm_search_method((VALUE)iseq, cd, recv);
return check_cfunc(vm_cc_cme(cc), func);
}
@@ -1892,7 +1895,7 @@ FLONUM_2_P(VALUE a, VALUE b)
}
static VALUE
-opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd)
+opt_equality_specialized(VALUE recv, VALUE obj)
{
if (FIXNUM_2_P(recv, obj) && EQ_UNREDEFINED_P(INTEGER)) {
goto compare_by_identity;
@@ -1904,7 +1907,7 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd)
goto compare_by_identity;
}
else if (SPECIAL_CONST_P(recv)) {
- goto compare_by_funcall;
+ //
}
else if (RBASIC_CLASS(recv) == rb_cFloat && RB_FLOAT_TYPE_P(obj) && EQ_UNREDEFINED_P(FLOAT)) {
double a = RFLOAT_VALUE(recv);
@@ -1934,11 +1937,7 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd)
return rb_str_eql_internal(obj, recv);
}
}
-
- compare_by_funcall:
- if (! vm_method_cfunc_is(cd_owner, cd, recv, rb_obj_equal)) {
- return Qundef;
- }
+ return Qundef;
compare_by_identity:
if (recv == obj) {
@@ -1949,47 +1948,77 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd)
}
}
+static VALUE
+opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd)
+{
+ VM_ASSERT(cd_owner != NULL);
+
+ VALUE val = opt_equality_specialized(recv, obj);
+ if (val != Qundef) return val;
+
+ if (!vm_method_cfunc_is(cd_owner, cd, recv, rb_obj_equal)) {
+ return Qundef;
+ }
+ else {
+ if (recv == obj) {
+ return Qtrue;
+ }
+ else {
+ return Qfalse;
+ }
+ }
+}
+
#undef EQ_UNREDEFINED_P
#ifndef MJIT_HEADER
-VALUE
-rb_equal_opt(VALUE obj1, VALUE obj2)
+
+static inline const struct rb_callcache *gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc); // vm_eval.c
+NOINLINE(static VALUE opt_equality_by_mid_slowpath(VALUE recv, VALUE obj, ID mid));
+
+static VALUE
+opt_equality_by_mid_slowpath(VALUE recv, VALUE obj, ID mid)
{
- STATIC_ASSERT(idEq_is_embeddable, VM_CI_EMBEDDABLE_P(idEq, 0, 1, 0));
+ const struct rb_callcache *cc = gccct_method_search(GET_EC(), recv, mid, 1);
-#if USE_EMBED_CI
- static struct rb_call_data cd = {
- .ci = vm_ci_new_id(idEq, 0, 1, 0),
- };
-#else
- struct rb_call_data cd = {
- .ci = &VM_CI_ON_STACK(idEq, 0, 1, 0),
- };
-#endif
+ if (cc && check_cfunc(vm_cc_cme(cc), rb_obj_equal)) {
+ if (recv == obj) {
+ return Qtrue;
+ }
+ else {
+ return Qfalse;
+ }
+ }
+ else {
+ return Qundef;
+ }
+}
- cd.cc = &vm_empty_cc;
- return opt_equality(NULL, obj1, obj2, &cd);
+static VALUE
+opt_equality_by_mid(VALUE recv, VALUE obj, ID mid)
+{
+ VALUE val = opt_equality_specialized(recv, obj);
+ if (val != Qundef) {
+ return val;
+ }
+ else {
+ return opt_equality_by_mid_slowpath(recv, obj, mid);
+ }
}
VALUE
-rb_eql_opt(VALUE obj1, VALUE obj2)
+rb_equal_opt(VALUE obj1, VALUE obj2)
{
- STATIC_ASSERT(idEqlP_is_embeddable, VM_CI_EMBEDDABLE_P(idEqlP, 0, 1, 0));
-
-#if USE_EMBED_CI
- static struct rb_call_data cd = {
- .ci = vm_ci_new_id(idEqlP, 0, 1, 0),
- };
-#else
- struct rb_call_data cd = {
- .ci = &VM_CI_ON_STACK(idEqlP, 0, 1, 0),
- };
-#endif
+ return opt_equality_by_mid(obj1, obj2, idEq);
+}
- cd.cc = &vm_empty_cc;
- return opt_equality(NULL, obj1, obj2, &cd);
+VALUE
+rb_eql_opt(VALUE obj1, VALUE obj2)
+{
+ return opt_equality_by_mid(obj1, obj2, idEqlP);
}
-#endif
+
+#endif // MJIT_HEADER
extern VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE, ID, int, const VALUE*, const rb_callable_method_entry_t *, int kw_splat);
@@ -2362,6 +2391,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
{
const struct rb_callinfo *ci = calling->ci;
const struct rb_callcache *cc = calling->cc;
+ bool cacheable_ci = vm_ci_markable(ci);
if (LIKELY(!(vm_ci_flag(ci) & VM_CALL_KW_SPLAT))) {
if (LIKELY(rb_simple_iseq_p(iseq))) {
@@ -2375,7 +2405,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
VM_ASSERT(ci == calling->ci);
VM_ASSERT(cc == calling->cc);
- CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(ci, param_size, local_size), vm_call_iseq_optimizable_p(ci, cc));
+ CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(ci, param_size, local_size), cacheable_ci && vm_call_iseq_optimizable_p(ci, cc));
return 0;
}
else if (rb_iseq_only_optparam_p(iseq)) {
@@ -2395,12 +2425,12 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
if (LIKELY(!(vm_ci_flag(ci) & VM_CALL_TAILCALL))) {
CC_SET_FASTPATH(cc, vm_call_iseq_setup_normal_opt_start,
!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
- METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
+ cacheable_ci && METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
}
else {
CC_SET_FASTPATH(cc, vm_call_iseq_setup_tailcall_opt_start,
!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
- METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
+ cacheable_ci && METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
}
/* initialize opt vars for self-references */
@@ -2428,7 +2458,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
args_setup_kw_parameters(ec, iseq, ci_kws, ci_kw_len, ci_keywords, klocals);
CC_SET_FASTPATH(cc, vm_call_iseq_setup_kwparm_kwarg,
- METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
+ cacheable_ci && METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
return 0;
}
@@ -2441,7 +2471,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
if (klocals[kw_param->num] == INT2FIX(0)) {
/* copy from default_values */
CC_SET_FASTPATH(cc, vm_call_iseq_setup_kwparm_nokwarg,
- METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
+ cacheable_ci && METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
}
return 0;
@@ -4612,19 +4642,26 @@ vm_opt_newarray_min(rb_num_t num, const VALUE *ptr)
#define IMEMO_CONST_CACHE_SHAREABLE IMEMO_FL_USER0
-static int
-vm_ic_hit_p(const struct iseq_inline_constant_cache_entry *ice, const VALUE *reg_ep)
+// For MJIT inlining
+static inline bool
+vm_inlined_ic_hit_p(VALUE flags, VALUE value, const rb_cref_t *ic_cref, rb_serial_t ic_serial, const VALUE *reg_ep)
{
- VM_ASSERT(IMEMO_TYPE_P(ice, imemo_constcache));
- if (ice->ic_serial == GET_GLOBAL_CONSTANT_STATE() &&
- (FL_TEST_RAW((VALUE)ice, IMEMO_CONST_CACHE_SHAREABLE) || rb_ractor_main_p())) {
+ if (ic_serial == GET_GLOBAL_CONSTANT_STATE() &&
+ ((flags & IMEMO_CONST_CACHE_SHAREABLE) || rb_ractor_main_p())) {
- VM_ASSERT(FL_TEST_RAW((VALUE)ice, IMEMO_CONST_CACHE_SHAREABLE) ? rb_ractor_shareable_p(ice->value) : true);
+ VM_ASSERT((flags & IMEMO_CONST_CACHE_SHAREABLE) ? rb_ractor_shareable_p(value) : true);
- return (ice->ic_cref == NULL || // no need to check CREF
- ice->ic_cref == vm_get_cref(reg_ep));
+ return (ic_cref == NULL || // no need to check CREF
+ ic_cref == vm_get_cref(reg_ep));
}
- return FALSE;
+ return false;
+}
+
+static bool
+vm_ic_hit_p(const struct iseq_inline_constant_cache_entry *ice, const VALUE *reg_ep)
+{
+ VM_ASSERT(IMEMO_TYPE_P(ice, imemo_constcache));
+ return vm_inlined_ic_hit_p(ice->flags, ice->value, ice->ic_cref, ice->ic_serial, reg_ep);
}
static void
@@ -5342,7 +5379,7 @@ vm_trace(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)
#if VM_CHECK_MODE > 0
NORETURN( NOINLINE( COLDFUNC
-void vm_canary_is_found_dead(enum ruby_vminsn_type i, VALUE c)));
+void rb_vm_canary_is_found_dead(enum ruby_vminsn_type i, VALUE c)));
void
Init_vm_stack_canary(void)
@@ -5357,7 +5394,7 @@ Init_vm_stack_canary(void)
#ifndef MJIT_HEADER
MJIT_FUNC_EXPORTED void
-vm_canary_is_found_dead(enum ruby_vminsn_type i, VALUE c)
+rb_vm_canary_is_found_dead(enum ruby_vminsn_type i, VALUE c)
{
/* Because a method has already been called, why not call
* another one. */
diff --git a/vm_insnhelper.h b/vm_insnhelper.h
index 56c4b96458..18e7056303 100644
--- a/vm_insnhelper.h
+++ b/vm_insnhelper.h
@@ -153,7 +153,7 @@ CC_SET_FASTPATH(const struct rb_callcache *cc, vm_call_handler func, bool enable
*canary = Qfalse; /* cleanup */ \
} \
else { \
- vm_canary_is_found_dead(insn, *canary); \
+ rb_vm_canary_is_found_dead(insn, *canary); \
} \
}
#else
diff --git a/vm_method.c b/vm_method.c
index 51b8b6b07b..8ef69b6401 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -8,6 +8,7 @@
static int vm_redefinition_check_flag(VALUE klass);
static void rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VALUE klass);
+static inline rb_method_entry_t *lookup_method_table(VALUE klass, ID id);
#define object_id idObject_id
#define added idMethod_added
@@ -173,9 +174,17 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
// invalidate cc by invalidating cc->cme
VALUE owner = cme->owner;
VM_ASSERT(BUILTIN_TYPE(owner) == T_CLASS);
+ VALUE klass_housing_cme;
+ if (cme->def->type == VM_METHOD_TYPE_REFINED && !cme->def->body.refined.orig_me) {
+ klass_housing_cme = owner;
+ }
+ else {
+ klass_housing_cme = RCLASS_ORIGIN(owner);
+ }
+ // replace the cme that will be invalid
+ VM_ASSERT(lookup_method_table(klass_housing_cme, mid) == (const rb_method_entry_t *)cme);
const rb_method_entry_t *new_cme = rb_method_entry_clone((const rb_method_entry_t *)cme);
- VALUE origin = RCLASS_ORIGIN(owner);
- rb_method_table_insert(origin, RCLASS_M_TBL(origin), mid, new_cme);
+ rb_method_table_insert(klass_housing_cme, RCLASS_M_TBL(klass_housing_cme), mid, new_cme);
}
vm_me_invalidate_cache((rb_callable_method_entry_t *)cme);
@@ -801,6 +810,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
if (RTEST(ruby_verbose) &&
type != VM_METHOD_TYPE_UNDEF &&
(old_def->alias_count == 0) &&
+ (!old_def->no_redef_warning) &&
!make_refined &&
old_def->type != VM_METHOD_TYPE_UNDEF &&
old_def->type != VM_METHOD_TYPE_ZSUPER &&
@@ -914,7 +924,13 @@ method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me,
rb_method_visibility_t visi, VALUE defined_class)
{
rb_method_entry_t *newme = rb_method_entry_make(klass, mid, defined_class, visi,
- me->def->type, method_definition_addref(me->def), 0, NULL);
+ me->def->type, me->def, 0, NULL);
+ if (newme == me) {
+ me->def->no_redef_warning = TRUE;
+ }
+ else {
+ method_definition_addref(me->def);
+ }
method_added(klass, mid);
return newme;
}
@@ -960,7 +976,7 @@ rb_method_entry_at(VALUE klass, ID id)
}
static inline rb_method_entry_t*
-search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
+search_method0(VALUE klass, ID id, VALUE *defined_class_ptr, bool skip_refined)
{
rb_method_entry_t *me = NULL;
@@ -969,7 +985,10 @@ search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
for (; klass; klass = RCLASS_SUPER(klass)) {
RB_DEBUG_COUNTER_INC(mc_search_super);
if ((me = lookup_method_table(klass, id)) != 0) {
- break;
+ if (!skip_refined || me->def->type != VM_METHOD_TYPE_REFINED ||
+ me->def->body.refined.orig_me) {
+ break;
+ }
}
}
@@ -981,6 +1000,12 @@ search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
return me;
}
+static inline rb_method_entry_t*
+search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
+{
+ return search_method0(klass, id, defined_class_ptr, false);
+}
+
static rb_method_entry_t *
search_method_protect(VALUE klass, ID id, VALUE *defined_class_ptr)
{
@@ -1186,11 +1211,10 @@ rb_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
}
static const rb_callable_method_entry_t *
-callable_method_entry_refeinements(VALUE klass, ID id, VALUE *defined_class_ptr, bool with_refinements)
+callable_method_entry_refeinements0(VALUE klass, ID id, VALUE *defined_class_ptr, bool with_refinements,
+ const rb_callable_method_entry_t *cme)
{
- const rb_callable_method_entry_t *cme = callable_method_entry(klass, id, defined_class_ptr);
-
- if (cme == NULL || cme->def->type != VM_METHOD_TYPE_REFINED) {
+ if (cme == NULL || LIKELY(cme->def->type != VM_METHOD_TYPE_REFINED)) {
return cme;
}
else {
@@ -1200,6 +1224,13 @@ callable_method_entry_refeinements(VALUE klass, ID id, VALUE *defined_class_ptr,
}
}
+static const rb_callable_method_entry_t *
+callable_method_entry_refeinements(VALUE klass, ID id, VALUE *defined_class_ptr, bool with_refinements)
+{
+ const rb_callable_method_entry_t *cme = callable_method_entry(klass, id, defined_class_ptr);
+ return callable_method_entry_refeinements0(klass, id, defined_class_ptr, with_refinements, cme);
+}
+
MJIT_FUNC_EXPORTED const rb_callable_method_entry_t *
rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class_ptr)
{
@@ -1362,7 +1393,7 @@ rb_export_method(VALUE klass, ID name, rb_method_visibility_t visi)
VALUE defined_class;
VALUE origin_class = RCLASS_ORIGIN(klass);
- me = search_method(origin_class, name, &defined_class);
+ me = search_method0(origin_class, name, &defined_class, true);
if (!me && RB_TYPE_P(klass, T_MODULE)) {
me = search_method(rb_cObject, name, &defined_class);
@@ -1377,11 +1408,16 @@ rb_export_method(VALUE klass, ID name, rb_method_visibility_t visi)
rb_vm_check_redefinition_opt_method(me, klass);
if (klass == defined_class || origin_class == defined_class) {
- METHOD_ENTRY_VISI_SET(me, visi);
-
- if (me->def->type == VM_METHOD_TYPE_REFINED && me->def->body.refined.orig_me) {
- METHOD_ENTRY_VISI_SET((rb_method_entry_t *)me->def->body.refined.orig_me, visi);
- }
+ if (me->def->type == VM_METHOD_TYPE_REFINED) {
+ // Refinement method entries should always be public because the refinement
+ // search is always performed.
+ if (me->def->body.refined.orig_me) {
+ METHOD_ENTRY_VISI_SET((rb_method_entry_t *)me->def->body.refined.orig_me, visi);
+ }
+ }
+ else {
+ METHOD_ENTRY_VISI_SET(me, visi);
+ }
rb_clear_method_cache(klass, name);
}
else {
@@ -1453,7 +1489,7 @@ static void
scope_visibility_check(void)
{
/* Check for public/protected/private/module_function called inside a method */
- rb_control_frame_t *cfp = rb_current_execution_context()->cfp+1;
+ rb_control_frame_t *cfp = GET_EC()->cfp+1;
if (cfp && cfp->iseq && cfp->iseq->body->type == ISEQ_TYPE_METHOD) {
rb_warn("calling %s without arguments inside a method may not have the intended effect",
rb_id2name(rb_frame_this_func()));
diff --git a/vm_sync.c b/vm_sync.c
index af205235ac..1ce8cad95f 100644
--- a/vm_sync.c
+++ b/vm_sync.c
@@ -14,7 +14,7 @@ vm_locked(rb_vm_t *vm)
#if RUBY_DEBUG > 0
void
-ASSERT_vm_locking(void)
+RUBY_ASSERT_vm_locking(void)
{
if (rb_multi_ractor_p()) {
rb_vm_t *vm = GET_VM();
@@ -23,7 +23,7 @@ ASSERT_vm_locking(void)
}
void
-ASSERT_vm_unlocking(void)
+RUBY_ASSERT_vm_unlocking(void)
{
if (rb_multi_ractor_p()) {
rb_vm_t *vm = GET_VM();
diff --git a/vm_sync.h b/vm_sync.h
index b8ebb3bbc5..3dc75a1514 100644
--- a/vm_sync.h
+++ b/vm_sync.h
@@ -126,8 +126,10 @@ rb_vm_lock_leave_cr(struct rb_ractor_struct *cr, unsigned int *levp, const char
#define RB_VM_LOCK_LEAVE_NO_BARRIER() RB_VM_LOCK_LEAVE_LEV(&_lev); }
#if RUBY_DEBUG > 0
-void ASSERT_vm_locking(void);
-void ASSERT_vm_unlocking(void);
+void RUBY_ASSERT_vm_locking(void);
+void RUBY_ASSERT_vm_unlocking(void);
+#define ASSERT_vm_locking() RUBY_ASSERT_vm_locking()
+#define ASSERT_vm_unlocking() RUBY_ASSERT_vm_unlocking()
#else
#define ASSERT_vm_locking()
#define ASSERT_vm_unlocking()
diff --git a/vm_trace.c b/vm_trace.c
index 06879812b3..6cbb5ed843 100644
--- a/vm_trace.c
+++ b/vm_trace.c
@@ -730,7 +730,7 @@ tp_memsize(const void *ptr)
static const rb_data_type_t tp_data_type = {
"tracepoint",
- {tp_mark, RUBY_TYPED_NEVER_FREE, tp_memsize,},
+ {tp_mark, RUBY_TYPED_DEFAULT_FREE, tp_memsize,},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
@@ -1592,6 +1592,14 @@ postponed_job_register(rb_execution_context_t *ec, rb_vm_t *vm,
return PJRR_SUCCESS;
}
+static rb_execution_context_t *
+get_valid_ec(rb_vm_t *vm)
+{
+ rb_execution_context_t *ec = rb_current_execution_context(false);
+ if (ec == NULL) ec = rb_vm_main_ractor_ec(vm);
+ return ec;
+}
+
/*
* return 0 if job buffer is full
* Async-signal-safe
@@ -1599,8 +1607,8 @@ postponed_job_register(rb_execution_context_t *ec, rb_vm_t *vm,
int
rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data)
{
- rb_execution_context_t *ec = GET_EC();
- rb_vm_t *vm = rb_ec_vm_ptr(ec);
+ rb_vm_t *vm = GET_VM();
+ rb_execution_context_t *ec = get_valid_ec(vm);
begin:
switch (postponed_job_register(ec, vm, flags, func, data, MAX_POSTPONED_JOB, vm->postponed_job_index)) {
@@ -1618,8 +1626,8 @@ rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void
int
rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data)
{
- rb_execution_context_t *ec = GET_EC();
- rb_vm_t *vm = rb_ec_vm_ptr(ec);
+ rb_vm_t *vm = GET_VM();
+ rb_execution_context_t *ec = get_valid_ec(vm);
rb_postponed_job_t *pjob;
rb_atomic_t i, index;
diff --git a/win32/win32.c b/win32/win32.c
index cfc571f145..5fd7595414 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -696,9 +696,14 @@ rtc_error_handler(int e, const char *src, int line, const char *exe, const char
#endif
static CRITICAL_SECTION select_mutex;
+
+static CRITICAL_SECTION socklist_mutex;
static st_table *socklist = NULL;
+
+static CRITICAL_SECTION conlist_mutex;
static st_table *conlist = NULL;
#define conlist_disabled ((st_table *)-1)
+
static char *uenvarea;
/* License: Ruby's */
@@ -723,11 +728,13 @@ free_conlist(st_data_t key, st_data_t val, st_data_t arg)
static void
constat_delete(HANDLE h)
{
+ EnterCriticalSection(&conlist_mutex);
if (conlist && conlist != conlist_disabled) {
st_data_t key = (st_data_t)h, val;
st_delete(conlist, &key, &val);
xfree((struct constat *)val);
}
+ LeaveCriticalSection(&conlist_mutex);
}
/* License: Ruby's */
@@ -736,6 +743,8 @@ exit_handler(void)
{
WSACleanup();
DeleteCriticalSection(&select_mutex);
+ DeleteCriticalSection(&socklist_mutex);
+ DeleteCriticalSection(&conlist_mutex);
if (uenvarea) {
free(uenvarea);
uenvarea = NULL;
@@ -746,15 +755,20 @@ exit_handler(void)
static void
vm_exit_handler(ruby_vm_t *vm)
{
+ EnterCriticalSection(&socklist_mutex);
if (socklist) {
st_free_table(socklist);
socklist = NULL;
}
+ LeaveCriticalSection(&socklist_mutex);
+
+ EnterCriticalSection(&conlist_mutex);
if (conlist && conlist != conlist_disabled) {
st_foreach(conlist, free_conlist, 0);
st_free_table(conlist);
conlist = NULL;
}
+ LeaveCriticalSection(&conlist_mutex);
}
/* License: Ruby's */
@@ -787,6 +801,8 @@ StartSockets(void)
rb_fatal("could not find version 2 of winsock dll");
InitializeCriticalSection(&select_mutex);
+ InitializeCriticalSection(&socklist_mutex);
+ InitializeCriticalSection(&conlist_mutex);
atexit(exit_handler);
}
@@ -799,11 +815,17 @@ StartSockets(void)
static inline int
socklist_insert(SOCKET sock, int flag)
{
+ int ret;
+
+ EnterCriticalSection(&socklist_mutex);
if (!socklist) {
socklist = st_init_numtable();
install_vm_exit_handler();
}
- return st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
+ ret = st_insert(socklist, (st_data_t)sock, (st_data_t)flag);
+ LeaveCriticalSection(&socklist_mutex);
+
+ return ret;
}
/* License: Ruby's */
@@ -813,11 +835,15 @@ socklist_lookup(SOCKET sock, int *flagp)
st_data_t data;
int ret;
- if (!socklist)
- return 0;
- ret = st_lookup(socklist, (st_data_t)sock, (st_data_t *)&data);
- if (ret && flagp)
- *flagp = (int)data;
+ EnterCriticalSection(&socklist_mutex);
+ if (socklist) {
+ ret = st_lookup(socklist, (st_data_t)sock, (st_data_t *)&data);
+ if (ret && flagp)
+ *flagp = (int)data;
+ } else {
+ ret = 0;
+ }
+ LeaveCriticalSection(&socklist_mutex);
return ret;
}
@@ -830,17 +856,21 @@ socklist_delete(SOCKET *sockp, int *flagp)
st_data_t data;
int ret;
- if (!socklist)
- return 0;
- key = (st_data_t)*sockp;
- if (flagp)
- data = (st_data_t)*flagp;
- ret = st_delete(socklist, &key, &data);
- if (ret) {
- *sockp = (SOCKET)key;
+ EnterCriticalSection(&socklist_mutex);
+ if (socklist) {
+ key = (st_data_t)*sockp;
if (flagp)
- *flagp = (int)data;
+ data = (st_data_t)*flagp;
+ ret = st_delete(socklist, &key, &data);
+ if (ret) {
+ *sockp = (SOCKET)key;
+ if (flagp)
+ *flagp = (int)data;
+ }
+ } else {
+ ret = 0;
}
+ LeaveCriticalSection(&socklist_mutex);
return ret;
}
@@ -2693,7 +2723,7 @@ init_stdhandle(void)
DWORD m;
if (GetConsoleMode(h, &m)) {
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
-#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x200
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
#endif
SetConsoleMode(h, m | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
@@ -5539,10 +5569,8 @@ filetime_to_nsec(const FILETIME *ft)
/* License: Ruby's */
static unsigned
-fileattr_to_unixmode(DWORD attr, const WCHAR *path)
+fileattr_to_unixmode(DWORD attr, const WCHAR *path, unsigned mode)
{
- unsigned mode = 0;
-
if (attr & FILE_ATTRIBUTE_READONLY) {
mode |= S_IREAD;
}
@@ -5550,7 +5578,10 @@ fileattr_to_unixmode(DWORD attr, const WCHAR *path)
mode |= S_IREAD | S_IWRITE | S_IWUSR;
}
- if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
+ if (mode & S_IFMT) {
+ /* format is already set */
+ }
+ else if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
if (rb_w32_reparse_symlink_p(path))
mode |= S_IFLNK | S_IEXEC;
else
@@ -5645,7 +5676,7 @@ stat_by_find(const WCHAR *path, struct stati128 *st)
return -1;
}
FindClose(h);
- st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path);
+ st->st_mode = fileattr_to_unixmode(wfd.dwFileAttributes, path, 0);
st->st_atime = filetime_to_unixtime(&wfd.ftLastAccessTime);
st->st_atimensec = filetime_to_nsec(&wfd.ftLastAccessTime);
st->st_mtime = filetime_to_unixtime(&wfd.ftLastWriteTime);
@@ -5680,6 +5711,15 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
if (f != INVALID_HANDLE_VALUE) {
DWORD attr = stati128_handle(f, st);
const DWORD len = get_final_path(f, finalname, numberof(finalname), 0);
+ unsigned mode = 0;
+ switch (GetFileType(f)) {
+ case FILE_TYPE_CHAR:
+ mode = S_IFCHR;
+ break;
+ case FILE_TYPE_PIPE:
+ mode = S_IFIFO;
+ break;
+ }
CloseHandle(f);
if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
/* TODO: size in which encoding? */
@@ -5691,7 +5731,7 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat)
if (attr & FILE_ATTRIBUTE_DIRECTORY) {
if (check_valid_dir(path)) return -1;
}
- st->st_mode = fileattr_to_unixmode(attr, path);
+ st->st_mode = fileattr_to_unixmode(attr, path, mode);
if (len) {
finalname[min(len, numberof(finalname)-1)] = L'\0';
path = finalname;
@@ -6555,32 +6595,36 @@ constat_handle(HANDLE h)
{
st_data_t data;
struct constat *p;
+
+ EnterCriticalSection(&conlist_mutex);
if (!conlist) {
if (console_emulator_p()) {
conlist = conlist_disabled;
- return NULL;
- }
- conlist = st_init_numtable();
- install_vm_exit_handler();
- }
- else if (conlist == conlist_disabled) {
- return NULL;
- }
- if (st_lookup(conlist, (st_data_t)h, &data)) {
- p = (struct constat *)data;
- }
- else {
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- p = ALLOC(struct constat);
- p->vt100.state = constat_init;
- p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
- p->vt100.reverse = 0;
- p->vt100.saved.X = p->vt100.saved.Y = 0;
- if (GetConsoleScreenBufferInfo(h, &csbi)) {
- p->vt100.attr = csbi.wAttributes;
+ } else {
+ conlist = st_init_numtable();
+ install_vm_exit_handler();
+ }
+ }
+ if (conlist != conlist_disabled) {
+ if (st_lookup(conlist, (st_data_t)h, &data)) {
+ p = (struct constat *)data;
+ } else {
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ p = ALLOC(struct constat);
+ p->vt100.state = constat_init;
+ p->vt100.attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
+ p->vt100.reverse = 0;
+ p->vt100.saved.X = p->vt100.saved.Y = 0;
+ if (GetConsoleScreenBufferInfo(h, &csbi)) {
+ p->vt100.attr = csbi.wAttributes;
+ }
+ st_insert(conlist, (st_data_t)h, (st_data_t)p);
}
- st_insert(conlist, (st_data_t)h, (st_data_t)p);
+ } else {
+ p = NULL;
}
+ LeaveCriticalSection(&conlist_mutex);
+
return p;
}
@@ -6590,10 +6634,16 @@ constat_reset(HANDLE h)
{
st_data_t data;
struct constat *p;
- if (!conlist || conlist == conlist_disabled) return;
- if (!st_lookup(conlist, (st_data_t)h, &data)) return;
- p = (struct constat *)data;
- p->vt100.state = constat_init;
+
+ EnterCriticalSection(&conlist_mutex);
+ if (
+ conlist && conlist != conlist_disabled &&
+ st_lookup(conlist, (st_data_t)h, &data)
+ ) {
+ p = (struct constat *)data;
+ p->vt100.state = constat_init;
+ }
+ LeaveCriticalSection(&conlist_mutex);
}
#define FOREGROUND_MASK (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
@@ -7285,7 +7335,7 @@ rb_w32_write_console(uintptr_t strarg, int fd)
break;
}
reslen = 0;
- if (dwMode & 4) { /* ENABLE_VIRTUAL_TERMINAL_PROCESSING */
+ if (dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
if (!WriteConsoleW(handle, ptr, len, &reslen, NULL))
reslen = (DWORD)-1L;
}