summaryrefslogtreecommitdiff
path: root/benchmark/array_small_diff.rb
diff options
context:
space:
mode:
Diffstat (limited to 'benchmark/array_small_diff.rb')
-rw-r--r--benchmark/array_small_diff.rb17
1 files changed, 17 insertions, 0 deletions
diff --git a/benchmark/array_small_diff.rb b/benchmark/array_small_diff.rb
new file mode 100644
index 0000000000..9661ee48db
--- /dev/null
+++ b/benchmark/array_small_diff.rb
@@ -0,0 +1,17 @@
+MIN_SIZE = ENV.fetch('SMALL_ARRAY_MIN', 0).to_i
+MAX_SIZE = ENV.fetch('SMALL_ARRAY_MAX', 16).to_i
+ITERATIONS = ENV.fetch('SMALL_ARRAY_ITERATIONS', 100).to_i
+
+ARRAYS = (MIN_SIZE..MAX_SIZE).map do |size1|
+ (MIN_SIZE..MAX_SIZE).map do |size2|
+ [Array.new(size1) { rand(MAX_SIZE) }, Array.new(size2) { rand(MAX_SIZE) }]
+ end
+end
+
+ITERATIONS.times do
+ ARRAYS.each do |group|
+ group.each do |arr1, arr2|
+ arr1 - arr2
+ end
+ end
+end
> -rw-r--r--spec/bundler/bundler/build_metadata_spec.rb23
-rw-r--r--spec/bundler/bundler/bundler_spec.rb338
-rw-r--r--spec/bundler/bundler/ci_detector_spec.rb21
-rw-r--r--spec/bundler/bundler/cli_common_spec.rb22
-rw-r--r--spec/bundler/bundler/cli_spec.rb184
-rw-r--r--spec/bundler/bundler/compact_index_client/parser_spec.rb249
-rw-r--r--spec/bundler/bundler/compact_index_client/updater_spec.rb236
-rw-r--r--spec/bundler/bundler/current_ruby_spec.rb157
-rw-r--r--spec/bundler/bundler/definition_spec.rb289
-rw-r--r--spec/bundler/bundler/dep_proxy_spec.rb22
-rw-r--r--spec/bundler/bundler/dependency_spec.rb49
-rw-r--r--spec/bundler/bundler/digest_spec.rb24
-rw-r--r--spec/bundler/bundler/dsl_spec.rb373
-rw-r--r--spec/bundler/bundler/endpoint_specification_spec.rb90
-rw-r--r--spec/bundler/bundler/env_spec.rb97
-rw-r--r--spec/bundler/bundler/environment_preserver_spec.rb16
-rw-r--r--spec/bundler/bundler/errors_spec.rb91
-rw-r--r--spec/bundler/bundler/fetcher/base_spec.rb11
-rw-r--r--spec/bundler/bundler/fetcher/compact_index_spec.rb18
-rw-r--r--spec/bundler/bundler/fetcher/dependency_spec.rb57
-rw-r--r--spec/bundler/bundler/fetcher/downloader_spec.rb219
-rw-r--r--spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb60
-rw-r--r--spec/bundler/bundler/fetcher/index_spec.rb117
-rw-r--r--spec/bundler/bundler/fetcher_spec.rb130
-rw-r--r--spec/bundler/bundler/friendly_errors_spec.rb57
-rw-r--r--spec/bundler/bundler/gem_helper_spec.rb122
-rw-r--r--spec/bundler/bundler/gem_version_promoter_spec.rb246
-rw-r--r--spec/bundler/bundler/installer/gem_installer_spec.rb29
-rw-r--r--spec/bundler/bundler/installer/parallel_installer_spec.rb100
-rw-r--r--spec/bundler/bundler/installer/spec_installation_spec.rb73
-rw-r--r--spec/bundler/bundler/lockfile_parser_spec.rb166
-rw-r--r--spec/bundler/bundler/mirror_spec.rb16
-rw-r--r--spec/bundler/bundler/override_spec.rb175
-rw-r--r--spec/bundler/bundler/plugin/api/source_spec.rb10
-rw-r--r--spec/bundler/bundler/plugin/dsl_spec.rb8
-rw-r--r--spec/bundler/bundler/plugin/events_spec.rb12
-rw-r--r--spec/bundler/bundler/plugin/index_spec.rb103
-rw-r--r--spec/bundler/bundler/plugin/installer_spec.rb30
-rw-r--r--spec/bundler/bundler/plugin_spec.rb97
-rw-r--r--spec/bundler/bundler/psyched_yaml_spec.rb9
-rw-r--r--spec/bundler/bundler/remote_specification_spec.rb14
-rw-r--r--spec/bundler/bundler/resolver/candidate_spec.rb20
-rw-r--r--spec/bundler/bundler/resolver/cooldown_spec.rb148
-rw-r--r--spec/bundler/bundler/retry_spec.rb113
-rw-r--r--spec/bundler/bundler/ruby_dsl_spec.rb163
-rw-r--r--spec/bundler/bundler/ruby_version_spec.rb68
-rw-r--r--spec/bundler/bundler/rubygems_ext_spec.rb39
-rw-r--r--spec/bundler/bundler/rubygems_integration_spec.rb96
-rw-r--r--spec/bundler/bundler/settings/validator_spec.rb6
-rw-r--r--spec/bundler/bundler/settings_spec.rb126
-rw-r--r--spec/bundler/bundler/shared_helpers_spec.rb99
-rw-r--r--spec/bundler/bundler/source/git/git_proxy_spec.rb305
-rw-r--r--spec/bundler/bundler/source/git_spec.rb99
-rw-r--r--spec/bundler/bundler/source/rubygems/remote_spec.rb55
-rw-r--r--spec/bundler/bundler/source/rubygems_spec.rb71
-rw-r--r--spec/bundler/bundler/source_list_spec.rb128
-rw-r--r--spec/bundler/bundler/source_spec.rb60
-rw-r--r--spec/bundler/bundler/spec_set_spec.rb97
-rw-r--r--spec/bundler/bundler/specifications/foo.gemspec13
-rw-r--r--spec/bundler/bundler/stub_specification_spec.rb62
-rw-r--r--spec/bundler/bundler/ui/shell_spec.rb71
-rw-r--r--spec/bundler/bundler/uri_credentials_filter_spec.rb20
-rw-r--r--spec/bundler/bundler/uri_normalizer_spec.rb25
-rw-r--r--spec/bundler/bundler/vendored_persistent_spec.rb77
-rw-r--r--spec/bundler/bundler/version_ranges_spec.rb40
-rw-r--r--spec/bundler/bundler/worker_spec.rb67
-rw-r--r--spec/bundler/bundler/yaml_serializer_spec.rb61
-rw-r--r--spec/bundler/cache/cache_path_spec.rb14
-rw-r--r--spec/bundler/cache/gems_spec.rb325
-rw-r--r--spec/bundler/cache/git_spec.rb383
-rw-r--r--spec/bundler/cache/path_spec.rb56
-rw-r--r--spec/bundler/cache/platform_spec.rb18
-rw-r--r--spec/bundler/commands/add_spec.rb299
-rw-r--r--spec/bundler/commands/binstubs_spec.rb379
-rw-r--r--spec/bundler/commands/cache_spec.rb474
-rw-r--r--spec/bundler/commands/check_spec.rb490
-rw-r--r--spec/bundler/commands/clean_spec.rb460
-rw-r--r--spec/bundler/commands/config_spec.rb244
-rw-r--r--spec/bundler/commands/console_spec.rb250
-rw-r--r--spec/bundler/commands/doctor_spec.rb121
-rw-r--r--spec/bundler/commands/exec_spec.rb761
-rw-r--r--spec/bundler/commands/fund_spec.rb118
-rw-r--r--spec/bundler/commands/help_spec.rb38
-rw-r--r--spec/bundler/commands/info_spec.rb116
-rw-r--r--spec/bundler/commands/init_spec.rb78
-rw-r--r--spec/bundler/commands/inject_spec.rb117
-rw-r--r--spec/bundler/commands/install_spec.rb1967
-rw-r--r--spec/bundler/commands/issue_spec.rb2
-rw-r--r--spec/bundler/commands/licenses_spec.rb12
-rw-r--r--spec/bundler/commands/list_spec.rb178
-rw-r--r--spec/bundler/commands/lock_spec.rb2685
-rw-r--r--spec/bundler/commands/newgem_spec.rb2030
-rw-r--r--spec/bundler/commands/open_spec.rb102
-rw-r--r--spec/bundler/commands/outdated_spec.rb862
-rw-r--r--spec/bundler/commands/platform_spec.rb1260
-rw-r--r--spec/bundler/commands/post_bundle_message_spec.rb250
-rw-r--r--spec/bundler/commands/pristine_spec.rb110
-rw-r--r--spec/bundler/commands/remove_spec.rb451
-rw-r--r--spec/bundler/commands/show_spec.rb93
-rw-r--r--spec/bundler/commands/ssl_spec.rb373
-rw-r--r--spec/bundler/commands/update_spec.rb1757
-rw-r--r--spec/bundler/commands/version_spec.rb54
-rw-r--r--spec/bundler/commands/viz_spec.rb146
-rw-r--r--spec/bundler/install/allow_offline_install_spec.rb51
-rw-r--r--spec/bundler/install/binstubs_spec.rb26
-rw-r--r--spec/bundler/install/bundler_spec.rb213
-rw-r--r--spec/bundler/install/cooldown_spec.rb433
-rw-r--r--spec/bundler/install/deploy_spec.rb462
-rw-r--r--spec/bundler/install/failure_spec.rb133
-rw-r--r--spec/bundler/install/force_spec.rb71
-rw-r--r--spec/bundler/install/gemfile/eval_gemfile_spec.rb63
-rw-r--r--spec/bundler/install/gemfile/force_ruby_platform_spec.rb136
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb572
-rw-r--r--spec/bundler/install/gemfile/git_spec.rb794
-rw-r--r--spec/bundler/install/gemfile/groups_spec.rb189
-rw-r--r--spec/bundler/install/gemfile/install_if_spec.rb27
-rw-r--r--spec/bundler/install/gemfile/lockfile_spec.rb32
-rw-r--r--spec/bundler/install/gemfile/override_spec.rb401
-rw-r--r--spec/bundler/install/gemfile/path_spec.rb574
-rw-r--r--spec/bundler/install/gemfile/platform_spec.rb655
-rw-r--r--spec/bundler/install/gemfile/ruby_spec.rb128
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb1356
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb2013
-rw-r--r--spec/bundler/install/gemfile_spec.rb130
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb775
-rw-r--r--spec/bundler/install/gems/dependency_api_fallback_spec.rb19
-rw-r--r--spec/bundler/install/gems/dependency_api_spec.rb449
-rw-r--r--spec/bundler/install/gems/env_spec.rb36
-rw-r--r--spec/bundler/install/gems/flex_spec.rb340
-rw-r--r--spec/bundler/install/gems/fund_spec.rb164
-rw-r--r--spec/bundler/install/gems/gemfile_source_header_spec.rb24
-rw-r--r--spec/bundler/install/gems/mirror_probe_spec.rb68
-rw-r--r--spec/bundler/install/gems/mirror_spec.rb24
-rw-r--r--spec/bundler/install/gems/native_extensions_spec.rb30
-rw-r--r--spec/bundler/install/gems/no_build_extension_spec.rb54
-rw-r--r--spec/bundler/install/gems/no_install_plugin_spec.rb53
-rw-r--r--spec/bundler/install/gems/post_install_spec.rb48
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb678
-rw-r--r--spec/bundler/install/gems/standalone_spec.rb370
-rw-r--r--spec/bundler/install/gems/sudo_spec.rb190
-rw-r--r--spec/bundler/install/gems/win32_spec.rb10
-rw-r--r--spec/bundler/install/gemspecs_spec.rb80
-rw-r--r--spec/bundler/install/git_spec.rb340
-rw-r--r--spec/bundler/install/global_cache_spec.rb238
-rw-r--r--spec/bundler/install/path_spec.rb164
-rw-r--r--spec/bundler/install/prereleases_spec.rb27
-rw-r--r--spec/bundler/install/process_lock_spec.rb88
-rw-r--r--spec/bundler/install/redownload_spec.rb90
-rw-r--r--spec/bundler/install/security_policy_spec.rb20
-rw-r--r--spec/bundler/install/yanked_spec.rb223
-rw-r--r--spec/bundler/lock/git_spec.rb226
-rw-r--r--spec/bundler/lock/lockfile_spec.rb1934
-rw-r--r--spec/bundler/other/cli_dispatch_spec.rb12
-rw-r--r--spec/bundler/other/cli_man_pages_spec.rb100
-rw-r--r--spec/bundler/other/ext_spec.rb67
-rw-r--r--spec/bundler/other/major_deprecation_spec.rb734
-rw-r--r--spec/bundler/other/platform_spec.rb1264
-rw-r--r--spec/bundler/plugins/command_spec.rb48
-rw-r--r--spec/bundler/plugins/hook_spec.rb256
-rw-r--r--spec/bundler/plugins/install_spec.rb226
-rw-r--r--spec/bundler/plugins/list_spec.rb4
-rw-r--r--spec/bundler/plugins/source/example_spec.rb135
-rw-r--r--spec/bundler/plugins/source_spec.rb16
-rw-r--r--spec/bundler/plugins/uninstall_spec.rb29
-rw-r--r--spec/bundler/quality_es_spec.rb8
-rw-r--r--spec/bundler/quality_spec.rb116
-rw-r--r--spec/bundler/realworld/dependency_api_spec.rb46
-rw-r--r--spec/bundler/realworld/double_check_spec.rb6
-rw-r--r--spec/bundler/realworld/edgecases_spec.rb303
-rw-r--r--spec/bundler/realworld/ffi_spec.rb57
-rw-r--r--spec/bundler/realworld/fixtures/tapioca/Gemfile5
-rw-r--r--spec/bundler/realworld/fixtures/tapioca/Gemfile.lock49
-rw-r--r--spec/bundler/realworld/fixtures/warbler/Gemfile6
-rw-r--r--spec/bundler/realworld/fixtures/warbler/Gemfile.lock30
-rw-r--r--spec/bundler/realworld/gemfile_source_header_spec.rb53
-rw-r--r--spec/bundler/realworld/git_spec.rb11
-rw-r--r--spec/bundler/realworld/mirror_probe_spec.rb144
-rw-r--r--spec/bundler/realworld/parallel_spec.rb10
-rw-r--r--spec/bundler/realworld/slow_perf_spec.rb35
-rw-r--r--spec/bundler/resolver/basic_spec.rb178
-rw-r--r--spec/bundler/resolver/platform_spec.rb291
-rw-r--r--spec/bundler/runtime/env_helpers_spec.rb236
-rw-r--r--spec/bundler/runtime/executable_spec.rb115
-rw-r--r--spec/bundler/runtime/gem_tasks_spec.rb94
-rw-r--r--spec/bundler/runtime/inline_spec.rb519
-rw-r--r--spec/bundler/runtime/load_spec.rb30
-rw-r--r--spec/bundler/runtime/platform_spec.rb370
-rw-r--r--spec/bundler/runtime/require_spec.rb138
-rw-r--r--spec/bundler/runtime/requiring_spec.rb15
-rw-r--r--spec/bundler/runtime/self_management_spec.rb279
-rw-r--r--spec/bundler/runtime/setup_spec.rb995
-rw-r--r--spec/bundler/runtime/with_unbundled_env_spec.rb290
-rw-r--r--spec/bundler/spec_helper.rb142
-rw-r--r--spec/bundler/support/activate.rb9
-rw-r--r--spec/bundler/support/artifice/compact_index.rb118
-rw-r--r--spec/bundler/support/artifice/compact_index_api_missing.rb13
-rw-r--r--spec/bundler/support/artifice/compact_index_basic_authentication.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_checksum_mismatch.rb10
-rw-r--r--spec/bundler/support/artifice/compact_index_concurrent_download.rb13
-rw-r--r--spec/bundler/support/artifice/compact_index_cooldown.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_creds_diff_host.rb8
-rw-r--r--spec/bundler/support/artifice/compact_index_etag_match.rb16
-rw-r--r--spec/bundler/support/artifice/compact_index_extra.rb35
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_api.rb50
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_api_missing.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_extra_missing.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_forbidden.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_host_redirect.rb8
-rw-r--r--spec/bundler/support/artifice/compact_index_mirror_down.rb21
-rw-r--r--spec/bundler/support/artifice/compact_index_no_checksums.rb16
-rw-r--r--spec/bundler/support/artifice/compact_index_no_gem.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update.rb8
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update_bad_digest.rb40
-rw-r--r--spec/bundler/support/artifice/compact_index_partial_update_no_digest_not_incremental.rb42
-rw-r--r--spec/bundler/support/artifice/compact_index_precompiled_before.rb25
-rw-r--r--spec/bundler/support/artifice/compact_index_range_ignored.rb40
-rw-r--r--spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_rate_limited.rb8
-rw-r--r--spec/bundler/support/artifice/compact_index_redirects.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb8
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_dependencies.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb9
-rw-r--r--spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb15
-rw-r--r--spec/bundler/support/artifice/endpoint.rb95
-rw-r--r--spec/bundler/support/artifice/endpoint_500.rb7
-rw-r--r--spec/bundler/support/artifice/endpoint_api_forbidden.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_api_missing.rb18
-rw-r--r--spec/bundler/support/artifice/endpoint_basic_authentication.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_creds_diff_host.rb8
-rw-r--r--spec/bundler/support/artifice/endpoint_extra.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_extra_api.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_extra_missing.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_fallback.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_host_redirect.rb8
-rw-r--r--spec/bundler/support/artifice/endpoint_marshal_fail.rb11
-rw-r--r--spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb15
-rw-r--r--spec/bundler/support/artifice/endpoint_mirror_source.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_redirect.rb6
-rw-r--r--spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb8
-rw-r--r--spec/bundler/support/artifice/endpoint_timeout.rb6
-rw-r--r--spec/bundler/support/artifice/fail.rb25
-rw-r--r--spec/bundler/support/artifice/helpers/artifice.rb30
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index.rb128
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index_cooldown.rb13
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index_extra.rb33
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index_extra_api.rb48
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint.rb113
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint_extra.rb29
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint_fallback.rb15
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb9
-rw-r--r--spec/bundler/support/artifice/helpers/rack_request.rb100
-rw-r--r--spec/bundler/support/artifice/vcr.rb63
-rw-r--r--spec/bundler/support/artifice/windows.rb11
-rw-r--r--spec/bundler/support/build_metadata.rb53
-rw-r--r--spec/bundler/support/builders.rb545
-rwxr-xr-xspec/bundler/support/bundle6
-rw-r--r--spec/bundler/support/bundle.rb7
-rw-r--r--spec/bundler/support/checksums.rb135
-rw-r--r--spec/bundler/support/command_execution.rb47
-rw-r--r--spec/bundler/support/env.rb13
-rw-r--r--spec/bundler/support/filters.rb43
-rw-r--r--spec/bundler/support/hax.rb82
-rw-r--r--spec/bundler/support/helpers.rb588
-rw-r--r--spec/bundler/support/indexes.rb69
-rw-r--r--spec/bundler/support/matchers.rb104
-rw-r--r--spec/bundler/support/options.rb15
-rw-r--r--spec/bundler/support/path.rb235
-rw-r--r--spec/bundler/support/platforms.rb81
-rw-r--r--spec/bundler/support/rubygems_ext.rb194
-rw-r--r--spec/bundler/support/rubygems_version_manager.rb55
-rw-r--r--spec/bundler/support/setup.rb9
-rw-r--r--spec/bundler/support/shards.rb200
-rw-r--r--spec/bundler/support/silent_logger.rb10
-rw-r--r--spec/bundler/support/sometimes.rb57
-rw-r--r--spec/bundler/support/streams.rb19
-rw-r--r--spec/bundler/support/subprocess.rb115
-rw-r--r--spec/bundler/support/sudo.rb18
-rw-r--r--spec/bundler/support/switch_rubygems.rb1
-rw-r--r--spec/bundler/support/the_bundle.rb16
-rw-r--r--spec/bundler/support/vendored_net_http.rb23
-rw-r--r--spec/bundler/update/force_spec.rb30
-rw-r--r--spec/bundler/update/gemfile_spec.rb26
-rw-r--r--spec/bundler/update/gems/fund_spec.rb50
-rw-r--r--spec/bundler/update/gems/post_install_spec.rb24
-rw-r--r--spec/bundler/update/git_spec.rb185
-rw-r--r--spec/bundler/update/path_spec.rb5
-rw-r--r--spec/bundler/update/redownload_spec.rb34
-rw-r--r--spec/default.mspec151
-rw-r--r--spec/lib/formatter_overrides.rb6
-rw-r--r--spec/lib/spec_coverage.rb1
-rw-r--r--spec/mmtk.mspec12
-rw-r--r--spec/mspec/.rspec1
-rw-r--r--spec/mspec/Gemfile2
-rw-r--r--spec/mspec/Gemfile.lock25
-rw-r--r--spec/mspec/README.md4
-rwxr-xr-xspec/mspec/bin/mspec2
-rw-r--r--[-rwxr-xr-x]spec/mspec/lib/mspec/commands/mkspec.rb18
-rw-r--r--spec/mspec/lib/mspec/commands/mspec-ci.rb5
-rw-r--r--spec/mspec/lib/mspec/commands/mspec-run.rb8
-rw-r--r--spec/mspec/lib/mspec/commands/mspec-tag.rb3
-rw-r--r--[-rwxr-xr-x]spec/mspec/lib/mspec/commands/mspec.rb13
-rw-r--r--spec/mspec/lib/mspec/expectations/expectations.rb4
-rw-r--r--spec/mspec/lib/mspec/expectations/should.rb10
-rw-r--r--spec/mspec/lib/mspec/guards/platform.rb28
-rw-r--r--spec/mspec/lib/mspec/guards/superuser.rb10
-rw-r--r--spec/mspec/lib/mspec/guards/version.rb28
-rw-r--r--spec/mspec/lib/mspec/helpers/datetime.rb1
-rw-r--r--spec/mspec/lib/mspec/helpers/io.rb8
-rw-r--r--spec/mspec/lib/mspec/helpers/numeric.rb30
-rw-r--r--spec/mspec/lib/mspec/helpers/ruby_exe.rb54
-rw-r--r--spec/mspec/lib/mspec/helpers/tmp.rb18
-rw-r--r--spec/mspec/lib/mspec/helpers/warning.rb2
-rw-r--r--spec/mspec/lib/mspec/matchers/base.rb46
-rw-r--r--spec/mspec/lib/mspec/matchers/complain.rb2
-rw-r--r--spec/mspec/lib/mspec/matchers/include.rb4
-rw-r--r--spec/mspec/lib/mspec/matchers/match_yaml.rb6
-rw-r--r--spec/mspec/lib/mspec/matchers/output.rb8
-rw-r--r--spec/mspec/lib/mspec/matchers/raise_error.rb71
-rw-r--r--spec/mspec/lib/mspec/mocks/mock.rb27
-rw-r--r--spec/mspec/lib/mspec/runner/actions/leakchecker.rb84
-rw-r--r--spec/mspec/lib/mspec/runner/actions/timeout.rb89
-rw-r--r--spec/mspec/lib/mspec/runner/context.rb1
-rw-r--r--spec/mspec/lib/mspec/runner/exception.rb13
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/base.rb34
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/junit.rb6
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/launchable.rb88
-rw-r--r--spec/mspec/lib/mspec/runner/formatters/multi.rb2
-rw-r--r--spec/mspec/lib/mspec/runner/mspec.rb16
-rw-r--r--spec/mspec/lib/mspec/runner/shared.rb8
-rw-r--r--spec/mspec/lib/mspec/utils/name_map.rb20
-rw-r--r--spec/mspec/lib/mspec/utils/options.rb34
-rw-r--r--spec/mspec/lib/mspec/utils/script.rb34
-rw-r--r--spec/mspec/lib/mspec/utils/version.rb2
-rw-r--r--spec/mspec/lib/mspec/utils/warnings.rb58
-rw-r--r--spec/mspec/spec/commands/mkspec_spec.rb196
-rw-r--r--spec/mspec/spec/commands/mspec_ci_spec.rb77
-rw-r--r--spec/mspec/spec/commands/mspec_run_spec.rb79
-rw-r--r--spec/mspec/spec/commands/mspec_spec.rb119
-rw-r--r--spec/mspec/spec/commands/mspec_tag_spec.rb170
-rw-r--r--spec/mspec/spec/expectations/expectations_spec.rb18
-rw-r--r--spec/mspec/spec/expectations/should.rb73
-rw-r--r--spec/mspec/spec/expectations/should_spec.rb18
-rw-r--r--spec/mspec/spec/fixtures/config.mspec2
-rw-r--r--spec/mspec/spec/fixtures/should.rb75
-rw-r--r--spec/mspec/spec/guards/block_device_spec.rb26
-rw-r--r--spec/mspec/spec/guards/bug_spec.rb84
-rw-r--r--spec/mspec/spec/guards/conflict_spec.rb30
-rw-r--r--spec/mspec/spec/guards/endian_spec.rb34
-rw-r--r--spec/mspec/spec/guards/feature_spec.rb82
-rw-r--r--spec/mspec/spec/guards/guard_spec.rb218
-rw-r--r--spec/mspec/spec/guards/platform_spec.rb208
-rw-r--r--spec/mspec/spec/guards/quarantine_spec.rb20
-rw-r--r--spec/mspec/spec/guards/superuser_spec.rb22
-rw-r--r--spec/mspec/spec/guards/support_spec.rb28
-rw-r--r--spec/mspec/spec/guards/user_spec.rb10
-rw-r--r--spec/mspec/spec/guards/version_spec.rb92
-rw-r--r--spec/mspec/spec/helpers/argf_spec.rb16
-rw-r--r--spec/mspec/spec/helpers/argv_spec.rb10
-rw-r--r--spec/mspec/spec/helpers/datetime_spec.rb18
-rw-r--r--spec/mspec/spec/helpers/fixture_spec.rb8
-rw-r--r--spec/mspec/spec/helpers/flunk_spec.rb10
-rw-r--r--spec/mspec/spec/helpers/fs_spec.rb60
-rw-r--r--spec/mspec/spec/helpers/io_spec.rb50
-rw-r--r--spec/mspec/spec/helpers/mock_to_path_spec.rb14
-rw-r--r--spec/mspec/spec/helpers/numeric_spec.rb20
-rw-r--r--spec/mspec/spec/helpers/ruby_exe_spec.rb165
-rw-r--r--spec/mspec/spec/helpers/scratch_spec.rb10
-rw-r--r--spec/mspec/spec/helpers/suppress_warning_spec.rb4
-rw-r--r--spec/mspec/spec/helpers/tmp_spec.rb10
-rw-r--r--spec/mspec/spec/integration/interpreter_spec.rb8
-rw-r--r--spec/mspec/spec/integration/object_methods_spec.rb6
-rw-r--r--spec/mspec/spec/integration/run_spec.rb45
-rw-r--r--spec/mspec/spec/integration/tag_spec.rb21
-rw-r--r--spec/mspec/spec/matchers/base_spec.rb140
-rw-r--r--spec/mspec/spec/matchers/be_an_instance_of_spec.rb22
-rw-r--r--spec/mspec/spec/matchers/be_ancestor_of_spec.rb10
-rw-r--r--spec/mspec/spec/matchers/be_close_spec.rb24
-rw-r--r--spec/mspec/spec/matchers/be_computed_by_spec.rb14
-rw-r--r--spec/mspec/spec/matchers/be_empty_spec.rb10
-rw-r--r--spec/mspec/spec/matchers/be_false_spec.rb16
-rw-r--r--spec/mspec/spec/matchers/be_kind_of_spec.rb22
-rw-r--r--spec/mspec/spec/matchers/be_nan_spec.rb12
-rw-r--r--spec/mspec/spec/matchers/be_nil_spec.rb14
-rw-r--r--spec/mspec/spec/matchers/be_true_or_false_spec.rb8
-rw-r--r--spec/mspec/spec/matchers/be_true_spec.rb16
-rw-r--r--spec/mspec/spec/matchers/block_caller_spec.rb6
-rw-r--r--spec/mspec/spec/matchers/complain_spec.rb43
-rw-r--r--spec/mspec/spec/matchers/eql_spec.rb22
-rw-r--r--spec/mspec/spec/matchers/equal_element_spec.rb78
-rw-r--r--spec/mspec/spec/matchers/equal_spec.rb20
-rw-r--r--spec/mspec/spec/matchers/have_class_variable_spec.rb18
-rw-r--r--spec/mspec/spec/matchers/have_constant_spec.rb14
-rw-r--r--spec/mspec/spec/matchers/have_instance_method_spec.rb20
-rw-r--r--spec/mspec/spec/matchers/have_instance_variable_spec.rb18
-rw-r--r--spec/mspec/spec/matchers/have_method_spec.rb24
-rw-r--r--spec/mspec/spec/matchers/have_private_instance_method_spec.rb20
-rw-r--r--spec/mspec/spec/matchers/have_private_method_spec.rb16
-rw-r--r--spec/mspec/spec/matchers/have_protected_instance_method_spec.rb20
-rw-r--r--spec/mspec/spec/matchers/have_public_instance_method_spec.rb20
-rw-r--r--spec/mspec/spec/matchers/have_singleton_method_spec.rb16
-rw-r--r--spec/mspec/spec/matchers/include_any_of_spec.rb26
-rw-r--r--spec/mspec/spec/matchers/include_spec.rb22
-rw-r--r--spec/mspec/spec/matchers/infinity_spec.rb18
-rw-r--r--spec/mspec/spec/matchers/match_yaml_spec.rb22
-rw-r--r--spec/mspec/spec/matchers/output_spec.rb48
-rw-r--r--spec/mspec/spec/matchers/output_to_fd_spec.rb30
-rw-r--r--spec/mspec/spec/matchers/raise_error_spec.rb163
-rw-r--r--spec/mspec/spec/matchers/respond_to_spec.rb26
-rw-r--r--spec/mspec/spec/matchers/signed_zero_spec.rb18
-rw-r--r--spec/mspec/spec/mocks/mock_spec.rb241
-rw-r--r--spec/mspec/spec/mocks/proxy_spec.rb196
-rw-r--r--spec/mspec/spec/runner/actions/filter_spec.rb44
-rw-r--r--spec/mspec/spec/runner/actions/tag_spec.rb144
-rw-r--r--spec/mspec/spec/runner/actions/taglist_spec.rb70
-rw-r--r--spec/mspec/spec/runner/actions/tagpurge_spec.rb66
-rw-r--r--spec/mspec/spec/runner/actions/tally_spec.rb167
-rw-r--r--spec/mspec/spec/runner/actions/timer_spec.rb22
-rw-r--r--spec/mspec/spec/runner/context_spec.rb342
-rw-r--r--spec/mspec/spec/runner/example_spec.rb50
-rw-r--r--spec/mspec/spec/runner/exception_spec.rb50
-rw-r--r--spec/mspec/spec/runner/filters/match_spec.rb16
-rw-r--r--spec/mspec/spec/runner/filters/profile_spec.rb70
-rw-r--r--spec/mspec/spec/runner/filters/regexp_spec.rb18
-rw-r--r--spec/mspec/spec/runner/filters/tag_spec.rb48
-rw-r--r--spec/mspec/spec/runner/formatters/describe_spec.rb24
-rw-r--r--spec/mspec/spec/runner/formatters/dotted_spec.rb119
-rw-r--r--spec/mspec/spec/runner/formatters/file_spec.rb34
-rw-r--r--spec/mspec/spec/runner/formatters/html_spec.rb88
-rw-r--r--spec/mspec/spec/runner/formatters/junit_spec.rb100
-rw-r--r--spec/mspec/spec/runner/formatters/method_spec.rb69
-rw-r--r--spec/mspec/spec/runner/formatters/multi_spec.rb20
-rw-r--r--spec/mspec/spec/runner/formatters/specdoc_spec.rb30
-rw-r--r--spec/mspec/spec/runner/formatters/spinner_spec.rb32
-rw-r--r--spec/mspec/spec/runner/formatters/summary_spec.rb4
-rw-r--r--spec/mspec/spec/runner/formatters/unit_spec.rb33
-rw-r--r--spec/mspec/spec/runner/formatters/yaml_spec.rb79
-rw-r--r--spec/mspec/spec/runner/mspec_spec.rb287
-rw-r--r--spec/mspec/spec/runner/shared_spec.rb20
-rw-r--r--spec/mspec/spec/runner/tag_spec.rb96
-rw-r--r--spec/mspec/spec/spec_helper.rb23
-rw-r--r--spec/mspec/spec/utils/deprecate_spec.rb10
-rw-r--r--spec/mspec/spec/utils/fixtures/this_file_raises.rb1
-rw-r--r--spec/mspec/spec/utils/fixtures/this_file_raises2.rb1
-rw-r--r--spec/mspec/spec/utils/name_map_spec.rb90
-rw-r--r--spec/mspec/spec/utils/options_spec.rb476
-rw-r--r--spec/mspec/spec/utils/script_spec.rb255
-rw-r--r--spec/mspec/spec/utils/version_spec.rb30
-rwxr-xr-xspec/mspec/tool/check_require_spec_helper.rb34
-rwxr-xr-x[-rw-r--r--]spec/mspec/tool/remove_old_guards.rb91
-rw-r--r--spec/mspec/tool/sync/sync-rubyspec.rb77
-rwxr-xr-xspec/mspec/tool/tag_from_output.rb39
-rwxr-xr-xspec/mspec/tool/wrap_with_guard.rb28
-rw-r--r--spec/ruby/.mspec.constants6
-rw-r--r--spec/ruby/.rubocop.yml80
-rw-r--r--spec/ruby/.rubocop_todo.yml86
-rw-r--r--spec/ruby/CONTRIBUTING.md99
-rw-r--r--spec/ruby/README.md60
-rwxr-xr-xspec/ruby/bin/rubocop12
-rw-r--r--spec/ruby/command_line/backtrace_limit_spec.rb93
-rwxr-xr-xspec/ruby/command_line/dash_0_spec.rb13
-rw-r--r--spec/ruby/command_line/dash_a_spec.rb4
-rw-r--r--spec/ruby/command_line/dash_e_spec.rb2
-rw-r--r--spec/ruby/command_line/dash_encoding_spec.rb8
-rw-r--r--spec/ruby/command_line/dash_l_spec.rb8
-rw-r--r--spec/ruby/command_line/dash_n_spec.rb8
-rw-r--r--spec/ruby/command_line/dash_p_spec.rb4
-rw-r--r--spec/ruby/command_line/dash_r_spec.rb10
-rw-r--r--spec/ruby/command_line/dash_upper_e_spec.rb3
-rw-r--r--spec/ruby/command_line/dash_upper_f_spec.rb2
-rw-r--r--spec/ruby/command_line/dash_upper_i_spec.rb10
-rw-r--r--spec/ruby/command_line/dash_upper_k_spec.rb4
-rw-r--r--spec/ruby/command_line/dash_upper_s_spec.rb42
-rw-r--r--spec/ruby/command_line/dash_upper_u_spec.rb37
-rw-r--r--spec/ruby/command_line/dash_upper_w_spec.rb24
-rw-r--r--spec/ruby/command_line/dash_v_spec.rb7
-rw-r--r--spec/ruby/command_line/dash_w_spec.rb4
-rw-r--r--spec/ruby/command_line/dash_x_spec.rb4
-rw-r--r--spec/ruby/command_line/error_message_spec.rb9
-rw-r--r--spec/ruby/command_line/feature_spec.rb37
-rw-r--r--spec/ruby/command_line/fixtures/backtrace.rb35
-rw-r--r--spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt2
-rw-r--r--spec/ruby/command_line/fixtures/bin/embedded_ruby.txt4
-rw-r--r--spec/ruby/command_line/fixtures/debug_info.rb1
-rw-r--r--spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rbbin121 -> 90 bytes-rw-r--r--spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb2
-rw-r--r--spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb4
-rw-r--r--spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb4
-rw-r--r--spec/ruby/command_line/fixtures/string_literal_raw.rb3
-rw-r--r--spec/ruby/command_line/frozen_strings_spec.rb73
-rw-r--r--spec/ruby/command_line/rubylib_spec.rb16
-rw-r--r--spec/ruby/command_line/rubyopt_spec.rb78
-rw-r--r--spec/ruby/command_line/syntax_error_spec.rb8
-rw-r--r--spec/ruby/core/argf/argf_spec.rb4
-rw-r--r--spec/ruby/core/argf/argv_spec.rb2
-rw-r--r--spec/ruby/core/argf/binmode_spec.rb2
-rw-r--r--spec/ruby/core/argf/bytes_spec.rb6
-rw-r--r--spec/ruby/core/argf/chars_spec.rb6
-rw-r--r--spec/ruby/core/argf/close_spec.rb8
-rw-r--r--spec/ruby/core/argf/closed_spec.rb2
-rw-r--r--spec/ruby/core/argf/codepoints_spec.rb6
-rw-r--r--spec/ruby/core/argf/each_byte_spec.rb58
-rw-r--r--spec/ruby/core/argf/each_char_spec.rb58
-rw-r--r--spec/ruby/core/argf/each_codepoint_spec.rb58
-rw-r--r--spec/ruby/core/argf/each_line_spec.rb62
-rw-r--r--spec/ruby/core/argf/each_spec.rb5
-rw-r--r--spec/ruby/core/argf/eof_spec.rb28
-rw-r--r--spec/ruby/core/argf/filename_spec.rb28
-rw-r--r--spec/ruby/core/argf/fileno_spec.rb24
-rw-r--r--spec/ruby/core/argf/inspect_spec.rb7
-rw-r--r--spec/ruby/core/argf/lines_spec.rb6
-rw-r--r--spec/ruby/core/argf/path_spec.rb5
-rw-r--r--spec/ruby/core/argf/pos_spec.rb31
-rw-r--r--spec/ruby/core/argf/read_nonblock_spec.rb2
-rw-r--r--spec/ruby/core/argf/readchar_spec.rb2
-rw-r--r--spec/ruby/core/argf/readline_spec.rb2
-rw-r--r--spec/ruby/core/argf/readlines_spec.rb22
-rw-r--r--spec/ruby/core/argf/readpartial_spec.rb10
-rw-r--r--spec/ruby/core/argf/rewind_spec.rb2
-rw-r--r--spec/ruby/core/argf/seek_spec.rb2
-rw-r--r--spec/ruby/core/argf/shared/each_byte.rb58
-rw-r--r--spec/ruby/core/argf/shared/each_char.rb58
-rw-r--r--spec/ruby/core/argf/shared/each_codepoint.rb58
-rw-r--r--spec/ruby/core/argf/shared/each_line.rb62
-rw-r--r--spec/ruby/core/argf/shared/eof.rb24
-rw-r--r--spec/ruby/core/argf/shared/filename.rb28
-rw-r--r--spec/ruby/core/argf/shared/fileno.rb24
-rw-r--r--spec/ruby/core/argf/shared/getc.rb2
-rw-r--r--spec/ruby/core/argf/shared/pos.rb31
-rw-r--r--spec/ruby/core/argf/shared/read.rb4
-rw-r--r--spec/ruby/core/argf/shared/readlines.rb22
-rw-r--r--spec/ruby/core/argf/skip_spec.rb2
-rw-r--r--spec/ruby/core/argf/tell_spec.rb5
-rw-r--r--spec/ruby/core/argf/to_a_spec.rb5
-rw-r--r--spec/ruby/core/argf/to_i_spec.rb5
-rw-r--r--spec/ruby/core/argf/to_io_spec.rb2
-rw-r--r--spec/ruby/core/array/all_spec.rb13
-rw-r--r--spec/ruby/core/array/allocate_spec.rb4
-rw-r--r--spec/ruby/core/array/any_spec.rb12
-rw-r--r--spec/ruby/core/array/append_spec.rb11
-rw-r--r--spec/ruby/core/array/assoc_spec.rb40
-rw-r--r--spec/ruby/core/array/at_spec.rb4
-rw-r--r--spec/ruby/core/array/bsearch_index_spec.rb34
-rw-r--r--spec/ruby/core/array/bsearch_spec.rb26
-rw-r--r--spec/ruby/core/array/clear_spec.rb28
-rw-r--r--spec/ruby/core/array/clone_spec.rb8
-rw-r--r--spec/ruby/core/array/collect_spec.rb138
-rw-r--r--spec/ruby/core/array/combination_spec.rb6
-rw-r--r--spec/ruby/core/array/compact_spec.rb44
-rw-r--r--spec/ruby/core/array/comparison_spec.rb2
-rw-r--r--spec/ruby/core/array/concat_spec.rb68
-rw-r--r--spec/ruby/core/array/constructor_spec.rb4
-rw-r--r--spec/ruby/core/array/count_spec.rb11
-rw-r--r--spec/ruby/core/array/cycle_spec.rb20
-rw-r--r--spec/ruby/core/array/deconstruct_spec.rb10
-rw-r--r--spec/ruby/core/array/delete_at_spec.rb24
-rw-r--r--spec/ruby/core/array/delete_if_spec.rb52
-rw-r--r--spec/ruby/core/array/delete_spec.rb24
-rw-r--r--spec/ruby/core/array/difference_spec.rb28
-rw-r--r--spec/ruby/core/array/dig_spec.rb8
-rw-r--r--spec/ruby/core/array/drop_spec.rb11
-rw-r--r--spec/ruby/core/array/drop_while_spec.rb9
-rw-r--r--spec/ruby/core/array/dup_spec.rb12
-rw-r--r--spec/ruby/core/array/each_index_spec.rb20
-rw-r--r--spec/ruby/core/array/each_spec.rb42
-rw-r--r--spec/ruby/core/array/element_reference_spec.rb861
-rw-r--r--spec/ruby/core/array/element_set_spec.rb160
-rw-r--r--spec/ruby/core/array/eql_spec.rb4
-rw-r--r--spec/ruby/core/array/equal_value_spec.rb10
-rw-r--r--spec/ruby/core/array/fetch_spec.rb10
-rw-r--r--spec/ruby/core/array/fetch_values_spec.rb55
-rw-r--r--spec/ruby/core/array/fill_spec.rb149
-rw-r--r--spec/ruby/core/array/filter_spec.rb17
-rw-r--r--spec/ruby/core/array/find_index_spec.rb40
-rw-r--r--spec/ruby/core/array/first_spec.rb24
-rw-r--r--spec/ruby/core/array/fixtures/classes.rb792
-rw-r--r--spec/ruby/core/array/fixtures/encoded_strings.rb18
-rw-r--r--spec/ruby/core/array/flatten_spec.rb50
-rw-r--r--spec/ruby/core/array/hash_spec.rb8
-rw-r--r--spec/ruby/core/array/index_spec.rb5
-rw-r--r--spec/ruby/core/array/initialize_spec.rb34
-rw-r--r--spec/ruby/core/array/insert_spec.rb14
-rw-r--r--spec/ruby/core/array/inspect_spec.rb105
-rw-r--r--spec/ruby/core/array/intersect_spec.rb64
-rw-r--r--spec/ruby/core/array/intersection_spec.rb16
-rw-r--r--spec/ruby/core/array/join_spec.rb102
-rw-r--r--spec/ruby/core/array/keep_if_spec.rb3
-rw-r--r--spec/ruby/core/array/last_spec.rb22
-rw-r--r--spec/ruby/core/array/length_spec.rb11
-rw-r--r--spec/ruby/core/array/map_spec.rb10
-rw-r--r--spec/ruby/core/array/max_spec.rb8
-rw-r--r--spec/ruby/core/array/min_spec.rb10
-rw-r--r--spec/ruby/core/array/minmax_spec.rb14
-rw-r--r--spec/ruby/core/array/multiply_spec.rb64
-rw-r--r--spec/ruby/core/array/new_spec.rb30
-rw-r--r--spec/ruby/core/array/none_spec.rb13
-rw-r--r--spec/ruby/core/array/one_spec.rb13
-rw-r--r--spec/ruby/core/array/pack/a_spec.rb17
-rw-r--r--spec/ruby/core/array/pack/at_spec.rb2
-rw-r--r--spec/ruby/core/array/pack/b_spec.rb9
-rw-r--r--spec/ruby/core/array/pack/buffer_spec.rb26
-rw-r--r--spec/ruby/core/array/pack/c_spec.rb8
-rw-r--r--spec/ruby/core/array/pack/comment_spec.rb2
-rw-r--r--spec/ruby/core/array/pack/h_spec.rb7
-rw-r--r--spec/ruby/core/array/pack/l_spec.rb16
-rw-r--r--spec/ruby/core/array/pack/m_spec.rb24
-rw-r--r--spec/ruby/core/array/pack/p_spec.rb24
-rw-r--r--spec/ruby/core/array/pack/percent_spec.rb2
-rw-r--r--spec/ruby/core/array/pack/r_spec.rb89
-rw-r--r--spec/ruby/core/array/pack/shared/basic.rb46
-rw-r--r--spec/ruby/core/array/pack/shared/encodings.rb4
-rw-r--r--spec/ruby/core/array/pack/shared/float.rb60
-rw-r--r--spec/ruby/core/array/pack/shared/integer.rb48
-rw-r--r--spec/ruby/core/array/pack/shared/numeric_basic.rb24
-rw-r--r--spec/ruby/core/array/pack/shared/string.rb10
-rw-r--r--spec/ruby/core/array/pack/shared/taint.rb33
-rw-r--r--spec/ruby/core/array/pack/shared/unicode.rb14
-rw-r--r--spec/ruby/core/array/pack/u_spec.rb20
-rw-r--r--spec/ruby/core/array/pack/w_spec.rb10
-rw-r--r--spec/ruby/core/array/pack/x_spec.rb7
-rw-r--r--spec/ruby/core/array/pack/z_spec.rb14
-rw-r--r--spec/ruby/core/array/partition_spec.rb6
-rw-r--r--spec/ruby/core/array/permutation_spec.rb10
-rw-r--r--spec/ruby/core/array/plus_spec.rb43
-rw-r--r--spec/ruby/core/array/pop_spec.rb76
-rw-r--r--spec/ruby/core/array/prepend_spec.rb6
-rw-r--r--spec/ruby/core/array/product_spec.rb19
-rw-r--r--spec/ruby/core/array/push_spec.rb33
-rw-r--r--spec/ruby/core/array/rassoc_spec.rb16
-rw-r--r--spec/ruby/core/array/reject_spec.rb29
-rw-r--r--spec/ruby/core/array/repeated_combination_spec.rb8
-rw-r--r--spec/ruby/core/array/repeated_permutation_spec.rb4
-rw-r--r--spec/ruby/core/array/replace_spec.rb60
-rw-r--r--spec/ruby/core/array/reverse_each_spec.rb18
-rw-r--r--spec/ruby/core/array/reverse_spec.rb6
-rw-r--r--spec/ruby/core/array/rindex_spec.rb21
-rw-r--r--spec/ruby/core/array/rotate_spec.rb42
-rw-r--r--spec/ruby/core/array/sample_spec.rb67
-rw-r--r--spec/ruby/core/array/select_spec.rb35
-rw-r--r--spec/ruby/core/array/shared/clone.rb32
-rw-r--r--spec/ruby/core/array/shared/collect.rb140
-rw-r--r--spec/ruby/core/array/shared/difference.rb8
-rw-r--r--spec/ruby/core/array/shared/enumeratorize.rb2
-rw-r--r--spec/ruby/core/array/shared/eql.rb66
-rw-r--r--spec/ruby/core/array/shared/index.rb37
-rw-r--r--spec/ruby/core/array/shared/inspect.rb133
-rw-r--r--spec/ruby/core/array/shared/intersection.rb9
-rw-r--r--spec/ruby/core/array/shared/iterable_and_tolerating_size_increasing.rb25
-rw-r--r--spec/ruby/core/array/shared/join.rb166
-rw-r--r--spec/ruby/core/array/shared/keep_if.rb49
-rw-r--r--spec/ruby/core/array/shared/length.rb11
-rw-r--r--spec/ruby/core/array/shared/push.rb33
-rw-r--r--spec/ruby/core/array/shared/replace.rb60
-rw-r--r--spec/ruby/core/array/shared/select.rb32
-rw-r--r--spec/ruby/core/array/shared/slice.rb495
-rw-r--r--spec/ruby/core/array/shared/union.rb8
-rw-r--r--spec/ruby/core/array/shared/unshift.rb46
-rw-r--r--spec/ruby/core/array/shift_spec.rb36
-rw-r--r--spec/ruby/core/array/shuffle_spec.rb51
-rw-r--r--spec/ruby/core/array/size_spec.rb6
-rw-r--r--spec/ruby/core/array/slice_spec.rb73
-rw-r--r--spec/ruby/core/array/sort_by_spec.rb47
-rw-r--r--spec/ruby/core/array/sort_spec.rb48
-rw-r--r--spec/ruby/core/array/sum_spec.rb50
-rw-r--r--spec/ruby/core/array/take_spec.rb7
-rw-r--r--spec/ruby/core/array/take_while_spec.rb11
-rw-r--r--spec/ruby/core/array/to_a_spec.rb4
-rw-r--r--spec/ruby/core/array/to_ary_spec.rb4
-rw-r--r--spec/ruby/core/array/to_h_spec.rb92
-rw-r--r--spec/ruby/core/array/to_s_spec.rb7
-rw-r--r--spec/ruby/core/array/transpose_spec.rb10
-rw-r--r--spec/ruby/core/array/try_convert_spec.rb16
-rw-r--r--spec/ruby/core/array/union_spec.rb26
-rw-r--r--spec/ruby/core/array/uniq_spec.rb124
-rw-r--r--spec/ruby/core/array/unshift_spec.rb64
-rw-r--r--spec/ruby/core/array/values_at_spec.rb16
-rw-r--r--spec/ruby/core/array/zip_spec.rb8
-rw-r--r--spec/ruby/core/basicobject/__send___spec.rb2
-rw-r--r--spec/ruby/core/basicobject/basicobject_spec.rb20
-rw-r--r--spec/ruby/core/basicobject/equal_spec.rb20
-rw-r--r--spec/ruby/core/basicobject/equal_value_spec.rb2
-rw-r--r--spec/ruby/core/basicobject/fixtures/classes.rb228
-rw-r--r--spec/ruby/core/basicobject/initialize_spec.rb4
-rw-r--r--spec/ruby/core/basicobject/instance_eval_spec.rb203
-rw-r--r--spec/ruby/core/basicobject/instance_exec_spec.rb48
-rw-r--r--spec/ruby/core/basicobject/method_missing_spec.rb3
-rw-r--r--spec/ruby/core/basicobject/not_equal_spec.rb16
-rw-r--r--spec/ruby/core/basicobject/not_spec.rb4
-rw-r--r--spec/ruby/core/basicobject/singleton_method_added_spec.rb65
-rw-r--r--spec/ruby/core/basicobject/singleton_method_removed_spec.rb2
-rw-r--r--spec/ruby/core/basicobject/singleton_method_undefined_spec.rb2
-rw-r--r--spec/ruby/core/binding/clone_spec.rb6
-rw-r--r--spec/ruby/core/binding/dup_spec.rb23
-rw-r--r--spec/ruby/core/binding/eval_spec.rb91
-rw-r--r--spec/ruby/core/binding/fixtures/irb.rb3
-rw-r--r--spec/ruby/core/binding/fixtures/irbrc1
-rw-r--r--spec/ruby/core/binding/irb_spec.rb16
-rw-r--r--spec/ruby/core/binding/local_variable_get_spec.rb10
-rw-r--r--spec/ruby/core/binding/local_variable_set_spec.rb8
-rw-r--r--spec/ruby/core/binding/local_variables_spec.rb2
-rw-r--r--spec/ruby/core/binding/shared/clone.rb22
-rw-r--r--spec/ruby/core/binding/source_location_spec.rb15
-rw-r--r--spec/ruby/core/builtin_constants/builtin_constants_spec.rb122
-rw-r--r--spec/ruby/core/class/allocate_spec.rb6
-rw-r--r--spec/ruby/core/class/attached_object_spec.rb29
-rw-r--r--spec/ruby/core/class/dup_spec.rb7
-rw-r--r--spec/ruby/core/class/inherited_spec.rb21
-rw-r--r--spec/ruby/core/class/initialize_spec.rb12
-rw-r--r--spec/ruby/core/class/new_spec.rb25
-rw-r--r--spec/ruby/core/class/subclasses_spec.rb85
-rw-r--r--spec/ruby/core/class/superclass_spec.rb4
-rw-r--r--spec/ruby/core/comparable/clamp_spec.rb201
-rw-r--r--spec/ruby/core/comparable/equal_value_spec.rb10
-rw-r--r--spec/ruby/core/comparable/fixtures/classes.rb1
-rw-r--r--spec/ruby/core/comparable/gt_spec.rb2
-rw-r--r--spec/ruby/core/comparable/gte_spec.rb2
-rw-r--r--spec/ruby/core/comparable/lt_spec.rb6
-rw-r--r--spec/ruby/core/comparable/lte_spec.rb2
-rw-r--r--spec/ruby/core/complex/abs_spec.rb10
-rw-r--r--spec/ruby/core/complex/angle_spec.rb5
-rw-r--r--spec/ruby/core/complex/arg_spec.rb9
-rw-r--r--spec/ruby/core/complex/coerce_spec.rb32
-rw-r--r--spec/ruby/core/complex/comparision_spec.rb27
-rw-r--r--spec/ruby/core/complex/comparison_spec.rb25
-rw-r--r--spec/ruby/core/complex/conj_spec.rb5
-rw-r--r--spec/ruby/core/complex/conjugate_spec.rb8
-rw-r--r--spec/ruby/core/complex/constants_spec.rb2
-rw-r--r--spec/ruby/core/complex/divide_spec.rb82
-rw-r--r--spec/ruby/core/complex/eql_spec.rb12
-rw-r--r--spec/ruby/core/complex/equal_value_spec.rb12
-rw-r--r--spec/ruby/core/complex/exponent_spec.rb6
-rw-r--r--spec/ruby/core/complex/fdiv_spec.rb42
-rw-r--r--spec/ruby/core/complex/imag_spec.rb5
-rw-r--r--spec/ruby/core/complex/imaginary_spec.rb8
-rw-r--r--spec/ruby/core/complex/inspect_spec.rb21
-rw-r--r--spec/ruby/core/complex/integer_spec.rb4
-rw-r--r--spec/ruby/core/complex/magnitude_spec.rb5
-rw-r--r--spec/ruby/core/complex/marshal_dump_spec.rb2
-rw-r--r--spec/ruby/core/complex/negative_spec.rb4
-rw-r--r--spec/ruby/core/complex/phase_spec.rb5
-rw-r--r--spec/ruby/core/complex/polar_spec.rb18
-rw-r--r--spec/ruby/core/complex/positive_spec.rb4
-rw-r--r--spec/ruby/core/complex/quo_spec.rb5
-rw-r--r--spec/ruby/core/complex/rationalize_spec.rb8
-rw-r--r--spec/ruby/core/complex/real_spec.rb8
-rw-r--r--spec/ruby/core/complex/rect_spec.rb9
-rw-r--r--spec/ruby/core/complex/rectangular_spec.rb110
-rw-r--r--spec/ruby/core/complex/shared/abs.rb10
-rw-r--r--spec/ruby/core/complex/shared/arg.rb9
-rw-r--r--spec/ruby/core/complex/shared/conjugate.rb8
-rw-r--r--spec/ruby/core/complex/shared/divide.rb82
-rw-r--r--spec/ruby/core/complex/shared/image.rb8
-rw-r--r--spec/ruby/core/complex/shared/rect.rb94
-rw-r--r--spec/ruby/core/complex/to_c_spec.rb12
-rw-r--r--spec/ruby/core/complex/to_f_spec.rb6
-rw-r--r--spec/ruby/core/complex/to_i_spec.rb6
-rw-r--r--spec/ruby/core/complex/to_r_spec.rb16
-rw-r--r--spec/ruby/core/complex/to_s_spec.rb11
-rw-r--r--spec/ruby/core/conditionvariable/broadcast_spec.rb39
-rw-r--r--spec/ruby/core/conditionvariable/marshal_dump_spec.rb8
-rw-r--r--spec/ruby/core/conditionvariable/signal_spec.rb76
-rw-r--r--spec/ruby/core/conditionvariable/wait_spec.rb174
-rw-r--r--spec/ruby/core/data/constants_spec.rb10
-rw-r--r--spec/ruby/core/data/deconstruct_keys_spec.rb110
-rw-r--r--spec/ruby/core/data/deconstruct_spec.rb8
-rw-r--r--spec/ruby/core/data/define_spec.rb34
-rw-r--r--spec/ruby/core/data/eql_spec.rb63
-rw-r--r--spec/ruby/core/data/equal_value_spec.rb63
-rw-r--r--spec/ruby/core/data/fixtures/classes.rb41
-rw-r--r--spec/ruby/core/data/hash_spec.rb25
-rw-r--r--spec/ruby/core/data/initialize_spec.rb204
-rw-r--r--spec/ruby/core/data/inspect_spec.rb63
-rw-r--r--spec/ruby/core/data/members_spec.rb21
-rw-r--r--spec/ruby/core/data/to_h_spec.rb63
-rw-r--r--spec/ruby/core/data/to_s_spec.rb9
-rw-r--r--spec/ruby/core/data/with_spec.rb55
-rw-r--r--spec/ruby/core/dir/chdir_spec.rb126
-rw-r--r--spec/ruby/core/dir/children_spec.rb125
-rw-r--r--spec/ruby/core/dir/chroot_spec.rb6
-rw-r--r--spec/ruby/core/dir/close_spec.rb42
-rw-r--r--spec/ruby/core/dir/each_child_spec.rb94
-rw-r--r--spec/ruby/core/dir/each_spec.rb15
-rw-r--r--spec/ruby/core/dir/empty_spec.rb8
-rw-r--r--spec/ruby/core/dir/entries_spec.rb16
-rw-r--r--spec/ruby/core/dir/exist_spec.rb63
-rw-r--r--spec/ruby/core/dir/fchdir_spec.rb71
-rw-r--r--spec/ruby/core/dir/fileno_spec.rb4
-rw-r--r--spec/ruby/core/dir/fixtures/common.rb66
-rw-r--r--spec/ruby/core/dir/for_fd_spec.rb77
-rw-r--r--spec/ruby/core/dir/foreach_spec.rb18
-rw-r--r--spec/ruby/core/dir/getwd_spec.rb12
-rw-r--r--spec/ruby/core/dir/glob_spec.rb200
-rw-r--r--spec/ruby/core/dir/home_spec.rb54
-rw-r--r--spec/ruby/core/dir/inspect_spec.rb4
-rw-r--r--spec/ruby/core/dir/mkdir_spec.rb26
-rw-r--r--spec/ruby/core/dir/open_spec.rb73
-rw-r--r--spec/ruby/core/dir/path_spec.rb26
-rw-r--r--spec/ruby/core/dir/pos_spec.rb23
-rw-r--r--spec/ruby/core/dir/pwd_spec.rb45
-rw-r--r--spec/ruby/core/dir/read_spec.rb35
-rw-r--r--spec/ruby/core/dir/scan_spec.rb224
-rw-r--r--spec/ruby/core/dir/shared/chroot.rb15
-rw-r--r--spec/ruby/core/dir/shared/closed.rb2
-rw-r--r--spec/ruby/core/dir/shared/delete.rb24
-rw-r--r--spec/ruby/core/dir/shared/exist.rb56
-rw-r--r--spec/ruby/core/dir/shared/glob.rb80
-rw-r--r--spec/ruby/core/dir/shared/open.rb73
-rw-r--r--spec/ruby/core/dir/shared/path.rb30
-rw-r--r--spec/ruby/core/dir/shared/pos.rb27
-rw-r--r--spec/ruby/core/dir/shared/pwd.rb45
-rw-r--r--spec/ruby/core/dir/tell_spec.rb27
-rw-r--r--spec/ruby/core/dir/to_path_spec.rb12
-rw-r--r--spec/ruby/core/encoding/aliases_spec.rb10
-rw-r--r--spec/ruby/core/encoding/ascii_compatible_spec.rb15
-rw-r--r--spec/ruby/core/encoding/compatible_spec.rb499
-rw-r--r--spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb6
-rw-r--r--spec/ruby/core/encoding/converter/constants_spec.rb78
-rw-r--r--spec/ruby/core/encoding/converter/convert_spec.rb22
-rw-r--r--spec/ruby/core/encoding/converter/finish_spec.rb6
-rw-r--r--spec/ruby/core/encoding/converter/last_error_spec.rb50
-rw-r--r--spec/ruby/core/encoding/converter/new_spec.rb18
-rw-r--r--spec/ruby/core/encoding/converter/primitive_convert_spec.rb31
-rw-r--r--spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb7
-rw-r--r--spec/ruby/core/encoding/converter/putback_spec.rb23
-rw-r--r--spec/ruby/core/encoding/converter/replacement_spec.rb28
-rw-r--r--spec/ruby/core/encoding/converter/search_convpath_spec.rb6
-rw-r--r--spec/ruby/core/encoding/default_external_spec.rb12
-rw-r--r--spec/ruby/core/encoding/default_internal_spec.rb12
-rw-r--r--spec/ruby/core/encoding/dummy_spec.rb21
-rw-r--r--spec/ruby/core/encoding/find_spec.rb8
-rw-r--r--spec/ruby/core/encoding/fixtures/classes.rb2
-rw-r--r--spec/ruby/core/encoding/inspect_spec.rb22
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb5
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb5
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb7
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb12
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb11
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb3
-rw-r--r--spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb5
-rw-r--r--spec/ruby/core/encoding/list_spec.rb22
-rw-r--r--spec/ruby/core/encoding/locale_charmap_spec.rb76
-rw-r--r--spec/ruby/core/encoding/name_list_spec.rb8
-rw-r--r--spec/ruby/core/encoding/name_spec.rb14
-rw-r--r--spec/ruby/core/encoding/names_spec.rb6
-rw-r--r--spec/ruby/core/encoding/replicate_spec.rb44
-rw-r--r--spec/ruby/core/encoding/shared/name.rb15
-rw-r--r--spec/ruby/core/encoding/to_s_spec.rb6
-rw-r--r--spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb3
-rw-r--r--spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_spec.rb3
-rw-r--r--spec/ruby/core/encoding/undefined_conversion_error/error_char_spec.rb5
-rw-r--r--spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb3
-rw-r--r--spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb5
-rw-r--r--spec/ruby/core/enumerable/all_spec.rb35
-rw-r--r--spec/ruby/core/enumerable/any_spec.rb35
-rw-r--r--spec/ruby/core/enumerable/chain_spec.rb30
-rw-r--r--spec/ruby/core/enumerable/chunk_spec.rb18
-rw-r--r--spec/ruby/core/enumerable/chunk_while_spec.rb4
-rw-r--r--spec/ruby/core/enumerable/collect_concat_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/collect_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/compact_spec.rb9
-rw-r--r--spec/ruby/core/enumerable/cycle_spec.rb10
-rw-r--r--spec/ruby/core/enumerable/detect_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/drop_spec.rb10
-rw-r--r--spec/ruby/core/enumerable/drop_while_spec.rb4
-rw-r--r--spec/ruby/core/enumerable/each_cons_spec.rb26
-rw-r--r--spec/ruby/core/enumerable/each_entry_spec.rb8
-rw-r--r--spec/ruby/core/enumerable/each_slice_spec.rb28
-rw-r--r--spec/ruby/core/enumerable/each_with_index_spec.rb6
-rw-r--r--spec/ruby/core/enumerable/each_with_object_spec.rb8
-rw-r--r--spec/ruby/core/enumerable/entries_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/filter_map_spec.rb34
-rw-r--r--spec/ruby/core/enumerable/filter_spec.rb6
-rw-r--r--spec/ruby/core/enumerable/find_all_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/find_index_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/find_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/first_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/fixtures/classes.rb7
-rw-r--r--spec/ruby/core/enumerable/flat_map_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/grep_spec.rb25
-rw-r--r--spec/ruby/core/enumerable/grep_v_spec.rb27
-rw-r--r--spec/ruby/core/enumerable/group_by_spec.rb18
-rw-r--r--spec/ruby/core/enumerable/lazy_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/map_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/max_by_spec.rb8
-rw-r--r--spec/ruby/core/enumerable/max_spec.rb8
-rw-r--r--spec/ruby/core/enumerable/min_by_spec.rb8
-rw-r--r--spec/ruby/core/enumerable/min_spec.rb10
-rw-r--r--spec/ruby/core/enumerable/minmax_by_spec.rb6
-rw-r--r--spec/ruby/core/enumerable/minmax_spec.rb34
-rw-r--r--spec/ruby/core/enumerable/none_spec.rb43
-rw-r--r--spec/ruby/core/enumerable/one_spec.rb48
-rw-r--r--spec/ruby/core/enumerable/partition_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/reject_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/reverse_each_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/select_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/shared/collect.rb43
-rw-r--r--spec/ruby/core/enumerable/shared/collect_concat.rb4
-rw-r--r--spec/ruby/core/enumerable/shared/entries.rb10
-rw-r--r--spec/ruby/core/enumerable/shared/find.rb2
-rw-r--r--spec/ruby/core/enumerable/shared/find_all.rb2
-rw-r--r--spec/ruby/core/enumerable/shared/include.rb2
-rw-r--r--spec/ruby/core/enumerable/shared/inject.rb87
-rw-r--r--spec/ruby/core/enumerable/shared/take.rb8
-rw-r--r--spec/ruby/core/enumerable/shared/value_packing.rb26
-rw-r--r--spec/ruby/core/enumerable/slice_after_spec.rb10
-rw-r--r--spec/ruby/core/enumerable/slice_before_spec.rb10
-rw-r--r--spec/ruby/core/enumerable/slice_when_spec.rb4
-rw-r--r--spec/ruby/core/enumerable/sort_by_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/sort_spec.rb8
-rw-r--r--spec/ruby/core/enumerable/sum_spec.rb26
-rw-r--r--spec/ruby/core/enumerable/take_spec.rb10
-rw-r--r--spec/ruby/core/enumerable/take_while_spec.rb4
-rw-r--r--spec/ruby/core/enumerable/tally_spec.rb116
-rw-r--r--spec/ruby/core/enumerable/to_a_spec.rb2
-rw-r--r--spec/ruby/core/enumerable/to_h_spec.rb92
-rw-r--r--spec/ruby/core/enumerable/to_set_spec.rb30
-rw-r--r--spec/ruby/core/enumerable/uniq_spec.rb76
-rw-r--r--spec/ruby/core/enumerable/zip_spec.rb5
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/begin_spec.rb17
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/each_spec.rb24
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/end_spec.rb17
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/eq_spec.rb24
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/exclude_end_spec.rb22
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/first_spec.rb12
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/hash_spec.rb26
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/inspect_spec.rb26
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/last_spec.rb12
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/new_spec.rb24
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/size_spec.rb22
-rw-r--r--spec/ruby/core/enumerator/arithmetic_sequence/step_spec.rb16
-rw-r--r--spec/ruby/core/enumerator/chain/each_spec.rb18
-rw-r--r--spec/ruby/core/enumerator/chain/initialize_spec.rb44
-rw-r--r--spec/ruby/core/enumerator/chain/inspect_spec.rb24
-rw-r--r--spec/ruby/core/enumerator/chain/rewind_spec.rb96
-rw-r--r--spec/ruby/core/enumerator/chain/size_spec.rb30
-rw-r--r--spec/ruby/core/enumerator/each_spec.rb47
-rw-r--r--spec/ruby/core/enumerator/each_with_index_spec.rb12
-rw-r--r--spec/ruby/core/enumerator/each_with_object_spec.rb2
-rw-r--r--spec/ruby/core/enumerator/enum_for_spec.rb2
-rw-r--r--spec/ruby/core/enumerator/feed_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/fixtures/classes.rb (renamed from spec/ruby/fixtures/enumerator/classes.rb)0
-rw-r--r--spec/ruby/core/enumerator/generator/each_spec.rb40
-rw-r--r--spec/ruby/core/enumerator/generator/initialize_spec.rb26
-rw-r--r--spec/ruby/core/enumerator/initialize_spec.rb22
-rw-r--r--spec/ruby/core/enumerator/inspect_spec.rb5
-rw-r--r--spec/ruby/core/enumerator/lazy/chunk_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/lazy/chunk_while_spec.rb5
-rw-r--r--spec/ruby/core/enumerator/lazy/compact_spec.rb14
-rw-r--r--spec/ruby/core/enumerator/lazy/drop_spec.rb4
-rw-r--r--spec/ruby/core/enumerator/lazy/drop_while_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/lazy/eager_spec.rb38
-rw-r--r--spec/ruby/core/enumerator/lazy/filter_map_spec.rb14
-rw-r--r--spec/ruby/core/enumerator/lazy/filter_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/lazy/grep_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/lazy/grep_v_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/lazy/initialize_spec.rb18
-rw-r--r--spec/ruby/core/enumerator/lazy/lazy_spec.rb13
-rw-r--r--spec/ruby/core/enumerator/lazy/reject_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/lazy/shared/collect.rb4
-rw-r--r--spec/ruby/core/enumerator/lazy/shared/collect_concat.rb10
-rw-r--r--spec/ruby/core/enumerator/lazy/shared/select.rb6
-rw-r--r--spec/ruby/core/enumerator/lazy/shared/to_enum.rb6
-rw-r--r--spec/ruby/core/enumerator/lazy/slice_after_spec.rb5
-rw-r--r--spec/ruby/core/enumerator/lazy/slice_before_spec.rb5
-rw-r--r--spec/ruby/core/enumerator/lazy/slice_when_spec.rb5
-rw-r--r--spec/ruby/core/enumerator/lazy/take_spec.rb12
-rw-r--r--spec/ruby/core/enumerator/lazy/take_while_spec.rb6
-rw-r--r--spec/ruby/core/enumerator/lazy/uniq_spec.rb4
-rw-r--r--spec/ruby/core/enumerator/lazy/with_index_spec.rb36
-rw-r--r--spec/ruby/core/enumerator/lazy/zip_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/new_spec.rb109
-rw-r--r--spec/ruby/core/enumerator/next_spec.rb8
-rw-r--r--spec/ruby/core/enumerator/next_values_spec.rb10
-rw-r--r--spec/ruby/core/enumerator/peek_spec.rb2
-rw-r--r--spec/ruby/core/enumerator/peek_values_spec.rb10
-rw-r--r--spec/ruby/core/enumerator/plus_spec.rb46
-rw-r--r--spec/ruby/core/enumerator/produce_spec.rb84
-rw-r--r--spec/ruby/core/enumerator/product/each_spec.rb85
-rw-r--r--spec/ruby/core/enumerator/product/initialize_copy_spec.rb52
-rw-r--r--spec/ruby/core/enumerator/product/initialize_spec.rb31
-rw-r--r--spec/ruby/core/enumerator/product/inspect_spec.rb20
-rw-r--r--spec/ruby/core/enumerator/product/rewind_spec.rb62
-rw-r--r--spec/ruby/core/enumerator/product/size_spec.rb64
-rw-r--r--spec/ruby/core/enumerator/product_spec.rb91
-rw-r--r--spec/ruby/core/enumerator/rewind_spec.rb4
-rw-r--r--spec/ruby/core/enumerator/shared/enum_for.rb57
-rw-r--r--spec/ruby/core/enumerator/shared/with_index.rb33
-rw-r--r--spec/ruby/core/enumerator/shared/with_object.rb42
-rw-r--r--spec/ruby/core/enumerator/size_spec.rb2
-rw-r--r--spec/ruby/core/enumerator/to_enum_spec.rb4
-rw-r--r--spec/ruby/core/enumerator/with_index_spec.rb27
-rw-r--r--spec/ruby/core/enumerator/with_object_spec.rb2
-rw-r--r--spec/ruby/core/enumerator/yielder/append_spec.rb47
-rw-r--r--spec/ruby/core/enumerator/yielder/initialize_spec.rb18
-rw-r--r--spec/ruby/core/enumerator/yielder/to_proc_spec.rb18
-rw-r--r--spec/ruby/core/enumerator/yielder/yield_spec.rb33
-rw-r--r--spec/ruby/core/env/assoc_spec.rb2
-rw-r--r--spec/ruby/core/env/clear_spec.rb2
-rw-r--r--spec/ruby/core/env/clone_spec.rb21
-rw-r--r--spec/ruby/core/env/delete_if_spec.rb10
-rw-r--r--spec/ruby/core/env/delete_spec.rb18
-rw-r--r--spec/ruby/core/env/dup_spec.rb9
-rw-r--r--spec/ruby/core/env/each_key_spec.rb8
-rw-r--r--spec/ruby/core/env/each_value_spec.rb8
-rw-r--r--spec/ruby/core/env/element_reference_spec.rb9
-rw-r--r--spec/ruby/core/env/except_spec.rb34
-rw-r--r--spec/ruby/core/env/fetch_spec.rb9
-rw-r--r--spec/ruby/core/env/fetch_values_spec.rb51
-rw-r--r--spec/ruby/core/env/filter_spec.rb16
-rw-r--r--spec/ruby/core/env/fixtures/common.rb9
-rw-r--r--spec/ruby/core/env/index_spec.rb12
-rw-r--r--spec/ruby/core/env/indexes_spec.rb1
-rw-r--r--spec/ruby/core/env/indices_spec.rb1
-rw-r--r--spec/ruby/core/env/inspect_spec.rb2
-rw-r--r--spec/ruby/core/env/keep_if_spec.rb10
-rw-r--r--spec/ruby/core/env/key_spec.rb32
-rw-r--r--spec/ruby/core/env/length_spec.rb2
-rw-r--r--spec/ruby/core/env/merge_spec.rb6
-rw-r--r--spec/ruby/core/env/rassoc_spec.rb2
-rw-r--r--spec/ruby/core/env/reject_spec.rb14
-rw-r--r--spec/ruby/core/env/replace_spec.rb16
-rw-r--r--spec/ruby/core/env/shared/each.rb12
-rw-r--r--spec/ruby/core/env/shared/include.rb9
-rw-r--r--spec/ruby/core/env/shared/key.rb23
-rw-r--r--spec/ruby/core/env/shared/select.rb4
-rw-r--r--spec/ruby/core/env/shared/store.rb14
-rw-r--r--spec/ruby/core/env/shared/to_hash.rb4
-rw-r--r--spec/ruby/core/env/shared/update.rb50
-rw-r--r--spec/ruby/core/env/shared/value.rb7
-rw-r--r--spec/ruby/core/env/shift_spec.rb46
-rw-r--r--spec/ruby/core/env/size_spec.rb2
-rw-r--r--spec/ruby/core/env/slice_spec.rb50
-rw-r--r--spec/ruby/core/env/to_a_spec.rb5
-rw-r--r--spec/ruby/core/env/to_h_spec.rb112
-rw-r--r--spec/ruby/core/env/values_at_spec.rb5
-rw-r--r--spec/ruby/core/exception/backtrace_locations_spec.rb6
-rw-r--r--spec/ruby/core/exception/backtrace_spec.rb37
-rw-r--r--spec/ruby/core/exception/case_compare_spec.rb2
-rw-r--r--spec/ruby/core/exception/cause_spec.rb54
-rw-r--r--spec/ruby/core/exception/detailed_message_spec.rb50
-rw-r--r--spec/ruby/core/exception/dup_spec.rb14
-rw-r--r--spec/ruby/core/exception/equal_value_spec.rb18
-rw-r--r--spec/ruby/core/exception/errno_spec.rb29
-rw-r--r--spec/ruby/core/exception/exception_spec.rb4
-rw-r--r--spec/ruby/core/exception/exit_value_spec.rb2
-rw-r--r--spec/ruby/core/exception/fixtures/common.rb7
-rw-r--r--spec/ruby/core/exception/fixtures/syntax_error.rb3
-rw-r--r--spec/ruby/core/exception/fixtures/thread_fiber_ensure.rb22
-rw-r--r--spec/ruby/core/exception/fixtures/thread_fiber_ensure_non_root_fiber.rb25
-rw-r--r--spec/ruby/core/exception/frozen_error_spec.rb60
-rw-r--r--spec/ruby/core/exception/full_message_spec.rb246
-rw-r--r--spec/ruby/core/exception/interrupt_spec.rb27
-rw-r--r--spec/ruby/core/exception/io_error_spec.rb16
-rw-r--r--spec/ruby/core/exception/key_error_spec.rb22
-rw-r--r--spec/ruby/core/exception/name_error_spec.rb12
-rw-r--r--spec/ruby/core/exception/name_spec.rb12
-rw-r--r--spec/ruby/core/exception/no_method_error_spec.rb178
-rw-r--r--spec/ruby/core/exception/reason_spec.rb2
-rw-r--r--spec/ruby/core/exception/receiver_spec.rb16
-rw-r--r--spec/ruby/core/exception/result_spec.rb8
-rw-r--r--spec/ruby/core/exception/set_backtrace_spec.rb51
-rw-r--r--spec/ruby/core/exception/shared/new.rb4
-rw-r--r--spec/ruby/core/exception/shared/set_backtrace.rb64
-rw-r--r--spec/ruby/core/exception/signal_exception_spec.rb20
-rw-r--r--spec/ruby/core/exception/signm_spec.rb2
-rw-r--r--spec/ruby/core/exception/signo_spec.rb2
-rw-r--r--spec/ruby/core/exception/standard_error_spec.rb2
-rw-r--r--spec/ruby/core/exception/status_spec.rb2
-rw-r--r--spec/ruby/core/exception/success_spec.rb4
-rw-r--r--spec/ruby/core/exception/syntax_error_spec.rb25
-rw-r--r--spec/ruby/core/exception/system_call_error_spec.rb62
-rw-r--r--spec/ruby/core/exception/system_exit_spec.rb59
-rw-r--r--spec/ruby/core/exception/to_s_spec.rb2
-rw-r--r--spec/ruby/core/exception/top_level_spec.rb47
-rw-r--r--spec/ruby/core/false/case_compare_spec.rb14
-rw-r--r--spec/ruby/core/false/dup_spec.rb2
-rw-r--r--spec/ruby/core/false/falseclass_spec.rb4
-rw-r--r--spec/ruby/core/false/singleton_method_spec.rb13
-rw-r--r--spec/ruby/core/false/to_s_spec.rb12
-rw-r--r--spec/ruby/core/fiber/alive_spec.rb44
-rw-r--r--spec/ruby/core/fiber/blocking_spec.rb73
-rw-r--r--spec/ruby/core/fiber/current_spec.rb50
-rw-r--r--spec/ruby/core/fiber/fixtures/classes.rb22
-rw-r--r--spec/ruby/core/fiber/fixtures/scheduler.rb35
-rw-r--r--spec/ruby/core/fiber/inspect_spec.rb35
-rw-r--r--spec/ruby/core/fiber/kill_spec.rb88
-rw-r--r--spec/ruby/core/fiber/new_spec.rb8
-rw-r--r--spec/ruby/core/fiber/raise_spec.rb141
-rw-r--r--spec/ruby/core/fiber/resume_spec.rb39
-rw-r--r--spec/ruby/core/fiber/scheduler_spec.rb8
-rw-r--r--spec/ruby/core/fiber/set_scheduler_spec.rb8
-rw-r--r--spec/ruby/core/fiber/shared/blocking.rb41
-rw-r--r--spec/ruby/core/fiber/shared/resume.rb58
-rw-r--r--spec/ruby/core/fiber/shared/scheduler.rb51
-rw-r--r--spec/ruby/core/fiber/storage_spec.rb177
-rw-r--r--spec/ruby/core/fiber/transfer_spec.rb84
-rw-r--r--spec/ruby/core/fiber/yield_spec.rb4
-rw-r--r--spec/ruby/core/file/absolute_path_spec.rb82
-rw-r--r--spec/ruby/core/file/atime_spec.rb29
-rw-r--r--spec/ruby/core/file/basename_spec.rb49
-rw-r--r--spec/ruby/core/file/birthtime_spec.rb68
-rw-r--r--spec/ruby/core/file/chmod_spec.rb12
-rw-r--r--spec/ruby/core/file/chown_spec.rb18
-rw-r--r--spec/ruby/core/file/constants/constants_spec.rb8
-rw-r--r--spec/ruby/core/file/ctime_spec.rb11
-rw-r--r--spec/ruby/core/file/dirname_spec.rb98
-rw-r--r--spec/ruby/core/file/empty_spec.rb6
-rw-r--r--spec/ruby/core/file/exist_spec.rb6
-rw-r--r--spec/ruby/core/file/expand_path_spec.rb22
-rw-r--r--spec/ruby/core/file/extname_spec.rb26
-rw-r--r--spec/ruby/core/file/flock_spec.rb36
-rw-r--r--spec/ruby/core/file/ftype_spec.rb8
-rw-r--r--spec/ruby/core/file/inspect_spec.rb2
-rw-r--r--spec/ruby/core/file/join_spec.rb14
-rw-r--r--spec/ruby/core/file/lchmod_spec.rb20
-rw-r--r--spec/ruby/core/file/link_spec.rb14
-rw-r--r--spec/ruby/core/file/lutime_spec.rb9
-rw-r--r--spec/ruby/core/file/mkfifo_spec.rb4
-rw-r--r--spec/ruby/core/file/mtime_spec.rb27
-rw-r--r--spec/ruby/core/file/new_spec.rb99
-rw-r--r--spec/ruby/core/file/open_spec.rb183
-rw-r--r--spec/ruby/core/file/path_spec.rb41
-rw-r--r--spec/ruby/core/file/readlink_spec.rb6
-rw-r--r--spec/ruby/core/file/realdirpath_spec.rb6
-rw-r--r--spec/ruby/core/file/realpath_spec.rb10
-rw-r--r--spec/ruby/core/file/rename_spec.rb8
-rw-r--r--spec/ruby/core/file/setuid_spec.rb4
-rw-r--r--spec/ruby/core/file/shared/fnmatch.rb91
-rw-r--r--spec/ruby/core/file/shared/open.rb2
-rw-r--r--spec/ruby/core/file/shared/path.rb33
-rw-r--r--spec/ruby/core/file/shared/read.rb8
-rw-r--r--spec/ruby/core/file/shared/stat.rb6
-rw-r--r--spec/ruby/core/file/shared/unlink.rb4
-rw-r--r--spec/ruby/core/file/shared/update_time.rb105
-rw-r--r--spec/ruby/core/file/size_spec.rb8
-rw-r--r--spec/ruby/core/file/socket_spec.rb32
-rw-r--r--spec/ruby/core/file/split_spec.rb6
-rw-r--r--spec/ruby/core/file/stat/atime_spec.rb2
-rw-r--r--spec/ruby/core/file/stat/birthtime_spec.rb36
-rw-r--r--spec/ruby/core/file/stat/blocks_spec.rb2
-rw-r--r--spec/ruby/core/file/stat/ctime_spec.rb2
-rw-r--r--spec/ruby/core/file/stat/dev_major_spec.rb4
-rw-r--r--spec/ruby/core/file/stat/dev_minor_spec.rb4
-rw-r--r--spec/ruby/core/file/stat/dev_spec.rb2
-rw-r--r--spec/ruby/core/file/stat/ftype_spec.rb2
-rw-r--r--spec/ruby/core/file/stat/ino_spec.rb4
-rw-r--r--spec/ruby/core/file/stat/mtime_spec.rb2
-rw-r--r--spec/ruby/core/file/stat/new_spec.rb4
-rw-r--r--spec/ruby/core/file/stat/nlink_spec.rb2
-rw-r--r--spec/ruby/core/file/stat/rdev_major_spec.rb17
-rw-r--r--spec/ruby/core/file/stat/rdev_minor_spec.rb17
-rw-r--r--spec/ruby/core/file/stat/rdev_spec.rb2
-rw-r--r--spec/ruby/core/file/stat_spec.rb12
-rw-r--r--spec/ruby/core/file/symlink_spec.rb12
-rw-r--r--spec/ruby/core/file/truncate_spec.rb24
-rw-r--r--spec/ruby/core/file/umask_spec.rb10
-rw-r--r--spec/ruby/core/file/utime_spec.rb83
-rw-r--r--spec/ruby/core/file/world_readable_spec.rb2
-rw-r--r--spec/ruby/core/file/world_writable_spec.rb2
-rw-r--r--spec/ruby/core/file/zero_spec.rb6
-rw-r--r--spec/ruby/core/filetest/exist_spec.rb6
-rw-r--r--spec/ruby/core/filetest/grpowned_spec.rb2
-rw-r--r--spec/ruby/core/filetest/socket_spec.rb4
-rw-r--r--spec/ruby/core/filetest/zero_spec.rb6
-rw-r--r--spec/ruby/core/float/ceil_spec.rb31
-rw-r--r--spec/ruby/core/float/coerce_spec.rb4
-rw-r--r--spec/ruby/core/float/comparison_spec.rb75
-rw-r--r--spec/ruby/core/float/constants_spec.rb2
-rw-r--r--spec/ruby/core/float/denominator_spec.rb2
-rw-r--r--spec/ruby/core/float/divide_spec.rb24
-rw-r--r--spec/ruby/core/float/divmod_spec.rb24
-rw-r--r--spec/ruby/core/float/dup_spec.rb2
-rw-r--r--spec/ruby/core/float/eql_spec.rb8
-rw-r--r--spec/ruby/core/float/float_spec.rb4
-rw-r--r--spec/ruby/core/float/floor_spec.rb31
-rw-r--r--spec/ruby/core/float/gt_spec.rb25
-rw-r--r--spec/ruby/core/float/gte_spec.rb25
-rw-r--r--spec/ruby/core/float/inspect_spec.rb6
-rw-r--r--spec/ruby/core/float/lt_spec.rb25
-rw-r--r--spec/ruby/core/float/lte_spec.rb25
-rw-r--r--spec/ruby/core/float/magnitude_spec.rb1
-rw-r--r--spec/ruby/core/float/minus_spec.rb2
-rw-r--r--spec/ruby/core/float/multiply_spec.rb6
-rw-r--r--spec/ruby/core/float/negative_spec.rb33
-rw-r--r--spec/ruby/core/float/next_float_spec.rb2
-rw-r--r--spec/ruby/core/float/numerator_spec.rb4
-rw-r--r--spec/ruby/core/float/plus_spec.rb2
-rw-r--r--spec/ruby/core/float/positive_spec.rb33
-rw-r--r--spec/ruby/core/float/prev_float_spec.rb2
-rw-r--r--spec/ruby/core/float/rationalize_spec.rb8
-rw-r--r--spec/ruby/core/float/round_spec.rb185
-rw-r--r--spec/ruby/core/float/shared/abs.rb2
-rw-r--r--spec/ruby/core/float/shared/arg.rb4
-rw-r--r--spec/ruby/core/float/shared/arithmetic_exception_in_coerce.rb2
-rw-r--r--spec/ruby/core/float/shared/comparison_exception_in_coerce.rb2
-rw-r--r--spec/ruby/core/float/shared/equal.rb21
-rw-r--r--spec/ruby/core/float/shared/modulo.rb12
-rw-r--r--spec/ruby/core/float/shared/quo.rb12
-rw-r--r--spec/ruby/core/float/shared/to_i.rb16
-rw-r--r--spec/ruby/core/float/shared/to_s.rb308
-rw-r--r--spec/ruby/core/float/to_s_spec.rb308
-rw-r--r--spec/ruby/core/float/truncate_spec.rb10
-rw-r--r--spec/ruby/core/float/uplus_spec.rb2
-rw-r--r--spec/ruby/core/gc/auto_compact_spec.rb24
-rw-r--r--spec/ruby/core/gc/config_spec.rb97
-rw-r--r--spec/ruby/core/gc/count_spec.rb2
-rw-r--r--spec/ruby/core/gc/disable_spec.rb2
-rw-r--r--spec/ruby/core/gc/enable_spec.rb2
-rw-r--r--spec/ruby/core/gc/measure_total_time_spec.rb17
-rw-r--r--spec/ruby/core/gc/profiler/enabled_spec.rb4
-rw-r--r--spec/ruby/core/gc/profiler/result_spec.rb2
-rw-r--r--spec/ruby/core/gc/profiler/total_time_spec.rb2
-rw-r--r--spec/ruby/core/gc/stat_spec.rb64
-rw-r--r--spec/ruby/core/gc/stress_spec.rb8
-rw-r--r--spec/ruby/core/gc/total_time_spec.rb13
-rw-r--r--spec/ruby/core/hash/allocate_spec.rb2
-rw-r--r--spec/ruby/core/hash/assoc_spec.rb14
-rw-r--r--spec/ruby/core/hash/clear_spec.rb6
-rw-r--r--spec/ruby/core/hash/clone_spec.rb2
-rw-r--r--spec/ruby/core/hash/compact_spec.rb30
-rw-r--r--spec/ruby/core/hash/compare_by_identity_spec.rb47
-rw-r--r--spec/ruby/core/hash/constructor_spec.rb72
-rw-r--r--spec/ruby/core/hash/deconstruct_keys_spec.rb32
-rw-r--r--spec/ruby/core/hash/default_proc_spec.rb20
-rw-r--r--spec/ruby/core/hash/default_spec.rb4
-rw-r--r--spec/ruby/core/hash/delete_if_spec.rb8
-rw-r--r--spec/ruby/core/hash/delete_spec.rb20
-rw-r--r--spec/ruby/core/hash/dig_spec.rb18
-rw-r--r--spec/ruby/core/hash/each_key_spec.rb2
-rw-r--r--spec/ruby/core/hash/each_pair_spec.rb106
-rw-r--r--spec/ruby/core/hash/each_spec.rb10
-rw-r--r--spec/ruby/core/hash/each_value_spec.rb2
-rw-r--r--spec/ruby/core/hash/element_reference_spec.rb20
-rw-r--r--spec/ruby/core/hash/element_set_spec.rb118
-rw-r--r--spec/ruby/core/hash/equal_value_spec.rb2
-rw-r--r--spec/ruby/core/hash/except_spec.rb42
-rw-r--r--spec/ruby/core/hash/fetch_spec.rb8
-rw-r--r--spec/ruby/core/hash/fetch_values_spec.rb2
-rw-r--r--spec/ruby/core/hash/filter_spec.rb13
-rw-r--r--spec/ruby/core/hash/fixtures/name.rb30
-rw-r--r--spec/ruby/core/hash/flatten_spec.rb4
-rw-r--r--spec/ruby/core/hash/gt_spec.rb2
-rw-r--r--spec/ruby/core/hash/gte_spec.rb2
-rw-r--r--spec/ruby/core/hash/has_key_spec.rb6
-rw-r--r--spec/ruby/core/hash/has_value_spec.rb15
-rw-r--r--spec/ruby/core/hash/hash_spec.rb7
-rw-r--r--spec/ruby/core/hash/include_spec.rb39
-rw-r--r--spec/ruby/core/hash/index_spec.rb7
-rw-r--r--spec/ruby/core/hash/initialize_spec.rb12
-rw-r--r--spec/ruby/core/hash/inspect_spec.rb122
-rw-r--r--spec/ruby/core/hash/invert_spec.rb21
-rw-r--r--spec/ruby/core/hash/keep_if_spec.rb10
-rw-r--r--spec/ruby/core/hash/key_spec.rb30
-rw-r--r--spec/ruby/core/hash/keys_spec.rb4
-rw-r--r--spec/ruby/core/hash/length_spec.rb6
-rw-r--r--spec/ruby/core/hash/lt_spec.rb2
-rw-r--r--spec/ruby/core/hash/lte_spec.rb2
-rw-r--r--spec/ruby/core/hash/member_spec.rb6
-rw-r--r--spec/ruby/core/hash/merge_spec.rb55
-rw-r--r--spec/ruby/core/hash/new_spec.rb40
-rw-r--r--spec/ruby/core/hash/rassoc_spec.rb10
-rw-r--r--spec/ruby/core/hash/rehash_spec.rb54
-rw-r--r--spec/ruby/core/hash/reject_spec.rb38
-rw-r--r--spec/ruby/core/hash/replace_spec.rb76
-rw-r--r--spec/ruby/core/hash/ruby2_keywords_hash_spec.rb81
-rw-r--r--spec/ruby/core/hash/select_spec.rb108
-rw-r--r--spec/ruby/core/hash/shared/comparison.rb10
-rw-r--r--spec/ruby/core/hash/shared/each.rb103
-rw-r--r--spec/ruby/core/hash/shared/eql.rb168
-rw-r--r--spec/ruby/core/hash/shared/equal.rb90
-rw-r--r--spec/ruby/core/hash/shared/greater_than.rb6
-rw-r--r--spec/ruby/core/hash/shared/index.rb27
-rw-r--r--spec/ruby/core/hash/shared/iteration.rb6
-rw-r--r--spec/ruby/core/hash/shared/key.rb38
-rw-r--r--spec/ruby/core/hash/shared/length.rb12
-rw-r--r--spec/ruby/core/hash/shared/less_than.rb6
-rw-r--r--spec/ruby/core/hash/shared/replace.rb51
-rw-r--r--spec/ruby/core/hash/shared/select.rb91
-rw-r--r--spec/ruby/core/hash/shared/store.rb98
-rw-r--r--spec/ruby/core/hash/shared/to_s.rb98
-rw-r--r--spec/ruby/core/hash/shared/update.rb78
-rw-r--r--spec/ruby/core/hash/shared/value.rb14
-rw-r--r--spec/ruby/core/hash/shared/values_at.rb9
-rw-r--r--spec/ruby/core/hash/shift_spec.rb19
-rw-r--r--spec/ruby/core/hash/size_spec.rb13
-rw-r--r--spec/ruby/core/hash/slice_spec.rb25
-rw-r--r--spec/ruby/core/hash/store_spec.rb6
-rw-r--r--spec/ruby/core/hash/to_a_spec.rb14
-rw-r--r--spec/ruby/core/hash/to_h_spec.rb114
-rw-r--r--spec/ruby/core/hash/to_hash_spec.rb4
-rw-r--r--spec/ruby/core/hash/to_proc_spec.rb32
-rw-r--r--spec/ruby/core/hash/to_s_spec.rb6
-rw-r--r--spec/ruby/core/hash/transform_keys_spec.rb98
-rw-r--r--spec/ruby/core/hash/transform_values_spec.rb39
-rw-r--r--spec/ruby/core/hash/try_convert_spec.rb16
-rw-r--r--spec/ruby/core/hash/update_spec.rb76
-rw-r--r--spec/ruby/core/hash/value_spec.rb6
-rw-r--r--spec/ruby/core/hash/values_at_spec.rb10
-rw-r--r--spec/ruby/core/hash/values_spec.rb2
-rw-r--r--spec/ruby/core/integer/allbits_spec.rb8
-rw-r--r--spec/ruby/core/integer/anybits_spec.rb8
-rw-r--r--spec/ruby/core/integer/bit_and_spec.rb24
-rw-r--r--spec/ruby/core/integer/bit_or_spec.rb43
-rw-r--r--spec/ruby/core/integer/bit_xor_spec.rb45
-rw-r--r--spec/ruby/core/integer/ceil_spec.rb12
-rw-r--r--spec/ruby/core/integer/ceildiv_spec.rb20
-rw-r--r--spec/ruby/core/integer/chr_spec.rb80
-rw-r--r--spec/ruby/core/integer/coerce_spec.rb43
-rw-r--r--spec/ruby/core/integer/comparison_spec.rb16
-rw-r--r--spec/ruby/core/integer/complement_spec.rb6
-rw-r--r--spec/ruby/core/integer/constants_spec.rb20
-rw-r--r--spec/ruby/core/integer/digits_spec.rb15
-rw-r--r--spec/ruby/core/integer/div_spec.rb54
-rw-r--r--spec/ruby/core/integer/divide_spec.rb65
-rw-r--r--spec/ruby/core/integer/divmod_spec.rb46
-rw-r--r--spec/ruby/core/integer/downto_spec.rb10
-rw-r--r--spec/ruby/core/integer/dup_spec.rb4
-rw-r--r--spec/ruby/core/integer/element_reference_spec.rb134
-rw-r--r--spec/ruby/core/integer/eql_spec.rb25
-rw-r--r--spec/ruby/core/integer/even_spec.rb26
-rw-r--r--spec/ruby/core/integer/fdiv_spec.rb59
-rw-r--r--spec/ruby/core/integer/fixtures/classes.rb10
-rw-r--r--spec/ruby/core/integer/floor_spec.rb12
-rw-r--r--spec/ruby/core/integer/gcd_spec.rb16
-rw-r--r--spec/ruby/core/integer/gcdlcm_spec.rb16
-rw-r--r--spec/ruby/core/integer/gt_spec.rb13
-rw-r--r--spec/ruby/core/integer/gte_spec.rb13
-rw-r--r--spec/ruby/core/integer/integer_spec.rb4
-rw-r--r--spec/ruby/core/integer/lcm_spec.rb16
-rw-r--r--spec/ruby/core/integer/left_shift_spec.rb77
-rw-r--r--spec/ruby/core/integer/lt_spec.rb13
-rw-r--r--spec/ruby/core/integer/lte_spec.rb13
-rw-r--r--spec/ruby/core/integer/minus_spec.rb35
-rw-r--r--spec/ruby/core/integer/multiply_spec.rb18
-rw-r--r--spec/ruby/core/integer/nobits_spec.rb8
-rw-r--r--spec/ruby/core/integer/odd_spec.rb26
-rw-r--r--spec/ruby/core/integer/ord_spec.rb16
-rw-r--r--spec/ruby/core/integer/plus_spec.rb52
-rw-r--r--spec/ruby/core/integer/pow_spec.rb28
-rw-r--r--spec/ruby/core/integer/pred_spec.rb10
-rw-r--r--spec/ruby/core/integer/rationalize_spec.rb6
-rw-r--r--spec/ruby/core/integer/remainder_spec.rb20
-rw-r--r--spec/ruby/core/integer/right_shift_spec.rb81
-rw-r--r--spec/ruby/core/integer/round_spec.rb66
-rw-r--r--spec/ruby/core/integer/shared/abs.rb4
-rw-r--r--spec/ruby/core/integer/shared/arithmetic_coerce.rb22
-rw-r--r--spec/ruby/core/integer/shared/comparison_coerce.rb24
-rw-r--r--spec/ruby/core/integer/shared/equal.rb9
-rw-r--r--spec/ruby/core/integer/shared/exponent.rb136
-rw-r--r--spec/ruby/core/integer/shared/integer_ceil_precision.rb54
-rw-r--r--spec/ruby/core/integer/shared/integer_floor_precision.rb42
-rw-r--r--spec/ruby/core/integer/shared/integer_rounding.rb6
-rw-r--r--spec/ruby/core/integer/shared/modulo.rb84
-rw-r--r--spec/ruby/core/integer/shared/to_i.rb8
-rw-r--r--spec/ruby/core/integer/size_spec.rb4
-rw-r--r--spec/ruby/core/integer/sqrt_spec.rb6
-rw-r--r--spec/ruby/core/integer/to_f_spec.rb6
-rw-r--r--spec/ruby/core/integer/to_r_spec.rb8
-rw-r--r--spec/ruby/core/integer/to_s_spec.rb30
-rw-r--r--spec/ruby/core/integer/truncate_spec.rb12
-rw-r--r--spec/ruby/core/integer/try_convert_spec.rb48
-rw-r--r--spec/ruby/core/integer/uminus_spec.rb8
-rw-r--r--spec/ruby/core/integer/upto_spec.rb10
-rw-r--r--spec/ruby/core/integer/zero_spec.rb13
-rw-r--r--spec/ruby/core/io/advise_spec.rb40
-rw-r--r--spec/ruby/core/io/autoclose_spec.rb77
-rw-r--r--spec/ruby/core/io/binmode_spec.rb12
-rw-r--r--spec/ruby/core/io/binread_spec.rb14
-rw-r--r--spec/ruby/core/io/buffer/and_spec.rb62
-rw-r--r--spec/ruby/core/io/buffer/bit_count_spec.rb64
-rw-r--r--spec/ruby/core/io/buffer/empty_spec.rb27
-rw-r--r--spec/ruby/core/io/buffer/external_spec.rb23
-rw-r--r--spec/ruby/core/io/buffer/for_spec.rb95
-rw-r--r--spec/ruby/core/io/buffer/free_spec.rb102
-rw-r--r--spec/ruby/core/io/buffer/initialize_spec.rb119
-rw-r--r--spec/ruby/core/io/buffer/internal_spec.rb23
-rw-r--r--spec/ruby/core/io/buffer/locked_spec.rb75
-rw-r--r--spec/ruby/core/io/buffer/map_spec.rb347
-rw-r--r--spec/ruby/core/io/buffer/mapped_spec.rb23
-rw-r--r--spec/ruby/core/io/buffer/not_spec.rb37
-rw-r--r--spec/ruby/core/io/buffer/null_spec.rb27
-rw-r--r--spec/ruby/core/io/buffer/or_spec.rb62
-rw-r--r--spec/ruby/core/io/buffer/private_spec.rb23
-rw-r--r--spec/ruby/core/io/buffer/readonly_spec.rb28
-rw-r--r--spec/ruby/core/io/buffer/resize_spec.rb151
-rw-r--r--spec/ruby/core/io/buffer/shared/null_and_empty.rb57
-rw-r--r--spec/ruby/core/io/buffer/shared_spec.rb33
-rw-r--r--spec/ruby/core/io/buffer/string_spec.rb62
-rw-r--r--spec/ruby/core/io/buffer/transfer_spec.rb117
-rw-r--r--spec/ruby/core/io/buffer/valid_spec.rb99
-rw-r--r--spec/ruby/core/io/buffer/xor_spec.rb62
-rw-r--r--spec/ruby/core/io/bytes_spec.rb43
-rw-r--r--spec/ruby/core/io/chars_spec.rb12
-rw-r--r--spec/ruby/core/io/close_on_exec_spec.rb8
-rw-r--r--spec/ruby/core/io/close_read_spec.rb13
-rw-r--r--spec/ruby/core/io/close_spec.rb59
-rw-r--r--spec/ruby/core/io/close_write_spec.rb24
-rw-r--r--spec/ruby/core/io/closed_spec.rb4
-rw-r--r--spec/ruby/core/io/codepoints_spec.rb25
-rw-r--r--spec/ruby/core/io/copy_stream_spec.rb53
-rw-r--r--spec/ruby/core/io/dup_spec.rb55
-rw-r--r--spec/ruby/core/io/each_byte_spec.rb6
-rw-r--r--spec/ruby/core/io/each_codepoint_spec.rb6
-rw-r--r--spec/ruby/core/io/eof_spec.rb8
-rw-r--r--spec/ruby/core/io/external_encoding_spec.rb51
-rw-r--r--spec/ruby/core/io/fcntl_spec.rb2
-rw-r--r--spec/ruby/core/io/fileno_spec.rb2
-rw-r--r--spec/ruby/core/io/fixtures/classes.rb29
-rw-r--r--spec/ruby/core/io/flush_spec.rb16
-rw-r--r--spec/ruby/core/io/foreach_spec.rb55
-rw-r--r--spec/ruby/core/io/fsync_spec.rb2
-rw-r--r--spec/ruby/core/io/getbyte_spec.rb18
-rw-r--r--spec/ruby/core/io/getc_spec.rb6
-rw-r--r--spec/ruby/core/io/gets_spec.rb95
-rw-r--r--spec/ruby/core/io/initialize_spec.rb23
-rw-r--r--spec/ruby/core/io/inspect_spec.rb4
-rw-r--r--spec/ruby/core/io/internal_encoding_spec.rb33
-rw-r--r--spec/ruby/core/io/ioctl_spec.rb8
-rw-r--r--spec/ruby/core/io/lineno_spec.rb51
-rw-r--r--spec/ruby/core/io/lines_spec.rb42
-rw-r--r--spec/ruby/core/io/new_spec.rb8
-rw-r--r--spec/ruby/core/io/nonblock_spec.rb48
-rw-r--r--spec/ruby/core/io/open_spec.rb23
-rw-r--r--spec/ruby/core/io/output_spec.rb2
-rw-r--r--spec/ruby/core/io/path_spec.rb12
-rw-r--r--spec/ruby/core/io/pid_spec.rb4
-rw-r--r--spec/ruby/core/io/pipe_spec.rb39
-rw-r--r--spec/ruby/core/io/popen_spec.rb34
-rw-r--r--spec/ruby/core/io/pread_spec.rb162
-rw-r--r--spec/ruby/core/io/print_spec.rb27
-rw-r--r--spec/ruby/core/io/printf_spec.rb2
-rw-r--r--spec/ruby/core/io/puts_spec.rb8
-rw-r--r--spec/ruby/core/io/pwrite_spec.rb86
-rw-r--r--spec/ruby/core/io/read_nonblock_spec.rb67
-rw-r--r--spec/ruby/core/io/read_spec.rb273
-rw-r--r--spec/ruby/core/io/readbyte_spec.rb2
-rw-r--r--spec/ruby/core/io/readchar_spec.rb72
-rw-r--r--spec/ruby/core/io/readline_spec.rb37
-rw-r--r--spec/ruby/core/io/readlines_spec.rb105
-rw-r--r--spec/ruby/core/io/readpartial_spec.rb41
-rw-r--r--spec/ruby/core/io/reopen_spec.rb26
-rw-r--r--spec/ruby/core/io/rewind_spec.rb17
-rw-r--r--spec/ruby/core/io/seek_spec.rb2
-rw-r--r--spec/ruby/core/io/select_spec.rb110
-rw-r--r--spec/ruby/core/io/set_encoding_by_bom_spec.rb262
-rw-r--r--spec/ruby/core/io/set_encoding_spec.rb79
-rw-r--r--spec/ruby/core/io/shared/binwrite.rb15
-rw-r--r--spec/ruby/core/io/shared/chars.rb10
-rw-r--r--spec/ruby/core/io/shared/codepoints.rb6
-rw-r--r--spec/ruby/core/io/shared/each.rb88
-rw-r--r--spec/ruby/core/io/shared/gets_ascii.rb2
-rw-r--r--spec/ruby/core/io/shared/new.rb123
-rw-r--r--spec/ruby/core/io/shared/pos.rb14
-rw-r--r--spec/ruby/core/io/shared/readlines.rb158
-rw-r--r--spec/ruby/core/io/shared/tty.rb2
-rw-r--r--spec/ruby/core/io/shared/write.rb81
-rw-r--r--spec/ruby/core/io/stat_spec.rb9
-rw-r--r--spec/ruby/core/io/sync_spec.rb4
-rw-r--r--spec/ruby/core/io/sysopen_spec.rb16
-rw-r--r--spec/ruby/core/io/sysread_spec.rb55
-rw-r--r--spec/ruby/core/io/sysseek_spec.rb11
-rw-r--r--spec/ruby/core/io/syswrite_spec.rb11
-rw-r--r--spec/ruby/core/io/to_i_spec.rb2
-rw-r--r--spec/ruby/core/io/to_io_spec.rb4
-rw-r--r--spec/ruby/core/io/try_convert_spec.rb12
-rw-r--r--spec/ruby/core/io/ungetbyte_spec.rb39
-rw-r--r--spec/ruby/core/io/ungetc_spec.rb28
-rw-r--r--spec/ruby/core/io/write_nonblock_spec.rb21
-rw-r--r--spec/ruby/core/io/write_spec.rb174
-rw-r--r--spec/ruby/core/kernel/Array_spec.rb6
-rw-r--r--spec/ruby/core/kernel/Complex_spec.rb195
-rw-r--r--spec/ruby/core/kernel/Float_spec.rb266
-rw-r--r--spec/ruby/core/kernel/Hash_spec.rb6
-rw-r--r--spec/ruby/core/kernel/Integer_spec.rb427
-rw-r--r--spec/ruby/core/kernel/Rational_spec.rb234
-rw-r--r--spec/ruby/core/kernel/String_spec.rb16
-rw-r--r--spec/ruby/core/kernel/__dir___spec.rb16
-rw-r--r--spec/ruby/core/kernel/abort_spec.rb2
-rw-r--r--spec/ruby/core/kernel/at_exit_spec.rb62
-rw-r--r--spec/ruby/core/kernel/autoload_relative_spec.rb114
-rw-r--r--spec/ruby/core/kernel/autoload_spec.rb34
-rw-r--r--spec/ruby/core/kernel/backtick_spec.rb18
-rw-r--r--spec/ruby/core/kernel/binding_spec.rb4
-rw-r--r--spec/ruby/core/kernel/block_given_spec.rb7
-rw-r--r--spec/ruby/core/kernel/caller_locations_spec.rb59
-rw-r--r--spec/ruby/core/kernel/caller_spec.rb89
-rw-r--r--spec/ruby/core/kernel/case_compare_spec.rb14
-rw-r--r--spec/ruby/core/kernel/catch_spec.rb10
-rw-r--r--spec/ruby/core/kernel/chomp_spec.rb2
-rw-r--r--spec/ruby/core/kernel/chop_spec.rb2
-rw-r--r--spec/ruby/core/kernel/class_spec.rb22
-rw-r--r--spec/ruby/core/kernel/clone_spec.rb101
-rw-r--r--spec/ruby/core/kernel/comparison_spec.rb6
-rw-r--r--spec/ruby/core/kernel/define_singleton_method_spec.rb37
-rw-r--r--spec/ruby/core/kernel/dup_spec.rb8
-rw-r--r--spec/ruby/core/kernel/eql_spec.rb2
-rw-r--r--spec/ruby/core/kernel/eval_spec.rb286
-rw-r--r--spec/ruby/core/kernel/exec_spec.rb6
-rw-r--r--spec/ruby/core/kernel/exit_spec.rb14
-rw-r--r--spec/ruby/core/kernel/extend_spec.rb20
-rw-r--r--spec/ruby/core/kernel/fail_spec.rb10
-rw-r--r--spec/ruby/core/kernel/fixtures/Complex.rb5
-rw-r--r--spec/ruby/core/kernel/fixtures/at_exit.rb3
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_relative_b.rb7
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_relative_d.rb5
-rw-r--r--spec/ruby/core/kernel/fixtures/classes.rb105
-rw-r--r--spec/ruby/core/kernel/fixtures/warn_core_method.rb14
-rw-r--r--spec/ruby/core/kernel/fork_spec.rb2
-rw-r--r--spec/ruby/core/kernel/format_spec.rb35
-rw-r--r--spec/ruby/core/kernel/freeze_spec.rb36
-rw-r--r--spec/ruby/core/kernel/frozen_spec.rb22
-rw-r--r--spec/ruby/core/kernel/gets_spec.rb2
-rw-r--r--spec/ruby/core/kernel/global_variables_spec.rb6
-rw-r--r--spec/ruby/core/kernel/gsub_spec.rb8
-rw-r--r--spec/ruby/core/kernel/initialize_clone_spec.rb26
-rw-r--r--spec/ruby/core/kernel/initialize_copy_spec.rb21
-rw-r--r--spec/ruby/core/kernel/initialize_dup_spec.rb20
-rw-r--r--spec/ruby/core/kernel/inspect_spec.rb92
-rw-r--r--spec/ruby/core/kernel/instance_of_spec.rb6
-rw-r--r--spec/ruby/core/kernel/instance_variable_defined_spec.rb12
-rw-r--r--spec/ruby/core/kernel/instance_variable_get_spec.rb34
-rw-r--r--spec/ruby/core/kernel/instance_variable_set_spec.rb36
-rw-r--r--spec/ruby/core/kernel/instance_variables_spec.rb17
-rw-r--r--spec/ruby/core/kernel/is_a_spec.rb2
-rw-r--r--spec/ruby/core/kernel/iterator_spec.rb12
-rw-r--r--spec/ruby/core/kernel/itself_spec.rb2
-rw-r--r--spec/ruby/core/kernel/kind_of_spec.rb2
-rw-r--r--spec/ruby/core/kernel/lambda_spec.rb84
-rw-r--r--spec/ruby/core/kernel/load_spec.rb2
-rw-r--r--spec/ruby/core/kernel/local_variables_spec.rb11
-rw-r--r--spec/ruby/core/kernel/loop_spec.rb6
-rw-r--r--spec/ruby/core/kernel/match_spec.rb21
-rw-r--r--spec/ruby/core/kernel/method_spec.rb59
-rw-r--r--spec/ruby/core/kernel/methods_spec.rb34
-rw-r--r--spec/ruby/core/kernel/nil_spec.rb10
-rw-r--r--spec/ruby/core/kernel/not_match_spec.rb4
-rw-r--r--spec/ruby/core/kernel/open_spec.rb130
-rw-r--r--spec/ruby/core/kernel/p_spec.rb8
-rw-r--r--spec/ruby/core/kernel/print_spec.rb14
-rw-r--r--spec/ruby/core/kernel/printf_spec.rb9
-rw-r--r--spec/ruby/core/kernel/private_methods_spec.rb10
-rw-r--r--spec/ruby/core/kernel/proc_spec.rb38
-rw-r--r--spec/ruby/core/kernel/protected_methods_spec.rb10
-rw-r--r--spec/ruby/core/kernel/public_method_spec.rb6
-rw-r--r--spec/ruby/core/kernel/public_methods_spec.rb19
-rw-r--r--spec/ruby/core/kernel/public_send_spec.rb20
-rw-r--r--spec/ruby/core/kernel/putc_spec.rb2
-rw-r--r--spec/ruby/core/kernel/puts_spec.rb2
-rw-r--r--spec/ruby/core/kernel/raise_spec.rb198
-rw-r--r--spec/ruby/core/kernel/rand_spec.rb116
-rw-r--r--spec/ruby/core/kernel/readline_spec.rb2
-rw-r--r--spec/ruby/core/kernel/readlines_spec.rb2
-rw-r--r--spec/ruby/core/kernel/remove_instance_variable_spec.rb23
-rw-r--r--spec/ruby/core/kernel/require_relative_spec.rb142
-rw-r--r--spec/ruby/core/kernel/require_spec.rb35
-rw-r--r--spec/ruby/core/kernel/respond_to_missing_spec.rb20
-rw-r--r--spec/ruby/core/kernel/respond_to_spec.rb32
-rw-r--r--spec/ruby/core/kernel/select_spec.rb6
-rw-r--r--spec/ruby/core/kernel/set_trace_func_spec.rb2
-rw-r--r--spec/ruby/core/kernel/shared/dup_clone.rb32
-rw-r--r--spec/ruby/core/kernel/shared/kind_of.rb8
-rw-r--r--spec/ruby/core/kernel/shared/lambda.rb4
-rw-r--r--spec/ruby/core/kernel/shared/load.rb123
-rw-r--r--spec/ruby/core/kernel/shared/method.rb18
-rw-r--r--spec/ruby/core/kernel/shared/require.rb309
-rw-r--r--spec/ruby/core/kernel/shared/sprintf.rb201
-rw-r--r--spec/ruby/core/kernel/shared/sprintf_encoding.rb49
-rw-r--r--spec/ruby/core/kernel/shared/then.rb12
-rw-r--r--spec/ruby/core/kernel/singleton_class_spec.rb53
-rw-r--r--spec/ruby/core/kernel/singleton_method_spec.rb52
-rw-r--r--spec/ruby/core/kernel/singleton_methods_spec.rb65
-rw-r--r--spec/ruby/core/kernel/sleep_spec.rb82
-rw-r--r--spec/ruby/core/kernel/spawn_spec.rb2
-rw-r--r--spec/ruby/core/kernel/sprintf_spec.rb48
-rw-r--r--spec/ruby/core/kernel/srand_spec.rb26
-rw-r--r--spec/ruby/core/kernel/sub_spec.rb4
-rw-r--r--spec/ruby/core/kernel/syscall_spec.rb2
-rw-r--r--spec/ruby/core/kernel/system_spec.rb43
-rw-r--r--spec/ruby/core/kernel/taint_spec.rb43
-rw-r--r--spec/ruby/core/kernel/tainted_spec.rb10
-rw-r--r--spec/ruby/core/kernel/tap_spec.rb4
-rw-r--r--spec/ruby/core/kernel/test_spec.rb16
-rw-r--r--spec/ruby/core/kernel/then_spec.rb6
-rw-r--r--spec/ruby/core/kernel/throw_spec.rb14
-rw-r--r--spec/ruby/core/kernel/to_s_spec.rb10
-rw-r--r--spec/ruby/core/kernel/trace_var_spec.rb4
-rw-r--r--spec/ruby/core/kernel/trap_spec.rb7
-rw-r--r--spec/ruby/core/kernel/trust_spec.rb23
-rw-r--r--spec/ruby/core/kernel/untaint_spec.rb23
-rw-r--r--spec/ruby/core/kernel/untrace_var_spec.rb2
-rw-r--r--spec/ruby/core/kernel/untrust_spec.rb23
-rw-r--r--spec/ruby/core/kernel/untrusted_spec.rb26
-rw-r--r--spec/ruby/core/kernel/warn_spec.rb156
-rw-r--r--spec/ruby/core/main/define_method_spec.rb6
-rw-r--r--spec/ruby/core/main/fixtures/classes.rb8
-rw-r--r--spec/ruby/core/main/fixtures/using.rb1
-rw-r--r--spec/ruby/core/main/fixtures/using_in_main.rb5
-rw-r--r--spec/ruby/core/main/fixtures/using_in_method.rb5
-rw-r--r--spec/ruby/core/main/include_spec.rb4
-rw-r--r--spec/ruby/core/main/private_spec.rb35
-rw-r--r--spec/ruby/core/main/public_spec.rb32
-rw-r--r--spec/ruby/core/main/ruby2_keywords_spec.rb9
-rw-r--r--spec/ruby/core/main/using_spec.rb26
-rw-r--r--spec/ruby/core/marshal/dump_spec.rb636
-rw-r--r--spec/ruby/core/marshal/fixtures/classes.rb4
-rw-r--r--spec/ruby/core/marshal/fixtures/marshal_data.rb217
-rw-r--r--spec/ruby/core/marshal/fixtures/marshal_multibyte_data.rb12
-rw-r--r--spec/ruby/core/marshal/float_spec.rb2
-rw-r--r--spec/ruby/core/marshal/shared/load.rb719
-rw-r--r--spec/ruby/core/matchdata/allocate_spec.rb8
-rw-r--r--spec/ruby/core/matchdata/begin_spec.rb30
-rw-r--r--spec/ruby/core/matchdata/bytebegin_spec.rb132
-rw-r--r--spec/ruby/core/matchdata/byteend_spec.rb104
-rw-r--r--spec/ruby/core/matchdata/byteoffset_spec.rb93
-rw-r--r--spec/ruby/core/matchdata/captures_spec.rb5
-rw-r--r--spec/ruby/core/matchdata/deconstruct_keys_spec.rb78
-rw-r--r--spec/ruby/core/matchdata/deconstruct_spec.rb6
-rw-r--r--spec/ruby/core/matchdata/element_reference_spec.rb43
-rw-r--r--spec/ruby/core/matchdata/end_spec.rb2
-rw-r--r--spec/ruby/core/matchdata/fixtures/classes.rb3
-rw-r--r--spec/ruby/core/matchdata/inspect_spec.rb2
-rw-r--r--spec/ruby/core/matchdata/integer_at_spec.rb38
-rw-r--r--spec/ruby/core/matchdata/match_length_spec.rb32
-rw-r--r--spec/ruby/core/matchdata/match_spec.rb32
-rw-r--r--spec/ruby/core/matchdata/named_captures_spec.rb10
-rw-r--r--spec/ruby/core/matchdata/names_spec.rb4
-rw-r--r--spec/ruby/core/matchdata/offset_spec.rb106
-rw-r--r--spec/ruby/core/matchdata/post_match_spec.rb32
-rw-r--r--spec/ruby/core/matchdata/pre_match_spec.rb32
-rw-r--r--spec/ruby/core/matchdata/regexp_spec.rb2
-rw-r--r--spec/ruby/core/matchdata/shared/captures.rb13
-rw-r--r--spec/ruby/core/matchdata/shared/eql.rb8
-rw-r--r--spec/ruby/core/matchdata/string_spec.rb7
-rw-r--r--spec/ruby/core/matchdata/to_a_spec.rb6
-rw-r--r--spec/ruby/core/matchdata/to_s_spec.rb6
-rw-r--r--spec/ruby/core/matchdata/values_at_spec.rb71
-rw-r--r--spec/ruby/core/math/acos_spec.rb14
-rw-r--r--spec/ruby/core/math/acosh_spec.rb14
-rw-r--r--spec/ruby/core/math/asin_spec.rb12
-rw-r--r--spec/ruby/core/math/asinh_spec.rb8
-rw-r--r--spec/ruby/core/math/atan2_spec.rb14
-rw-r--r--spec/ruby/core/math/atan_spec.rb8
-rw-r--r--spec/ruby/core/math/atanh_spec.rb4
-rw-r--r--spec/ruby/core/math/cbrt_spec.rb6
-rw-r--r--spec/ruby/core/math/cos_spec.rb32
-rw-r--r--spec/ruby/core/math/cosh_spec.rb8
-rw-r--r--spec/ruby/core/math/erf_spec.rb8
-rw-r--r--spec/ruby/core/math/erfc_spec.rb8
-rw-r--r--spec/ruby/core/math/exp_spec.rb8
-rw-r--r--spec/ruby/core/math/expm1_spec.rb37
-rw-r--r--spec/ruby/core/math/fixtures/common.rb (renamed from spec/ruby/fixtures/math/common.rb)0
-rw-r--r--spec/ruby/core/math/frexp_spec.rb6
-rw-r--r--spec/ruby/core/math/gamma_spec.rb6
-rw-r--r--spec/ruby/core/math/hypot_spec.rb12
-rw-r--r--spec/ruby/core/math/ldexp_spec.rb20
-rw-r--r--spec/ruby/core/math/lgamma_spec.rb13
-rw-r--r--spec/ruby/core/math/log10_spec.rb14
-rw-r--r--spec/ruby/core/math/log1p_spec.rb49
-rw-r--r--spec/ruby/core/math/log2_spec.rb12
-rw-r--r--spec/ruby/core/math/log_spec.rb16
-rw-r--r--spec/ruby/core/math/shared/atanh.rb44
-rw-r--r--spec/ruby/core/math/sin_spec.rb8
-rw-r--r--spec/ruby/core/math/sinh_spec.rb8
-rw-r--r--spec/ruby/core/math/sqrt_spec.rb12
-rw-r--r--spec/ruby/core/math/tan_spec.rb8
-rw-r--r--spec/ruby/core/math/tanh_spec.rb8
-rw-r--r--spec/ruby/core/method/clone_spec.rb15
-rw-r--r--spec/ruby/core/method/compose_spec.rb133
-rw-r--r--spec/ruby/core/method/curry_spec.rb18
-rw-r--r--spec/ruby/core/method/dup_spec.rb15
-rw-r--r--spec/ruby/core/method/fixtures/classes.rb37
-rw-r--r--spec/ruby/core/method/inspect_spec.rb2
-rw-r--r--spec/ruby/core/method/original_name_spec.rb37
-rw-r--r--spec/ruby/core/method/owner_spec.rb4
-rw-r--r--spec/ruby/core/method/parameters_spec.rb62
-rw-r--r--spec/ruby/core/method/private_spec.rb9
-rw-r--r--spec/ruby/core/method/protected_spec.rb9
-rw-r--r--spec/ruby/core/method/public_spec.rb9
-rw-r--r--spec/ruby/core/method/receiver_spec.rb8
-rw-r--r--spec/ruby/core/method/shared/aliased_inspect.rb31
-rw-r--r--spec/ruby/core/method/shared/call.rb4
-rw-r--r--spec/ruby/core/method/shared/dup.rb32
-rw-r--r--spec/ruby/core/method/shared/eql.rb32
-rw-r--r--spec/ruby/core/method/shared/to_s.rb55
-rw-r--r--spec/ruby/core/method/source_location_spec.rb37
-rw-r--r--spec/ruby/core/method/super_method_spec.rb19
-rw-r--r--spec/ruby/core/method/to_proc_spec.rb2
-rw-r--r--spec/ruby/core/method/to_s_spec.rb2
-rw-r--r--spec/ruby/core/method/unbind_spec.rb31
-rw-r--r--spec/ruby/core/module/alias_method_spec.rb55
-rw-r--r--spec/ruby/core/module/allocate_spec.rb14
-rw-r--r--spec/ruby/core/module/ancestors_spec.rb40
-rw-r--r--spec/ruby/core/module/append_features_spec.rb28
-rw-r--r--spec/ruby/core/module/attr_accessor_spec.rb48
-rw-r--r--spec/ruby/core/module/attr_reader_spec.rb21
-rw-r--r--spec/ruby/core/module/attr_spec.rb27
-rw-r--r--spec/ruby/core/module/attr_writer_spec.rb31
-rw-r--r--spec/ruby/core/module/autoload_relative_spec.rb128
-rw-r--r--spec/ruby/core/module/autoload_spec.rb309
-rw-r--r--spec/ruby/core/module/class_variable_defined_spec.rb14
-rw-r--r--spec/ruby/core/module/class_variable_get_spec.rb18
-rw-r--r--spec/ruby/core/module/class_variable_set_spec.rb14
-rw-r--r--spec/ruby/core/module/class_variables_spec.rb16
-rw-r--r--spec/ruby/core/module/const_added_spec.rb238
-rw-r--r--spec/ruby/core/module/const_defined_spec.rb85
-rw-r--r--spec/ruby/core/module/const_get_spec.rb86
-rw-r--r--spec/ruby/core/module/const_missing_spec.rb2
-rw-r--r--spec/ruby/core/module/const_set_spec.rb44
-rw-r--r--spec/ruby/core/module/const_source_location_spec.rb281
-rw-r--r--spec/ruby/core/module/constants_spec.rb25
-rw-r--r--spec/ruby/core/module/define_method_spec.rb281
-rw-r--r--spec/ruby/core/module/deprecate_constant_spec.rb40
-rw-r--r--spec/ruby/core/module/extend_object_spec.rb24
-rw-r--r--spec/ruby/core/module/extended_spec.rb2
-rw-r--r--spec/ruby/core/module/fixtures/autoload_const_source_location.rb6
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_autoload_after_define.rb6
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_require_current_file.rb5
-rw-r--r--spec/ruby/core/module/fixtures/autoload_location.rb3
-rw-r--r--spec/ruby/core/module/fixtures/autoload_relative_a.rb9
-rw-r--r--spec/ruby/core/module/fixtures/classes.rb41
-rw-r--r--spec/ruby/core/module/fixtures/const_added.rb4
-rw-r--r--spec/ruby/core/module/fixtures/module.rb4
-rw-r--r--spec/ruby/core/module/fixtures/name.rb3
-rw-r--r--spec/ruby/core/module/fixtures/set_temporary_name.rb4
-rw-r--r--spec/ruby/core/module/gt_spec.rb10
-rw-r--r--spec/ruby/core/module/gte_spec.rb2
-rw-r--r--spec/ruby/core/module/include_spec.rb408
-rw-r--r--spec/ruby/core/module/included_modules_spec.rb8
-rw-r--r--spec/ruby/core/module/included_spec.rb2
-rw-r--r--spec/ruby/core/module/instance_method_spec.rb53
-rw-r--r--spec/ruby/core/module/instance_methods_spec.rb30
-rw-r--r--spec/ruby/core/module/lt_spec.rb10
-rw-r--r--spec/ruby/core/module/lte_spec.rb2
-rw-r--r--spec/ruby/core/module/method_added_spec.rb96
-rw-r--r--spec/ruby/core/module/method_defined_spec.rb98
-rw-r--r--spec/ruby/core/module/method_removed_spec.rb2
-rw-r--r--spec/ruby/core/module/method_undefined_spec.rb2
-rw-r--r--spec/ruby/core/module/module_function_spec.rb177
-rw-r--r--spec/ruby/core/module/name_spec.rb119
-rw-r--r--spec/ruby/core/module/new_spec.rb4
-rw-r--r--spec/ruby/core/module/prepend_features_spec.rb22
-rw-r--r--spec/ruby/core/module/prepend_spec.rb529
-rw-r--r--spec/ruby/core/module/prepended_spec.rb2
-rw-r--r--spec/ruby/core/module/private_class_method_spec.rb30
-rw-r--r--spec/ruby/core/module/private_constant_spec.rb8
-rw-r--r--spec/ruby/core/module/private_instance_methods_spec.rb18
-rw-r--r--spec/ruby/core/module/private_method_defined_spec.rb100
-rw-r--r--spec/ruby/core/module/private_spec.rb22
-rw-r--r--spec/ruby/core/module/protected_instance_methods_spec.rb12
-rw-r--r--spec/ruby/core/module/protected_method_defined_spec.rb100
-rw-r--r--spec/ruby/core/module/protected_spec.rb20
-rw-r--r--spec/ruby/core/module/public_class_method_spec.rb28
-rw-r--r--spec/ruby/core/module/public_constant_spec.rb2
-rw-r--r--spec/ruby/core/module/public_instance_method_spec.rb20
-rw-r--r--spec/ruby/core/module/public_instance_methods_spec.rb14
-rw-r--r--spec/ruby/core/module/public_method_defined_spec.rb12
-rw-r--r--spec/ruby/core/module/public_spec.rb19
-rw-r--r--spec/ruby/core/module/refine_spec.rb604
-rw-r--r--spec/ruby/core/module/refinements_spec.rb43
-rw-r--r--spec/ruby/core/module/remove_class_variable_spec.rb12
-rw-r--r--spec/ruby/core/module/remove_const_spec.rb53
-rw-r--r--spec/ruby/core/module/remove_method_spec.rb38
-rw-r--r--spec/ruby/core/module/ruby2_keywords_spec.rb248
-rw-r--r--spec/ruby/core/module/set_temporary_name_spec.rb145
-rw-r--r--spec/ruby/core/module/shared/attr_added.rb34
-rw-r--r--spec/ruby/core/module/shared/class_eval.rb29
-rw-r--r--spec/ruby/core/module/shared/class_exec.rb12
-rw-r--r--spec/ruby/core/module/shared/set_visibility.rb60
-rw-r--r--spec/ruby/core/module/to_s_spec.rb25
-rw-r--r--spec/ruby/core/module/undef_method_spec.rb34
-rw-r--r--spec/ruby/core/module/undefined_instance_methods_spec.rb25
-rw-r--r--spec/ruby/core/module/used_refinements_spec.rb85
-rw-r--r--spec/ruby/core/module/using_spec.rb14
-rw-r--r--spec/ruby/core/mutex/lock_spec.rb70
-rw-r--r--spec/ruby/core/mutex/locked_spec.rb8
-rw-r--r--spec/ruby/core/mutex/owned_spec.rb16
-rw-r--r--spec/ruby/core/mutex/sleep_spec.rb28
-rw-r--r--spec/ruby/core/mutex/synchronize_spec.rb8
-rw-r--r--spec/ruby/core/mutex/try_lock_spec.rb8
-rw-r--r--spec/ruby/core/mutex/unlock_spec.rb6
-rw-r--r--spec/ruby/core/nil/case_compare_spec.rb13
-rw-r--r--spec/ruby/core/nil/dup_spec.rb2
-rw-r--r--spec/ruby/core/nil/match_spec.rb30
-rw-r--r--spec/ruby/core/nil/nilclass_spec.rb4
-rw-r--r--spec/ruby/core/nil/rationalize_spec.rb4
-rw-r--r--spec/ruby/core/nil/singleton_method_spec.rb13
-rw-r--r--spec/ruby/core/nil/to_c_spec.rb2
-rw-r--r--spec/ruby/core/nil/to_i_spec.rb2
-rw-r--r--spec/ruby/core/nil/to_s_spec.rb12
-rw-r--r--spec/ruby/core/numeric/abs2_spec.rb4
-rw-r--r--spec/ruby/core/numeric/clone_spec.rb30
-rw-r--r--spec/ruby/core/numeric/coerce_spec.rb12
-rw-r--r--spec/ruby/core/numeric/comparison_spec.rb8
-rw-r--r--spec/ruby/core/numeric/div_spec.rb6
-rw-r--r--spec/ruby/core/numeric/dup_spec.rb16
-rw-r--r--spec/ruby/core/numeric/eql_spec.rb12
-rw-r--r--spec/ruby/core/numeric/fdiv_spec.rb5
-rw-r--r--spec/ruby/core/numeric/finite_spec.rb2
-rw-r--r--spec/ruby/core/numeric/i_spec.rb2
-rw-r--r--spec/ruby/core/numeric/magnitude_spec.rb1
-rw-r--r--spec/ruby/core/numeric/negative_spec.rb12
-rw-r--r--spec/ruby/core/numeric/polar_spec.rb6
-rw-r--r--spec/ruby/core/numeric/positive_spec.rb12
-rw-r--r--spec/ruby/core/numeric/quo_spec.rb32
-rw-r--r--spec/ruby/core/numeric/real_spec.rb4
-rw-r--r--spec/ruby/core/numeric/remainder_spec.rb7
-rw-r--r--spec/ruby/core/numeric/shared/conj.rb2
-rw-r--r--spec/ruby/core/numeric/shared/imag.rb6
-rw-r--r--spec/ruby/core/numeric/shared/quo.rb7
-rw-r--r--spec/ruby/core/numeric/shared/rect.rb28
-rw-r--r--spec/ruby/core/numeric/shared/step.rb115
-rw-r--r--spec/ruby/core/numeric/singleton_method_added_spec.rb8
-rw-r--r--spec/ruby/core/numeric/step_spec.rb101
-rw-r--r--spec/ruby/core/numeric/to_c_spec.rb4
-rw-r--r--spec/ruby/core/objectspace/_id2ref_spec.rb74
-rw-r--r--spec/ruby/core/objectspace/add_finalizer_spec.rb5
-rw-r--r--spec/ruby/core/objectspace/call_finalizer_spec.rb5
-rw-r--r--spec/ruby/core/objectspace/define_finalizer_spec.rb183
-rw-r--r--spec/ruby/core/objectspace/each_object_spec.rb46
-rw-r--r--spec/ruby/core/objectspace/finalizers_spec.rb5
-rw-r--r--spec/ruby/core/objectspace/garbage_collect_spec.rb8
-rw-r--r--spec/ruby/core/objectspace/remove_finalizer_spec.rb5
-rw-r--r--spec/ruby/core/objectspace/undefine_finalizer_spec.rb30
-rw-r--r--spec/ruby/core/objectspace/weakkeymap/clear_spec.rb25
-rw-r--r--spec/ruby/core/objectspace/weakkeymap/delete_spec.rb49
-rw-r--r--spec/ruby/core/objectspace/weakkeymap/element_reference_spec.rb105
-rw-r--r--spec/ruby/core/objectspace/weakkeymap/element_set_spec.rb80
-rw-r--r--spec/ruby/core/objectspace/weakkeymap/fixtures/classes.rb5
-rw-r--r--spec/ruby/core/objectspace/weakkeymap/getkey_spec.rb26
-rw-r--r--spec/ruby/core/objectspace/weakkeymap/inspect_spec.rb19
-rw-r--r--spec/ruby/core/objectspace/weakkeymap/key_spec.rb42
-rw-r--r--spec/ruby/core/objectspace/weakmap/delete_spec.rb28
-rw-r--r--spec/ruby/core/objectspace/weakmap/element_set_spec.rb53
-rw-r--r--spec/ruby/core/objectspace/weakmap/shared/each.rb2
-rw-r--r--spec/ruby/core/objectspace/weakmap/shared/include.rb16
-rw-r--r--spec/ruby/core/proc/allocate_spec.rb2
-rw-r--r--spec/ruby/core/proc/arity_spec.rb16
-rw-r--r--spec/ruby/core/proc/binding_spec.rb2
-rw-r--r--spec/ruby/core/proc/block_pass_spec.rb26
-rw-r--r--spec/ruby/core/proc/clone_spec.rb22
-rw-r--r--spec/ruby/core/proc/compose_spec.rb226
-rw-r--r--spec/ruby/core/proc/curry_spec.rb69
-rw-r--r--spec/ruby/core/proc/dup_spec.rb20
-rw-r--r--spec/ruby/core/proc/element_reference_spec.rb10
-rw-r--r--spec/ruby/core/proc/eql_spec.rb8
-rw-r--r--spec/ruby/core/proc/equal_value_spec.rb8
-rw-r--r--spec/ruby/core/proc/fixtures/common.rb23
-rw-r--r--spec/ruby/core/proc/fixtures/proc_aref.rb1
-rw-r--r--spec/ruby/core/proc/hash_spec.rb6
-rw-r--r--spec/ruby/core/proc/lambda_spec.rb43
-rw-r--r--spec/ruby/core/proc/new_spec.rb94
-rw-r--r--spec/ruby/core/proc/parameters_spec.rb100
-rw-r--r--spec/ruby/core/proc/ruby2_keywords_spec.rb66
-rw-r--r--spec/ruby/core/proc/shared/call.rb11
-rw-r--r--spec/ruby/core/proc/shared/compose.rb51
-rw-r--r--spec/ruby/core/proc/shared/dup.rb31
-rw-r--r--spec/ruby/core/proc/shared/equal.rb51
-rw-r--r--spec/ruby/core/proc/shared/to_s.rb15
-rw-r--r--spec/ruby/core/proc/source_location_spec.rb39
-rw-r--r--spec/ruby/core/proc/to_proc_spec.rb2
-rw-r--r--spec/ruby/core/process/_fork_spec.rb24
-rw-r--r--spec/ruby/core/process/argv0_spec.rb23
-rw-r--r--spec/ruby/core/process/clock_gettime_spec.rb133
-rw-r--r--spec/ruby/core/process/constants_spec.rb153
-rw-r--r--spec/ruby/core/process/daemon_spec.rb5
-rw-r--r--spec/ruby/core/process/detach_spec.rb41
-rw-r--r--spec/ruby/core/process/egid_spec.rb43
-rw-r--r--spec/ruby/core/process/euid_spec.rb18
-rw-r--r--spec/ruby/core/process/exec_spec.rb70
-rw-r--r--spec/ruby/core/process/exit_spec.rb2
-rw-r--r--spec/ruby/core/process/fixtures/argv0.rb6
-rw-r--r--spec/ruby/core/process/fixtures/clocks.rb2
-rw-r--r--spec/ruby/core/process/fixtures/kill.rb2
-rw-r--r--spec/ruby/core/process/getpriority_spec.rb8
-rw-r--r--spec/ruby/core/process/getrlimit_spec.rb23
-rw-r--r--spec/ruby/core/process/gid_spec.rb4
-rw-r--r--spec/ruby/core/process/groups_spec.rb4
-rw-r--r--spec/ruby/core/process/initgroups_spec.rb2
-rw-r--r--spec/ruby/core/process/kill_spec.rb10
-rw-r--r--spec/ruby/core/process/last_status_spec.rb2
-rw-r--r--spec/ruby/core/process/maxgroups_spec.rb2
-rw-r--r--spec/ruby/core/process/pid_spec.rb2
-rw-r--r--spec/ruby/core/process/set_proctitle_spec.rb2
-rw-r--r--spec/ruby/core/process/setrlimit_spec.rb117
-rw-r--r--spec/ruby/core/process/spawn_spec.rb220
-rw-r--r--spec/ruby/core/process/status/bit_and_spec.rb37
-rw-r--r--spec/ruby/core/process/status/equal_value_spec.rb4
-rw-r--r--spec/ruby/core/process/status/exited_spec.rb13
-rw-r--r--spec/ruby/core/process/status/exitstatus_spec.rb4
-rw-r--r--spec/ruby/core/process/status/right_shift_spec.rb36
-rw-r--r--spec/ruby/core/process/status/signaled_spec.rb12
-rw-r--r--spec/ruby/core/process/status/success_spec.rb22
-rw-r--r--spec/ruby/core/process/status/termsig_spec.rb10
-rw-r--r--spec/ruby/core/process/status/to_i_spec.rb8
-rw-r--r--spec/ruby/core/process/status/wait_spec.rb100
-rw-r--r--spec/ruby/core/process/times_spec.rb34
-rw-r--r--spec/ruby/core/process/tms/cstime_spec.rb17
-rw-r--r--spec/ruby/core/process/tms/cutime_spec.rb17
-rw-r--r--spec/ruby/core/process/tms/stime_spec.rb17
-rw-r--r--spec/ruby/core/process/tms/utime_spec.rb17
-rw-r--r--spec/ruby/core/process/uid_spec.rb6
-rw-r--r--spec/ruby/core/process/wait2_spec.rb19
-rw-r--r--spec/ruby/core/process/wait_spec.rb18
-rw-r--r--spec/ruby/core/process/waitall_spec.rb10
-rw-r--r--spec/ruby/core/process/waitpid_spec.rb3
-rw-r--r--spec/ruby/core/process/warmup_spec.rb9
-rw-r--r--spec/ruby/core/queue/deq_spec.rb5
-rw-r--r--spec/ruby/core/queue/freeze_spec.rb6
-rw-r--r--spec/ruby/core/queue/initialize_spec.rb60
-rw-r--r--spec/ruby/core/queue/pop_spec.rb5
-rw-r--r--spec/ruby/core/queue/shift_spec.rb5
-rw-r--r--spec/ruby/core/random/bytes_spec.rb11
-rw-r--r--spec/ruby/core/random/default_spec.rb10
-rw-r--r--spec/ruby/core/random/new_seed_spec.rb4
-rw-r--r--spec/ruby/core/random/new_spec.rb9
-rw-r--r--spec/ruby/core/random/rand_spec.rb39
-rw-r--r--spec/ruby/core/random/random_number_spec.rb4
-rw-r--r--spec/ruby/core/random/raw_seed_spec.rb6
-rw-r--r--spec/ruby/core/random/seed_spec.rb2
-rw-r--r--spec/ruby/core/random/shared/bytes.rb2
-rw-r--r--spec/ruby/core/random/shared/rand.rb4
-rw-r--r--spec/ruby/core/random/shared/urandom.rb23
-rw-r--r--spec/ruby/core/random/urandom_spec.rb25
-rw-r--r--spec/ruby/core/range/bsearch_spec.rb393
-rw-r--r--spec/ruby/core/range/case_compare_spec.rb27
-rw-r--r--spec/ruby/core/range/clone_spec.rb26
-rw-r--r--spec/ruby/core/range/count_spec.rb12
-rw-r--r--spec/ruby/core/range/cover_spec.rb7
-rw-r--r--spec/ruby/core/range/dup_spec.rb10
-rw-r--r--spec/ruby/core/range/each_spec.rb67
-rw-r--r--spec/ruby/core/range/eql_spec.rb2
-rw-r--r--spec/ruby/core/range/equal_value_spec.rb10
-rw-r--r--spec/ruby/core/range/first_spec.rb12
-rw-r--r--spec/ruby/core/range/frozen_spec.rb25
-rw-r--r--spec/ruby/core/range/hash_spec.rb10
-rw-r--r--spec/ruby/core/range/include_spec.rb6
-rw-r--r--spec/ruby/core/range/initialize_spec.rb22
-rw-r--r--spec/ruby/core/range/inspect_spec.rb26
-rw-r--r--spec/ruby/core/range/last_spec.rb18
-rw-r--r--spec/ruby/core/range/max_spec.rb57
-rw-r--r--spec/ruby/core/range/member_spec.rb2
-rw-r--r--spec/ruby/core/range/min_spec.rb38
-rw-r--r--spec/ruby/core/range/minmax_spec.rb130
-rw-r--r--spec/ruby/core/range/new_spec.rb58
-rw-r--r--spec/ruby/core/range/overlap_spec.rb87
-rw-r--r--spec/ruby/core/range/percent_spec.rb24
-rw-r--r--spec/ruby/core/range/reverse_each_spec.rb125
-rw-r--r--spec/ruby/core/range/shared/cover.rb184
-rw-r--r--spec/ruby/core/range/shared/cover_and_include.rb57
-rw-r--r--spec/ruby/core/range/shared/equal_value.rb10
-rw-r--r--spec/ruby/core/range/shared/include.rb34
-rw-r--r--spec/ruby/core/range/size_spec.rb84
-rw-r--r--spec/ruby/core/range/step_spec.rb638
-rw-r--r--spec/ruby/core/range/to_a_spec.rb21
-rw-r--r--spec/ruby/core/range/to_s_spec.rb23
-rw-r--r--spec/ruby/core/range/to_set_spec.rb54
-rw-r--r--spec/ruby/core/rational/abs_spec.rb3
-rw-r--r--spec/ruby/core/rational/ceil_spec.rb47
-rw-r--r--spec/ruby/core/rational/coerce_spec.rb5
-rw-r--r--spec/ruby/core/rational/comparison_spec.rb87
-rw-r--r--spec/ruby/core/rational/denominator_spec.rb13
-rw-r--r--spec/ruby/core/rational/div_spec.rb47
-rw-r--r--spec/ruby/core/rational/divide_spec.rb67
-rw-r--r--spec/ruby/core/rational/divmod_spec.rb37
-rw-r--r--spec/ruby/core/rational/equal_value_spec.rb32
-rw-r--r--spec/ruby/core/rational/exponent_spec.rb235
-rw-r--r--spec/ruby/core/rational/fdiv_spec.rb4
-rw-r--r--spec/ruby/core/rational/fixtures/rational.rb (renamed from spec/ruby/fixtures/rational.rb)0
-rw-r--r--spec/ruby/core/rational/floor_spec.rb48
-rw-r--r--spec/ruby/core/rational/hash_spec.rb8
-rw-r--r--spec/ruby/core/rational/inspect_spec.rb13
-rw-r--r--spec/ruby/core/rational/integer_spec.rb5
-rw-r--r--spec/ruby/core/rational/magnitude_spec.rb3
-rw-r--r--spec/ruby/core/rational/marshal_dump_spec.rb2
-rw-r--r--spec/ruby/core/rational/minus_spec.rb50
-rw-r--r--spec/ruby/core/rational/modulo_spec.rb42
-rw-r--r--spec/ruby/core/rational/multiply_spec.rb58
-rw-r--r--spec/ruby/core/rational/numerator_spec.rb9
-rw-r--r--spec/ruby/core/rational/plus_spec.rb44
-rw-r--r--spec/ruby/core/rational/quo_spec.rb24
-rw-r--r--spec/ruby/core/rational/rational_spec.rb4
-rw-r--r--spec/ruby/core/rational/rationalize_spec.rb4
-rw-r--r--spec/ruby/core/rational/remainder_spec.rb4
-rw-r--r--spec/ruby/core/rational/round_spec.rb104
-rw-r--r--spec/ruby/core/rational/shared/abs.rb11
-rw-r--r--spec/ruby/core/rational/shared/arithmetic_exception_in_coerce.rb11
-rw-r--r--spec/ruby/core/rational/to_f_spec.rb15
-rw-r--r--spec/ruby/core/rational/to_i_spec.rb11
-rw-r--r--spec/ruby/core/rational/to_r_spec.rb14
-rw-r--r--spec/ruby/core/rational/to_s_spec.rb13
-rw-r--r--spec/ruby/core/rational/truncate_spec.rb70
-rw-r--r--spec/ruby/core/rational/zero_spec.rb7
-rw-r--r--spec/ruby/core/refinement/append_features_spec.rb19
-rw-r--r--spec/ruby/core/refinement/extend_object_spec.rb21
-rw-r--r--spec/ruby/core/refinement/fixtures/classes.rb10
-rw-r--r--spec/ruby/core/refinement/import_methods_spec.rb287
-rw-r--r--spec/ruby/core/refinement/include_spec.rb13
-rw-r--r--spec/ruby/core/refinement/prepend_features_spec.rb19
-rw-r--r--spec/ruby/core/refinement/prepend_spec.rb13
-rw-r--r--spec/ruby/core/refinement/refined_class_spec.rb34
-rw-r--r--spec/ruby/core/refinement/shared/target.rb13
-rw-r--r--spec/ruby/core/refinement/target_spec.rb6
-rw-r--r--spec/ruby/core/regexp/case_compare_spec.rb22
-rw-r--r--spec/ruby/core/regexp/compile_spec.rb4
-rw-r--r--spec/ruby/core/regexp/encoding_spec.rb2
-rw-r--r--spec/ruby/core/regexp/fixed_encoding_spec.rb16
-rw-r--r--spec/ruby/core/regexp/initialize_spec.rb24
-rw-r--r--spec/ruby/core/regexp/last_match_spec.rb46
-rw-r--r--spec/ruby/core/regexp/linear_time_spec.rb80
-rw-r--r--spec/ruby/core/regexp/match_spec.rb38
-rw-r--r--spec/ruby/core/regexp/named_captures_spec.rb4
-rw-r--r--spec/ruby/core/regexp/names_spec.rb4
-rw-r--r--spec/ruby/core/regexp/new_spec.rb14
-rw-r--r--spec/ruby/core/regexp/options_spec.rb8
-rw-r--r--spec/ruby/core/regexp/shared/new.rb402
-rw-r--r--spec/ruby/core/regexp/shared/quote.rb26
-rw-r--r--spec/ruby/core/regexp/source_spec.rb26
-rw-r--r--spec/ruby/core/regexp/timeout_spec.rb33
-rw-r--r--spec/ruby/core/regexp/try_convert_spec.rb8
-rw-r--r--spec/ruby/core/regexp/union_spec.rb51
-rw-r--r--spec/ruby/core/set/add_spec.rb34
-rw-r--r--spec/ruby/core/set/append_spec.rb6
-rw-r--r--spec/ruby/core/set/case_compare_spec.rb11
-rw-r--r--spec/ruby/core/set/case_equality_spec.rb6
-rw-r--r--spec/ruby/core/set/classify_spec.rb26
-rw-r--r--spec/ruby/core/set/clear_spec.rb16
-rw-r--r--spec/ruby/core/set/collect_spec.rb6
-rw-r--r--spec/ruby/core/set/compare_by_identity_spec.rb153
-rw-r--r--spec/ruby/core/set/comparison_spec.rb26
-rw-r--r--spec/ruby/core/set/constructor_spec.rb14
-rw-r--r--spec/ruby/core/set/delete_if_spec.rb37
-rw-r--r--spec/ruby/core/set/delete_spec.rb36
-rw-r--r--spec/ruby/core/set/difference_spec.rb6
-rw-r--r--spec/ruby/core/set/disjoint_spec.rb22
-rw-r--r--spec/ruby/core/set/divide_spec.rb68
-rw-r--r--spec/ruby/core/set/each_spec.rb26
-rw-r--r--spec/ruby/core/set/empty_spec.rb9
-rw-r--r--spec/ruby/core/set/enumerable/to_set_spec.rb12
-rw-r--r--spec/ruby/core/set/eql_spec.rb14
-rw-r--r--spec/ruby/core/set/equal_value_spec.rb34
-rw-r--r--spec/ruby/core/set/exclusion_spec.rb17
-rw-r--r--spec/ruby/core/set/filter_spec.rb6
-rw-r--r--spec/ruby/core/set/fixtures/set_like.rb30
-rw-r--r--spec/ruby/core/set/flatten_merge_spec.rb24
-rw-r--r--spec/ruby/core/set/flatten_spec.rb49
-rw-r--r--spec/ruby/core/set/hash_spec.rb19
-rw-r--r--spec/ruby/core/set/include_spec.rb6
-rw-r--r--spec/ruby/core/set/initialize_clone_spec.rb15
-rw-r--r--spec/ruby/core/set/initialize_spec.rb88
-rw-r--r--spec/ruby/core/set/inspect_spec.rb6
-rw-r--r--spec/ruby/core/set/intersect_spec.rb22
-rw-r--r--spec/ruby/core/set/intersection_spec.rb10
-rw-r--r--spec/ruby/core/set/join_spec.rb30
-rw-r--r--spec/ruby/core/set/keep_if_spec.rb37
-rw-r--r--spec/ruby/core/set/length_spec.rb6
-rw-r--r--spec/ruby/core/set/map_spec.rb6
-rw-r--r--spec/ruby/core/set/member_spec.rb6
-rw-r--r--spec/ruby/core/set/merge_spec.rb29
-rw-r--r--spec/ruby/core/set/minus_spec.rb6
-rw-r--r--spec/ruby/core/set/plus_spec.rb6
-rw-r--r--spec/ruby/core/set/pretty_print_cycle_spec.rb14
-rw-r--r--spec/ruby/core/set/proper_subset_spec.rb35
-rw-r--r--spec/ruby/core/set/proper_superset_spec.rb42
-rw-r--r--spec/ruby/core/set/reject_spec.rb41
-rw-r--r--spec/ruby/core/set/replace_spec.rb24
-rw-r--r--spec/ruby/core/set/select_spec.rb (renamed from spec/ruby/library/set/select_spec.rb)0
-rw-r--r--spec/ruby/core/set/set_spec.rb10
-rw-r--r--spec/ruby/core/set/shared/add.rb14
-rw-r--r--spec/ruby/core/set/shared/collect.rb20
-rw-r--r--spec/ruby/core/set/shared/difference.rb15
-rw-r--r--spec/ruby/core/set/shared/include.rb29
-rw-r--r--spec/ruby/core/set/shared/inspect.rb45
-rw-r--r--spec/ruby/core/set/shared/intersection.rb15
-rw-r--r--spec/ruby/core/set/shared/length.rb (renamed from spec/ruby/library/set/shared/length.rb)0
-rw-r--r--spec/ruby/core/set/shared/select.rb41
-rw-r--r--spec/ruby/core/set/shared/union.rb15
-rw-r--r--spec/ruby/core/set/size_spec.rb6
-rw-r--r--spec/ruby/core/set/sortedset/sortedset_spec.rb13
-rw-r--r--spec/ruby/core/set/subset_spec.rb35
-rw-r--r--spec/ruby/core/set/subtract_spec.rb16
-rw-r--r--spec/ruby/core/set/superset_spec.rb42
-rw-r--r--spec/ruby/core/set/to_a_spec.rb7
-rw-r--r--spec/ruby/core/set/to_s_spec.rb11
-rw-r--r--spec/ruby/core/set/union_spec.rb10
-rw-r--r--spec/ruby/core/signal/signame_spec.rb14
-rw-r--r--spec/ruby/core/signal/trap_spec.rb188
-rw-r--r--spec/ruby/core/sizedqueue/append_spec.rb5
-rw-r--r--spec/ruby/core/sizedqueue/deq_spec.rb5
-rw-r--r--spec/ruby/core/sizedqueue/enq_spec.rb5
-rw-r--r--spec/ruby/core/sizedqueue/freeze_spec.rb6
-rw-r--r--spec/ruby/core/sizedqueue/pop_spec.rb5
-rw-r--r--spec/ruby/core/sizedqueue/push_spec.rb5
-rw-r--r--spec/ruby/core/sizedqueue/shift_spec.rb5
-rw-r--r--spec/ruby/core/string/allocate_spec.rb4
-rw-r--r--spec/ruby/core/string/append_as_bytes_spec.rb60
-rw-r--r--spec/ruby/core/string/append_spec.rb6
-rw-r--r--spec/ruby/core/string/ascii_only_spec.rb41
-rw-r--r--spec/ruby/core/string/b_spec.rb12
-rw-r--r--spec/ruby/core/string/byteindex_spec.rb298
-rw-r--r--spec/ruby/core/string/byterindex_spec.rb353
-rw-r--r--spec/ruby/core/string/bytes_spec.rb10
-rw-r--r--spec/ruby/core/string/bytesize_spec.rb12
-rw-r--r--spec/ruby/core/string/byteslice_spec.rb18
-rw-r--r--spec/ruby/core/string/bytesplice_spec.rb290
-rw-r--r--spec/ruby/core/string/capitalize_spec.rb70
-rw-r--r--spec/ruby/core/string/casecmp_spec.rb20
-rw-r--r--spec/ruby/core/string/center_spec.rb58
-rw-r--r--spec/ruby/core/string/chars_spec.rb8
-rw-r--r--spec/ruby/core/string/chilled_string_spec.rb151
-rw-r--r--spec/ruby/core/string/chomp_spec.rb113
-rw-r--r--spec/ruby/core/string/chop_spec.rb27
-rw-r--r--spec/ruby/core/string/chr_spec.rb4
-rw-r--r--spec/ruby/core/string/clear_spec.rb7
-rw-r--r--spec/ruby/core/string/clone_spec.rb8
-rw-r--r--spec/ruby/core/string/codepoints_spec.rb8
-rw-r--r--spec/ruby/core/string/comparison_spec.rb14
-rw-r--r--spec/ruby/core/string/concat_spec.rb9
-rw-r--r--spec/ruby/core/string/count_spec.rb14
-rw-r--r--spec/ruby/core/string/crypt_spec.rb60
-rw-r--r--spec/ruby/core/string/dedup_spec.rb6
-rw-r--r--spec/ruby/core/string/delete_prefix_spec.rb30
-rw-r--r--spec/ruby/core/string/delete_spec.rb38
-rw-r--r--spec/ruby/core/string/delete_suffix_spec.rb30
-rw-r--r--spec/ruby/core/string/downcase_spec.rb45
-rw-r--r--spec/ruby/core/string/dump_spec.rb26
-rw-r--r--spec/ruby/core/string/dup_spec.rb19
-rw-r--r--spec/ruby/core/string/each_byte_spec.rb20
-rw-r--r--spec/ruby/core/string/each_char_spec.rb1
-rw-r--r--spec/ruby/core/string/each_grapheme_cluster_spec.rb7
-rw-r--r--spec/ruby/core/string/element_set_spec.rb127
-rw-r--r--spec/ruby/core/string/encode_spec.rb143
-rw-r--r--spec/ruby/core/string/encoding_spec.rb37
-rw-r--r--spec/ruby/core/string/end_with_spec.rb52
-rw-r--r--spec/ruby/core/string/eql_spec.rb8
-rw-r--r--spec/ruby/core/string/fixtures/classes.rb11
-rw-r--r--spec/ruby/core/string/fixtures/iso-8859-9-encoding.rb2
-rw-r--r--spec/ruby/core/string/fixtures/to_c.rb5
-rw-r--r--spec/ruby/core/string/fixtures/utf-8-encoding.rb7
-rw-r--r--spec/ruby/core/string/force_encoding_spec.rb13
-rw-r--r--spec/ruby/core/string/freeze_spec.rb5
-rw-r--r--spec/ruby/core/string/getbyte_spec.rb12
-rw-r--r--spec/ruby/core/string/grapheme_clusters_spec.rb1
-rw-r--r--spec/ruby/core/string/gsub_spec.rb243
-rw-r--r--spec/ruby/core/string/include_spec.rb22
-rw-r--r--spec/ruby/core/string/index_spec.rb41
-rw-r--r--spec/ruby/core/string/initialize_spec.rb4
-rw-r--r--spec/ruby/core/string/insert_spec.rb39
-rw-r--r--spec/ruby/core/string/inspect_spec.rb40
-rw-r--r--spec/ruby/core/string/lines_spec.rb1
-rw-r--r--spec/ruby/core/string/ljust_spec.rb56
-rw-r--r--spec/ruby/core/string/lstrip_spec.rb64
-rw-r--r--spec/ruby/core/string/match_spec.rb34
-rw-r--r--spec/ruby/core/string/modulo_spec.rb249
-rw-r--r--spec/ruby/core/string/new_spec.rb10
-rw-r--r--spec/ruby/core/string/ord_spec.rb11
-rw-r--r--spec/ruby/core/string/partition_spec.rb29
-rw-r--r--spec/ruby/core/string/plus_spec.rb36
-rw-r--r--spec/ruby/core/string/prepend_spec.rb23
-rw-r--r--spec/ruby/core/string/reverse_spec.rb38
-rw-r--r--spec/ruby/core/string/rindex_spec.rb38
-rw-r--r--spec/ruby/core/string/rjust_spec.rb56
-rw-r--r--spec/ruby/core/string/rpartition_spec.rb42
-rw-r--r--spec/ruby/core/string/rstrip_spec.rb50
-rw-r--r--spec/ruby/core/string/scan_spec.rb82
-rw-r--r--spec/ruby/core/string/scrub_spec.rb69
-rw-r--r--spec/ruby/core/string/setbyte_spec.rb23
-rw-r--r--spec/ruby/core/string/shared/byte_index_common.rb63
-rw-r--r--spec/ruby/core/string/shared/chars.rb44
-rw-r--r--spec/ruby/core/string/shared/codepoints.rb29
-rw-r--r--spec/ruby/core/string/shared/concat.rb75
-rw-r--r--spec/ruby/core/string/shared/dedup.rb51
-rw-r--r--spec/ruby/core/string/shared/each_char_without_block.rb2
-rw-r--r--spec/ruby/core/string/shared/each_codepoint_without_block.rb14
-rw-r--r--spec/ruby/core/string/shared/each_line.rb78
-rw-r--r--spec/ruby/core/string/shared/each_line_without_block.rb2
-rw-r--r--spec/ruby/core/string/shared/encode.rb215
-rw-r--r--spec/ruby/core/string/shared/eql.rb22
-rw-r--r--spec/ruby/core/string/shared/equal_value.rb10
-rw-r--r--spec/ruby/core/string/shared/grapheme_clusters.rb11
-rw-r--r--spec/ruby/core/string/shared/length.rb28
-rw-r--r--spec/ruby/core/string/shared/partition.rb33
-rw-r--r--spec/ruby/core/string/shared/replace.rb45
-rw-r--r--spec/ruby/core/string/shared/slice.rb276
-rw-r--r--spec/ruby/core/string/shared/strip.rb14
-rw-r--r--spec/ruby/core/string/shared/succ.rb25
-rw-r--r--spec/ruby/core/string/shared/to_a.rb9
-rw-r--r--spec/ruby/core/string/shared/to_s.rb11
-rw-r--r--spec/ruby/core/string/shared/to_sym.rb43
-rw-r--r--spec/ruby/core/string/slice_spec.rb156
-rw-r--r--spec/ruby/core/string/split_spec.rb335
-rw-r--r--spec/ruby/core/string/squeeze_spec.rb42
-rw-r--r--spec/ruby/core/string/start_with_spec.rb72
-rw-r--r--spec/ruby/core/string/strip_spec.rb42
-rw-r--r--spec/ruby/core/string/sub_spec.rb197
-rw-r--r--spec/ruby/core/string/swapcase_spec.rb48
-rw-r--r--spec/ruby/core/string/to_c_spec.rb116
-rw-r--r--spec/ruby/core/string/to_f_spec.rb104
-rw-r--r--spec/ruby/core/string/to_i_spec.rb36
-rw-r--r--spec/ruby/core/string/to_r_spec.rb6
-rw-r--r--spec/ruby/core/string/tr_s_spec.rb31
-rw-r--r--spec/ruby/core/string/tr_spec.rb35
-rw-r--r--spec/ruby/core/string/try_convert_spec.rb16
-rw-r--r--spec/ruby/core/string/uminus_spec.rb70
-rw-r--r--spec/ruby/core/string/undump_spec.rb48
-rw-r--r--spec/ruby/core/string/unicode_normalize_spec.rb9
-rw-r--r--spec/ruby/core/string/unicode_normalized_spec.rb21
-rw-r--r--spec/ruby/core/string/unpack/a_spec.rb4
-rw-r--r--spec/ruby/core/string/unpack/at_spec.rb4
-rw-r--r--spec/ruby/core/string/unpack/b_spec.rb22
-rw-r--r--spec/ruby/core/string/unpack/c_spec.rb8
-rw-r--r--spec/ruby/core/string/unpack/carret_spec.rb43
-rw-r--r--spec/ruby/core/string/unpack/comment_spec.rb2
-rw-r--r--spec/ruby/core/string/unpack/h_spec.rb14
-rw-r--r--spec/ruby/core/string/unpack/l_spec.rb16
-rw-r--r--spec/ruby/core/string/unpack/m_spec.rb21
-rw-r--r--spec/ruby/core/string/unpack/p_spec.rb16
-rw-r--r--spec/ruby/core/string/unpack/percent_spec.rb2
-rw-r--r--spec/ruby/core/string/unpack/r_spec.rb85
-rw-r--r--spec/ruby/core/string/unpack/shared/basic.rb18
-rw-r--r--spec/ruby/core/string/unpack/shared/float.rb36
-rw-r--r--spec/ruby/core/string/unpack/shared/integer.rb40
-rw-r--r--spec/ruby/core/string/unpack/shared/taint.rb81
-rw-r--r--spec/ruby/core/string/unpack/shared/unicode.rb6
-rw-r--r--spec/ruby/core/string/unpack/u_spec.rb8
-rw-r--r--spec/ruby/core/string/unpack/w_spec.rb18
-rw-r--r--spec/ruby/core/string/unpack/x_spec.rb8
-rw-r--r--spec/ruby/core/string/unpack/z_spec.rb7
-rw-r--r--spec/ruby/core/string/unpack1_spec.rb51
-rw-r--r--spec/ruby/core/string/unpack_spec.rb46
-rw-r--r--spec/ruby/core/string/upcase_spec.rb47
-rw-r--r--spec/ruby/core/string/uplus_spec.rb48
-rw-r--r--spec/ruby/core/string/upto_spec.rb20
-rw-r--r--spec/ruby/core/string/valid_encoding/utf_8_spec.rb214
-rw-r--r--spec/ruby/core/string/valid_encoding_spec.rb206
-rw-r--r--spec/ruby/core/struct/constants_spec.rb13
-rw-r--r--spec/ruby/core/struct/deconstruct_keys_spec.rb192
-rw-r--r--spec/ruby/core/struct/deconstruct_spec.rb12
-rw-r--r--spec/ruby/core/struct/dig_spec.rb14
-rw-r--r--spec/ruby/core/struct/each_pair_spec.rb4
-rw-r--r--spec/ruby/core/struct/each_spec.rb2
-rw-r--r--spec/ruby/core/struct/element_reference_spec.rb14
-rw-r--r--spec/ruby/core/struct/element_set_spec.rb15
-rw-r--r--spec/ruby/core/struct/eql_spec.rb2
-rw-r--r--spec/ruby/core/struct/filter_spec.rb10
-rw-r--r--spec/ruby/core/struct/fixtures/classes.rb9
-rw-r--r--spec/ruby/core/struct/hash_spec.rb10
-rw-r--r--spec/ruby/core/struct/initialize_spec.rb33
-rw-r--r--spec/ruby/core/struct/inspect_spec.rb5
-rw-r--r--spec/ruby/core/struct/instance_variable_get_spec.rb2
-rw-r--r--spec/ruby/core/struct/keyword_init_spec.rb45
-rw-r--r--spec/ruby/core/struct/members_spec.rb12
-rw-r--r--spec/ruby/core/struct/new_spec.rb118
-rw-r--r--spec/ruby/core/struct/shared/inspect.rb35
-rw-r--r--spec/ruby/core/struct/shared/select.rb6
-rw-r--r--spec/ruby/core/struct/struct_spec.rb9
-rw-r--r--spec/ruby/core/struct/to_h_spec.rb90
-rw-r--r--spec/ruby/core/struct/values_at_spec.rb55
-rw-r--r--spec/ruby/core/symbol/all_symbols_spec.rb8
-rw-r--r--spec/ruby/core/symbol/capitalize_spec.rb2
-rw-r--r--spec/ruby/core/symbol/casecmp_spec.rb16
-rw-r--r--spec/ruby/core/symbol/comparison_spec.rb8
-rw-r--r--spec/ruby/core/symbol/downcase_spec.rb2
-rw-r--r--spec/ruby/core/symbol/dup_spec.rb2
-rw-r--r--spec/ruby/core/symbol/empty_spec.rb4
-rw-r--r--spec/ruby/core/symbol/end_with_spec.rb8
-rw-r--r--spec/ruby/core/symbol/inspect_spec.rb34
-rw-r--r--spec/ruby/core/symbol/intern_spec.rb2
-rw-r--r--spec/ruby/core/symbol/match_spec.rb18
-rw-r--r--spec/ruby/core/symbol/name_spec.rb17
-rw-r--r--spec/ruby/core/symbol/shared/id2name.rb21
-rw-r--r--spec/ruby/core/symbol/shared/slice.rb78
-rw-r--r--spec/ruby/core/symbol/start_with_spec.rb8
-rw-r--r--spec/ruby/core/symbol/swapcase_spec.rb2
-rw-r--r--spec/ruby/core/symbol/symbol_spec.rb4
-rw-r--r--spec/ruby/core/symbol/to_proc_spec.rb62
-rw-r--r--spec/ruby/core/symbol/upcase_spec.rb2
-rw-r--r--spec/ruby/core/thread/abort_on_exception_spec.rb10
-rw-r--r--spec/ruby/core/thread/allocate_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace/limit_spec.rb13
-rw-r--r--spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb31
-rw-r--r--spec/ruby/core/thread/backtrace/location/base_label_spec.rb27
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/classes.rb104
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/subdir/absolute_path_main_chdir.rb11
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/subdir/sibling.rb1
-rw-r--r--spec/ruby/core/thread/backtrace/location/inspect_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace/location/label_spec.rb204
-rw-r--r--spec/ruby/core/thread/backtrace/location/lineno_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace/location/path_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace/location/to_s_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace_locations_spec.rb30
-rw-r--r--spec/ruby/core/thread/backtrace_spec.rb6
-rw-r--r--spec/ruby/core/thread/current_spec.rb10
-rw-r--r--spec/ruby/core/thread/each_caller_location_spec.rb47
-rw-r--r--spec/ruby/core/thread/element_reference_spec.rb15
-rw-r--r--spec/ruby/core/thread/element_set_spec.rb31
-rw-r--r--spec/ruby/core/thread/exclusive_spec.rb47
-rw-r--r--spec/ruby/core/thread/exit_spec.rb2
-rw-r--r--spec/ruby/core/thread/fetch_spec.rb36
-rw-r--r--spec/ruby/core/thread/fixtures/classes.rb27
-rw-r--r--spec/ruby/core/thread/group_spec.rb15
-rw-r--r--spec/ruby/core/thread/handle_interrupt_spec.rb125
-rw-r--r--spec/ruby/core/thread/ignore_deadlock_spec.rb19
-rw-r--r--spec/ruby/core/thread/initialize_spec.rb2
-rw-r--r--spec/ruby/core/thread/join_spec.rb25
-rw-r--r--spec/ruby/core/thread/key_spec.rb23
-rw-r--r--spec/ruby/core/thread/keys_spec.rb12
-rw-r--r--spec/ruby/core/thread/kill_spec.rb6
-rw-r--r--spec/ruby/core/thread/list_spec.rb16
-rw-r--r--spec/ruby/core/thread/name_spec.rb2
-rw-r--r--spec/ruby/core/thread/native_thread_id_spec.rb31
-rw-r--r--spec/ruby/core/thread/new_spec.rb4
-rw-r--r--spec/ruby/core/thread/pending_interrupt_spec.rb32
-rw-r--r--spec/ruby/core/thread/priority_spec.rb8
-rw-r--r--spec/ruby/core/thread/raise_spec.rb91
-rw-r--r--spec/ruby/core/thread/report_on_exception_spec.rb57
-rw-r--r--spec/ruby/core/thread/shared/exit.rb47
-rw-r--r--spec/ruby/core/thread/shared/start.rb8
-rw-r--r--spec/ruby/core/thread/shared/to_s.rb24
-rw-r--r--spec/ruby/core/thread/shared/wakeup.rb5
-rw-r--r--spec/ruby/core/thread/thread_variable_get_spec.rb45
-rw-r--r--spec/ruby/core/thread/thread_variable_set_spec.rb44
-rw-r--r--spec/ruby/core/thread/thread_variable_spec.rb47
-rw-r--r--spec/ruby/core/thread/thread_variables_spec.rb25
-rw-r--r--spec/ruby/core/thread/value_spec.rb2
-rw-r--r--spec/ruby/core/threadgroup/default_spec.rb2
-rw-r--r--spec/ruby/core/threadgroup/enclose_spec.rb2
-rw-r--r--spec/ruby/core/threadgroup/enclosed_spec.rb4
-rw-r--r--spec/ruby/core/threadgroup/list_spec.rb4
-rw-r--r--spec/ruby/core/time/_dump_spec.rb6
-rw-r--r--spec/ruby/core/time/_load_spec.rb7
-rw-r--r--spec/ruby/core/time/at_spec.rb170
-rw-r--r--spec/ruby/core/time/ceil_spec.rb64
-rw-r--r--spec/ruby/core/time/comparison_spec.rb28
-rw-r--r--spec/ruby/core/time/deconstruct_keys_spec.rb43
-rw-r--r--spec/ruby/core/time/dup_spec.rb14
-rw-r--r--spec/ruby/core/time/eql_spec.rb16
-rw-r--r--spec/ruby/core/time/fixtures/classes.rb1
-rw-r--r--spec/ruby/core/time/floor_spec.rb52
-rw-r--r--spec/ruby/core/time/getlocal_spec.rb151
-rw-r--r--spec/ruby/core/time/hash_spec.rb4
-rw-r--r--spec/ruby/core/time/inspect_spec.rb34
-rw-r--r--spec/ruby/core/time/iso8601_spec.rb6
-rw-r--r--spec/ruby/core/time/localtime_spec.rb83
-rw-r--r--spec/ruby/core/time/minus_spec.rb28
-rw-r--r--spec/ruby/core/time/new_spec.rb752
-rw-r--r--spec/ruby/core/time/now_spec.rb175
-rw-r--r--spec/ruby/core/time/plus_spec.rb28
-rw-r--r--spec/ruby/core/time/round_spec.rb4
-rw-r--r--spec/ruby/core/time/shared/gmtime.rb17
-rw-r--r--spec/ruby/core/time/shared/inspect.rb2
-rw-r--r--spec/ruby/core/time/shared/local.rb11
-rw-r--r--spec/ruby/core/time/shared/now.rb16
-rw-r--r--spec/ruby/core/time/shared/time_params.rb41
-rw-r--r--spec/ruby/core/time/shared/xmlschema.rb31
-rw-r--r--spec/ruby/core/time/strftime_spec.rb41
-rw-r--r--spec/ruby/core/time/subsec_spec.rb14
-rw-r--r--spec/ruby/core/time/succ_spec.rb39
-rw-r--r--spec/ruby/core/time/to_r_spec.rb4
-rw-r--r--spec/ruby/core/time/utc_spec.rb49
-rw-r--r--spec/ruby/core/time/xmlschema_spec.rb6
-rw-r--r--spec/ruby/core/time/yday_spec.rb13
-rw-r--r--spec/ruby/core/time/zone_spec.rb31
-rw-r--r--spec/ruby/core/tracepoint/allow_reentry_spec.rb30
-rw-r--r--spec/ruby/core/tracepoint/binding_spec.rb2
-rw-r--r--spec/ruby/core/tracepoint/defined_class_spec.rb10
-rw-r--r--spec/ruby/core/tracepoint/enable_spec.rb562
-rw-r--r--spec/ruby/core/tracepoint/eval_script_spec.rb30
-rw-r--r--spec/ruby/core/tracepoint/event_spec.rb6
-rw-r--r--spec/ruby/core/tracepoint/inspect_spec.rb50
-rw-r--r--spec/ruby/core/tracepoint/lineno_spec.rb2
-rw-r--r--spec/ruby/core/tracepoint/method_id_spec.rb2
-rw-r--r--spec/ruby/core/tracepoint/new_spec.rb22
-rw-r--r--spec/ruby/core/tracepoint/parameters_spec.rb42
-rw-r--r--spec/ruby/core/tracepoint/path_spec.rb4
-rw-r--r--spec/ruby/core/tracepoint/raised_exception_spec.rb18
-rw-r--r--spec/ruby/core/tracepoint/self_spec.rb4
-rw-r--r--spec/ruby/core/true/case_compare_spec.rb13
-rw-r--r--spec/ruby/core/true/dup_spec.rb2
-rw-r--r--spec/ruby/core/true/singleton_method_spec.rb13
-rw-r--r--spec/ruby/core/true/to_s_spec.rb12
-rw-r--r--spec/ruby/core/true/trueclass_spec.rb4
-rw-r--r--spec/ruby/core/unboundmethod/bind_call_spec.rb82
-rw-r--r--spec/ruby/core/unboundmethod/bind_spec.rb24
-rw-r--r--spec/ruby/core/unboundmethod/clone_spec.rb13
-rw-r--r--spec/ruby/core/unboundmethod/dup_spec.rb15
-rw-r--r--spec/ruby/core/unboundmethod/equal_value_spec.rb86
-rw-r--r--spec/ruby/core/unboundmethod/fixtures/classes.rb37
-rw-r--r--spec/ruby/core/unboundmethod/hash_spec.rb7
-rw-r--r--spec/ruby/core/unboundmethod/inspect_spec.rb2
-rw-r--r--spec/ruby/core/unboundmethod/original_name_spec.rb37
-rw-r--r--spec/ruby/core/unboundmethod/owner_spec.rb5
-rw-r--r--spec/ruby/core/unboundmethod/private_spec.rb9
-rw-r--r--spec/ruby/core/unboundmethod/protected_spec.rb9
-rw-r--r--spec/ruby/core/unboundmethod/public_spec.rb9
-rw-r--r--spec/ruby/core/unboundmethod/shared/dup.rb32
-rw-r--r--spec/ruby/core/unboundmethod/shared/to_s.rb14
-rw-r--r--spec/ruby/core/unboundmethod/source_location_spec.rb15
-rw-r--r--spec/ruby/core/unboundmethod/super_method_spec.rb21
-rw-r--r--spec/ruby/core/unboundmethod/to_s_spec.rb2
-rw-r--r--spec/ruby/core/warning/categories_spec.rb12
-rw-r--r--spec/ruby/core/warning/element_reference_spec.rb27
-rw-r--r--spec/ruby/core/warning/element_set_spec.rb39
-rw-r--r--spec/ruby/core/warning/performance_warning_spec.rb28
-rw-r--r--spec/ruby/core/warning/warn_spec.rb129
-rw-r--r--spec/ruby/default.mspec7
-rw-r--r--spec/ruby/fixtures/class.rb4
-rw-r--r--spec/ruby/fixtures/code/a/load_fixture.dylib1
-rw-r--r--spec/ruby/fixtures/code/c/load_fixture.rb1
-rw-r--r--spec/ruby/fixtures/code/concurrent.rb2
-rw-r--r--spec/ruby/fixtures/code/concurrent_require_fixture.rb4
-rw-r--r--spec/ruby/fixtures/code/d/load_fixture.rb.rb1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.dylib1
-rw-r--r--spec/ruby/fixtures/code/load_fixture.ext.dylib1
-rw-r--r--spec/ruby/fixtures/code/load_wrap_fixture.rb12
-rw-r--r--spec/ruby/fixtures/code/wrap_fixture.rb9
-rw-r--r--spec/ruby/fixtures/code_loading.rb15
-rw-r--r--spec/ruby/fixtures/constants.rb42
-rw-r--r--spec/ruby/fixtures/io.rb12
-rw-r--r--spec/ruby/language/BEGIN_spec.rb7
-rw-r--r--spec/ruby/language/END_spec.rb26
-rw-r--r--spec/ruby/language/README2
-rw-r--r--spec/ruby/language/alias_spec.rb47
-rw-r--r--spec/ruby/language/and_spec.rb16
-rw-r--r--spec/ruby/language/array_spec.rb18
-rw-r--r--spec/ruby/language/assignments_spec.rb582
-rw-r--r--spec/ruby/language/block_spec.rb572
-rw-r--r--spec/ruby/language/break_spec.rb37
-rw-r--r--spec/ruby/language/case_spec.rb223
-rw-r--r--spec/ruby/language/class_spec.rb122
-rw-r--r--spec/ruby/language/class_variable_spec.rb44
-rw-r--r--spec/ruby/language/comment_spec.rb16
-rw-r--r--spec/ruby/language/constants_spec.rb310
-rw-r--r--spec/ruby/language/def_spec.rb171
-rw-r--r--spec/ruby/language/defined_spec.rb442
-rw-r--r--spec/ruby/language/delegation_spec.rb171
-rw-r--r--spec/ruby/language/encoding_spec.rb12
-rw-r--r--spec/ruby/language/ensure_spec.rb49
-rw-r--r--spec/ruby/language/execution_spec.rb78
-rw-r--r--spec/ruby/language/file_spec.rb18
-rw-r--r--spec/ruby/language/fixtures/class_with_class_variable.rb9
-rw-r--r--spec/ruby/language/fixtures/constant_visibility.rb18
-rw-r--r--spec/ruby/language/fixtures/defined.rb36
-rw-r--r--spec/ruby/language/fixtures/delegation.rb4
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_across_files.rb3
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_across_files_diff_enc.rb3
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_across_files_no_comment.rb3
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_one_literal.rb4
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_required.rb2
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_required_diff_enc.rbbin181 -> 107 bytes-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_required_no_comment.rb2
-rw-r--r--spec/ruby/language/fixtures/freeze_magic_comment_two_literals.rb2
-rw-r--r--spec/ruby/language/fixtures/module.rb9
-rw-r--r--spec/ruby/language/fixtures/private.rb26
-rw-r--r--spec/ruby/language/fixtures/rescue/top_level.rb7
-rw-r--r--spec/ruby/language/fixtures/return.rb8
-rw-r--r--spec/ruby/language/fixtures/send.rb16
-rw-r--r--spec/ruby/language/fixtures/squiggly_heredoc.rb8
-rw-r--r--spec/ruby/language/fixtures/super.rb110
-rw-r--r--spec/ruby/language/fixtures/variables.rb72
-rw-r--r--spec/ruby/language/for_spec.rb202
-rw-r--r--spec/ruby/language/hash_spec.rb210
-rw-r--r--spec/ruby/language/heredoc_spec.rb21
-rw-r--r--spec/ruby/language/if_spec.rb53
-rw-r--r--spec/ruby/language/it_parameter_spec.rb108
-rw-r--r--spec/ruby/language/keyword_arguments_spec.rb398
-rw-r--r--spec/ruby/language/lambda_spec.rb267
-rw-r--r--spec/ruby/language/line_spec.rb2
-rw-r--r--spec/ruby/language/loop_spec.rb2
-rw-r--r--spec/ruby/language/magic_comment_spec.rb3
-rw-r--r--spec/ruby/language/match_spec.rb8
-rw-r--r--spec/ruby/language/metaclass_spec.rb22
-rw-r--r--spec/ruby/language/method_spec.rb1256
-rw-r--r--spec/ruby/language/module_spec.rb68
-rw-r--r--spec/ruby/language/next_spec.rb4
-rw-r--r--spec/ruby/language/not_spec.rb32
-rw-r--r--spec/ruby/language/numbered_parameters_spec.rb179
-rw-r--r--spec/ruby/language/numbers_spec.rb12
-rw-r--r--spec/ruby/language/optional_assignments_spec.rb423
-rw-r--r--spec/ruby/language/or_spec.rb32
-rw-r--r--spec/ruby/language/pattern_matching_spec.rb1939
-rw-r--r--spec/ruby/language/precedence_spec.rb114
-rw-r--r--spec/ruby/language/predefined_spec.rb871
-rw-r--r--spec/ruby/language/private_spec.rb22
-rw-r--r--spec/ruby/language/proc_spec.rb73
-rw-r--r--spec/ruby/language/range_spec.rb22
-rw-r--r--spec/ruby/language/redo_spec.rb2
-rw-r--r--spec/ruby/language/regexp/anchors_spec.rb62
-rw-r--r--spec/ruby/language/regexp/back-references_spec.rb98
-rw-r--r--spec/ruby/language/regexp/character_classes_spec.rb236
-rw-r--r--spec/ruby/language/regexp/empty_checks_spec.rb135
-rw-r--r--spec/ruby/language/regexp/encoding_spec.rb63
-rw-r--r--spec/ruby/language/regexp/escapes_spec.rb112
-rw-r--r--spec/ruby/language/regexp/grouping_spec.rb42
-rw-r--r--spec/ruby/language/regexp/interpolation_spec.rb6
-rw-r--r--spec/ruby/language/regexp/modifiers_spec.rb40
-rw-r--r--spec/ruby/language/regexp/repetition_spec.rb92
-rw-r--r--spec/ruby/language/regexp/subexpression_call_spec.rb50
-rw-r--r--spec/ruby/language/regexp_spec.rb60
-rw-r--r--spec/ruby/language/rescue_spec.rb193
-rw-r--r--spec/ruby/language/reserved_keywords.rb149
-rw-r--r--spec/ruby/language/retry_spec.rb7
-rw-r--r--spec/ruby/language/return_spec.rb73
-rw-r--r--spec/ruby/language/safe_navigator_spec.rb84
-rw-r--r--spec/ruby/language/safe_spec.rb139
-rw-r--r--spec/ruby/language/send_spec.rb125
-rw-r--r--spec/ruby/language/shared/__FILE__.rb4
-rw-r--r--spec/ruby/language/shared/__LINE__.rb2
-rw-r--r--spec/ruby/language/singleton_class_spec.rb114
-rw-r--r--spec/ruby/language/source_encoding_spec.rb6
-rw-r--r--spec/ruby/language/string_spec.rb73
-rw-r--r--spec/ruby/language/super_spec.rb69
-rw-r--r--spec/ruby/language/symbol_spec.rb34
-rw-r--r--spec/ruby/language/throw_spec.rb10
-rw-r--r--spec/ruby/language/undef_spec.rb23
-rw-r--r--spec/ruby/language/variables_spec.rb218
-rw-r--r--spec/ruby/language/while_spec.rb16
-rw-r--r--spec/ruby/language/yield_spec.rb59
-rw-r--r--spec/ruby/library/English/English_spec.rb62
-rw-r--r--spec/ruby/library/English/alias_spec.rb6
-rw-r--r--spec/ruby/library/base64/decode64_spec.rb4
-rw-r--r--spec/ruby/library/base64/strict_decode64_spec.rb8
-rw-r--r--spec/ruby/library/bigdecimal/BigDecimal_spec.rb111
-rw-r--r--spec/ruby/library/bigdecimal/add_spec.rb20
-rw-r--r--spec/ruby/library/bigdecimal/ceil_spec.rb6
-rw-r--r--spec/ruby/library/bigdecimal/constants_spec.rb6
-rw-r--r--spec/ruby/library/bigdecimal/core_spec.rb62
-rw-r--r--spec/ruby/library/bigdecimal/div_spec.rb28
-rw-r--r--spec/ruby/library/bigdecimal/divmod_spec.rb88
-rw-r--r--spec/ruby/library/bigdecimal/exponent_spec.rb11
-rw-r--r--spec/ruby/library/bigdecimal/fix_spec.rb30
-rw-r--r--spec/ruby/library/bigdecimal/floor_spec.rb6
-rw-r--r--spec/ruby/library/bigdecimal/gt_spec.rb12
-rw-r--r--spec/ruby/library/bigdecimal/gte_spec.rb12
-rw-r--r--spec/ruby/library/bigdecimal/lt_spec.rb12
-rw-r--r--spec/ruby/library/bigdecimal/lte_spec.rb12
-rw-r--r--spec/ruby/library/bigdecimal/mode_spec.rb10
-rw-r--r--spec/ruby/library/bigdecimal/mult_spec.rb8
-rw-r--r--spec/ruby/library/bigdecimal/nonzero_spec.rb10
-rw-r--r--spec/ruby/library/bigdecimal/precs_spec.rb48
-rw-r--r--spec/ruby/library/bigdecimal/remainder_spec.rb27
-rw-r--r--spec/ruby/library/bigdecimal/round_spec.rb16
-rw-r--r--spec/ruby/library/bigdecimal/shared/clone.rb2
-rw-r--r--spec/ruby/library/bigdecimal/shared/modulo.rb22
-rw-r--r--spec/ruby/library/bigdecimal/shared/power.rb4
-rw-r--r--spec/ruby/library/bigdecimal/shared/quo.rb1
-rw-r--r--spec/ruby/library/bigdecimal/shared/to_int.rb8
-rw-r--r--spec/ruby/library/bigdecimal/split_spec.rb20
-rw-r--r--spec/ruby/library/bigdecimal/sqrt_spec.rb24
-rw-r--r--spec/ruby/library/bigdecimal/sub_spec.rb8
-rw-r--r--spec/ruby/library/bigdecimal/to_f_spec.rb6
-rw-r--r--spec/ruby/library/bigdecimal/to_i_spec.rb2
-rw-r--r--spec/ruby/library/bigdecimal/to_r_spec.rb18
-rw-r--r--spec/ruby/library/bigdecimal/to_s_spec.rb27
-rw-r--r--spec/ruby/library/bigdecimal/truncate_spec.rb16
-rw-r--r--spec/ruby/library/bigdecimal/util_spec.rb8
-rw-r--r--spec/ruby/library/bigmath/log_spec.rb10
-rw-r--r--spec/ruby/library/cgi/cookie/domain_spec.rb33
-rw-r--r--spec/ruby/library/cgi/cookie/expires_spec.rb33
-rw-r--r--spec/ruby/library/cgi/cookie/initialize_spec.rb235
-rw-r--r--spec/ruby/library/cgi/cookie/name_spec.rb33
-rw-r--r--spec/ruby/library/cgi/cookie/parse_spec.rb41
-rw-r--r--spec/ruby/library/cgi/cookie/path_spec.rb33
-rw-r--r--spec/ruby/library/cgi/cookie/secure_spec.rb99
-rw-r--r--spec/ruby/library/cgi/cookie/to_s_spec.rb51
-rw-r--r--spec/ruby/library/cgi/cookie/value_spec.rb121
-rw-r--r--spec/ruby/library/cgi/escapeElement_spec.rb8
-rw-r--r--spec/ruby/library/cgi/escapeHTML_spec.rb6
-rw-r--r--spec/ruby/library/cgi/escapeURIComponent_spec.rb78
-rw-r--r--spec/ruby/library/cgi/escape_spec.rb6
-rw-r--r--spec/ruby/library/cgi/htmlextension/a_spec.rb73
-rw-r--r--spec/ruby/library/cgi/htmlextension/base_spec.rb47
-rw-r--r--spec/ruby/library/cgi/htmlextension/blockquote_spec.rb47
-rw-r--r--spec/ruby/library/cgi/htmlextension/br_spec.rb31
-rw-r--r--spec/ruby/library/cgi/htmlextension/caption_spec.rb47
-rw-r--r--spec/ruby/library/cgi/htmlextension/checkbox_group_spec.rb121
-rw-r--r--spec/ruby/library/cgi/htmlextension/checkbox_spec.rb113
-rw-r--r--spec/ruby/library/cgi/htmlextension/doctype_spec.rb41
-rw-r--r--spec/ruby/library/cgi/htmlextension/file_field_spec.rb105
-rw-r--r--spec/ruby/library/cgi/htmlextension/form_spec.rb85
-rw-r--r--spec/ruby/library/cgi/htmlextension/frame_spec.rb21
-rw-r--r--spec/ruby/library/cgi/htmlextension/frameset_spec.rb21
-rw-r--r--spec/ruby/library/cgi/htmlextension/hidden_spec.rb87
-rw-r--r--spec/ruby/library/cgi/htmlextension/html_spec.rb99
-rw-r--r--spec/ruby/library/cgi/htmlextension/image_button_spec.rb101
-rw-r--r--spec/ruby/library/cgi/htmlextension/img_spec.rb123
-rw-r--r--spec/ruby/library/cgi/htmlextension/multipart_form_spec.rb93
-rw-r--r--spec/ruby/library/cgi/htmlextension/password_field_spec.rb123
-rw-r--r--spec/ruby/library/cgi/htmlextension/popup_menu_spec.rb13
-rw-r--r--spec/ruby/library/cgi/htmlextension/radio_button_spec.rb113
-rw-r--r--spec/ruby/library/cgi/htmlextension/radio_group_spec.rb123
-rw-r--r--spec/ruby/library/cgi/htmlextension/reset_spec.rb83
-rw-r--r--spec/ruby/library/cgi/htmlextension/scrolling_list_spec.rb13
-rw-r--r--spec/ruby/library/cgi/htmlextension/submit_spec.rb83
-rw-r--r--spec/ruby/library/cgi/htmlextension/text_field_spec.rb123
-rw-r--r--spec/ruby/library/cgi/htmlextension/textarea_spec.rb107
-rw-r--r--spec/ruby/library/cgi/http_header_spec.rb11
-rw-r--r--spec/ruby/library/cgi/initialize_spec.rb209
-rw-r--r--spec/ruby/library/cgi/out_spec.rb97
-rw-r--r--spec/ruby/library/cgi/parse_spec.rb37
-rw-r--r--spec/ruby/library/cgi/pretty_spec.rb19
-rw-r--r--spec/ruby/library/cgi/print_spec.rb39
-rw-r--r--spec/ruby/library/cgi/queryextension/accept_charset_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/accept_encoding_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/accept_language_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/accept_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/auth_type_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/cache_control_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/content_length_spec.rb39
-rw-r--r--spec/ruby/library/cgi/queryextension/content_type_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/cookies_spec.rb15
-rw-r--r--spec/ruby/library/cgi/queryextension/element_reference_spec.rb41
-rw-r--r--spec/ruby/library/cgi/queryextension/from_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/gateway_interface_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/has_key_spec.rb11
-rw-r--r--spec/ruby/library/cgi/queryextension/host_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/include_spec.rb11
-rw-r--r--spec/ruby/library/cgi/queryextension/key_spec.rb11
-rw-r--r--spec/ruby/library/cgi/queryextension/keys_spec.rb29
-rw-r--r--spec/ruby/library/cgi/queryextension/multipart_spec.rb47
-rw-r--r--spec/ruby/library/cgi/queryextension/negotiate_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/params_spec.rb55
-rw-r--r--spec/ruby/library/cgi/queryextension/path_info_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/path_translated_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/pragma_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/query_string_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/raw_cookie2_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/raw_cookie_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/referer_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/remote_addr_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/remote_host_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/remote_ident_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/remote_user_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/request_method_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/script_name_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/server_name_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/server_port_spec.rb39
-rw-r--r--spec/ruby/library/cgi/queryextension/server_protocol_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/server_software_spec.rb33
-rw-r--r--spec/ruby/library/cgi/queryextension/shared/has_key.rb6
-rw-r--r--spec/ruby/library/cgi/queryextension/user_agent_spec.rb33
-rw-r--r--spec/ruby/library/cgi/rfc1123_date_spec.rb15
-rw-r--r--spec/ruby/library/cgi/shared/http_header.rb10
-rw-r--r--spec/ruby/library/cgi/unescapeElement_spec.rb8
-rw-r--r--spec/ruby/library/cgi/unescapeHTML_spec.rb11
-rw-r--r--spec/ruby/library/cgi/unescapeURIComponent_spec.rb128
-rw-r--r--spec/ruby/library/cgi/unescape_spec.rb8
-rw-r--r--spec/ruby/library/cmath/math/acos_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/acosh_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/asin_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/asinh_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/atan2_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/atan_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/atanh_spec.rb20
-rw-r--r--spec/ruby/library/cmath/math/cos_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/cosh_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/exp_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/fixtures/classes.rb4
-rw-r--r--spec/ruby/library/cmath/math/log10_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/log_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/shared/acos.rb41
-rw-r--r--spec/ruby/library/cmath/math/shared/acosh.rb37
-rw-r--r--spec/ruby/library/cmath/math/shared/asin.rb47
-rw-r--r--spec/ruby/library/cmath/math/shared/asinh.rb32
-rw-r--r--spec/ruby/library/cmath/math/shared/atan.rb32
-rw-r--r--spec/ruby/library/cmath/math/shared/atan2.rb34
-rw-r--r--spec/ruby/library/cmath/math/shared/atanh.rb30
-rw-r--r--spec/ruby/library/cmath/math/shared/cos.rb30
-rw-r--r--spec/ruby/library/cmath/math/shared/cosh.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/exp.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/log.rb39
-rw-r--r--spec/ruby/library/cmath/math/shared/log10.rb41
-rw-r--r--spec/ruby/library/cmath/math/shared/sin.rb30
-rw-r--r--spec/ruby/library/cmath/math/shared/sinh.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/sqrt.rb34
-rw-r--r--spec/ruby/library/cmath/math/shared/tan.rb28
-rw-r--r--spec/ruby/library/cmath/math/shared/tanh.rb32
-rw-r--r--spec/ruby/library/cmath/math/sin_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/sinh_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/sqrt_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/tan_spec.rb18
-rw-r--r--spec/ruby/library/cmath/math/tanh_spec.rb18
-rw-r--r--spec/ruby/library/conditionvariable/broadcast_spec.rb40
-rw-r--r--spec/ruby/library/conditionvariable/marshal_dump_spec.rb9
-rw-r--r--spec/ruby/library/conditionvariable/signal_spec.rb77
-rw-r--r--spec/ruby/library/conditionvariable/wait_spec.rb175
-rw-r--r--spec/ruby/library/coverage/fixtures/code_with_begin.rb3
-rw-r--r--spec/ruby/library/coverage/fixtures/eval_code.rb11
-rw-r--r--spec/ruby/library/coverage/result_spec.rb279
-rw-r--r--spec/ruby/library/coverage/running_spec.rb20
-rw-r--r--spec/ruby/library/coverage/start_spec.rb83
-rw-r--r--spec/ruby/library/coverage/supported_spec.rb30
-rw-r--r--spec/ruby/library/csv/generate_spec.rb4
-rw-r--r--spec/ruby/library/csv/parse_spec.rb4
-rw-r--r--spec/ruby/library/csv/readlines_spec.rb2
-rw-r--r--spec/ruby/library/date/accessor_spec.rb2
-rw-r--r--spec/ruby/library/date/add_month_spec.rb8
-rw-r--r--spec/ruby/library/date/add_spec.rb8
-rw-r--r--spec/ruby/library/date/civil_spec.rb7
-rw-r--r--spec/ruby/library/date/constants_spec.rb4
-rw-r--r--spec/ruby/library/date/deconstruct_keys_spec.rb42
-rw-r--r--spec/ruby/library/date/eql_spec.rb4
-rw-r--r--spec/ruby/library/date/friday_spec.rb4
-rw-r--r--spec/ruby/library/date/gregorian_leap_spec.rb10
-rw-r--r--spec/ruby/library/date/gregorian_spec.rb6
-rw-r--r--spec/ruby/library/date/iso8601_spec.rb28
-rw-r--r--spec/ruby/library/date/julian_leap_spec.rb10
-rw-r--r--spec/ruby/library/date/julian_spec.rb4
-rw-r--r--spec/ruby/library/date/minus_month_spec.rb8
-rw-r--r--spec/ruby/library/date/minus_spec.rb6
-rw-r--r--spec/ruby/library/date/mon_spec.rb3
-rw-r--r--spec/ruby/library/date/monday_spec.rb2
-rw-r--r--spec/ruby/library/date/month_spec.rb6
-rw-r--r--spec/ruby/library/date/new_spec.rb1
-rw-r--r--spec/ruby/library/date/parse_spec.rb16
-rw-r--r--spec/ruby/library/date/plus_spec.rb2
-rw-r--r--spec/ruby/library/date/saturday_spec.rb2
-rw-r--r--spec/ruby/library/date/shared/civil.rb16
-rw-r--r--spec/ruby/library/date/shared/commercial.rb18
-rw-r--r--spec/ruby/library/date/shared/month.rb6
-rw-r--r--spec/ruby/library/date/shared/new_bang.rb14
-rw-r--r--spec/ruby/library/date/shared/parse.rb4
-rw-r--r--spec/ruby/library/date/shared/parse_eu.rb8
-rw-r--r--spec/ruby/library/date/shared/parse_us.rb8
-rw-r--r--spec/ruby/library/date/shared/valid_civil.rb16
-rw-r--r--spec/ruby/library/date/shared/valid_commercial.rb24
-rw-r--r--spec/ruby/library/date/shared/valid_jd.rb30
-rw-r--r--spec/ruby/library/date/strftime_spec.rb7
-rw-r--r--spec/ruby/library/date/sunday_spec.rb2
-rw-r--r--spec/ruby/library/date/thursday_spec.rb2
-rw-r--r--spec/ruby/library/date/time/to_date_spec.rb42
-rw-r--r--spec/ruby/library/date/today_spec.rb2
-rw-r--r--spec/ruby/library/date/tuesday_spec.rb2
-rw-r--r--spec/ruby/library/date/wednesday_spec.rb2
-rw-r--r--spec/ruby/library/date/yday_spec.rb3
-rw-r--r--spec/ruby/library/datetime/deconstruct_keys_spec.rb44
-rw-r--r--spec/ruby/library/datetime/hour_spec.rb10
-rw-r--r--spec/ruby/library/datetime/new_spec.rb2
-rw-r--r--spec/ruby/library/datetime/now_spec.rb2
-rw-r--r--spec/ruby/library/datetime/parse_spec.rb12
-rw-r--r--spec/ruby/library/datetime/rfc2822_spec.rb4
-rw-r--r--spec/ruby/library/datetime/shared/min.rb10
-rw-r--r--spec/ruby/library/datetime/shared/sec.rb6
-rw-r--r--spec/ruby/library/datetime/strftime_spec.rb7
-rw-r--r--spec/ruby/library/datetime/time/to_datetime_spec.rb40
-rw-r--r--spec/ruby/library/datetime/to_date_spec.rb2
-rw-r--r--spec/ruby/library/datetime/to_s_spec.rb2
-rw-r--r--spec/ruby/library/datetime/to_time_spec.rb18
-rw-r--r--spec/ruby/library/datetime/yday_spec.rb7
-rw-r--r--spec/ruby/library/delegate/delegate_class/instance_method_spec.rb14
-rw-r--r--spec/ruby/library/delegate/delegate_class/instance_methods_spec.rb12
-rw-r--r--spec/ruby/library/delegate/delegate_class/private_instance_methods_spec.rb12
-rw-r--r--spec/ruby/library/delegate/delegate_class/protected_instance_methods_spec.rb12
-rw-r--r--spec/ruby/library/delegate/delegate_class/public_instance_methods_spec.rb10
-rw-r--r--spec/ruby/library/delegate/delegate_class/respond_to_missing_spec.rb1
-rw-r--r--spec/ruby/library/delegate/delegator/eql_spec.rb8
-rw-r--r--spec/ruby/library/delegate/delegator/equal_spec.rb6
-rw-r--r--spec/ruby/library/delegate/delegator/equal_value_spec.rb6
-rw-r--r--spec/ruby/library/delegate/delegator/frozen_spec.rb14
-rw-r--r--spec/ruby/library/delegate/delegator/marshal_spec.rb2
-rw-r--r--spec/ruby/library/delegate/delegator/method_spec.rb18
-rw-r--r--spec/ruby/library/delegate/delegator/methods_spec.rb14
-rw-r--r--spec/ruby/library/delegate/delegator/not_equal_spec.rb6
-rw-r--r--spec/ruby/library/delegate/delegator/private_methods_spec.rb8
-rw-r--r--spec/ruby/library/delegate/delegator/protected_methods_spec.rb4
-rw-r--r--spec/ruby/library/delegate/delegator/public_methods_spec.rb4
-rw-r--r--spec/ruby/library/delegate/delegator/send_spec.rb8
-rw-r--r--spec/ruby/library/delegate/delegator/taint_spec.rb17
-rw-r--r--spec/ruby/library/delegate/delegator/tap_spec.rb2
-rw-r--r--spec/ruby/library/delegate/delegator/trust_spec.rb16
-rw-r--r--spec/ruby/library/delegate/delegator/untaint_spec.rb18
-rw-r--r--spec/ruby/library/delegate/delegator/untrust_spec.rb17
-rw-r--r--spec/ruby/library/digest/bubblebabble_spec.rb8
-rw-r--r--spec/ruby/library/digest/hexencode_spec.rb6
-rw-r--r--spec/ruby/library/digest/instance/append_spec.rb7
-rw-r--r--spec/ruby/library/digest/instance/shared/update.rb8
-rw-r--r--spec/ruby/library/digest/instance/update_spec.rb7
-rw-r--r--spec/ruby/library/digest/md5/append_spec.rb2
-rw-r--r--spec/ruby/library/digest/md5/file_spec.rb8
-rw-r--r--spec/ruby/library/digest/md5/shared/constants.rb2
-rw-r--r--spec/ruby/library/digest/md5/shared/sample.rb17
-rw-r--r--spec/ruby/library/digest/sha1/file_spec.rb8
-rw-r--r--spec/ruby/library/digest/sha1/shared/constants.rb2
-rw-r--r--spec/ruby/library/digest/sha256/append_spec.rb2
-rw-r--r--spec/ruby/library/digest/sha256/file_spec.rb8
-rw-r--r--spec/ruby/library/digest/sha256/shared/constants.rb2
-rw-r--r--spec/ruby/library/digest/sha384/append_spec.rb2
-rw-r--r--spec/ruby/library/digest/sha384/file_spec.rb8
-rw-r--r--spec/ruby/library/digest/sha384/shared/constants.rb2
-rw-r--r--spec/ruby/library/digest/sha512/append_spec.rb2
-rw-r--r--spec/ruby/library/digest/sha512/file_spec.rb8
-rw-r--r--spec/ruby/library/digest/sha512/shared/constants.rb2
-rw-r--r--spec/ruby/library/drb/start_service_spec.rb47
-rw-r--r--spec/ruby/library/erb/def_class_spec.rb2
-rw-r--r--spec/ruby/library/erb/def_module_spec.rb3
-rw-r--r--spec/ruby/library/erb/defmethod/def_erb_method_spec.rb2
-rw-r--r--spec/ruby/library/erb/filename_spec.rb4
-rw-r--r--spec/ruby/library/erb/fixtures/classes.rb6
-rw-r--r--spec/ruby/library/erb/new_spec.rb41
-rw-r--r--spec/ruby/library/erb/result_spec.rb4
-rw-r--r--spec/ruby/library/erb/run_spec.rb6
-rw-r--r--spec/ruby/library/etc/confstr_spec.rb6
-rw-r--r--spec/ruby/library/etc/getgrgid_spec.rb8
-rw-r--r--spec/ruby/library/etc/getgrnam_spec.rb2
-rw-r--r--spec/ruby/library/etc/getlogin_spec.rb4
-rw-r--r--spec/ruby/library/etc/getpwnam_spec.rb2
-rw-r--r--spec/ruby/library/etc/getpwuid_spec.rb2
-rw-r--r--spec/ruby/library/etc/group_spec.rb4
-rw-r--r--spec/ruby/library/etc/nprocessors_spec.rb2
-rw-r--r--spec/ruby/library/etc/passwd_spec.rb4
-rw-r--r--spec/ruby/library/etc/sysconf_spec.rb4
-rw-r--r--spec/ruby/library/etc/sysconfdir_spec.rb4
-rw-r--r--spec/ruby/library/etc/systmpdir_spec.rb4
-rw-r--r--spec/ruby/library/etc/uname_spec.rb14
-rw-r--r--spec/ruby/library/expect/expect_spec.rb7
-rw-r--r--spec/ruby/library/fiber/alive_spec.rb46
-rw-r--r--spec/ruby/library/fiber/current_spec.rb51
-rw-r--r--spec/ruby/library/fiber/resume_spec.rb12
-rw-r--r--spec/ruby/library/fiber/transfer_spec.rb86
-rw-r--r--spec/ruby/library/fiddle/handle/initialize_spec.rb10
-rw-r--r--spec/ruby/library/find/find_spec.rb2
-rw-r--r--spec/ruby/library/find/fixtures/common.rb14
-rw-r--r--spec/ruby/library/getoptlong/error_message_spec.rb2
-rw-r--r--spec/ruby/library/getoptlong/ordering_spec.rb4
-rw-r--r--spec/ruby/library/getoptlong/set_options_spec.rb14
-rw-r--r--spec/ruby/library/getoptlong/shared/get.rb2
-rw-r--r--spec/ruby/library/io-wait/wait_readable_spec.rb42
-rw-r--r--spec/ruby/library/io-wait/wait_spec.rb162
-rw-r--r--spec/ruby/library/io-wait/wait_writable_spec.rb37
-rw-r--r--spec/ruby/library/ipaddr/new_spec.rb9
-rw-r--r--spec/ruby/library/ipaddr/operator_spec.rb16
-rw-r--r--spec/ruby/library/ipaddr/reverse_spec.rb4
-rw-r--r--spec/ruby/library/irb/fixtures/irb.rb3
-rw-r--r--spec/ruby/library/irb/irb_spec.rb19
-rw-r--r--spec/ruby/library/logger/device/close_spec.rb15
-rw-r--r--spec/ruby/library/logger/device/new_spec.rb8
-rw-r--r--spec/ruby/library/logger/device/write_spec.rb15
-rw-r--r--spec/ruby/library/logger/logger/add_spec.rb6
-rw-r--r--spec/ruby/library/logger/logger/datetime_format_spec.rb2
-rw-r--r--spec/ruby/library/logger/logger/new_spec.rb32
-rw-r--r--spec/ruby/library/logger/logger/unknown_spec.rb2
-rw-r--r--spec/ruby/library/matrix/antisymmetric_spec.rb53
-rw-r--r--spec/ruby/library/matrix/build_spec.rb20
-rw-r--r--spec/ruby/library/matrix/clone_spec.rb8
-rw-r--r--spec/ruby/library/matrix/coerce_spec.rb2
-rw-r--r--spec/ruby/library/matrix/column_spec.rb6
-rw-r--r--spec/ruby/library/matrix/column_vector_spec.rb6
-rw-r--r--spec/ruby/library/matrix/column_vectors_spec.rb4
-rw-r--r--spec/ruby/library/matrix/columns_spec.rb4
-rw-r--r--spec/ruby/library/matrix/constructor_spec.rb20
-rw-r--r--spec/ruby/library/matrix/diagonal_spec.rb16
-rw-r--r--spec/ruby/library/matrix/divide_spec.rb16
-rw-r--r--spec/ruby/library/matrix/each_spec.rb10
-rw-r--r--spec/ruby/library/matrix/each_with_index_spec.rb10
-rw-r--r--spec/ruby/library/matrix/eigenvalue_decomposition/initialize_spec.rb6
-rw-r--r--spec/ruby/library/matrix/element_reference_spec.rb4
-rw-r--r--spec/ruby/library/matrix/empty_spec.rb22
-rw-r--r--spec/ruby/library/matrix/eql_spec.rb2
-rw-r--r--spec/ruby/library/matrix/exponent_spec.rb25
-rw-r--r--spec/ruby/library/matrix/find_index_spec.rb16
-rw-r--r--spec/ruby/library/matrix/hash_spec.rb4
-rw-r--r--spec/ruby/library/matrix/hermitian_spec.rb12
-rw-r--r--spec/ruby/library/matrix/lower_triangular_spec.rb22
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/determinant_spec.rb2
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/initialize_spec.rb4
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/l_spec.rb2
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/p_spec.rb2
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/solve_spec.rb6
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/to_a_spec.rb4
-rw-r--r--spec/ruby/library/matrix/lup_decomposition/u_spec.rb2
-rw-r--r--spec/ruby/library/matrix/minor_spec.rb2
-rw-r--r--spec/ruby/library/matrix/minus_spec.rb20
-rw-r--r--spec/ruby/library/matrix/multiply_spec.rb19
-rw-r--r--spec/ruby/library/matrix/new_spec.rb2
-rw-r--r--spec/ruby/library/matrix/normal_spec.rb2
-rw-r--r--spec/ruby/library/matrix/orthogonal_spec.rb2
-rw-r--r--spec/ruby/library/matrix/permutation_spec.rb14
-rw-r--r--spec/ruby/library/matrix/plus_spec.rb20
-rw-r--r--spec/ruby/library/matrix/real_spec.rb12
-rw-r--r--spec/ruby/library/matrix/regular_spec.rb12
-rw-r--r--spec/ruby/library/matrix/round_spec.rb2
-rw-r--r--spec/ruby/library/matrix/row_spec.rb6
-rw-r--r--spec/ruby/library/matrix/row_vector_spec.rb4
-rw-r--r--spec/ruby/library/matrix/row_vectors_spec.rb4
-rw-r--r--spec/ruby/library/matrix/rows_spec.rb8
-rw-r--r--spec/ruby/library/matrix/scalar_spec.rb4
-rw-r--r--spec/ruby/library/matrix/shared/collect.rb6
-rw-r--r--spec/ruby/library/matrix/shared/conjugate.rb2
-rw-r--r--spec/ruby/library/matrix/shared/determinant.rb4
-rw-r--r--spec/ruby/library/matrix/shared/equal_value.rb20
-rw-r--r--spec/ruby/library/matrix/shared/identity.rb4
-rw-r--r--spec/ruby/library/matrix/shared/imaginary.rb2
-rw-r--r--spec/ruby/library/matrix/shared/inverse.rb6
-rw-r--r--spec/ruby/library/matrix/shared/rectangular.rb2
-rw-r--r--spec/ruby/library/matrix/shared/trace.rb2
-rw-r--r--spec/ruby/library/matrix/shared/transpose.rb2
-rw-r--r--spec/ruby/library/matrix/singular_spec.rb12
-rw-r--r--spec/ruby/library/matrix/square_spec.rb16
-rw-r--r--spec/ruby/library/matrix/symmetric_spec.rb8
-rw-r--r--spec/ruby/library/matrix/unitary_spec.rb9
-rw-r--r--spec/ruby/library/matrix/upper_triangular_spec.rb22
-rw-r--r--spec/ruby/library/matrix/vector/cross_product_spec.rb2
-rw-r--r--spec/ruby/library/matrix/vector/each2_spec.rb12
-rw-r--r--spec/ruby/library/matrix/vector/eql_spec.rb4
-rw-r--r--spec/ruby/library/matrix/vector/inner_product_spec.rb2
-rw-r--r--spec/ruby/library/matrix/vector/normalize_spec.rb4
-rw-r--r--spec/ruby/library/matrix/zero_spec.rb4
-rw-r--r--spec/ruby/library/monitor/enter_spec.rb28
-rw-r--r--spec/ruby/library/monitor/exit_spec.rb10
-rw-r--r--spec/ruby/library/monitor/mon_initialize_spec.rb2
-rw-r--r--spec/ruby/library/monitor/new_cond_spec.rb88
-rw-r--r--spec/ruby/library/monitor/synchronize_spec.rb41
-rw-r--r--spec/ruby/library/monitor/try_enter_spec.rb39
-rw-r--r--spec/ruby/library/net-ftp/FTPError_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/FTPPermError_spec.rb15
-rw-r--r--spec/ruby/library/net-ftp/FTPProtoError_spec.rb15
-rw-r--r--spec/ruby/library/net-ftp/FTPReplyError_spec.rb15
-rw-r--r--spec/ruby/library/net-ftp/FTPTempError_spec.rb15
-rw-r--r--spec/ruby/library/net-ftp/abort_spec.rb65
-rw-r--r--spec/ruby/library/net-ftp/acct_spec.rb61
-rw-r--r--spec/ruby/library/net-ftp/binary_spec.rb27
-rw-r--r--spec/ruby/library/net-ftp/chdir_spec.rb102
-rw-r--r--spec/ruby/library/net-ftp/close_spec.rb33
-rw-r--r--spec/ruby/library/net-ftp/closed_spec.rb24
-rw-r--r--spec/ruby/library/net-ftp/connect_spec.rb46
-rw-r--r--spec/ruby/library/net-ftp/debug_mode_spec.rb26
-rw-r--r--spec/ruby/library/net-ftp/default_passive_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/delete_spec.rb62
-rw-r--r--spec/ruby/library/net-ftp/dir_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/fixtures/default_passive.rb (renamed from spec/ruby/library/net/ftp/fixtures/default_passive.rb)0
-rw-r--r--spec/ruby/library/net-ftp/fixtures/passive.rb (renamed from spec/ruby/library/net/ftp/fixtures/passive.rb)0
-rw-r--r--spec/ruby/library/net-ftp/fixtures/putbinaryfile (renamed from spec/ruby/library/net/ftp/fixtures/putbinaryfile)0
-rw-r--r--spec/ruby/library/net-ftp/fixtures/puttextfile (renamed from spec/ruby/library/net/ftp/fixtures/puttextfile)0
-rw-r--r--spec/ruby/library/net-ftp/fixtures/server.rb279
-rw-r--r--spec/ruby/library/net-ftp/get_spec.rb24
-rw-r--r--spec/ruby/library/net-ftp/getbinaryfile_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/getdir_spec.rb10
-rw-r--r--spec/ruby/library/net-ftp/gettextfile_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/help_spec.rb69
-rw-r--r--spec/ruby/library/net-ftp/initialize_spec.rb408
-rw-r--r--spec/ruby/library/net-ftp/last_response_code_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/last_response_spec.rb28
-rw-r--r--spec/ruby/library/net-ftp/lastresp_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/list_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/login_spec.rb198
-rw-r--r--spec/ruby/library/net-ftp/ls_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/mdtm_spec.rb41
-rw-r--r--spec/ruby/library/net-ftp/mkdir_spec.rb64
-rw-r--r--spec/ruby/library/net-ftp/mtime_spec.rb53
-rw-r--r--spec/ruby/library/net-ftp/nlst_spec.rb95
-rw-r--r--spec/ruby/library/net-ftp/noop_spec.rb41
-rw-r--r--spec/ruby/library/net-ftp/open_spec.rb58
-rw-r--r--spec/ruby/library/net-ftp/passive_spec.rb31
-rw-r--r--spec/ruby/library/net-ftp/put_spec.rb24
-rw-r--r--spec/ruby/library/net-ftp/putbinaryfile_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/puttextfile_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/pwd_spec.rb56
-rw-r--r--spec/ruby/library/net-ftp/quit_spec.rb36
-rw-r--r--spec/ruby/library/net-ftp/rename_spec.rb97
-rw-r--r--spec/ruby/library/net-ftp/resume_spec.rb26
-rw-r--r--spec/ruby/library/net-ftp/retrbinary_spec.rb33
-rw-r--r--spec/ruby/library/net-ftp/retrlines_spec.rb37
-rw-r--r--spec/ruby/library/net-ftp/return_code_spec.rb27
-rw-r--r--spec/ruby/library/net-ftp/rmdir_spec.rb61
-rw-r--r--spec/ruby/library/net-ftp/sendcmd_spec.rb57
-rw-r--r--spec/ruby/library/net-ftp/set_socket_spec.rb11
-rw-r--r--spec/ruby/library/net-ftp/shared/getbinaryfile.rb152
-rw-r--r--spec/ruby/library/net-ftp/shared/gettextfile.rb102
-rw-r--r--spec/ruby/library/net-ftp/shared/last_response_code.rb27
-rw-r--r--spec/ruby/library/net-ftp/shared/list.rb106
-rw-r--r--spec/ruby/library/net-ftp/shared/putbinaryfile.rb169
-rw-r--r--spec/ruby/library/net-ftp/shared/puttextfile.rb130
-rw-r--r--spec/ruby/library/net-ftp/shared/pwd.rb5
-rw-r--r--spec/ruby/library/net-ftp/site_spec.rb56
-rw-r--r--spec/ruby/library/net-ftp/size_spec.rb51
-rw-r--r--spec/ruby/library/net-ftp/spec_helper.rb7
-rw-r--r--spec/ruby/library/net-ftp/status_spec.rb70
-rw-r--r--spec/ruby/library/net-ftp/storbinary_spec.rb52
-rw-r--r--spec/ruby/library/net-ftp/storlines_spec.rb47
-rw-r--r--spec/ruby/library/net-ftp/system_spec.rb51
-rw-r--r--spec/ruby/library/net-ftp/voidcmd_spec.rb57
-rw-r--r--spec/ruby/library/net-ftp/welcome_spec.rb28
-rw-r--r--spec/ruby/library/net-http/HTTPBadResponse_spec.rb8
-rw-r--r--spec/ruby/library/net-http/HTTPClientExcepton_spec.rb12
-rw-r--r--spec/ruby/library/net-http/HTTPError_spec.rb12
-rw-r--r--spec/ruby/library/net-http/HTTPFatalError_spec.rb12
-rw-r--r--spec/ruby/library/net-http/HTTPHeaderSyntaxError_spec.rb8
-rw-r--r--spec/ruby/library/net-http/HTTPRetriableError_spec.rb12
-rw-r--r--spec/ruby/library/net-http/HTTPServerException_spec.rb12
-rw-r--r--spec/ruby/library/net-http/http/Proxy_spec.rb35
-rw-r--r--spec/ruby/library/net-http/http/active_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/address_spec.rb9
-rw-r--r--spec/ruby/library/net-http/http/close_on_empty_response_spec.rb10
-rw-r--r--spec/ruby/library/net-http/http/copy_spec.rb21
-rw-r--r--spec/ruby/library/net-http/http/default_port_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/delete_spec.rb21
-rw-r--r--spec/ruby/library/net-http/http/finish_spec.rb29
-rw-r--r--spec/ruby/library/net-http/http/fixtures/http_server.rb123
-rw-r--r--spec/ruby/library/net-http/http/get2_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/get_print_spec.rb30
-rw-r--r--spec/ruby/library/net-http/http/get_response_spec.rb30
-rw-r--r--spec/ruby/library/net-http/http/get_spec.rb94
-rw-r--r--spec/ruby/library/net-http/http/head2_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/head_spec.rb25
-rw-r--r--spec/ruby/library/net-http/http/http_default_port_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/https_default_port_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/initialize_spec.rb46
-rw-r--r--spec/ruby/library/net-http/http/inspect_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/is_version_1_1_spec.rb7
-rw-r--r--spec/ruby/library/net-http/http/is_version_1_2_spec.rb7
-rw-r--r--spec/ruby/library/net-http/http/lock_spec.rb21
-rw-r--r--spec/ruby/library/net-http/http/mkcol_spec.rb21
-rw-r--r--spec/ruby/library/net-http/http/move_spec.rb25
-rw-r--r--spec/ruby/library/net-http/http/new_spec.rb86
-rw-r--r--spec/ruby/library/net-http/http/newobj_spec.rb48
-rw-r--r--spec/ruby/library/net-http/http/open_timeout_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/options_spec.rb25
-rw-r--r--spec/ruby/library/net-http/http/port_spec.rb9
-rw-r--r--spec/ruby/library/net-http/http/post2_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/post_form_spec.rb22
-rw-r--r--spec/ruby/library/net-http/http/post_spec.rb76
-rw-r--r--spec/ruby/library/net-http/http/propfind_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/proppatch_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/proxy_address_spec.rb31
-rw-r--r--spec/ruby/library/net-http/http/proxy_class_spec.rb9
-rw-r--r--spec/ruby/library/net-http/http/proxy_pass_spec.rb39
-rw-r--r--spec/ruby/library/net-http/http/proxy_port_spec.rb39
-rw-r--r--spec/ruby/library/net-http/http/proxy_user_spec.rb39
-rw-r--r--spec/ruby/library/net-http/http/put2_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/put_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/read_timeout_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/request_get_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/request_head_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/request_post_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/request_put_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/request_spec.rb109
-rw-r--r--spec/ruby/library/net-http/http/request_types_spec.rb254
-rw-r--r--spec/ruby/library/net-http/http/send_request_spec.rb61
-rw-r--r--spec/ruby/library/net-http/http/set_debug_output_spec.rb33
-rw-r--r--spec/ruby/library/net-http/http/shared/request_get.rb41
-rw-r--r--spec/ruby/library/net-http/http/shared/request_head.rb41
-rw-r--r--spec/ruby/library/net-http/http/shared/request_post.rb41
-rw-r--r--spec/ruby/library/net-http/http/shared/request_put.rb41
-rw-r--r--spec/ruby/library/net-http/http/shared/started.rb26
-rw-r--r--spec/ruby/library/net-http/http/shared/version_1_1.rb6
-rw-r--r--spec/ruby/library/net-http/http/shared/version_1_2.rb6
-rw-r--r--spec/ruby/library/net-http/http/socket_type_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/start_spec.rb111
-rw-r--r--spec/ruby/library/net-http/http/started_spec.rb8
-rw-r--r--spec/ruby/library/net-http/http/trace_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/unlock_spec.rb24
-rw-r--r--spec/ruby/library/net-http/http/use_ssl_spec.rb9
-rw-r--r--spec/ruby/library/net-http/http/version_1_1_spec.rb7
-rw-r--r--spec/ruby/library/net-http/http/version_1_2_spec.rb20
-rw-r--r--spec/ruby/library/net-http/httpexceptions/fixtures/classes.rb (renamed from spec/ruby/library/net/http/httpexceptions/fixtures/classes.rb)0
-rw-r--r--spec/ruby/library/net-http/httpexceptions/initialize_spec.rb17
-rw-r--r--spec/ruby/library/net-http/httpexceptions/response_spec.rb10
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/body_exist_spec.rb21
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/body_spec.rb30
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/body_stream_spec.rb32
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/exec_spec.rb135
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/inspect_spec.rb25
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/method_spec.rb15
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/path_spec.rb12
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/request_body_permitted_spec.rb12
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/response_body_permitted_spec.rb12
-rw-r--r--spec/ruby/library/net-http/httpgenericrequest/set_body_internal_spec.rb21
-rw-r--r--spec/ruby/library/net-http/httpheader/add_field_spec.rb31
-rw-r--r--spec/ruby/library/net-http/httpheader/basic_auth_spec.rb14
-rw-r--r--spec/ruby/library/net-http/httpheader/canonical_each_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/chunked_spec.rb22
-rw-r--r--spec/ruby/library/net-http/httpheader/content_length_spec.rb54
-rw-r--r--spec/ruby/library/net-http/httpheader/content_range_spec.rb32
-rw-r--r--spec/ruby/library/net-http/httpheader/content_type_spec.rb26
-rw-r--r--spec/ruby/library/net-http/httpheader/delete_spec.rb30
-rw-r--r--spec/ruby/library/net-http/httpheader/each_capitalized_name_spec.rb35
-rw-r--r--spec/ruby/library/net-http/httpheader/each_capitalized_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/each_header_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/each_key_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/each_name_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/each_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/each_value_spec.rb35
-rw-r--r--spec/ruby/library/net-http/httpheader/element_reference_spec.rb39
-rw-r--r--spec/ruby/library/net-http/httpheader/element_set_spec.rb41
-rw-r--r--spec/ruby/library/net-http/httpheader/fetch_spec.rb68
-rw-r--r--spec/ruby/library/net-http/httpheader/fixtures/classes.rb (renamed from spec/ruby/library/net/http/httpheader/fixtures/classes.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/form_data_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/get_fields_spec.rb39
-rw-r--r--spec/ruby/library/net-http/httpheader/initialize_http_header_spec.rb21
-rw-r--r--spec/ruby/library/net-http/httpheader/key_spec.rb21
-rw-r--r--spec/ruby/library/net-http/httpheader/length_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/main_type_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httpheader/proxy_basic_auth_spec.rb14
-rw-r--r--spec/ruby/library/net-http/httpheader/range_length_spec.rb32
-rw-r--r--spec/ruby/library/net-http/httpheader/range_spec.rb48
-rw-r--r--spec/ruby/library/net-http/httpheader/set_content_type_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/set_form_data_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/set_range_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/each_capitalized.rb31
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/each_header.rb31
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/each_name.rb31
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/set_content_type.rb (renamed from spec/ruby/library/net/http/httpheader/shared/set_content_type.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/set_form_data.rb (renamed from spec/ruby/library/net/http/httpheader/shared/set_form_data.rb)0
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/set_range.rb89
-rw-r--r--spec/ruby/library/net-http/httpheader/shared/size.rb18
-rw-r--r--spec/ruby/library/net-http/httpheader/size_spec.rb8
-rw-r--r--spec/ruby/library/net-http/httpheader/sub_type_spec.rb32
-rw-r--r--spec/ruby/library/net-http/httpheader/to_hash_spec.rb25
-rw-r--r--spec/ruby/library/net-http/httpheader/type_params_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httprequest/initialize_spec.rb45
-rw-r--r--spec/ruby/library/net-http/httpresponse/body_permitted_spec.rb13
-rw-r--r--spec/ruby/library/net-http/httpresponse/body_spec.rb7
-rw-r--r--spec/ruby/library/net-http/httpresponse/code_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httpresponse/code_type_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httpresponse/entity_spec.rb7
-rw-r--r--spec/ruby/library/net-http/httpresponse/error_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httpresponse/error_type_spec.rb24
-rw-r--r--spec/ruby/library/net-http/httpresponse/exception_type_spec.rb13
-rw-r--r--spec/ruby/library/net-http/httpresponse/header_spec.rb9
-rw-r--r--spec/ruby/library/net-http/httpresponse/http_version_spec.rb12
-rw-r--r--spec/ruby/library/net-http/httpresponse/initialize_spec.rb11
-rw-r--r--spec/ruby/library/net-http/httpresponse/inspect_spec.rb15
-rw-r--r--spec/ruby/library/net-http/httpresponse/message_spec.rb9
-rw-r--r--spec/ruby/library/net-http/httpresponse/msg_spec.rb9
-rw-r--r--spec/ruby/library/net-http/httpresponse/read_body_spec.rb86
-rw-r--r--spec/ruby/library/net-http/httpresponse/read_header_spec.rb9
-rw-r--r--spec/ruby/library/net-http/httpresponse/read_new_spec.rb23
-rw-r--r--spec/ruby/library/net-http/httpresponse/reading_body_spec.rb58
-rw-r--r--spec/ruby/library/net-http/httpresponse/response_spec.rb9
-rw-r--r--spec/ruby/library/net-http/httpresponse/shared/body.rb20
-rw-r--r--spec/ruby/library/net-http/httpresponse/value_spec.rb24
-rw-r--r--spec/ruby/library/net/FTPError_spec.rb8
-rw-r--r--spec/ruby/library/net/FTPPermError_spec.rb12
-rw-r--r--spec/ruby/library/net/FTPProtoError_spec.rb12
-rw-r--r--spec/ruby/library/net/FTPReplyError_spec.rb12
-rw-r--r--spec/ruby/library/net/FTPTempError_spec.rb12
-rw-r--r--spec/ruby/library/net/ftp/abort_spec.rb62
-rw-r--r--spec/ruby/library/net/ftp/acct_spec.rb58
-rw-r--r--spec/ruby/library/net/ftp/binary_spec.rb24
-rw-r--r--spec/ruby/library/net/ftp/chdir_spec.rb99
-rw-r--r--spec/ruby/library/net/ftp/close_spec.rb30
-rw-r--r--spec/ruby/library/net/ftp/closed_spec.rb21
-rw-r--r--spec/ruby/library/net/ftp/connect_spec.rb49
-rw-r--r--spec/ruby/library/net/ftp/debug_mode_spec.rb23
-rw-r--r--spec/ruby/library/net/ftp/default_passive_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/delete_spec.rb59
-rw-r--r--spec/ruby/library/net/ftp/dir_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/fixtures/server.rb277
-rw-r--r--spec/ruby/library/net/ftp/get_spec.rb21
-rw-r--r--spec/ruby/library/net/ftp/getbinaryfile_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/getdir_spec.rb7
-rw-r--r--spec/ruby/library/net/ftp/gettextfile_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/help_spec.rb66
-rw-r--r--spec/ruby/library/net/ftp/initialize_spec.rb405
-rw-r--r--spec/ruby/library/net/ftp/last_response_code_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/last_response_spec.rb25
-rw-r--r--spec/ruby/library/net/ftp/lastresp_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/list_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/login_spec.rb195
-rw-r--r--spec/ruby/library/net/ftp/ls_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/mdtm_spec.rb38
-rw-r--r--spec/ruby/library/net/ftp/mkdir_spec.rb61
-rw-r--r--spec/ruby/library/net/ftp/mtime_spec.rb50
-rw-r--r--spec/ruby/library/net/ftp/nlst_spec.rb92
-rw-r--r--spec/ruby/library/net/ftp/noop_spec.rb38
-rw-r--r--spec/ruby/library/net/ftp/open_spec.rb55
-rw-r--r--spec/ruby/library/net/ftp/passive_spec.rb28
-rw-r--r--spec/ruby/library/net/ftp/put_spec.rb21
-rw-r--r--spec/ruby/library/net/ftp/putbinaryfile_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/puttextfile_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/pwd_spec.rb53
-rw-r--r--spec/ruby/library/net/ftp/quit_spec.rb33
-rw-r--r--spec/ruby/library/net/ftp/rename_spec.rb94
-rw-r--r--spec/ruby/library/net/ftp/resume_spec.rb23
-rw-r--r--spec/ruby/library/net/ftp/retrbinary_spec.rb30
-rw-r--r--spec/ruby/library/net/ftp/retrlines_spec.rb34
-rw-r--r--spec/ruby/library/net/ftp/return_code_spec.rb24
-rw-r--r--spec/ruby/library/net/ftp/rmdir_spec.rb58
-rw-r--r--spec/ruby/library/net/ftp/sendcmd_spec.rb54
-rw-r--r--spec/ruby/library/net/ftp/set_socket_spec.rb8
-rw-r--r--spec/ruby/library/net/ftp/shared/getbinaryfile.rb150
-rw-r--r--spec/ruby/library/net/ftp/shared/gettextfile.rb100
-rw-r--r--spec/ruby/library/net/ftp/shared/last_response_code.rb25
-rw-r--r--spec/ruby/library/net/ftp/shared/list.rb104
-rw-r--r--spec/ruby/library/net/ftp/shared/putbinaryfile.rb167
-rw-r--r--spec/ruby/library/net/ftp/shared/puttextfile.rb120
-rw-r--r--spec/ruby/library/net/ftp/shared/pwd.rb3
-rw-r--r--spec/ruby/library/net/ftp/site_spec.rb53
-rw-r--r--spec/ruby/library/net/ftp/size_spec.rb48
-rw-r--r--spec/ruby/library/net/ftp/spec_helper.rb5
-rw-r--r--spec/ruby/library/net/ftp/status_spec.rb67
-rw-r--r--spec/ruby/library/net/ftp/storbinary_spec.rb48
-rw-r--r--spec/ruby/library/net/ftp/storlines_spec.rb43
-rw-r--r--spec/ruby/library/net/ftp/system_spec.rb48
-rw-r--r--spec/ruby/library/net/ftp/voidcmd_spec.rb54
-rw-r--r--spec/ruby/library/net/ftp/welcome_spec.rb25
-rw-r--r--spec/ruby/library/net/http/HTTPBadResponse_spec.rb8
-rw-r--r--spec/ruby/library/net/http/HTTPClientExcepton_spec.rb14
-rw-r--r--spec/ruby/library/net/http/HTTPError_spec.rb12
-rw-r--r--spec/ruby/library/net/http/HTTPFatalError_spec.rb12
-rw-r--r--spec/ruby/library/net/http/HTTPHeaderSyntaxError_spec.rb8
-rw-r--r--spec/ruby/library/net/http/HTTPRetriableError_spec.rb12
-rw-r--r--spec/ruby/library/net/http/HTTPServerException_spec.rb26
-rw-r--r--spec/ruby/library/net/http/http/Proxy_spec.rb35
-rw-r--r--spec/ruby/library/net/http/http/active_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/address_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/close_on_empty_response_spec.rb10
-rw-r--r--spec/ruby/library/net/http/http/copy_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/default_port_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/delete_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/finish_spec.rb29
-rw-r--r--spec/ruby/library/net/http/http/fixtures/http_server.rb109
-rw-r--r--spec/ruby/library/net/http/http/get2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/get_print_spec.rb30
-rw-r--r--spec/ruby/library/net/http/http/get_response_spec.rb30
-rw-r--r--spec/ruby/library/net/http/http/get_spec.rb95
-rw-r--r--spec/ruby/library/net/http/http/head2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/head_spec.rb25
-rw-r--r--spec/ruby/library/net/http/http/http_default_port_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/https_default_port_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/initialize_spec.rb46
-rw-r--r--spec/ruby/library/net/http/http/inspect_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/is_version_1_1_spec.rb7
-rw-r--r--spec/ruby/library/net/http/http/is_version_1_2_spec.rb7
-rw-r--r--spec/ruby/library/net/http/http/lock_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/mkcol_spec.rb21
-rw-r--r--spec/ruby/library/net/http/http/move_spec.rb25
-rw-r--r--spec/ruby/library/net/http/http/new_spec.rb86
-rw-r--r--spec/ruby/library/net/http/http/newobj_spec.rb48
-rw-r--r--spec/ruby/library/net/http/http/open_timeout_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/options_spec.rb25
-rw-r--r--spec/ruby/library/net/http/http/port_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/post2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/post_form_spec.rb22
-rw-r--r--spec/ruby/library/net/http/http/post_spec.rb74
-rw-r--r--spec/ruby/library/net/http/http/propfind_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/proppatch_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/proxy_address_spec.rb31
-rw-r--r--spec/ruby/library/net/http/http/proxy_class_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/proxy_pass_spec.rb39
-rw-r--r--spec/ruby/library/net/http/http/proxy_port_spec.rb39
-rw-r--r--spec/ruby/library/net/http/http/proxy_user_spec.rb39
-rw-r--r--spec/ruby/library/net/http/http/put2_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/put_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/read_timeout_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/request_get_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_head_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_post_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_put_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/request_spec.rb109
-rw-r--r--spec/ruby/library/net/http/http/request_types_spec.rb254
-rw-r--r--spec/ruby/library/net/http/http/send_request_spec.rb61
-rw-r--r--spec/ruby/library/net/http/http/set_debug_output_spec.rb33
-rw-r--r--spec/ruby/library/net/http/http/shared/request_get.rb41
-rw-r--r--spec/ruby/library/net/http/http/shared/request_head.rb41
-rw-r--r--spec/ruby/library/net/http/http/shared/request_post.rb41
-rw-r--r--spec/ruby/library/net/http/http/shared/request_put.rb41
-rw-r--r--spec/ruby/library/net/http/http/shared/started.rb26
-rw-r--r--spec/ruby/library/net/http/http/shared/version_1_1.rb6
-rw-r--r--spec/ruby/library/net/http/http/shared/version_1_2.rb6
-rw-r--r--spec/ruby/library/net/http/http/socket_type_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/start_spec.rb111
-rw-r--r--spec/ruby/library/net/http/http/started_spec.rb8
-rw-r--r--spec/ruby/library/net/http/http/trace_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/unlock_spec.rb24
-rw-r--r--spec/ruby/library/net/http/http/use_ssl_spec.rb9
-rw-r--r--spec/ruby/library/net/http/http/version_1_1_spec.rb7
-rw-r--r--spec/ruby/library/net/http/http/version_1_2_spec.rb20
-rw-r--r--spec/ruby/library/net/http/httpexceptions/initialize_spec.rb17
-rw-r--r--spec/ruby/library/net/http/httpexceptions/response_spec.rb10
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/body_exist_spec.rb21
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/body_spec.rb30
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/body_stream_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/exec_spec.rb131
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/inspect_spec.rb25
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/method_spec.rb15
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/path_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/request_body_permitted_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/response_body_permitted_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpgenericrequest/set_body_internal_spec.rb21
-rw-r--r--spec/ruby/library/net/http/httpheader/add_field_spec.rb31
-rw-r--r--spec/ruby/library/net/http/httpheader/basic_auth_spec.rb14
-rw-r--r--spec/ruby/library/net/http/httpheader/canonical_each_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/chunked_spec.rb22
-rw-r--r--spec/ruby/library/net/http/httpheader/content_length_spec.rb54
-rw-r--r--spec/ruby/library/net/http/httpheader/content_range_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpheader/content_type_spec.rb26
-rw-r--r--spec/ruby/library/net/http/httpheader/delete_spec.rb30
-rw-r--r--spec/ruby/library/net/http/httpheader/each_capitalized_name_spec.rb35
-rw-r--r--spec/ruby/library/net/http/httpheader/each_capitalized_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_header_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_key_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_name_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/each_value_spec.rb35
-rw-r--r--spec/ruby/library/net/http/httpheader/element_reference_spec.rb39
-rw-r--r--spec/ruby/library/net/http/httpheader/element_set_spec.rb41
-rw-r--r--spec/ruby/library/net/http/httpheader/fetch_spec.rb68
-rw-r--r--spec/ruby/library/net/http/httpheader/form_data_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/get_fields_spec.rb39
-rw-r--r--spec/ruby/library/net/http/httpheader/initialize_http_header_spec.rb21
-rw-r--r--spec/ruby/library/net/http/httpheader/key_spec.rb21
-rw-r--r--spec/ruby/library/net/http/httpheader/length_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/main_type_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpheader/proxy_basic_auth_spec.rb14
-rw-r--r--spec/ruby/library/net/http/httpheader/range_length_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpheader/range_spec.rb48
-rw-r--r--spec/ruby/library/net/http/httpheader/set_content_type_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/set_form_data_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/set_range_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/each_capitalized.rb31
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/each_header.rb31
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/each_name.rb31
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/set_range.rb89
-rw-r--r--spec/ruby/library/net/http/httpheader/shared/size.rb18
-rw-r--r--spec/ruby/library/net/http/httpheader/size_spec.rb8
-rw-r--r--spec/ruby/library/net/http/httpheader/sub_type_spec.rb32
-rw-r--r--spec/ruby/library/net/http/httpheader/to_hash_spec.rb25
-rw-r--r--spec/ruby/library/net/http/httpheader/type_params_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httprequest/initialize_spec.rb45
-rw-r--r--spec/ruby/library/net/http/httpresponse/body_permitted_spec.rb13
-rw-r--r--spec/ruby/library/net/http/httpresponse/body_spec.rb7
-rw-r--r--spec/ruby/library/net/http/httpresponse/code_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpresponse/code_type_spec.rb24
-rw-r--r--spec/ruby/library/net/http/httpresponse/entity_spec.rb7
-rw-r--r--spec/ruby/library/net/http/httpresponse/error_spec.rb29
-rw-r--r--spec/ruby/library/net/http/httpresponse/error_type_spec.rb29
-rw-r--r--spec/ruby/library/net/http/httpresponse/exception_type_spec.rb18
-rw-r--r--spec/ruby/library/net/http/httpresponse/header_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/http_version_spec.rb12
-rw-r--r--spec/ruby/library/net/http/httpresponse/initialize_spec.rb11
-rw-r--r--spec/ruby/library/net/http/httpresponse/inspect_spec.rb15
-rw-r--r--spec/ruby/library/net/http/httpresponse/message_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/msg_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/read_body_spec.rb85
-rw-r--r--spec/ruby/library/net/http/httpresponse/read_header_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/read_new_spec.rb22
-rw-r--r--spec/ruby/library/net/http/httpresponse/reading_body_spec.rb58
-rw-r--r--spec/ruby/library/net/http/httpresponse/response_spec.rb9
-rw-r--r--spec/ruby/library/net/http/httpresponse/shared/body.rb18
-rw-r--r--spec/ruby/library/net/http/httpresponse/value_spec.rb29
-rw-r--r--spec/ruby/library/objectspace/dump_all_spec.rb112
-rw-r--r--spec/ruby/library/objectspace/dump_spec.rb70
-rw-r--r--spec/ruby/library/objectspace/fixtures/trace.rb6
-rw-r--r--spec/ruby/library/objectspace/memsize_of_all_spec.rb22
-rw-r--r--spec/ruby/library/objectspace/memsize_of_spec.rb6
-rw-r--r--spec/ruby/library/objectspace/reachable_objects_from_spec.rb14
-rw-r--r--spec/ruby/library/objectspace/trace_object_allocations_spec.rb163
-rw-r--r--spec/ruby/library/objectspace/trace_spec.rb13
-rw-r--r--spec/ruby/library/observer/notify_observers_spec.rb2
-rw-r--r--spec/ruby/library/open3/popen3_spec.rb8
-rw-r--r--spec/ruby/library/openssl/cipher_spec.rb2
-rw-r--r--spec/ruby/library/openssl/config/freeze_spec.rb22
-rw-r--r--spec/ruby/library/openssl/digest/append_spec.rb6
-rw-r--r--spec/ruby/library/openssl/digest/block_length_spec.rb44
-rw-r--r--spec/ruby/library/openssl/digest/digest_length_spec.rb44
-rw-r--r--spec/ruby/library/openssl/digest/digest_spec.rb62
-rw-r--r--spec/ruby/library/openssl/digest/initialize_spec.rb137
-rw-r--r--spec/ruby/library/openssl/digest/name_spec.rb16
-rw-r--r--spec/ruby/library/openssl/digest/reset_spec.rb36
-rw-r--r--spec/ruby/library/openssl/digest/shared/update.rb123
-rw-r--r--spec/ruby/library/openssl/digest/update_spec.rb6
-rw-r--r--spec/ruby/library/openssl/digest_spec.rb63
-rw-r--r--spec/ruby/library/openssl/fixed_length_secure_compare_spec.rb42
-rw-r--r--spec/ruby/library/openssl/kdf/pbkdf2_hmac_spec.rb162
-rw-r--r--spec/ruby/library/openssl/kdf/scrypt_spec.rb210
-rw-r--r--spec/ruby/library/openssl/random/shared/random_bytes.rb6
-rw-r--r--spec/ruby/library/openssl/secure_compare_spec.rb38
-rw-r--r--spec/ruby/library/openssl/shared/constants.rb2
-rw-r--r--spec/ruby/library/openssl/x509/name/parse_spec.rb4
-rw-r--r--spec/ruby/library/openssl/x509/store/verify_spec.rb78
-rw-r--r--spec/ruby/library/openstruct/delete_field_spec.rb6
-rw-r--r--spec/ruby/library/openstruct/equal_value_spec.rb22
-rw-r--r--spec/ruby/library/openstruct/frozen_spec.rb14
-rw-r--r--spec/ruby/library/openstruct/initialize_spec.rb2
-rw-r--r--spec/ruby/library/openstruct/marshal_load_spec.rb4
-rw-r--r--spec/ruby/library/openstruct/method_missing_spec.rb35
-rw-r--r--spec/ruby/library/openstruct/new_spec.rb4
-rw-r--r--spec/ruby/library/openstruct/shared/inspect.rb2
-rw-r--r--spec/ruby/library/openstruct/to_h_spec.rb62
-rw-r--r--spec/ruby/library/pathname/birthtime_spec.rb16
-rw-r--r--spec/ruby/library/pathname/divide_spec.rb6
-rw-r--r--spec/ruby/library/pathname/empty_spec.rb8
-rw-r--r--spec/ruby/library/pathname/glob_spec.rb92
-rw-r--r--spec/ruby/library/pathname/inspect_spec.rb10
-rw-r--r--spec/ruby/library/pathname/new_spec.rb19
-rw-r--r--spec/ruby/library/pathname/pathname_spec.rb19
-rw-r--r--spec/ruby/library/pathname/plus_spec.rb6
-rw-r--r--spec/ruby/library/pathname/realdirpath_spec.rb2
-rw-r--r--spec/ruby/library/pathname/realpath_spec.rb2
-rw-r--r--spec/ruby/library/pathname/relative_path_from_spec.rb8
-rw-r--r--spec/ruby/library/pathname/shared/plus.rb8
-rw-r--r--spec/ruby/library/pp/pp_spec.rb7
-rw-r--r--spec/ruby/library/prime/each_spec.rb22
-rw-r--r--spec/ruby/library/prime/instance_spec.rb8
-rw-r--r--spec/ruby/library/prime/integer/prime_division_spec.rb2
-rw-r--r--spec/ruby/library/prime/integer/prime_spec.rb14
-rw-r--r--spec/ruby/library/prime/prime_division_spec.rb4
-rw-r--r--spec/ruby/library/prime/prime_spec.rb14
-rw-r--r--spec/ruby/library/random/formatter/alphanumeric_spec.rb54
-rw-r--r--spec/ruby/library/rbconfig/rbconfig_spec.rb102
-rw-r--r--spec/ruby/library/rbconfig/sizeof/limits_spec.rb6
-rw-r--r--spec/ruby/library/rbconfig/sizeof/sizeof_spec.rb6
-rw-r--r--spec/ruby/library/rbconfig/unicode_emoji_version_spec.rb19
-rw-r--r--spec/ruby/library/rbconfig/unicode_version_spec.rb25
-rw-r--r--spec/ruby/library/readline/basic_quote_characters_spec.rb2
-rw-r--r--spec/ruby/library/readline/basic_word_break_characters_spec.rb2
-rw-r--r--spec/ruby/library/readline/completer_quote_characters_spec.rb2
-rw-r--r--spec/ruby/library/readline/completer_word_break_characters_spec.rb2
-rw-r--r--spec/ruby/library/readline/completion_append_character_spec.rb2
-rw-r--r--spec/ruby/library/readline/completion_case_fold_spec.rb2
-rw-r--r--spec/ruby/library/readline/completion_proc_spec.rb4
-rw-r--r--spec/ruby/library/readline/constants_spec.rb4
-rw-r--r--spec/ruby/library/readline/emacs_editing_mode_spec.rb2
-rw-r--r--spec/ruby/library/readline/filename_quote_characters_spec.rb2
-rw-r--r--spec/ruby/library/readline/history/append_spec.rb2
-rw-r--r--spec/ruby/library/readline/history/delete_at_spec.rb13
-rw-r--r--spec/ruby/library/readline/history/each_spec.rb8
-rw-r--r--spec/ruby/library/readline/history/element_reference_spec.rb19
-rw-r--r--spec/ruby/library/readline/history/element_set_spec.rb4
-rw-r--r--spec/ruby/library/readline/history/empty_spec.rb6
-rw-r--r--spec/ruby/library/readline/history/history_spec.rb2
-rw-r--r--spec/ruby/library/readline/history/pop_spec.rb11
-rw-r--r--spec/ruby/library/readline/history/push_spec.rb2
-rw-r--r--spec/ruby/library/readline/history/shift_spec.rb11
-rw-r--r--spec/ruby/library/readline/readline_spec.rb7
-rw-r--r--spec/ruby/library/readline/vi_editing_mode_spec.rb2
-rw-r--r--spec/ruby/library/resolv/get_address_spec.rb2
-rw-r--r--spec/ruby/library/resolv/get_name_spec.rb2
-rw-r--r--spec/ruby/library/rexml/attribute/clone_spec.rb14
-rw-r--r--spec/ruby/library/rexml/attribute/element_spec.rb26
-rw-r--r--spec/ruby/library/rexml/attribute/equal_value_spec.rb21
-rw-r--r--spec/ruby/library/rexml/attribute/hash_spec.rb16
-rw-r--r--spec/ruby/library/rexml/attribute/initialize_spec.rb32
-rw-r--r--spec/ruby/library/rexml/attribute/inspect_spec.rb22
-rw-r--r--spec/ruby/library/rexml/attribute/namespace_spec.rb27
-rw-r--r--spec/ruby/library/rexml/attribute/node_type_spec.rb13
-rw-r--r--spec/ruby/library/rexml/attribute/prefix_spec.rb21
-rw-r--r--spec/ruby/library/rexml/attribute/remove_spec.rb23
-rw-r--r--spec/ruby/library/rexml/attribute/to_s_spec.rb17
-rw-r--r--spec/ruby/library/rexml/attribute/to_string_spec.rb17
-rw-r--r--spec/ruby/library/rexml/attribute/value_spec.rb17
-rw-r--r--spec/ruby/library/rexml/attribute/write_spec.rb26
-rw-r--r--spec/ruby/library/rexml/attribute/xpath_spec.rb22
-rw-r--r--spec/ruby/library/rexml/attributes/add_spec.rb10
-rw-r--r--spec/ruby/library/rexml/attributes/append_spec.rb10
-rw-r--r--spec/ruby/library/rexml/attributes/delete_all_spec.rb34
-rw-r--r--spec/ruby/library/rexml/attributes/delete_spec.rb30
-rw-r--r--spec/ruby/library/rexml/attributes/each_attribute_spec.rb25
-rw-r--r--spec/ruby/library/rexml/attributes/each_spec.rb26
-rw-r--r--spec/ruby/library/rexml/attributes/element_reference_spec.rb21
-rw-r--r--spec/ruby/library/rexml/attributes/element_set_spec.rb28
-rw-r--r--spec/ruby/library/rexml/attributes/get_attribute_ns_spec.rb17
-rw-r--r--spec/ruby/library/rexml/attributes/get_attribute_spec.rb32
-rw-r--r--spec/ruby/library/rexml/attributes/initialize_spec.rb21
-rw-r--r--spec/ruby/library/rexml/attributes/length_spec.rb10
-rw-r--r--spec/ruby/library/rexml/attributes/namespaces_spec.rb9
-rw-r--r--spec/ruby/library/rexml/attributes/prefixes_spec.rb27
-rw-r--r--spec/ruby/library/rexml/attributes/shared/add.rb17
-rw-r--r--spec/ruby/library/rexml/attributes/shared/length.rb13
-rw-r--r--spec/ruby/library/rexml/attributes/size_spec.rb10
-rw-r--r--spec/ruby/library/rexml/attributes/to_a_spec.rb22
-rw-r--r--spec/ruby/library/rexml/cdata/clone_spec.rb13
-rw-r--r--spec/ruby/library/rexml/cdata/initialize_spec.rb27
-rw-r--r--spec/ruby/library/rexml/cdata/shared/to_s.rb11
-rw-r--r--spec/ruby/library/rexml/cdata/to_s_spec.rb10
-rw-r--r--spec/ruby/library/rexml/cdata/value_spec.rb10
-rw-r--r--spec/ruby/library/rexml/document/add_element_spec.rb34
-rw-r--r--spec/ruby/library/rexml/document/add_spec.rb60
-rw-r--r--spec/ruby/library/rexml/document/clone_spec.rb23
-rw-r--r--spec/ruby/library/rexml/document/doctype_spec.rb18
-rw-r--r--spec/ruby/library/rexml/document/encoding_spec.rb25
-rw-r--r--spec/ruby/library/rexml/document/expanded_name_spec.rb19
-rw-r--r--spec/ruby/library/rexml/document/new_spec.rb39
-rw-r--r--spec/ruby/library/rexml/document/node_type_spec.rb11
-rw-r--r--spec/ruby/library/rexml/document/root_spec.rb15
-rw-r--r--spec/ruby/library/rexml/document/stand_alone_spec.rb22
-rw-r--r--spec/ruby/library/rexml/document/version_spec.rb17
-rw-r--r--spec/ruby/library/rexml/document/write_spec.rb38
-rw-r--r--spec/ruby/library/rexml/document/xml_decl_spec.rb18
-rw-r--r--spec/ruby/library/rexml/element/add_attribute_spec.rb44
-rw-r--r--spec/ruby/library/rexml/element/add_attributes_spec.rb25
-rw-r--r--spec/ruby/library/rexml/element/add_element_spec.rb41
-rw-r--r--spec/ruby/library/rexml/element/add_namespace_spec.rb26
-rw-r--r--spec/ruby/library/rexml/element/add_text_spec.rb27
-rw-r--r--spec/ruby/library/rexml/element/attribute_spec.rb20
-rw-r--r--spec/ruby/library/rexml/element/attributes_spec.rb22
-rw-r--r--spec/ruby/library/rexml/element/cdatas_spec.rb27
-rw-r--r--spec/ruby/library/rexml/element/clone_spec.rb32
-rw-r--r--spec/ruby/library/rexml/element/comments_spec.rb23
-rw-r--r--spec/ruby/library/rexml/element/delete_attribute_spec.rb42
-rw-r--r--spec/ruby/library/rexml/element/delete_element_spec.rb52
-rw-r--r--spec/ruby/library/rexml/element/delete_namespace_spec.rb28
-rw-r--r--spec/ruby/library/rexml/element/document_spec.rb19
-rw-r--r--spec/ruby/library/rexml/element/each_element_with_attribute_spec.rb38
-rw-r--r--spec/ruby/library/rexml/element/each_element_with_text_spec.rb34
-rw-r--r--spec/ruby/library/rexml/element/element_reference_spec.rb23
-rw-r--r--spec/ruby/library/rexml/element/get_text_spec.rb21
-rw-r--r--spec/ruby/library/rexml/element/has_attributes_spec.rb20
-rw-r--r--spec/ruby/library/rexml/element/has_elements_spec.rb21
-rw-r--r--spec/ruby/library/rexml/element/has_text_spec.rb19
-rw-r--r--spec/ruby/library/rexml/element/inspect_spec.rb30
-rw-r--r--spec/ruby/library/rexml/element/instructions_spec.rb24
-rw-r--r--spec/ruby/library/rexml/element/namespace_spec.rb30
-rw-r--r--spec/ruby/library/rexml/element/namespaces_spec.rb35
-rw-r--r--spec/ruby/library/rexml/element/new_spec.rb38
-rw-r--r--spec/ruby/library/rexml/element/next_element_spec.rb22
-rw-r--r--spec/ruby/library/rexml/element/node_type_spec.rb11
-rw-r--r--spec/ruby/library/rexml/element/prefixes_spec.rb26
-rw-r--r--spec/ruby/library/rexml/element/previous_element_spec.rb23
-rw-r--r--spec/ruby/library/rexml/element/raw_spec.rb27
-rw-r--r--spec/ruby/library/rexml/element/root_spec.rb31
-rw-r--r--spec/ruby/library/rexml/element/text_spec.rb49
-rw-r--r--spec/ruby/library/rexml/element/texts_spec.rb19
-rw-r--r--spec/ruby/library/rexml/element/whitespace_spec.rb26
-rw-r--r--spec/ruby/library/rexml/node/each_recursive_spec.rb24
-rw-r--r--spec/ruby/library/rexml/node/find_first_recursive_spec.rb28
-rw-r--r--spec/ruby/library/rexml/node/index_in_parent_spec.rb18
-rw-r--r--spec/ruby/library/rexml/node/next_sibling_node_spec.rb24
-rw-r--r--spec/ruby/library/rexml/node/parent_spec.rb23
-rw-r--r--spec/ruby/library/rexml/node/previous_sibling_node_spec.rb24
-rw-r--r--spec/ruby/library/rexml/shared/each_element.rb36
-rw-r--r--spec/ruby/library/rexml/shared/elements_to_a.rb34
-rw-r--r--spec/ruby/library/rexml/text/append_spec.rb13
-rw-r--r--spec/ruby/library/rexml/text/clone_spec.rb13
-rw-r--r--spec/ruby/library/rexml/text/comparison_spec.rb28
-rw-r--r--spec/ruby/library/rexml/text/empty_spec.rb15
-rw-r--r--spec/ruby/library/rexml/text/indent_text_spec.rb26
-rw-r--r--spec/ruby/library/rexml/text/inspect_spec.rb11
-rw-r--r--spec/ruby/library/rexml/text/new_spec.rb51
-rw-r--r--spec/ruby/library/rexml/text/node_type_spec.rb11
-rw-r--r--spec/ruby/library/rexml/text/normalize_spec.rb11
-rw-r--r--spec/ruby/library/rexml/text/read_with_substitution_spec.rb15
-rw-r--r--spec/ruby/library/rexml/text/to_s_spec.rb20
-rw-r--r--spec/ruby/library/rexml/text/unnormalize_spec.rb11
-rw-r--r--spec/ruby/library/rexml/text/value_spec.rb40
-rw-r--r--spec/ruby/library/rexml/text/wrap_spec.rb23
-rw-r--r--spec/ruby/library/rexml/text/write_with_substitution_spec.rb36
-rw-r--r--spec/ruby/library/ripper/lex_spec.rb6
-rw-r--r--spec/ruby/library/rubygems/gem/bin_path_spec.rb1
-rw-r--r--spec/ruby/library/rubygems/gem/load_path_insert_index_spec.rb10
-rw-r--r--spec/ruby/library/scanf/io/block_scanf_spec.rb10
-rw-r--r--spec/ruby/library/scanf/io/fixtures/date.txt4
-rw-r--r--spec/ruby/library/scanf/io/fixtures/helloworld.txt1
-rw-r--r--spec/ruby/library/scanf/io/scanf_spec.rb38
-rw-r--r--spec/ruby/library/scanf/io/shared/block_scanf.rb28
-rw-r--r--spec/ruby/library/scanf/string/block_scanf_spec.rb10
-rw-r--r--spec/ruby/library/scanf/string/scanf_spec.rb56
-rw-r--r--spec/ruby/library/scanf/string/shared/block_scanf.rb25
-rw-r--r--spec/ruby/library/securerandom/base64_spec.rb10
-rw-r--r--spec/ruby/library/securerandom/hex_spec.rb14
-rw-r--r--spec/ruby/library/securerandom/random_bytes_spec.rb12
-rw-r--r--spec/ruby/library/securerandom/random_number_spec.rb52
-rw-r--r--spec/ruby/library/set/add_spec.rb27
-rw-r--r--spec/ruby/library/set/append_spec.rb7
-rw-r--r--spec/ruby/library/set/case_compare_spec.rb12
-rw-r--r--spec/ruby/library/set/case_equality_spec.rb7
-rw-r--r--spec/ruby/library/set/classify_spec.rb27
-rw-r--r--spec/ruby/library/set/clear_spec.rb17
-rw-r--r--spec/ruby/library/set/collect_spec.rb7
-rw-r--r--spec/ruby/library/set/compare_by_identity_spec.rb143
-rw-r--r--spec/ruby/library/set/constructor_spec.rb15
-rw-r--r--spec/ruby/library/set/delete_if_spec.rb38
-rw-r--r--spec/ruby/library/set/delete_spec.rb37
-rw-r--r--spec/ruby/library/set/difference_spec.rb7
-rw-r--r--spec/ruby/library/set/disjoint_spec.rb23
-rw-r--r--spec/ruby/library/set/divide_spec.rb34
-rw-r--r--spec/ruby/library/set/each_spec.rb26
-rw-r--r--spec/ruby/library/set/empty_spec.rb10
-rw-r--r--spec/ruby/library/set/enumerable/to_set_spec.rb19
-rw-r--r--spec/ruby/library/set/eql_spec.rb15
-rw-r--r--spec/ruby/library/set/equal_value_spec.rb33
-rw-r--r--spec/ruby/library/set/exclusion_spec.rb18
-rw-r--r--spec/ruby/library/set/filter_spec.rb8
-rw-r--r--spec/ruby/library/set/fixtures/set_like.rb31
-rw-r--r--spec/ruby/library/set/flatten_merge_spec.rb23
-rw-r--r--spec/ruby/library/set/flatten_spec.rb53
-rw-r--r--spec/ruby/library/set/hash_spec.rb13
-rw-r--r--spec/ruby/library/set/include_spec.rb7
-rw-r--r--spec/ruby/library/set/initialize_spec.rb48
-rw-r--r--spec/ruby/library/set/inspect_spec.rb7
-rw-r--r--spec/ruby/library/set/intersect_spec.rb23
-rw-r--r--spec/ruby/library/set/intersection_spec.rb11
-rw-r--r--spec/ruby/library/set/keep_if_spec.rb38
-rw-r--r--spec/ruby/library/set/length_spec.rb7
-rw-r--r--spec/ruby/library/set/map_spec.rb7
-rw-r--r--spec/ruby/library/set/member_spec.rb7
-rw-r--r--spec/ruby/library/set/merge_spec.rb19
-rw-r--r--spec/ruby/library/set/minus_spec.rb7
-rw-r--r--spec/ruby/library/set/plus_spec.rb7
-rw-r--r--spec/ruby/library/set/pretty_print_cycle_spec.rb10
-rw-r--r--spec/ruby/library/set/pretty_print_spec.rb17
-rw-r--r--spec/ruby/library/set/proper_subset_spec.rb41
-rw-r--r--spec/ruby/library/set/proper_superset_spec.rb41
-rw-r--r--spec/ruby/library/set/reject_spec.rb42
-rw-r--r--spec/ruby/library/set/replace_spec.rb17
-rw-r--r--spec/ruby/library/set/shared/add.rb14
-rw-r--r--spec/ruby/library/set/shared/collect.rb20
-rw-r--r--spec/ruby/library/set/shared/difference.rb15
-rw-r--r--spec/ruby/library/set/shared/include.rb29
-rw-r--r--spec/ruby/library/set/shared/inspect.rb15
-rw-r--r--spec/ruby/library/set/shared/intersection.rb15
-rw-r--r--spec/ruby/library/set/shared/select.rb42
-rw-r--r--spec/ruby/library/set/shared/union.rb15
-rw-r--r--spec/ruby/library/set/size_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/add_spec.rb39
-rw-r--r--spec/ruby/library/set/sortedset/append_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/case_equality_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/classify_spec.rb27
-rw-r--r--spec/ruby/library/set/sortedset/clear_spec.rb17
-rw-r--r--spec/ruby/library/set/sortedset/collect_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/constructor_spec.rb15
-rw-r--r--spec/ruby/library/set/sortedset/delete_if_spec.rb38
-rw-r--r--spec/ruby/library/set/sortedset/delete_spec.rb37
-rw-r--r--spec/ruby/library/set/sortedset/difference_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/divide_spec.rb34
-rw-r--r--spec/ruby/library/set/sortedset/each_spec.rb26
-rw-r--r--spec/ruby/library/set/sortedset/empty_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/eql_spec.rb16
-rw-r--r--spec/ruby/library/set/sortedset/equal_value_spec.rb13
-rw-r--r--spec/ruby/library/set/sortedset/exclusion_spec.rb18
-rw-r--r--spec/ruby/library/set/sortedset/filter_spec.rb9
-rw-r--r--spec/ruby/library/set/sortedset/flatten_merge_spec.rb8
-rw-r--r--spec/ruby/library/set/sortedset/flatten_spec.rb44
-rw-r--r--spec/ruby/library/set/sortedset/hash_spec.rb13
-rw-r--r--spec/ruby/library/set/sortedset/include_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/initialize_spec.rb30
-rw-r--r--spec/ruby/library/set/sortedset/inspect_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/intersection_spec.rb11
-rw-r--r--spec/ruby/library/set/sortedset/keep_if_spec.rb31
-rw-r--r--spec/ruby/library/set/sortedset/length_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/map_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/member_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/merge_spec.rb19
-rw-r--r--spec/ruby/library/set/sortedset/minus_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/plus_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/pretty_print_cycle_spec.rb10
-rw-r--r--spec/ruby/library/set/sortedset/pretty_print_spec.rb17
-rw-r--r--spec/ruby/library/set/sortedset/proper_subset_spec.rb33
-rw-r--r--spec/ruby/library/set/sortedset/proper_superset_spec.rb33
-rw-r--r--spec/ruby/library/set/sortedset/reject_spec.rb42
-rw-r--r--spec/ruby/library/set/sortedset/replace_spec.rb17
-rw-r--r--spec/ruby/library/set/sortedset/select_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/shared/add.rb14
-rw-r--r--spec/ruby/library/set/sortedset/shared/collect.rb20
-rw-r--r--spec/ruby/library/set/sortedset/shared/difference.rb15
-rw-r--r--spec/ruby/library/set/sortedset/shared/include.rb7
-rw-r--r--spec/ruby/library/set/sortedset/shared/intersection.rb15
-rw-r--r--spec/ruby/library/set/sortedset/shared/length.rb6
-rw-r--r--spec/ruby/library/set/sortedset/shared/select.rb35
-rw-r--r--spec/ruby/library/set/sortedset/shared/union.rb15
-rw-r--r--spec/ruby/library/set/sortedset/size_spec.rb7
-rw-r--r--spec/ruby/library/set/sortedset/subset_spec.rb33
-rw-r--r--spec/ruby/library/set/sortedset/subtract_spec.rb17
-rw-r--r--spec/ruby/library/set/sortedset/superset_spec.rb33
-rw-r--r--spec/ruby/library/set/sortedset/to_a_spec.rb17
-rw-r--r--spec/ruby/library/set/sortedset/union_spec.rb11
-rw-r--r--spec/ruby/library/set/subset_spec.rb41
-rw-r--r--spec/ruby/library/set/subtract_spec.rb17
-rw-r--r--spec/ruby/library/set/superset_spec.rb41
-rw-r--r--spec/ruby/library/set/to_a_spec.rb8
-rw-r--r--spec/ruby/library/set/to_s_spec.rb11
-rw-r--r--spec/ruby/library/set/union_spec.rb11
-rw-r--r--spec/ruby/library/shellwords/shellwords_spec.rb15
-rw-r--r--spec/ruby/library/singleton/allocate_spec.rb2
-rw-r--r--spec/ruby/library/singleton/clone_spec.rb2
-rw-r--r--spec/ruby/library/singleton/dup_spec.rb2
-rw-r--r--spec/ruby/library/singleton/instance_spec.rb12
-rw-r--r--spec/ruby/library/singleton/load_spec.rb13
-rw-r--r--spec/ruby/library/singleton/new_spec.rb2
-rw-r--r--spec/ruby/library/socket/addrinfo/afamily_spec.rb16
-rw-r--r--spec/ruby/library/socket/addrinfo/bind_spec.rb8
-rw-r--r--spec/ruby/library/socket/addrinfo/canonname_spec.rb4
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_from_spec.rb12
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_spec.rb6
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_to_spec.rb12
-rw-r--r--spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb62
-rw-r--r--spec/ruby/library/socket/addrinfo/foreach_spec.rb2
-rw-r--r--spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb24
-rw-r--r--spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/initialize_spec.rb92
-rw-r--r--spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/inspect_spec.rb26
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_address_spec.rb14
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_port_spec.rb14
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb14
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb14
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb22
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/listen_spec.rb4
-rw-r--r--spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb58
-rw-r--r--spec/ruby/library/socket/addrinfo/marshal_load_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/pfamily_spec.rb16
-rw-r--r--spec/ruby/library/socket/addrinfo/protocol_spec.rb14
-rw-r--r--spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/socktype_spec.rb14
-rw-r--r--spec/ruby/library/socket/addrinfo/tcp_spec.rb2
-rw-r--r--spec/ruby/library/socket/addrinfo/udp_spec.rb8
-rw-r--r--spec/ruby/library/socket/addrinfo/unix_path_spec.rb46
-rw-r--r--spec/ruby/library/socket/addrinfo/unix_spec.rb52
-rw-r--r--spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb2
-rw-r--r--spec/ruby/library/socket/ancillarydata/initialize_spec.rb36
-rw-r--r--spec/ruby/library/socket/ancillarydata/int_spec.rb4
-rw-r--r--spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb16
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb2
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb10
-rw-r--r--spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb10
-rw-r--r--spec/ruby/library/socket/basicsocket/close_read_spec.rb12
-rw-r--r--spec/ruby/library/socket/basicsocket/close_write_spec.rb12
-rw-r--r--spec/ruby/library/socket/basicsocket/connect_address_spec.rb86
-rw-r--r--spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb2
-rw-r--r--spec/ruby/library/socket/basicsocket/for_fd_spec.rb6
-rw-r--r--spec/ruby/library/socket/basicsocket/getpeereid_spec.rb4
-rw-r--r--spec/ruby/library/socket/basicsocket/getpeername_spec.rb2
-rw-r--r--spec/ruby/library/socket/basicsocket/getsockname_spec.rb4
-rw-r--r--spec/ruby/library/socket/basicsocket/getsockopt_spec.rb12
-rw-r--r--spec/ruby/library/socket/basicsocket/local_address_spec.rb10
-rw-r--r--spec/ruby/library/socket/basicsocket/read_nonblock_spec.rb32
-rw-r--r--spec/ruby/library/socket/basicsocket/read_spec.rb47
-rw-r--r--spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb88
-rw-r--r--spec/ruby/library/socket/basicsocket/recv_spec.rb120
-rw-r--r--spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb80
-rw-r--r--spec/ruby/library/socket/basicsocket/recvmsg_spec.rb76
-rw-r--r--spec/ruby/library/socket/basicsocket/remote_address_spec.rb10
-rw-r--r--spec/ruby/library/socket/basicsocket/send_spec.rb102
-rw-r--r--spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb9
-rw-r--r--spec/ruby/library/socket/basicsocket/sendmsg_spec.rb2
-rw-r--r--spec/ruby/library/socket/basicsocket/setsockopt_spec.rb58
-rw-r--r--spec/ruby/library/socket/basicsocket/shutdown_spec.rb44
-rw-r--r--spec/ruby/library/socket/constants/constants_spec.rb28
-rw-r--r--spec/ruby/library/socket/fixtures/classes.rb6
-rw-r--r--spec/ruby/library/socket/ipsocket/addr_spec.rb8
-rw-r--r--spec/ruby/library/socket/ipsocket/getaddress_spec.rb9
-rw-r--r--spec/ruby/library/socket/ipsocket/inspect_spec.rb24
-rw-r--r--spec/ruby/library/socket/ipsocket/peeraddr_spec.rb4
-rw-r--r--spec/ruby/library/socket/ipsocket/recvfrom_spec.rb60
-rw-r--r--spec/ruby/library/socket/option/bool_spec.rb4
-rw-r--r--spec/ruby/library/socket/option/initialize_spec.rb18
-rw-r--r--spec/ruby/library/socket/option/int_spec.rb6
-rw-r--r--spec/ruby/library/socket/option/linger_spec.rb18
-rw-r--r--spec/ruby/library/socket/option/new_spec.rb6
-rw-r--r--spec/ruby/library/socket/shared/address.rb259
-rw-r--r--spec/ruby/library/socket/shared/pack_sockaddr.rb69
-rw-r--r--spec/ruby/library/socket/shared/partially_closable_sockets.rb2
-rw-r--r--spec/ruby/library/socket/shared/socketpair.rb38
-rw-r--r--spec/ruby/library/socket/socket/accept_loop_spec.rb8
-rw-r--r--spec/ruby/library/socket/socket/accept_nonblock_spec.rb21
-rw-r--r--spec/ruby/library/socket/socket/accept_spec.rb12
-rw-r--r--spec/ruby/library/socket/socket/bind_spec.rb34
-rw-r--r--spec/ruby/library/socket/socket/connect_nonblock_spec.rb68
-rw-r--r--spec/ruby/library/socket/socket/connect_spec.rb24
-rw-r--r--spec/ruby/library/socket/socket/getaddrinfo_spec.rb70
-rw-r--r--spec/ruby/library/socket/socket/gethostbyaddr_spec.rb41
-rw-r--r--spec/ruby/library/socket/socket/gethostbyname_spec.rb26
-rw-r--r--spec/ruby/library/socket/socket/gethostname_spec.rb12
-rw-r--r--spec/ruby/library/socket/socket/getifaddrs_spec.rb42
-rw-r--r--spec/ruby/library/socket/socket/getnameinfo_spec.rb18
-rw-r--r--spec/ruby/library/socket/socket/getservbyname_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/getservbyport_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/initialize_spec.rb16
-rw-r--r--spec/ruby/library/socket/socket/ip_address_list_spec.rb10
-rw-r--r--spec/ruby/library/socket/socket/listen_spec.rb10
-rw-r--r--spec/ruby/library/socket/socket/local_address_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/new_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/pair_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb78
-rw-r--r--spec/ruby/library/socket/socket/recvfrom_spec.rb69
-rw-r--r--spec/ruby/library/socket/socket/remote_address_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/socketpair_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/sysaccept_spec.rb10
-rw-r--r--spec/ruby/library/socket/socket/tcp_server_loop_spec.rb4
-rw-r--r--spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb8
-rw-r--r--spec/ruby/library/socket/socket/tcp_spec.rb30
-rw-r--r--spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/udp_server_loop_spec.rb6
-rw-r--r--spec/ruby/library/socket/socket/udp_server_recv_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/udp_server_sockets_spec.rb8
-rw-r--r--spec/ruby/library/socket/socket/unix_server_loop_spec.rb76
-rw-r--r--spec/ruby/library/socket/socket/unix_server_socket_spec.rb56
-rw-r--r--spec/ruby/library/socket/socket/unix_spec.rb56
-rw-r--r--spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb16
-rw-r--r--spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb34
-rw-r--r--spec/ruby/library/socket/spec_helper.rb21
-rw-r--r--spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb12
-rw-r--r--spec/ruby/library/socket/tcpserver/accept_spec.rb23
-rw-r--r--spec/ruby/library/socket/tcpserver/gets_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpserver/initialize_spec.rb12
-rw-r--r--spec/ruby/library/socket/tcpserver/listen_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpserver/new_spec.rb34
-rw-r--r--spec/ruby/library/socket/tcpserver/sysaccept_spec.rb4
-rw-r--r--spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb32
-rw-r--r--spec/ruby/library/socket/tcpsocket/initialize_spec.rb49
-rw-r--r--spec/ruby/library/socket/tcpsocket/local_address_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpsocket/new_spec.rb5
-rw-r--r--spec/ruby/library/socket/tcpsocket/open_spec.rb1
-rw-r--r--spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb14
-rw-r--r--spec/ruby/library/socket/tcpsocket/remote_address_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpsocket/shared/new.rb69
-rw-r--r--spec/ruby/library/socket/udpsocket/bind_spec.rb4
-rw-r--r--spec/ruby/library/socket/udpsocket/initialize_spec.rb27
-rw-r--r--spec/ruby/library/socket/udpsocket/inspect_spec.rb17
-rw-r--r--spec/ruby/library/socket/udpsocket/local_address_spec.rb2
-rw-r--r--spec/ruby/library/socket/udpsocket/new_spec.rb18
-rw-r--r--spec/ruby/library/socket/udpsocket/open_spec.rb2
-rw-r--r--spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb24
-rw-r--r--spec/ruby/library/socket/udpsocket/remote_address_spec.rb2
-rw-r--r--spec/ruby/library/socket/udpsocket/send_spec.rb14
-rw-r--r--spec/ruby/library/socket/udpsocket/write_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb113
-rw-r--r--spec/ruby/library/socket/unixserver/accept_spec.rb161
-rw-r--r--spec/ruby/library/socket/unixserver/for_fd_spec.rb28
-rw-r--r--spec/ruby/library/socket/unixserver/initialize_spec.rb36
-rw-r--r--spec/ruby/library/socket/unixserver/listen_spec.rb24
-rw-r--r--spec/ruby/library/socket/unixserver/new_spec.rb6
-rw-r--r--spec/ruby/library/socket/unixserver/open_spec.rb26
-rw-r--r--spec/ruby/library/socket/unixserver/shared/new.rb26
-rw-r--r--spec/ruby/library/socket/unixserver/sysaccept_spec.rb64
-rw-r--r--spec/ruby/library/socket/unixsocket/addr_spec.rb47
-rw-r--r--spec/ruby/library/socket/unixsocket/initialize_spec.rb62
-rw-r--r--spec/ruby/library/socket/unixsocket/inspect_spec.rb18
-rw-r--r--spec/ruby/library/socket/unixsocket/local_address_spec.rb132
-rw-r--r--spec/ruby/library/socket/unixsocket/new_spec.rb6
-rw-r--r--spec/ruby/library/socket/unixsocket/open_spec.rb26
-rw-r--r--spec/ruby/library/socket/unixsocket/pair_spec.rb41
-rw-r--r--spec/ruby/library/socket/unixsocket/partially_closable_spec.rb32
-rw-r--r--spec/ruby/library/socket/unixsocket/path_spec.rb34
-rw-r--r--spec/ruby/library/socket/unixsocket/peeraddr_spec.rb38
-rw-r--r--spec/ruby/library/socket/unixsocket/recv_io_spec.rb15
-rw-r--r--spec/ruby/library/socket/unixsocket/recvfrom_spec.rb138
-rw-r--r--spec/ruby/library/socket/unixsocket/remote_address_spec.rb60
-rw-r--r--spec/ruby/library/socket/unixsocket/send_io_spec.rb9
-rw-r--r--spec/ruby/library/socket/unixsocket/shared/new.rb28
-rw-r--r--spec/ruby/library/socket/unixsocket/shared/pair.rb47
-rw-r--r--spec/ruby/library/socket/unixsocket/socketpair_spec.rb46
-rw-r--r--spec/ruby/library/stringio/append_spec.rb32
-rw-r--r--spec/ruby/library/stringio/binmode_spec.rb6
-rw-r--r--spec/ruby/library/stringio/bytes_spec.rb11
-rw-r--r--spec/ruby/library/stringio/chars_spec.rb11
-rw-r--r--spec/ruby/library/stringio/close_read_spec.rb10
-rw-r--r--spec/ruby/library/stringio/close_spec.rb8
-rw-r--r--spec/ruby/library/stringio/close_write_spec.rb12
-rw-r--r--spec/ruby/library/stringio/closed_read_spec.rb6
-rw-r--r--spec/ruby/library/stringio/closed_spec.rb10
-rw-r--r--spec/ruby/library/stringio/closed_write_spec.rb6
-rw-r--r--spec/ruby/library/stringio/codepoints_spec.rb9
-rw-r--r--spec/ruby/library/stringio/each_char_spec.rb2
-rw-r--r--spec/ruby/library/stringio/each_codepoint_spec.rb2
-rw-r--r--spec/ruby/library/stringio/each_line_spec.rb8
-rw-r--r--spec/ruby/library/stringio/each_spec.rb12
-rw-r--r--spec/ruby/library/stringio/fcntl_spec.rb2
-rw-r--r--spec/ruby/library/stringio/fileno_spec.rb5
-rw-r--r--spec/ruby/library/stringio/fixtures/classes.rb4
-rw-r--r--spec/ruby/library/stringio/flush_spec.rb4
-rw-r--r--spec/ruby/library/stringio/fsync_spec.rb4
-rw-r--r--spec/ruby/library/stringio/gets_spec.rb259
-rw-r--r--spec/ruby/library/stringio/initialize_spec.rb278
-rw-r--r--spec/ruby/library/stringio/inspect_spec.rb2
-rw-r--r--spec/ruby/library/stringio/lineno_spec.rb8
-rw-r--r--spec/ruby/library/stringio/lines_spec.rb19
-rw-r--r--spec/ruby/library/stringio/new_spec.rb10
-rw-r--r--spec/ruby/library/stringio/open_spec.rb188
-rw-r--r--spec/ruby/library/stringio/path_spec.rb2
-rw-r--r--spec/ruby/library/stringio/pid_spec.rb2
-rw-r--r--spec/ruby/library/stringio/pos_spec.rb2
-rw-r--r--spec/ruby/library/stringio/print_spec.rb22
-rw-r--r--spec/ruby/library/stringio/printf_spec.rb45
-rw-r--r--spec/ruby/library/stringio/putc_spec.rb33
-rw-r--r--spec/ruby/library/stringio/puts_spec.rb30
-rw-r--r--spec/ruby/library/stringio/read_nonblock_spec.rb15
-rw-r--r--spec/ruby/library/stringio/read_spec.rb6
-rw-r--r--spec/ruby/library/stringio/readline_spec.rb140
-rw-r--r--spec/ruby/library/stringio/readlines_spec.rb32
-rw-r--r--spec/ruby/library/stringio/readpartial_spec.rb50
-rw-r--r--spec/ruby/library/stringio/reopen_spec.rb159
-rw-r--r--spec/ruby/library/stringio/rewind_spec.rb2
-rw-r--r--spec/ruby/library/stringio/seek_spec.rb24
-rw-r--r--spec/ruby/library/stringio/set_encoding_by_bom_spec.rb237
-rw-r--r--spec/ruby/library/stringio/set_encoding_spec.rb8
-rw-r--r--spec/ruby/library/stringio/shared/codepoints.rb12
-rw-r--r--spec/ruby/library/stringio/shared/each.rb112
-rw-r--r--spec/ruby/library/stringio/shared/each_byte.rb12
-rw-r--r--spec/ruby/library/stringio/shared/each_char.rb10
-rw-r--r--spec/ruby/library/stringio/shared/eof.rb10
-rw-r--r--spec/ruby/library/stringio/shared/getc.rb22
-rw-r--r--spec/ruby/library/stringio/shared/gets.rb249
-rw-r--r--spec/ruby/library/stringio/shared/isatty.rb2
-rw-r--r--spec/ruby/library/stringio/shared/read.rb58
-rw-r--r--spec/ruby/library/stringio/shared/readchar.rb8
-rw-r--r--spec/ruby/library/stringio/shared/sysread.rb4
-rw-r--r--spec/ruby/library/stringio/shared/write.rb82
-rw-r--r--spec/ruby/library/stringio/string_spec.rb12
-rw-r--r--spec/ruby/library/stringio/stringio_spec.rb2
-rw-r--r--spec/ruby/library/stringio/sync_spec.rb4
-rw-r--r--spec/ruby/library/stringio/sysread_spec.rb7
-rw-r--r--spec/ruby/library/stringio/truncate_spec.rb34
-rw-r--r--spec/ruby/library/stringio/ungetbyte_spec.rb38
-rw-r--r--spec/ruby/library/stringio/ungetc_spec.rb24
-rw-r--r--spec/ruby/library/stringio/write_nonblock_spec.rb6
-rw-r--r--spec/ruby/library/stringscanner/append_spec.rb2
-rw-r--r--spec/ruby/library/stringscanner/captures_spec.rb36
-rw-r--r--spec/ruby/library/stringscanner/charpos_spec.rb18
-rw-r--r--spec/ruby/library/stringscanner/check_spec.rb77
-rw-r--r--spec/ruby/library/stringscanner/check_until_spec.rb120
-rw-r--r--spec/ruby/library/stringscanner/clear_spec.rb18
-rw-r--r--spec/ruby/library/stringscanner/concat_spec.rb2
-rw-r--r--spec/ruby/library/stringscanner/dup_spec.rb4
-rw-r--r--spec/ruby/library/stringscanner/element_reference_spec.rb19
-rw-r--r--spec/ruby/library/stringscanner/empty_spec.rb18
-rw-r--r--spec/ruby/library/stringscanner/eos_spec.rb17
-rw-r--r--spec/ruby/library/stringscanner/exist_spec.rb99
-rw-r--r--spec/ruby/library/stringscanner/fixed_anchor_spec.rb17
-rw-r--r--spec/ruby/library/stringscanner/get_byte_spec.rb81
-rw-r--r--spec/ruby/library/stringscanner/getbyte_spec.rb21
-rw-r--r--spec/ruby/library/stringscanner/getch_spec.rb65
-rw-r--r--spec/ruby/library/stringscanner/initialize_spec.rb11
-rw-r--r--spec/ruby/library/stringscanner/inspect_spec.rb2
-rw-r--r--spec/ruby/library/stringscanner/match_spec.rb23
-rw-r--r--spec/ruby/library/stringscanner/matched_size_spec.rb21
-rw-r--r--spec/ruby/library/stringscanner/matched_spec.rb4
-rw-r--r--spec/ruby/library/stringscanner/must_C_version_spec.rb2
-rw-r--r--spec/ruby/library/stringscanner/named_captures_spec.rb28
-rw-r--r--spec/ruby/library/stringscanner/peek_byte_spec.rb35
-rw-r--r--spec/ruby/library/stringscanner/peek_spec.rb39
-rw-r--r--spec/ruby/library/stringscanner/peep_spec.rb18
-rw-r--r--spec/ruby/library/stringscanner/rest_size_spec.rb27
-rw-r--r--spec/ruby/library/stringscanner/rest_spec.rb6
-rw-r--r--spec/ruby/library/stringscanner/restsize_spec.rb18
-rw-r--r--spec/ruby/library/stringscanner/scan_byte_spec.rb98
-rw-r--r--spec/ruby/library/stringscanner/scan_full_spec.rb14
-rw-r--r--spec/ruby/library/stringscanner/scan_integer_spec.rb157
-rw-r--r--spec/ruby/library/stringscanner/scan_spec.rb70
-rw-r--r--spec/ruby/library/stringscanner/scan_until_spec.rb120
-rw-r--r--spec/ruby/library/stringscanner/search_full_spec.rb105
-rw-r--r--spec/ruby/library/stringscanner/shared/bol.rb16
-rw-r--r--spec/ruby/library/stringscanner/shared/concat.rb16
-rw-r--r--spec/ruby/library/stringscanner/shared/eos.rb17
-rw-r--r--spec/ruby/library/stringscanner/shared/extract_range.rb17
-rw-r--r--spec/ruby/library/stringscanner/shared/extract_range_matched.rb15
-rw-r--r--spec/ruby/library/stringscanner/shared/get_byte.rb29
-rw-r--r--spec/ruby/library/stringscanner/shared/matched_size.rb21
-rw-r--r--spec/ruby/library/stringscanner/shared/peek.rb49
-rw-r--r--spec/ruby/library/stringscanner/shared/pos.rb11
-rw-r--r--spec/ruby/library/stringscanner/shared/rest_size.rb18
-rw-r--r--spec/ruby/library/stringscanner/shared/terminate.rb8
-rw-r--r--spec/ruby/library/stringscanner/size_spec.rb17
-rw-r--r--spec/ruby/library/stringscanner/skip_spec.rb14
-rw-r--r--spec/ruby/library/stringscanner/skip_until_spec.rb116
-rw-r--r--spec/ruby/library/stringscanner/string_spec.rb4
-rw-r--r--spec/ruby/library/stringscanner/terminate_spec.rb8
-rw-r--r--spec/ruby/library/stringscanner/unscan_spec.rb6
-rw-r--r--spec/ruby/library/stringscanner/values_at_spec.rb68
-rw-r--r--spec/ruby/library/syslog/close_spec.rb16
-rw-r--r--spec/ruby/library/syslog/constants_spec.rb4
-rw-r--r--spec/ruby/library/syslog/facility_spec.rb6
-rw-r--r--spec/ruby/library/syslog/ident_spec.rb4
-rw-r--r--spec/ruby/library/syslog/inspect_spec.rb4
-rw-r--r--spec/ruby/library/syslog/log_spec.rb10
-rw-r--r--spec/ruby/library/syslog/mask_spec.rb14
-rw-r--r--spec/ruby/library/syslog/open_spec.rb10
-rw-r--r--spec/ruby/library/syslog/opened_spec.rb16
-rw-r--r--spec/ruby/library/syslog/options_spec.rb6
-rw-r--r--spec/ruby/library/syslog/shared/log.rb6
-rw-r--r--spec/ruby/library/syslog/shared/reopen.rb14
-rw-r--r--spec/ruby/library/tempfile/_close_spec.rb4
-rw-r--r--spec/ruby/library/tempfile/callback_spec.rb6
-rw-r--r--spec/ruby/library/tempfile/close_spec.rb6
-rw-r--r--spec/ruby/library/tempfile/create_spec.rb176
-rw-r--r--spec/ruby/library/tempfile/initialize_spec.rb2
-rw-r--r--spec/ruby/library/tempfile/open_spec.rb18
-rw-r--r--spec/ruby/library/tempfile/path_spec.rb2
-rw-r--r--spec/ruby/library/tempfile/shared/length.rb6
-rw-r--r--spec/ruby/library/thread/queue_spec.rb4
-rw-r--r--spec/ruby/library/thread/sizedqueue_spec.rb4
-rw-r--r--spec/ruby/library/time/iso8601_spec.rb4
-rw-r--r--spec/ruby/library/time/shared/rfc2822.rb26
-rw-r--r--spec/ruby/library/time/shared/xmlschema.rb52
-rw-r--r--spec/ruby/library/time/to_date_spec.rb42
-rw-r--r--spec/ruby/library/time/to_datetime_spec.rb27
-rw-r--r--spec/ruby/library/time/to_time_spec.rb4
-rw-r--r--spec/ruby/library/time/xmlschema_spec.rb2
-rw-r--r--spec/ruby/library/timeout/error_spec.rb2
-rw-r--r--spec/ruby/library/timeout/timeout_spec.rb16
-rw-r--r--spec/ruby/library/tmpdir/dir/mktmpdir_spec.rb18
-rw-r--r--spec/ruby/library/tmpdir/dir/tmpdir_spec.rb4
-rw-r--r--spec/ruby/library/uri/generic/host_spec.rb5
-rw-r--r--spec/ruby/library/uri/generic/to_s_spec.rb5
-rw-r--r--spec/ruby/library/uri/join_spec.rb2
-rw-r--r--spec/ruby/library/uri/mailto/build_spec.rb2
-rw-r--r--spec/ruby/library/uri/parse_spec.rb24
-rw-r--r--spec/ruby/library/uri/plus_spec.rb170
-rw-r--r--spec/ruby/library/uri/select_spec.rb6
-rw-r--r--spec/ruby/library/uri/set_component_spec.rb60
-rw-r--r--spec/ruby/library/uri/shared/eql.rb6
-rw-r--r--spec/ruby/library/uri/shared/join.rb2
-rw-r--r--spec/ruby/library/uri/shared/parse.rb37
-rw-r--r--spec/ruby/library/uri/uri_spec.rb4
-rw-r--r--spec/ruby/library/weakref/__getobj___spec.rb4
-rw-r--r--spec/ruby/library/weakref/allocate_spec.rb2
-rw-r--r--spec/ruby/library/weakref/fixtures/classes.rb6
-rw-r--r--spec/ruby/library/weakref/send_spec.rb4
-rw-r--r--spec/ruby/library/weakref/weakref_alive_spec.rb4
-rw-r--r--spec/ruby/library/win32ole/fixtures/classes.rb17
-rw-r--r--spec/ruby/library/win32ole/win32ole/_getproperty_spec.rb1
-rw-r--r--spec/ruby/library/win32ole/win32ole/_invoke_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole/codepage_spec.rb1
-rw-r--r--spec/ruby/library/win32ole/win32ole/connect_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole/const_load_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole/constants_spec.rb1
-rw-r--r--spec/ruby/library/win32ole/win32ole/create_guid_spec.rb1
-rw-r--r--spec/ruby/library/win32ole/win32ole/invoke_spec.rb1
-rw-r--r--spec/ruby/library/win32ole/win32ole/locale_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole/new_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_func_methods_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_get_methods_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_method_help_spec.rb1
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_method_spec.rb1
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_methods_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_obj_help_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole/ole_put_methods_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole/setproperty_spec.rb1
-rw-r--r--spec/ruby/library/win32ole/win32ole/shared/ole_method.rb6
-rw-r--r--spec/ruby/library/win32ole/win32ole/shared/setproperty.rb2
-rw-r--r--spec/ruby/library/win32ole/win32ole_event/new_spec.rb15
-rw-r--r--spec/ruby/library/win32ole/win32ole_event/on_event_spec.rb13
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/dispid_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/event_interface_spec.rb15
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/event_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/helpcontext_spec.rb13
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/helpfile_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/helpstring_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/invkind_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/invoke_kind_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/name_spec.rb3
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/new_spec.rb23
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/offset_vtbl_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/params_spec.rb21
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/return_type_detail_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/return_type_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/return_vtype_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/shared/name.rb6
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/size_opt_params_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/size_params_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/to_s_spec.rb3
-rw-r--r--spec/ruby/library/win32ole/win32ole_method/visible_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/default_spec.rb17
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/input_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/name_spec.rb3
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/ole_type_detail_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/ole_type_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/optional_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/retval_spec.rb11
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/shared/name.rb6
-rw-r--r--spec/ruby/library/win32ole/win32ole_param/to_s_spec.rb3
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/guid_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/helpcontext_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/helpfile_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/helpstring_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/major_version_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/minor_version_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/name_spec.rb3
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/new_spec.rb35
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/ole_classes_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/ole_methods_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/ole_type_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/progid_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/progids_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/shared/name.rb4
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/src_type_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/to_s_spec.rb3
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/typekind_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/typelibs_spec.rb9
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/variables_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole_type/visible_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/name_spec.rb1
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/ole_type_detail_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/ole_type_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/shared/name.rb4
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/to_s_spec.rb1
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/value_spec.rb7
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/variable_kind_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/varkind_spec.rb5
-rw-r--r--spec/ruby/library/win32ole/win32ole_variable/visible_spec.rb5
-rw-r--r--spec/ruby/library/yaml/dump_spec.rb27
-rw-r--r--spec/ruby/library/yaml/dump_stream_spec.rb3
-rw-r--r--spec/ruby/library/yaml/fixtures/common.rb10
-rw-r--r--spec/ruby/library/yaml/fixtures/strings.rb56
-rw-r--r--spec/ruby/library/yaml/load_file_spec.rb13
-rw-r--r--spec/ruby/library/yaml/load_spec.rb135
-rw-r--r--spec/ruby/library/yaml/load_stream_spec.rb3
-rw-r--r--spec/ruby/library/yaml/parse_file_spec.rb8
-rw-r--r--spec/ruby/library/yaml/parse_spec.rb9
-rw-r--r--spec/ruby/library/yaml/shared/each_document.rb5
-rw-r--r--spec/ruby/library/yaml/shared/load.rb142
-rw-r--r--spec/ruby/library/yaml/to_yaml_spec.rb52
-rw-r--r--spec/ruby/library/yaml/unsafe_load_spec.rb9
-rw-r--r--spec/ruby/library/zlib/adler32_spec.rb4
-rw-r--r--spec/ruby/library/zlib/crc32_spec.rb4
-rw-r--r--spec/ruby/library/zlib/crc_table_spec.rb143
-rw-r--r--spec/ruby/library/zlib/deflate/deflate_spec.rb9
-rw-r--r--spec/ruby/library/zlib/deflate/new_spec.rb1
-rw-r--r--spec/ruby/library/zlib/deflate/params_spec.rb2
-rw-r--r--spec/ruby/library/zlib/gzipfile/close_spec.rb6
-rw-r--r--spec/ruby/library/zlib/gzipfile/comment_spec.rb3
-rw-r--r--spec/ruby/library/zlib/gzipfile/orig_name_spec.rb3
-rw-r--r--spec/ruby/library/zlib/gzipreader/each_char_spec.rb51
-rw-r--r--spec/ruby/library/zlib/gzipreader/each_line_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/each_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/eof_spec.rb22
-rw-r--r--spec/ruby/library/zlib/gzipreader/getc_spec.rb2
-rw-r--r--spec/ruby/library/zlib/gzipreader/gets_spec.rb2
-rw-r--r--spec/ruby/library/zlib/gzipreader/mtime_spec.rb11
-rw-r--r--spec/ruby/library/zlib/gzipreader/new_spec.rb1
-rw-r--r--spec/ruby/library/zlib/gzipreader/read_spec.rb6
-rw-r--r--spec/ruby/library/zlib/gzipreader/ungetbyte_spec.rb4
-rw-r--r--spec/ruby/library/zlib/gzipreader/ungetc_spec.rb12
-rw-r--r--spec/ruby/library/zlib/gzipwriter/append_spec.rb2
-rw-r--r--spec/ruby/library/zlib/gzipwriter/mtime_spec.rb3
-rw-r--r--spec/ruby/library/zlib/inflate/append_spec.rb2
-rw-r--r--spec/ruby/library/zlib/inflate/finish_spec.rb3
-rw-r--r--spec/ruby/library/zlib/inflate/inflate_spec.rb19
-rw-r--r--spec/ruby/library/zlib/inflate/new_spec.rb1
-rw-r--r--spec/ruby/library/zlib/inflate/set_dictionary_spec.rb2
-rw-r--r--spec/ruby/library/zlib/zlib_version_spec.rb2
-rw-r--r--spec/ruby/optional/capi/array_spec.rb82
-rw-r--r--spec/ruby/optional/capi/bignum_spec.rb48
-rw-r--r--spec/ruby/optional/capi/binding_spec.rb16
-rw-r--r--spec/ruby/optional/capi/class_spec.rb188
-rw-r--r--spec/ruby/optional/capi/constants_spec.rb2
-rw-r--r--spec/ruby/optional/capi/data_spec.rb68
-rw-r--r--spec/ruby/optional/capi/debug_spec.rb74
-rw-r--r--spec/ruby/optional/capi/digest_spec.rb103
-rw-r--r--spec/ruby/optional/capi/encoding_spec.rb284
-rw-r--r--spec/ruby/optional/capi/exception_spec.rb153
-rw-r--r--spec/ruby/optional/capi/ext/array_spec.c32
-rw-r--r--spec/ruby/optional/capi/ext/binding_spec.c19
-rw-r--r--spec/ruby/optional/capi/ext/class_spec.c46
-rw-r--r--spec/ruby/optional/capi/ext/constants_spec.c6
-rw-r--r--spec/ruby/optional/capi/ext/data_spec.c10
-rw-r--r--spec/ruby/optional/capi/ext/debug_spec.c93
-rw-r--r--spec/ruby/optional/capi/ext/digest_spec.c168
-rw-r--r--spec/ruby/optional/capi/ext/encoding_spec.c72
-rw-r--r--spec/ruby/optional/capi/ext/exception_spec.c40
-rw-r--r--spec/ruby/optional/capi/ext/fiber_spec.c64
-rw-r--r--spec/ruby/optional/capi/ext/finalizer_spec.c25
-rw-r--r--spec/ruby/optional/capi/ext/gc_spec.c118
-rw-r--r--spec/ruby/optional/capi/ext/globals_spec.c34
-rw-r--r--spec/ruby/optional/capi/ext/hash_spec.c20
-rw-r--r--spec/ruby/optional/capi/ext/integer_spec.c7
-rw-r--r--spec/ruby/optional/capi/ext/io_spec.c188
-rw-r--r--spec/ruby/optional/capi/ext/kernel_spec.c142
-rw-r--r--spec/ruby/optional/capi/ext/module_spec.c41
-rw-r--r--spec/ruby/optional/capi/ext/mutex_spec.c23
-rw-r--r--spec/ruby/optional/capi/ext/object_spec.c148
-rw-r--r--spec/ruby/optional/capi/ext/proc_spec.c74
-rw-r--r--spec/ruby/optional/capi/ext/range_spec.c50
-rw-r--r--spec/ruby/optional/capi/ext/rbasic_spec.c70
-rw-r--r--spec/ruby/optional/capi/ext/regexp_spec.c19
-rw-r--r--spec/ruby/optional/capi/ext/rubyspec.h73
-rw-r--r--spec/ruby/optional/capi/ext/set_spec.c65
-rw-r--r--spec/ruby/optional/capi/ext/string_spec.c219
-rw-r--r--spec/ruby/optional/capi/ext/struct_spec.c46
-rw-r--r--spec/ruby/optional/capi/ext/symbol_spec.c11
-rw-r--r--spec/ruby/optional/capi/ext/thread_spec.c59
-rw-r--r--spec/ruby/optional/capi/ext/tracepoint_spec.c2
-rw-r--r--spec/ruby/optional/capi/ext/typed_data_spec.c29
-rw-r--r--spec/ruby/optional/capi/ext/util_spec.c38
-rw-r--r--spec/ruby/optional/capi/fiber_spec.rb86
-rw-r--r--spec/ruby/optional/capi/file_spec.rb10
-rw-r--r--spec/ruby/optional/capi/finalizer_spec.rb40
-rw-r--r--spec/ruby/optional/capi/fixnum_spec.rb24
-rw-r--r--spec/ruby/optional/capi/fixtures/class.rb13
-rw-r--r--spec/ruby/optional/capi/fixtures/kernel.rb19
-rw-r--r--spec/ruby/optional/capi/fixtures/module.rb4
-rw-r--r--spec/ruby/optional/capi/fixtures/object.rb29
-rw-r--r--spec/ruby/optional/capi/fixtures/read.txt1
-rw-r--r--spec/ruby/optional/capi/float_spec.rb4
-rw-r--r--spec/ruby/optional/capi/gc_spec.rb97
-rw-r--r--spec/ruby/optional/capi/globals_spec.rb81
-rw-r--r--spec/ruby/optional/capi/hash_spec.rb109
-rw-r--r--spec/ruby/optional/capi/integer_spec.rb19
-rw-r--r--spec/ruby/optional/capi/io_spec.rb479
-rw-r--r--spec/ruby/optional/capi/kernel_spec.rb479
-rw-r--r--spec/ruby/optional/capi/module_spec.rb121
-rw-r--r--spec/ruby/optional/capi/mutex_spec.rb47
-rw-r--r--spec/ruby/optional/capi/numeric_spec.rb85
-rw-r--r--spec/ruby/optional/capi/object_spec.rb374
-rw-r--r--spec/ruby/optional/capi/proc_spec.rb129
-rw-r--r--spec/ruby/optional/capi/range_spec.rb158
-rw-r--r--spec/ruby/optional/capi/rbasic_spec.rb40
-rw-r--r--spec/ruby/optional/capi/regexp_spec.rb49
-rw-r--r--spec/ruby/optional/capi/set_spec.rb96
-rw-r--r--spec/ruby/optional/capi/shared/rbasic.rb58
-rw-r--r--spec/ruby/optional/capi/spec_helper.rb62
-rw-r--r--spec/ruby/optional/capi/string_spec.rb735
-rw-r--r--spec/ruby/optional/capi/struct_spec.rb135
-rw-r--r--spec/ruby/optional/capi/symbol_spec.rb8
-rw-r--r--spec/ruby/optional/capi/thread_spec.rb92
-rw-r--r--spec/ruby/optional/capi/time_spec.rb111
-rw-r--r--spec/ruby/optional/capi/tracepoint_spec.rb2
-rw-r--r--spec/ruby/optional/capi/typed_data_spec.rb29
-rw-r--r--spec/ruby/optional/capi/util_spec.rb124
-rw-r--r--spec/ruby/optional/thread_safety/fixtures/classes.rb39
-rw-r--r--spec/ruby/optional/thread_safety/hash_spec.rb210
-rw-r--r--spec/ruby/security/cve_2010_1330_spec.rb6
-rw-r--r--spec/ruby/security/cve_2013_4164_spec.rb4
-rw-r--r--spec/ruby/security/cve_2014_8080_spec.rb35
-rw-r--r--spec/ruby/security/cve_2017_17742_spec.rb34
-rw-r--r--spec/ruby/security/cve_2018_16396_spec.rb18
-rw-r--r--spec/ruby/security/cve_2018_8778_spec.rb4
-rw-r--r--spec/ruby/security/cve_2018_8779_spec.rb4
-rw-r--r--spec/ruby/security/cve_2018_8780_spec.rb12
-rw-r--r--spec/ruby/security/cve_2019_8321_spec.rb26
-rw-r--r--spec/ruby/security/cve_2019_8322_spec.rb11
-rw-r--r--spec/ruby/security/cve_2019_8323_spec.rb58
-rw-r--r--spec/ruby/security/cve_2019_8325_spec.rb58
-rw-r--r--spec/ruby/security/cve_2020_10663_spec.rb66
-rw-r--r--spec/ruby/security/cve_2024_49761_spec.rb7
-rw-r--r--spec/ruby/shared/basicobject/method_missing.rb18
-rw-r--r--spec/ruby/shared/basicobject/send.rb18
-rw-r--r--spec/ruby/shared/enumerable/minmax.rb24
-rw-r--r--spec/ruby/shared/enumerator/enum_for.rb50
-rw-r--r--spec/ruby/shared/enumerator/with_index.rb33
-rw-r--r--spec/ruby/shared/enumerator/with_object.rb42
-rw-r--r--spec/ruby/shared/fiber/resume.rb79
-rw-r--r--spec/ruby/shared/file/directory.rb18
-rw-r--r--spec/ruby/shared/file/executable.rb43
-rw-r--r--spec/ruby/shared/file/executable_real.rb43
-rw-r--r--spec/ruby/shared/file/exist.rb11
-rw-r--r--spec/ruby/shared/file/file.rb8
-rw-r--r--spec/ruby/shared/file/grpowned.rb6
-rw-r--r--spec/ruby/shared/file/identical.rb24
-rw-r--r--spec/ruby/shared/file/readable.rb16
-rw-r--r--spec/ruby/shared/file/readable_real.rb16
-rw-r--r--spec/ruby/shared/file/size.rb4
-rw-r--r--spec/ruby/shared/file/socket.rb32
-rw-r--r--spec/ruby/shared/file/sticky.rb2
-rw-r--r--spec/ruby/shared/file/world_readable.rb16
-rw-r--r--spec/ruby/shared/file/world_writable.rb16
-rw-r--r--spec/ruby/shared/file/writable.rb16
-rw-r--r--spec/ruby/shared/file/writable_real.rb24
-rw-r--r--spec/ruby/shared/file/zero.rb10
-rw-r--r--spec/ruby/shared/hash/key_error.rb8
-rw-r--r--spec/ruby/shared/io/putc.rb12
-rw-r--r--spec/ruby/shared/kernel/at_exit.rb73
-rw-r--r--spec/ruby/shared/kernel/complex.rb133
-rw-r--r--spec/ruby/shared/kernel/equal.rb4
-rw-r--r--spec/ruby/shared/kernel/fixtures/END.rb3
-rw-r--r--spec/ruby/shared/kernel/fixtures/at_exit.rb3
-rw-r--r--spec/ruby/shared/kernel/object_id.rb30
-rw-r--r--spec/ruby/shared/kernel/raise.rb364
-rw-r--r--spec/ruby/shared/math/atanh.rb44
-rw-r--r--spec/ruby/shared/process/abort.rb12
-rw-r--r--spec/ruby/shared/process/exit.rb42
-rw-r--r--spec/ruby/shared/process/fork.rb39
-rw-r--r--spec/ruby/shared/queue/clear.rb4
-rw-r--r--spec/ruby/shared/queue/close.rb4
-rw-r--r--spec/ruby/shared/queue/closed.rb4
-rw-r--r--spec/ruby/shared/queue/deque.rb83
-rw-r--r--spec/ruby/shared/queue/empty.rb4
-rw-r--r--spec/ruby/shared/queue/enque.rb2
-rw-r--r--spec/ruby/shared/queue/freeze.rb8
-rw-r--r--spec/ruby/shared/rational/Rational.rb143
-rw-r--r--spec/ruby/shared/rational/abs.rb11
-rw-r--r--spec/ruby/shared/rational/arithmetic_exception_in_coerce.rb11
-rw-r--r--spec/ruby/shared/rational/ceil.rb45
-rw-r--r--spec/ruby/shared/rational/coerce.rb29
-rw-r--r--spec/ruby/shared/rational/comparison.rb95
-rw-r--r--spec/ruby/shared/rational/denominator.rb14
-rw-r--r--spec/ruby/shared/rational/div.rb54
-rw-r--r--spec/ruby/shared/rational/divide.rb71
-rw-r--r--spec/ruby/shared/rational/divmod.rb42
-rw-r--r--spec/ruby/shared/rational/equal_value.rb39
-rw-r--r--spec/ruby/shared/rational/exponent.rb178
-rw-r--r--spec/ruby/shared/rational/fdiv.rb5
-rw-r--r--spec/ruby/shared/rational/floor.rb45
-rw-r--r--spec/ruby/shared/rational/hash.rb9
-rw-r--r--spec/ruby/shared/rational/inspect.rb14
-rw-r--r--spec/ruby/shared/rational/marshal_dump.rb5
-rw-r--r--spec/ruby/shared/rational/marshal_load.rb5
-rw-r--r--spec/ruby/shared/rational/minus.rb48
-rw-r--r--spec/ruby/shared/rational/modulo.rb43
-rw-r--r--spec/ruby/shared/rational/multiply.rb62
-rw-r--r--spec/ruby/shared/rational/numerator.rb10
-rw-r--r--spec/ruby/shared/rational/plus.rb48
-rw-r--r--spec/ruby/shared/rational/quo.rb5
-rw-r--r--spec/ruby/shared/rational/remainder.rb5
-rw-r--r--spec/ruby/shared/rational/round.rb106
-rw-r--r--spec/ruby/shared/rational/to_f.rb10
-rw-r--r--spec/ruby/shared/rational/to_i.rb12
-rw-r--r--spec/ruby/shared/rational/to_r.rb11
-rw-r--r--spec/ruby/shared/rational/to_s.rb14
-rw-r--r--spec/ruby/shared/rational/truncate.rb45
-rw-r--r--spec/ruby/shared/sizedqueue/enque.rb83
-rw-r--r--spec/ruby/shared/sizedqueue/max.rb10
-rw-r--r--spec/ruby/shared/sizedqueue/new.rb17
-rw-r--r--spec/ruby/shared/string/end_with.rb61
-rw-r--r--spec/ruby/shared/string/start_with.rb76
-rw-r--r--spec/ruby/shared/string/times.rb42
-rw-r--r--spec/ruby/shared/time/strftime_for_time.rb8
-rw-r--r--spec/ruby/shared/time/yday.rb18
-rw-r--r--spec/ruby/shared/types/rb_num2dbl_fails.rb17
-rw-r--r--spec/ruby/spec_helper.rb12
-rw-r--r--spec/syntax_suggest/fixtures/derailed_require_tree.rb.txt74
-rwxr-xr-xspec/syntax_suggest/fixtures/rexe.rb.txt569
-rw-r--r--spec/syntax_suggest/fixtures/routes.rb.txt121
-rw-r--r--spec/syntax_suggest/fixtures/ruby_buildpack.rb.txt1344
-rw-r--r--spec/syntax_suggest/fixtures/syntax_tree.rb.txt9234
-rw-r--r--spec/syntax_suggest/fixtures/this_project_extra_def.rb.txt64
-rw-r--r--spec/syntax_suggest/fixtures/webmock.rb.txt35
-rw-r--r--spec/syntax_suggest/integration/exe_cli_spec.rb27
-rw-r--r--spec/syntax_suggest/integration/ruby_command_line_spec.rb189
-rw-r--r--spec/syntax_suggest/integration/syntax_suggest_spec.rb260
-rw-r--r--spec/syntax_suggest/spec_helper.rb117
-rw-r--r--spec/syntax_suggest/unit/api_spec.rb104
-rw-r--r--spec/syntax_suggest/unit/around_block_scan_spec.rb165
-rw-r--r--spec/syntax_suggest/unit/block_expand_spec.rb230
-rw-r--r--spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb47
-rw-r--r--spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb44
-rw-r--r--spec/syntax_suggest/unit/capture_code_context_spec.rb229
-rw-r--r--spec/syntax_suggest/unit/clean_document_spec.rb260
-rw-r--r--spec/syntax_suggest/unit/cli_spec.rb224
-rw-r--r--spec/syntax_suggest/unit/code_block_spec.rb77
-rw-r--r--spec/syntax_suggest/unit/code_frontier_spec.rb135
-rw-r--r--spec/syntax_suggest/unit/code_line_spec.rb164
-rw-r--r--spec/syntax_suggest/unit/code_search_spec.rb505
-rw-r--r--spec/syntax_suggest/unit/core_ext_spec.rb32
-rw-r--r--spec/syntax_suggest/unit/display_invalid_blocks_spec.rb174
-rw-r--r--spec/syntax_suggest/unit/explain_syntax_spec.rb283
-rw-r--r--spec/syntax_suggest/unit/mini_stringio_spec.rb25
-rw-r--r--spec/syntax_suggest/unit/pathname_from_message_spec.rb65
-rw-r--r--spec/syntax_suggest/unit/priority_queue_spec.rb95
-rw-r--r--spec/syntax_suggest/unit/scan_history_spec.rb114
-rw-r--r--spec/syntax_suggest/unit/visitor_spec.rb119
4345 files changed, 143488 insertions, 67451 deletions
diff --git a/spec/README.md b/spec/README.md
index c67aedbb80..6a88c06e09 100644
--- a/spec/README.md
+++ b/spec/README.md
@@ -1,14 +1,23 @@
# spec/bundler
-spec/bundler is rspec examples for bundler library(lib/bundler.rb, lib/bundler/*).
+spec/bundler is rspec examples for bundler library (`lib/bundler.rb`, `lib/bundler/*`).
## Running spec/bundler
To run rspec for bundler:
+
```bash
make test-bundler
```
+or run rspec with parallel execution:
+
+```bash
+make test-bundler-parallel
+```
+
+If you specify `BUNDLER_SPECS=foo/bar_spec.rb` then only `spec/bundler/foo/bar_spec.rb` will be run.
+
# spec/ruby
ruby/spec (https://github.com/ruby/spec/) is
@@ -37,6 +46,7 @@ which change behavior or are removed. This is necessary for other Ruby implement
to still be able to run the specs and contribute new specs.
For example, change:
+
```ruby
describe "Some spec" do
it "some example" do
@@ -44,7 +54,9 @@ describe "Some spec" do
end
end
```
+
to:
+
```ruby
describe "Some spec" do
ruby_version_is ""..."2.7" do
@@ -64,7 +76,8 @@ end
See `spec/ruby/CONTRIBUTING.md` for more documentation about guards.
To verify specs are compatible with older Ruby versions:
-```
+
+```bash
cd spec/ruby
$RUBY_MANAGER use 2.4.9
../mspec/bin/mspec -j
@@ -73,28 +86,33 @@ $RUBY_MANAGER use 2.4.9
## Running ruby/spec
To run all specs:
+
```bash
make test-spec
```
-Extra arguments can be added via `MSPECOPT`.
+Extra arguments can be added via `SPECOPTS`.
For instance, to show the help:
+
```bash
-make test-spec MSPECOPT=-h
+make test-spec SPECOPTS=-h
```
You can also run the specs in parallel, which is currently experimental.
It takes around 10s instead of 60s on a quad-core laptop.
+
```bash
-make test-spec MSPECOPT=-j
+make test-spec SPECOPTS=-j
```
To run a specific test, add its path to the command:
+
```bash
-make test-spec MSPECOPT=spec/ruby/language/for_spec.rb
+make test-spec SPECOPTS=spec/ruby/language/for_spec.rb
```
If ruby trunk is your current `ruby` in `$PATH`, you can also run `mspec` directly:
+
```bash
# change ruby to trunk
ruby -v # => trunk
@@ -128,3 +146,15 @@ end
```
For more details, see `spec/ruby/CONTRIBUTING.md`.
+
+# spec/syntax_suggest
+
+## Running spec/syntax_suggest
+
+To run rspec for syntax_suggest:
+
+```bash
+make test-syntax-suggest
+```
+
+If you specify `SYNTAX_SUGGEST_SPECS=foo/bar_spec.rb` then only `spec/syntax_suggest/foo/bar_spec.rb` will be run.
diff --git a/spec/bin/bundle b/spec/bin/bundle
new file mode 100755
index 0000000000..8f8b535295
--- /dev/null
+++ b/spec/bin/bundle
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require_relative "../bundler/support/activate"
+
+load File.expand_path("bundle", Spec::Path.exedir)
diff --git a/spec/bin/parallel_rspec b/spec/bin/parallel_rspec
new file mode 100755
index 0000000000..412defa58b
--- /dev/null
+++ b/spec/bin/parallel_rspec
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require_relative "../bundler/support/setup"
+
+require "turbo_tests"
+TurboTests::CLI.new(ARGV).run
diff --git a/spec/bin/rspec b/spec/bin/rspec
new file mode 100755
index 0000000000..a41aa4896a
--- /dev/null
+++ b/spec/bin/rspec
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require_relative "../bundler/support/switch_rubygems"
+require_relative "../bundler/support/rubygems_ext"
+
+Spec::Rubygems.gem_load("rspec-core", "rspec")
diff --git a/spec/bundled_gems.mspec b/spec/bundled_gems.mspec
new file mode 100644
index 0000000000..f7edb34c75
--- /dev/null
+++ b/spec/bundled_gems.mspec
@@ -0,0 +1,14 @@
+load File.dirname(__FILE__) + '/default.mspec'
+
+class MSpecScript
+ test_bundled_gems = get(:stdlibs).to_a & get(:bundled_gems).to_a
+ unless ENV["BUNDLED_GEMS"].nil? || ENV["BUNDLED_GEMS"].empty?
+ test_bundled_gems = ENV["BUNDLED_GEMS"].split(",").map do |gem|
+ gem = "openstruct" if gem == "ostruct"
+ test_bundled_gems.find{|test_gem| test_gem.include?(gem) }
+ end.compact
+ exit if test_bundled_gems.empty?
+ end
+ set :library, test_bundled_gems
+ set :files, get(:library)
+end
diff --git a/spec/bundled_gems_spec.rb b/spec/bundled_gems_spec.rb
new file mode 100644
index 0000000000..9af06dd181
--- /dev/null
+++ b/spec/bundled_gems_spec.rb
@@ -0,0 +1,422 @@
+require "bundled_gems"
+
+require "bundler"
+require "fileutils"
+
+require_relative "bundler/support/builders"
+require_relative "bundler/support/helpers"
+require_relative "bundler/support/path"
+
+module Gem
+ def self.ruby=(ruby)
+ @ruby = ruby
+ end
+end
+
+RSpec.configure do |config|
+ config.include Spec::Builders
+ config.include Spec::Helpers
+ config.include Spec::Path
+
+ config.before(:suite) do
+ Gem.ruby = ENV["RUBY"] if ENV["RUBY"]
+
+ require_relative "bundler/support/rubygems_ext"
+ Spec::Helpers.install_dev_bundler
+ FileUtils.mkdir_p Spec::Path.gem_path
+ end
+
+ config.around(:each) do |example|
+ FileUtils.cp_r Spec::Path.pristine_system_gem_path, Spec::Path.system_gem_path
+
+ with_gem_path_as(system_gem_path) do
+ Bundler.ui.silence { example.run }
+
+ all_output = all_commands_output
+ if example.exception && !all_output.empty?
+ message = all_output + "\n" + example.exception.message
+ (class << example.exception; self; end).send(:define_method, :message) do
+ message
+ end
+ end
+ end
+ ensure
+ reset!
+ end
+
+ config.after :suite do
+ FileUtils.rm_rf Spec::Path.pristine_system_gem_path
+ end
+end
+
+RSpec.describe "bundled_gems.rb" do
+ let(:stub_code) {
+ source_lib_dir = File.realpath(Spec::Path.source_lib_dir.to_s)
+ <<~STUB
+ Gem::BUNDLED_GEMS.send(:remove_const, :LIBDIR)
+ Gem::BUNDLED_GEMS.send(:remove_const, :ARCHDIR)
+ Gem::BUNDLED_GEMS.send(:remove_const, :SINCE)
+ Gem::BUNDLED_GEMS.const_set(:LIBDIR, "#{source_lib_dir}/")
+ Gem::BUNDLED_GEMS.const_set(:ARCHDIR, File.expand_path($LOAD_PATH.find{|path| path.include?(".ext/common") }) + "/")
+ Gem::BUNDLED_GEMS.const_set(:SINCE, { "openssl" => RUBY_VERSION, "fileutils" => RUBY_VERSION, "csv" => "3.4.0", "net-smtp" => "3.1.0" })
+ STUB
+ }
+
+ def script(code, options = {})
+ options[:artifice] ||= "compact_index"
+ code = <<~RUBY
+ #{stub_code}
+ require 'bundler/inline'
+
+ #{code}
+ RUBY
+ ruby(code, options)
+ end
+
+ it "Show warning require and LoadError" do
+ script <<-RUBY
+ gemfile do
+ source "https://rubygems.org"
+ end
+
+ begin
+ require "csv"
+ rescue LoadError
+ end
+ require "openssl"
+ RUBY
+
+ expect(err).to include(/csv used to be loaded from (.*) since Ruby 3.4.0/)
+ expect(err).to include(/-e:15/)
+ expect(err).to include(/openssl used to be loaded from (.*) since Ruby #{RUBY_VERSION}/)
+ expect(err).to include(/-e:18/)
+ end
+
+ it "Show warning when bundled gems called as dependency" do
+ build_lib "activesupport", "7.0.7.2" do |s|
+ s.write "lib/active_support/all.rb", "require 'openssl'"
+ end
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ gemfile do
+ source "https://gem.repo1"
+ path "#{lib_path}" do
+ gem "activesupport", "7.0.7.2"
+ end
+ end
+
+ require "active_support/all"
+ RUBY
+
+ expect(err).to include(/openssl used to be loaded from (.*) since Ruby #{RUBY_VERSION}/)
+ expect(err).to include(/lib\/active_support\/all\.rb:1/)
+ end
+
+ it "Show warning dash gem like net/smtp" do
+ script <<-RUBY
+ gemfile do
+ source "https://rubygems.org"
+ end
+
+ begin
+ require "net/smtp"
+ rescue LoadError
+ end
+ RUBY
+
+ expect(err).to include(/net\/smtp used to be loaded from (.*) since Ruby 3.1.0/)
+ expect(err).to include(/-e:15/)
+ expect(err).to include("You can add net-smtp")
+ end
+
+ it "Show warning sub-feature like openssl/bn" do
+ skip "This test is not working on Windows" if Gem.win_platform?
+
+ script <<-RUBY
+ gemfile do
+ source "https://rubygems.org"
+ end
+
+ require "openssl/bn"
+ RUBY
+
+ expect(err).to include(/openssl\/bn is found in openssl, (.*) part of the default gems since Ruby #{RUBY_VERSION}/)
+ expect(err).to include(/-e:14/)
+ end
+
+ it "Show warning when bundle exec with ruby and script" do
+ code = <<-RUBY
+ #{stub_code}
+ require "openssl"
+ RUBY
+ create_file("script.rb", code)
+ create_file("Gemfile", "source 'https://rubygems.org'")
+
+ bundle "exec ruby script.rb"
+
+ expect(err).to include(/openssl used to be loaded from (.*) since Ruby #{RUBY_VERSION}/)
+ expect(err).to include(/script\.rb:8/)
+ end
+
+ it "Show warning when bundle exec with shebang's script" do
+ skip "This test is not working on Windows" if Gem.win_platform?
+
+ code = <<-RUBY
+ #!/usr/bin/env ruby
+ #{stub_code}
+ require "openssl"
+ RUBY
+ create_file("script.rb", code)
+ FileUtils.chmod(0o777, bundled_app("script.rb"))
+ create_file("Gemfile", "source 'https://rubygems.org'")
+
+ bundle "exec ./script.rb"
+
+ expect(err).to include(/openssl used to be loaded from (.*) since Ruby #{RUBY_VERSION}/)
+ expect(err).to include(/script\.rb:9/)
+ end
+
+ it "Show warning when bundle exec with -r option" do
+ create_file("stub.rb", stub_code)
+ create_file("Gemfile", "source 'https://rubygems.org'")
+ bundle "exec ruby -r./stub -ropenssl -e ''"
+
+ expect(err).to include(/openssl used to be loaded from (.*) since Ruby #{RUBY_VERSION}/)
+ end
+
+ it "Show warning when warn is not the standard one in the current scope" do
+ script <<-RUBY
+ module My
+ def warn(msg)
+ end
+
+ def my
+ gemfile do
+ source "https://rubygems.org"
+ end
+
+ require "openssl"
+ end
+
+ extend self
+ end
+
+ My.my
+ RUBY
+
+ expect(err).to include(/openssl used to be loaded from (.*) since Ruby #{RUBY_VERSION}/)
+ expect(err).to include(/-e:19/)
+ end
+
+ it "Don't show warning when bundled gems called as dependency" do
+ build_lib "activesupport", "7.0.7.2" do |s|
+ s.write "lib/active_support/all.rb", "require 'openssl'"
+ end
+ build_lib "openssl", "1.0.0" do |s|
+ s.write "lib/openssl.rb", "puts 'openssl'"
+ end
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ gemfile do
+ source "https://gem.repo1"
+ path "#{lib_path}" do
+ gem "activesupport", "7.0.7.2"
+ gem "openssl"
+ end
+ end
+
+ require "active_support/all"
+ RUBY
+
+ expect(err).to be_empty
+ end
+
+ it "Show warning with bootsnap cases" do
+ script <<-RUBY
+ gemfile do
+ source "https://rubygems.org"
+ # gem "bootsnap", require: false
+ end
+
+ # require 'bootsnap'
+ # Bootsnap.setup(cache_dir: 'tmp/cache')
+
+ # bootsnap expand required feature to full path
+ # require 'openssl'
+ require Gem::BUNDLED_GEMS::ARCHDIR + 'openssl'
+ RUBY
+
+ expect(err).to include(/openssl used to be loaded from (.*) since Ruby #{RUBY_VERSION}/)
+ # TODO: We should assert caller location like below:
+ # test_warn_bootsnap.rb:14: warning: ...
+ end
+
+ it "Show warning with bootsnap for gem with native extension" do
+ script <<-RUBY
+ gemfile do
+ source "https://rubygems.org"
+ # gem "bootsnap", require: false
+ end
+
+ # require 'bootsnap'
+ # Bootsnap.setup(cache_dir: 'tmp/cache')
+
+ # bootsnap expand required feature to full path
+ # require 'openssl'
+ require Gem::BUNDLED_GEMS::ARCHDIR + "openssl"
+ RUBY
+
+ expect(err).to include(/openssl used to be loaded from (.*) since Ruby #{RUBY_VERSION}/)
+ # TODO: We should assert caller location like below:
+ # test_warn_bootsnap_rubyarchdir_gem.rb:14: warning: ...
+ end
+
+ it "Show warning with bootsnap and some gem in Gemfile" do
+ # Original issue is childprocess 5.0.0 and logger.
+ build_lib "fileutils2", "5.0.0" do |s|
+ # bootsnap expand required feature to full path
+ rubylibpath = File.realpath(File.join(__dir__, "..", "lib"))
+ s.write "lib/fileutils2.rb", "require '#{rubylibpath}/fileutils'"
+ end
+
+ script <<-RUBY
+ gemfile do
+ source "https://rubygems.org"
+ # gem "bootsnap", require: false
+ path "#{lib_path}" do
+ gem "fileutils2", "5.0.0"
+ end
+ end
+
+ # require 'bootsnap'
+ # Bootsnap.setup(cache_dir: 'tmp/cache')
+
+ # bootsnap expand required feature to full path
+ require Gem.loaded_specs["fileutils2"].full_gem_path + '/lib/fileutils2'
+ RUBY
+
+ expect(err).to include(/fileutils used to be loaded from (.*) since Ruby #{RUBY_VERSION}/)
+ # TODO: We should assert caller location like below:
+ # $GEM_HOME/gems/childprocess-5.0.0/lib/childprocess.rb:7: warning:
+ end
+
+ it "Show warning with zeitwerk" do
+ libpath = Dir[File.expand_path("../.bundle/gems/{zeitwerk}-*/lib", __dir__)].map(&:to_s).first
+ code = <<-RUBY
+ #{stub_code}
+ $LOAD_PATH.unshift("#{libpath}")
+ require "zeitwerk"
+ loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
+ loader.setup
+
+ require 'openssl'
+ RUBY
+ create_file("script.rb", code)
+ create_file("Gemfile", "source 'https://rubygems.org'")
+ bundle "exec ruby script.rb"
+
+ expect(err).to include(/openssl used to be loaded from (.*) since Ruby #{RUBY_VERSION}/)
+ expect(err).to include(/script\.rb:13/)
+ end
+
+ it "Don't show warning openssl/bn when openssl on Gemfile" do
+ build_lib "openssl", "1.0.0" do |s|
+ s.write "lib/openssl.rb", "puts 'openssl'"
+ s.write "lib/openssl/bn.rb", "puts 'openssl/bn'"
+ end
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ gemfile do
+ source "https://gem.repo1"
+ path "#{lib_path}" do
+ gem "openssl"
+ end
+ end
+
+ require "openssl/bn"
+ RUBY
+
+ expect(err).to be_empty
+ end
+
+ it "Don't show warning with net/smtp when net-smtp on Gemfile" do
+ build_lib "net-smtp", "1.0.0" do |s|
+ s.write "lib/net/smtp.rb", "puts 'net-smtp'"
+ end
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ gemfile do
+ source "https://gem.repo1"
+ path "#{lib_path}" do
+ gem "net-smtp"
+ end
+ end
+
+ require "net/smtp"
+ RUBY
+
+ expect(err).to be_empty
+ end
+
+ describe ".force_activate" do
+ before do
+ allow_any_instance_of(Bundler::Runtime).to receive(:setup).and_raise(Bundler::GemNotFound)
+ end
+
+ context "with bundle environment" do
+ before do
+ code = <<-RUBY
+ #!/usr/bin/env ruby
+
+ Gem::BUNDLED_GEMS.force_activate("csv")
+ RUBY
+ create_file("script.rb", code)
+ create_file("Gemfile", "source 'https://rubygems.org'")
+ end
+
+ it "lockfile is available" do
+ bundle "install"
+ bundle "exec ./script.rb"
+
+ expect(err).to include("gem install csv")
+ end
+
+ it "lockfile is not available" do
+ bundle "exec ./script.rb"
+
+ expect(err).to include("gem install csv")
+ end
+ end
+
+ context "with bundler/inline" do
+ it "foo is available on LOAD_PATH" do
+ build_lib "foo", "1.0.0" do |s|
+ s.write "lib/foo.rb", "puts :foo"
+ end
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ #!/usr/bin/env ruby
+ gemfile do
+ source "https://gem.repo1"
+ path "#{lib_path}" do
+ gem "foo", "1.0.0"
+ end
+ end
+
+ Gem::BUNDLED_GEMS.force_activate("csv")
+ puts $LOAD_PATH
+ RUBY
+
+ expect(err).to include("gem install csv")
+ expect(out).to include("foo-1.0.0/lib")
+ end
+ end
+
+ context "without bundle environment" do
+ it "warns about installation requirement" do
+ expect_any_instance_of(Object).to receive(:warn)
+ Gem::BUNDLED_GEMS.force_activate("csv")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/build_metadata_spec.rb b/spec/bundler/bundler/build_metadata_spec.rb
index afa2d1716f..2e69821f68 100644
--- a/spec/bundler/bundler/build_metadata_spec.rb
+++ b/spec/bundler/bundler/build_metadata_spec.rb
@@ -6,18 +6,20 @@ require "bundler/build_metadata"
RSpec.describe Bundler::BuildMetadata do
before do
allow(Time).to receive(:now).and_return(Time.at(0))
- Bundler::BuildMetadata.instance_variable_set(:@built_at, nil)
+ Bundler::BuildMetadata.instance_variable_set(:@timestamp, nil)
end
- describe "#built_at" do
- it "returns %Y-%m-%d formatted time" do
- expect(Bundler::BuildMetadata.built_at).to eq "1970-01-01"
+ describe "#timestamp" do
+ it "returns %Y-%m-%d formatted current time if built_at not set" do
+ Bundler::BuildMetadata.instance_variable_set(:@built_at, nil)
+ expect(Bundler::BuildMetadata.timestamp).to eq "1970-01-01"
end
- end
- describe "#release?" do
- it "returns false as default" do
- expect(Bundler::BuildMetadata.release?).to be_falsey
+ it "returns %Y-%m-%d formatted current time if built_at not set" do
+ Bundler::BuildMetadata.instance_variable_set(:@built_at, "2025-01-01")
+ expect(Bundler::BuildMetadata.timestamp).to eq "2025-01-01"
+ ensure
+ Bundler::BuildMetadata.instance_variable_set(:@built_at, nil)
end
end
@@ -40,10 +42,9 @@ RSpec.describe Bundler::BuildMetadata do
describe "#to_h" do
subject { Bundler::BuildMetadata.to_h }
- it "returns a hash includes Built At, Git SHA and Released Version" do
- expect(subject["Built At"]).to eq "1970-01-01"
+ it "returns a hash includes Timestamp, and Git SHA" do
+ expect(subject["Timestamp"]).to eq "1970-01-01"
expect(subject["Git SHA"]).to be_instance_of(String)
- expect(subject["Released Version"]).to be_falsey
end
end
end
diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb
index 56ef4ce75a..bddcbdaef3 100644
--- a/spec/bundler/bundler/bundler_spec.rb
+++ b/spec/bundler/bundler/bundler_spec.rb
@@ -4,6 +4,71 @@ require "bundler"
require "tmpdir"
RSpec.describe Bundler do
+ describe "#load_marshal" do
+ it "is a private method and raises an error" do
+ data = Marshal.dump(Bundler)
+ expect { Bundler.load_marshal(data) }.to raise_error(NoMethodError, /private method [`']load_marshal' called/)
+ end
+
+ it "loads any data" do
+ data = Marshal.dump(Bundler)
+ expect(Bundler.send(:load_marshal, data)).to eq(Bundler)
+ end
+ end
+
+ describe "#safe_load_marshal" do
+ it "fails on unexpected class" do
+ data = Marshal.dump(Bundler)
+ expect { Bundler.safe_load_marshal(data) }.to raise_error(Bundler::MarshalError)
+ end
+
+ it "loads simple structure" do
+ simple_structure = { "name" => [:development] }
+ data = Marshal.dump(simple_structure)
+ expect(Bundler.safe_load_marshal(data)).to eq(simple_structure)
+ end
+
+ it "loads Gem::Specification" do
+ gem_spec = Gem::Specification.new do |s|
+ s.name = "bundler"
+ s.version = Gem::Version.new("2.4.7")
+ s.installed_by_version = Gem::Version.new("0")
+ s.authors = ["André Arko",
+ "Samuel Giddins",
+ "Colby Swandale",
+ "Hiroshi Shibata",
+ "David Rodríguez",
+ "Grey Baker",
+ "Stephanie Morillo",
+ "Chris Morris",
+ "James Wen",
+ "Tim Moore",
+ "André Medeiros",
+ "Jessica Lynn Suttles",
+ "Terence Lee",
+ "Carl Lerche",
+ "Yehuda Katz"]
+ s.date = Time.utc(2023, 2, 15)
+ s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably"
+ s.email = ["team@bundler.io"]
+ s.homepage = "https://bundler.io"
+ s.metadata = { "bug_tracker_uri" => "https://github.com/ruby/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler",
+ "changelog_uri" => "https://github.com/ruby/rubygems/blob/master/bundler/CHANGELOG.md",
+ "homepage_uri" => "https://bundler.io/",
+ "source_code_uri" => "https://github.com/ruby/rubygems/tree/master/bundler" }
+ s.require_paths = ["lib"]
+ s.required_ruby_version = Gem::Requirement.new([">= 2.6.0"])
+ s.required_rubygems_version = Gem::Requirement.new([">= 3.0.1"])
+ s.rubygems_version = "3.4.7"
+ s.specification_version = 4
+ s.summary = "The best way to manage your application's dependencies"
+ s.license = false
+ end
+ data = Marshal.dump(gem_spec)
+ expect(Bundler.safe_load_marshal(data)).to eq(gem_spec)
+ end
+ end
+
describe "#load_gemspec_uncached" do
let(:app_gemspec_path) { tmp("test.gemspec") }
subject { Bundler.load_gemspec_uncached(app_gemspec_path) }
@@ -11,7 +76,7 @@ RSpec.describe Bundler do
context "with incorrect YAML file" do
before do
File.open(app_gemspec_path, "wb") do |f|
- f.write strip_whitespace(<<-GEMSPEC)
+ f.write <<~GEMSPEC
---
{:!00 ao=gu\g1= 7~f
GEMSPEC
@@ -21,33 +86,9 @@ RSpec.describe Bundler do
it "catches YAML syntax errors" do
expect { subject }.to raise_error(Bundler::GemspecError, /error while loading `test.gemspec`/)
end
-
- context "on Rubies with a settable YAML engine", :if => defined?(YAML::ENGINE) do
- context "with Syck as YAML::Engine" do
- it "raises a GemspecError after YAML load throws ArgumentError" do
- orig_yamler = YAML::ENGINE.yamler
- YAML::ENGINE.yamler = "syck"
-
- expect { subject }.to raise_error(Bundler::GemspecError)
-
- YAML::ENGINE.yamler = orig_yamler
- end
- end
-
- context "with Psych as YAML::Engine" do
- it "raises a GemspecError after YAML load throws Psych::SyntaxError" do
- orig_yamler = YAML::ENGINE.yamler
- YAML::ENGINE.yamler = "psych"
-
- expect { subject }.to raise_error(Bundler::GemspecError)
-
- YAML::ENGINE.yamler = orig_yamler
- end
- end
- end
end
- context "with correct YAML file", :if => defined?(Encoding) do
+ context "with correct YAML file", if: defined?(Encoding) do
it "can load a gemspec with unicode characters with default ruby encoding" do
# spec_helper forces the external encoding to UTF-8 but that's not the
# default until Ruby 2.0
@@ -58,7 +99,7 @@ RSpec.describe Bundler do
$VERBOSE = verbose
File.open(app_gemspec_path, "wb") do |file|
- file.puts <<-GEMSPEC.gsub(/^\s+/, "")
+ file.puts <<~GEMSPEC
# -*- encoding: utf-8 -*-
Gem::Specification.new do |gem|
gem.author = "André the Giant"
@@ -98,7 +139,7 @@ RSpec.describe Bundler do
end
GEMSPEC
end
- expect(Bundler.rubygems).to receive(:validate).with have_attributes(:name => "validated")
+ expect(Bundler.rubygems).to receive(:validate).with have_attributes(name: "validated")
subject
end
end
@@ -106,7 +147,7 @@ RSpec.describe Bundler do
context "with gemspec containing local variables" do
before do
File.open(app_gemspec_path, "wb") do |f|
- f.write strip_whitespace(<<-GEMSPEC)
+ f.write <<~GEMSPEC
must_not_leak = true
Gem::Specification.new do |gem|
gem.name = "leak check"
@@ -123,82 +164,45 @@ RSpec.describe Bundler do
end
describe "#which" do
- let(:executable) { "executable" }
+ it "can detect relative path" do
+ script_path = bundled_app("tmp/test_command")
+ create_file(script_path, "#!/usr/bin/env ruby\n")
- let(:path) do
- if Gem.win_platform?
- %w[C:/a C:/b C:/c C:/../d C:/e]
- else
- %w[/a /b c ../d /e]
+ result = Dir.chdir script_path.dirname.dirname do
+ Bundler.which("test_command")
end
- end
+ expect(result).to eq(nil)
- let(:expected) { "executable" }
-
- before do
- ENV["PATH"] = path.join(File::PATH_SEPARATOR)
-
- allow(File).to receive(:file?).and_return(false)
- allow(File).to receive(:executable?).and_return(false)
- if expected
- expect(File).to receive(:file?).with(expected).and_return(true)
- expect(File).to receive(:executable?).with(expected).and_return(true)
+ result = Dir.chdir script_path.dirname do
+ Bundler.which("test_command")
end
- end
- subject { described_class.which(executable) }
-
- shared_examples_for "it returns the correct executable" do
- it "returns the expected file" do
- expect(subject).to eq(expected)
- end
+ expect(result).to eq("test_command") unless Gem.win_platform?
+ expect(result).to eq("test_command.bat") if Gem.win_platform?
end
- it_behaves_like "it returns the correct executable"
+ it "can detect absolute path" do
+ create_file("test_command", "#!/usr/bin/env ruby\n")
- context "when the executable in inside a quoted path" do
- let(:expected) do
- if Gem.win_platform?
- "C:/e/executable"
- else
- "/e/executable"
- end
- end
- it_behaves_like "it returns the correct executable"
+ ENV["PATH"] = bundled_app("test_command").parent.to_s
+
+ result = Bundler.which("test_command")
+ expect(result).to eq(bundled_app("test_command").to_s) unless Gem.win_platform?
+ expect(result).to eq(bundled_app("test_command.bat").to_s) if Gem.win_platform?
end
- context "when the executable is not found" do
- let(:expected) { nil }
- it_behaves_like "it returns the correct executable"
+ it "returns nil when not found" do
+ result = Bundler.which("test_command")
+ expect(result).to eq(nil)
end
end
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 ""
- end
- end
- end
-
- describe "#rm_rf" do
- context "the directory is world writable" do
- let(:bundler_ui) { Bundler.ui }
- it "should raise a friendly error" do
- allow(File).to receive(:exist?).and_return(true)
- allow(::Bundler::FileUtils).to receive(:remove_entry_secure).and_raise(ArgumentError)
- allow(File).to receive(:world_writable?).and_return(true)
- message = <<EOF
-It is a security vulnerability to allow your home directory to be world-writable, and bundler can not continue.
-You should probably consider fixing this issue by running `chmod o-w ~` on *nix.
-Please refer to https://ruby-doc.org/stdlib-2.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details.
-EOF
- expect(bundler_ui).to receive(:warn).with(message)
- expect { Bundler.send(:rm_rf, bundled_app) }.to raise_error(Bundler::PathError)
+ Bundler.send(:configure_gem_path)
+ expect(ENV["GEM_PATH"]).to eq ""
end
end
end
@@ -206,30 +210,14 @@ EOF
describe "#mkdir_p" do
it "creates a folder at the given path" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
allow(Bundler).to receive(:root).and_return(bundled_app)
- Bundler.mkdir_p(bundled_app.join("foo", "bar"))
- expect(bundled_app.join("foo", "bar")).to exist
- end
-
- context "when mkdir_p requires sudo" do
- it "creates a new folder using sudo" do
- expect(Bundler).to receive(:requires_sudo?).and_return(true)
- expect(Bundler).to receive(:sudo).and_return true
- Bundler.mkdir_p(bundled_app.join("foo"))
- end
- end
-
- context "with :no_sudo option" do
- it "forces mkdir_p to not use sudo" do
- expect(Bundler).to receive(:requires_sudo?).and_return(true)
- expect(Bundler).to_not receive(:sudo)
- Bundler.mkdir_p(bundled_app.join("foo"), :no_sudo => true)
- end
+ Bundler.mkdir_p(bundled_app("foo", "bar"))
+ expect(bundled_app("foo", "bar")).to exist
end
end
@@ -249,11 +237,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
@@ -265,14 +250,12 @@ EOF
it "should issue a warning and return a temporary user home" do
allow(Bundler.rubygems).to receive(:user_home).and_return(path)
allow(File).to receive(:directory?).with(path).and_return true
+ allow(File).to receive(:writable?).and_call_original
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,128 +276,13 @@ 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
end
- describe "#requires_sudo?" do
- let!(:tmpdir) { Dir.mktmpdir }
- let(:bundle_path) { Pathname("#{tmpdir}/bundle") }
-
- def clear_cached_requires_sudo
- return unless Bundler.instance_variable_defined?(:@requires_sudo_ran)
- Bundler.remove_instance_variable(:@requires_sudo_ran)
- Bundler.remove_instance_variable(:@requires_sudo)
- end
-
- before do
- clear_cached_requires_sudo
- allow(Bundler).to receive(:which).with("sudo").and_return("/usr/bin/sudo")
- allow(Bundler).to receive(:bundle_path).and_return(bundle_path)
- end
-
- after do
- FileUtils.rm_rf(tmpdir)
- clear_cached_requires_sudo
- end
-
- subject { Bundler.requires_sudo? }
-
- context "bundle_path doesn't exist" do
- it { should be false }
-
- context "and parent dir can't be written" do
- before do
- FileUtils.chmod(0o500, tmpdir)
- end
-
- it { should be true }
- end
-
- context "with unwritable files in a parent dir" do
- # Regression test for https://github.com/rubygems/bundler/pull/6316
- # It doesn't matter if there are other unwritable files so long as
- # bundle_path can be created
- before do
- file = File.join(tmpdir, "unrelated_file")
- FileUtils.touch(file)
- FileUtils.chmod(0o400, file)
- end
-
- it { should be false }
- end
- end
-
- context "bundle_path exists" do
- before do
- FileUtils.mkdir_p(bundle_path)
- end
-
- it { should be false }
-
- context "and is unwritable" do
- before do
- FileUtils.chmod(0o500, bundle_path)
- end
-
- it { should be true }
- end
- end
-
- context "path writability" do
- before do
- FileUtils.mkdir_p("tmp/vendor/bundle")
- FileUtils.mkdir_p("tmp/vendor/bin_dir")
- end
- after do
- FileUtils.rm_rf("tmp/vendor/bundle")
- FileUtils.rm_rf("tmp/vendor/bin_dir")
- end
- context "writable paths" do
- it "should return false and display nothing" do
- allow(Bundler).to receive(:bundle_path).and_return(Pathname("tmp/vendor/bundle"))
- expect(Bundler.ui).to_not receive(:warn)
- expect(Bundler.requires_sudo?).to eq(false)
- end
- end
- context "unwritable paths" do
- before do
- FileUtils.touch("tmp/vendor/bundle/unwritable1.txt")
- FileUtils.touch("tmp/vendor/bundle/unwritable2.txt")
- FileUtils.touch("tmp/vendor/bin_dir/unwritable3.txt")
- FileUtils.chmod(0o400, "tmp/vendor/bundle/unwritable1.txt")
- FileUtils.chmod(0o400, "tmp/vendor/bundle/unwritable2.txt")
- FileUtils.chmod(0o400, "tmp/vendor/bin_dir/unwritable3.txt")
- end
- it "should return true and display warn message" do
- allow(Bundler).to receive(:bundle_path).and_return(Pathname("tmp/vendor/bundle"))
- bin_dir = Pathname("tmp/vendor/bin_dir/")
-
- # allow File#writable? to be called with args other than the stubbed on below
- allow(File).to receive(:writable?).and_call_original
-
- # fake make the directory unwritable
- allow(File).to receive(:writable?).with(bin_dir).and_return(false)
- allow(Bundler).to receive(:system_bindir).and_return(Pathname("tmp/vendor/bin_dir/"))
- message = <<-MESSAGE.chomp
-Following files may not be writable, so sudo is needed:
- tmp/vendor/bin_dir/
- tmp/vendor/bundle/unwritable1.txt
- tmp/vendor/bundle/unwritable2.txt
-MESSAGE
- expect(Bundler.ui).to receive(:warn).with(message)
- expect(Bundler.requires_sudo?).to eq(true)
- end
- end
- end
- end
-
context "user cache dir" do
let(:home_path) { Pathname.new(ENV["HOME"]) }
diff --git a/spec/bundler/bundler/ci_detector_spec.rb b/spec/bundler/bundler/ci_detector_spec.rb
new file mode 100644
index 0000000000..299d8005e8
--- /dev/null
+++ b/spec/bundler/bundler/ci_detector_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::CIDetector do
+ # This is properly tested in rubygems, under the name Gem::CIDetector
+ # But the test that confirms that our version _stays in sync_ with that version
+ # will live here.
+
+ it "stays in sync with the rubygems implementation" do
+ rubygems_implementation_path = File.join(git_root, "lib", "rubygems", "ci_detector.rb")
+ expect(File.exist?(rubygems_implementation_path)).to be_truthy
+ rubygems_code = File.read(rubygems_implementation_path)
+ denamespaced_rubygems_code = rubygems_code.sub("Gem", "NAMESPACE")
+
+ bundler_implementation_path = File.join(source_lib_dir, "bundler", "ci_detector.rb")
+ expect(File.exist?(bundler_implementation_path)).to be_truthy
+ bundler_code = File.read(bundler_implementation_path)
+ denamespaced_bundler_code = bundler_code.sub("Bundler", "NAMESPACE")
+
+ expect(denamespaced_bundler_code).to eq(denamespaced_rubygems_code)
+ end
+end
diff --git a/spec/bundler/bundler/cli_common_spec.rb b/spec/bundler/bundler/cli_common_spec.rb
new file mode 100644
index 0000000000..015894b3a1
--- /dev/null
+++ b/spec/bundler/bundler/cli_common_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "bundler/cli"
+
+RSpec.describe Bundler::CLI::Common do
+ describe "gem_not_found_message" do
+ it "should suggest alternate gem names" do
+ message = subject.gem_not_found_message("ralis", ["BOGUS"])
+ expect(message).to match("Could not find gem 'ralis'.$")
+ message = subject.gem_not_found_message("ralis", ["rails"])
+ expect(message).to match("Did you mean 'rails'?")
+ message = subject.gem_not_found_message("Rails", ["rails"])
+ expect(message).to match("Did you mean 'rails'?")
+ message = subject.gem_not_found_message("meail", %w[email fail eval])
+ expect(message).to match("Did you mean 'email'?")
+ message = subject.gem_not_found_message("nokogri", %w[nokogiri rails sidekiq dog])
+ expect(message).to match("Did you mean 'nokogiri'?")
+ message = subject.gem_not_found_message("methosd", %w[method methods bogus])
+ expect(message).to match(/Did you mean 'method(|s)' or 'method(|s)'?/)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb
index 50e2a698eb..56caf9937e 100644
--- a/spec/bundler/bundler/cli_spec.rb
+++ b/spec/bundler/bundler/cli_spec.rb
@@ -4,12 +4,12 @@ require "bundler/cli"
RSpec.describe "bundle executable" do
it "returns non-zero exit status when passed unrecognized options" do
- bundle "--invalid_argument", :raise_on_error => false
+ bundle "--invalid_argument", raise_on_error: false
expect(exitstatus).to_not be_zero
end
it "returns non-zero exit status when passed unrecognized task" do
- bundle "unrecognized-task", :raise_on_error => false
+ bundle "unrecognized-task", raise_on_error: false
expect(exitstatus).to_not be_zero
end
@@ -32,123 +32,185 @@ 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
context "with no arguments" do
- it "prints a concise help message", :bundler => "3" do
- bundle ""
- expect(err).to be_empty
+ it "tries to installs by default but print help on missing Gemfile" do
+ bundle "", raise_on_error: false
+ expect(err).to include("Could not locate Gemfile")
+ expect(out).to include("In a future version of Bundler")
+
expect(out).to include("Bundler version #{Bundler::VERSION}").
and include("\n\nBundler commands:\n\n").
and include("\n\n Primary commands:\n").
and include("\n\n Utilities:\n").
and include("\n\nOptions:\n")
end
+
+ it "runs bundle install when default_cli_command set to install" do
+ bundle_config "default_cli_command install"
+ bundle "", raise_on_error: false
+ expect(out).to_not include("In a future version of Bundler")
+ expect(err).to include("Could not locate Gemfile")
+ expect(exitstatus).to_not be_zero
+ end
end
context "when ENV['BUNDLE_GEMFILE'] is set to an empty string" do
it "ignores it" do
gemfile bundled_app_gemfile, <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
- bundle :install, :env => { "BUNDLE_GEMFILE" => "" }
+ bundle :install, env: { "BUNDLE_GEMFILE" => "" }
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
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
+ context "with --verbose" do
+ before do
+ gemfile "source 'https://gem.repo1'"
+ end
- bundle :install, :env => { "RUBYGEMS_GEMDEPS" => "foo" }
- expect(err).to include("RUBYGEMS_GEMDEPS")
- expect(err).to include("conflict with Bundler")
+ it "prints the running command" do
+ bundle "info bundler", verbose: true
+ expect(out).to start_with("Running `bundle info bundler --verbose` with bundler #{Bundler::VERSION}")
+
+ bundle "install", verbose: true
+ expect(out).to start_with("Running `bundle install --verbose` with bundler #{Bundler::VERSION}")
+ end
- bundle :install, :env => { "RUBYGEMS_GEMDEPS" => "" }
- expect(err).not_to include("RUBYGEMS_GEMDEPS")
+ it "prints the simulated version too when setting is enabled" do
+ bundle "config set simulate_version 4", verbose: true
+ bundle "info bundler", verbose: true
+ expect(out).to start_with("Running `bundle info bundler --verbose` with bundler #{Bundler::VERSION} (simulating Bundler 4)")
end
end
- context "with --verbose" do
+ context "with verbose configuration" do
+ before do
+ bundle_config "verbose true"
+ end
+
it "prints the running command" do
- gemfile ""
- bundle "info bundler", :verbose => true
- expect(out).to start_with("Running `bundle info bundler --verbose` with bundler #{Bundler::VERSION}")
+ gemfile "source 'https://gem.repo1'"
+ bundle "info bundler"
+ expect(out).to start_with("Running `bundle info bundler` with bundler #{Bundler::VERSION}")
end
+ end
+
+ describe "bundle outdated" do
+ let(:run_command) do
+ bundle "install"
- it "doesn't print defaults" do
- install_gemfile "", :verbose => true
- expect(out).to start_with("Running `bundle install --retry 0 --verbose` with bundler #{Bundler::VERSION}")
+ bundle "outdated #{flags}", raise_on_error: false
end
- it "doesn't print defaults" do
- install_gemfile "", :verbose => true
- expect(out).to start_with("Running `bundle install --retry 0 --verbose` with bundler #{Bundler::VERSION}")
+ before do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", '0.9.1'
+ G
+ end
+
+ context "with --groups flag" do
+ let(:flags) { "--groups" }
+
+ it "prints a message when there are outdated gems" do
+ run_command
+
+ expect(out).to include("Gem Current Latest Requested Groups")
+ expect(out).to include("myrack 0.9.1 1.0.0 = 0.9.1 default")
+ end
+ end
+
+ context "with --parseable" do
+ let(:flags) { "--parseable" }
+
+ it "prints a message when there are outdated gems" do
+ run_command
+
+ expect(out).to include("myrack (newest 1.0.0, installed 0.9.1, requested = 0.9.1)")
+ end
+ end
+
+ context "with --groups and --parseable" do
+ let(:flags) { "--groups --parseable" }
+
+ it "prints a simplified message when there are outdated gems" do
+ run_command
+
+ expect(out).to include("myrack (newest 1.0.0, installed 0.9.1, requested = 0.9.1)")
+ end
end
end
describe "printing the outdated warning" do
shared_examples_for "no warning" do
it "prints no warning" do
- bundle "fail", :env => { "BUNDLER_VERSION" => bundler_version }, :raise_on_error => false
- expect(last_command.stdboth).to eq("Could not find command \"fail\".")
+ bundle "fail", env: { "BUNDLER_VERSION" => bundler_version }, raise_on_error: false
+ expect(stdboth).to eq("Could not find command \"fail\".")
end
end
let(:bundler_version) { "2.0" }
let(:latest_version) { nil }
before do
- bundle "config set --global disable_version_check false"
+ bundle_config_global "disable_version_check false"
pristine_system_gems "bundler-#{bundler_version}"
if latest_version
@@ -175,35 +237,37 @@ RSpec.describe "bundle executable" do
context "when the latest version is greater than the current version" do
let(:latest_version) { "222.0" }
it "prints the version warning" do
- bundle "fail", :env => { "BUNDLER_VERSION" => bundler_version }, :raise_on_error => false
+ bundle "fail", env: { "BUNDLER_VERSION" => bundler_version }, raise_on_error: false
expect(err).to start_with(<<-EOS.strip)
The latest bundler is #{latest_version}, but you are currently running #{bundler_version}.
-To install the latest version, run `gem install bundler`
+To update to the most recent version, run `bundle update --bundler`
EOS
end
context "and disable_version_check is set" do
- before { bundle "config set disable_version_check true", :env => { "BUNDLER_VERSION" => bundler_version } }
+ before { bundle "config set disable_version_check true", env: { "BUNDLER_VERSION" => bundler_version } }
include_examples "no warning"
end
context "running a parseable command" do
it "prints no warning" do
- bundle "config get --parseable foo", :env => { "BUNDLER_VERSION" => bundler_version }
- expect(last_command.stdboth).to eq ""
+ bundle "config set foo value", env: { "BUNDLER_VERSION" => bundler_version }
+ bundle "config get --parseable foo", env: { "BUNDLER_VERSION" => bundler_version }
+ expect(out).to eq "foo=value"
+ expect(err).to eq ""
- bundle "platform --ruby", :env => { "BUNDLER_VERSION" => bundler_version }, :raise_on_error => false
- expect(last_command.stdboth).to eq "Could not locate Gemfile"
+ bundle "platform --ruby", env: { "BUNDLER_VERSION" => bundler_version }, raise_on_error: false
+ expect(stdboth).to eq "Could not locate Gemfile"
end
end
context "and is a pre-release" do
let(:latest_version) { "222.0.0.pre.4" }
it "prints the version warning" do
- bundle "fail", :env => { "BUNDLER_VERSION" => bundler_version }, :raise_on_error => false
+ bundle "fail", env: { "BUNDLER_VERSION" => bundler_version }, raise_on_error: false
expect(err).to start_with(<<-EOS.strip)
The latest bundler is #{latest_version}, but you are currently running #{bundler_version}.
-To install the latest version, run `gem install bundler --pre`
+To update to the most recent version, run `bundle update --bundler`
EOS
end
end
@@ -212,13 +276,23 @@ To install the latest version, run `gem install bundler --pre`
end
RSpec.describe "bundler executable" do
- it "shows the bundler version just as the `bundle` executable does", :bundler => "< 3" do
+ it "shows the bundler version just as the `bundle` executable does" do
bundler "--version"
- expect(out).to eq("Bundler version #{Bundler::VERSION}")
- end
+ expect(out).to eq(Bundler::VERSION.to_s)
- it "shows the bundler version just as the `bundle` executable does", :bundler => "3" do
+ bundle_config "simulate_version 5"
bundler "--version"
- expect(out).to eq(Bundler::VERSION)
+ expect(out).to eq("#{Bundler::VERSION} (simulating Bundler 5)")
+ end
+
+ it "shows cli_help when bundler install and no Gemfile is found" do
+ bundler "install", raise_on_error: false
+ expect(err).to include("Could not locate Gemfile")
+
+ expect(out).to include("Bundler version #{Bundler::VERSION}").
+ and include("\n\nBundler commands:\n\n").
+ and include("\n\n Primary commands:\n").
+ and include("\n\n Utilities:\n").
+ and include("\n\nOptions:\n")
end
end
diff --git a/spec/bundler/bundler/compact_index_client/parser_spec.rb b/spec/bundler/bundler/compact_index_client/parser_spec.rb
new file mode 100644
index 0000000000..6aa867f058
--- /dev/null
+++ b/spec/bundler/bundler/compact_index_client/parser_spec.rb
@@ -0,0 +1,249 @@
+# frozen_string_literal: true
+
+require "bundler/compact_index_client"
+require "bundler/compact_index_client/parser"
+
+TestCompactIndexClient = Struct.new(:names, :versions, :info_data) do
+ # Requiring the checksum to match the input data helps ensure
+ # that we are parsing the correct checksum from the versions file
+ def info(name, checksum)
+ info_data.dig(name, checksum)
+ end
+
+ def set_info_data(name, value)
+ info_data[name] = value
+ end
+end
+
+RSpec.describe Bundler::CompactIndexClient::Parser do
+ subject(:parser) { described_class.new(compact_index) }
+
+ let(:compact_index) { TestCompactIndexClient.new(names, versions, info_data) }
+ let(:names) { "a\nb\nc\n" }
+ let(:versions) { <<~VERSIONS.dup }
+ created_at: 2024-05-01T00:00:04Z
+ ---
+ a 1.0.0,1.0.1,1.1.0 aaa111
+ b 2.0.0,2.0.0-java bbb222
+ c 3.0.0,3.0.3,3.3.3 ccc333
+ c -3.0.3 ccc333yanked
+ VERSIONS
+ let(:info_data) do
+ {
+ "a" => { "aaa111" => a_info },
+ "b" => { "bbb222" => b_info },
+ "c" => { "ccc333yanked" => c_info },
+ }
+ end
+ let(:a_info) { <<~INFO.dup }
+ ---
+ 1.0.0 |checksum:aaa1,ruby:>= 3.0.0,rubygems:>= 3.2.3
+ 1.0.1 |checksum:aaa2,ruby:>= 3.0.0,rubygems:>= 3.2.3
+ 1.1.0 |checksum:aaa3,ruby:>= 3.0.0,rubygems:>= 3.2.3
+ INFO
+ let(:b_info) { <<~INFO }
+ 2.0.0 a:~> 1.0&<= 3.0|checksum:bbb1
+ 2.0.0-java a:~> 1.0&<= 3.0|checksum:bbb2
+ INFO
+ let(:c_info) { <<~INFO }
+ 3.0.0 a:= 1.0.0,b:~> 2.0|checksum:ccc1,ruby:>= 2.7.0,rubygems:>= 3.0.0
+ 3.3.3 a:>= 1.1.0,b:~> 2.0|checksum:ccc3,ruby:>= 3.0.0,rubygems:>= 3.2.3,created_at:2026-05-12T10:00:00Z
+ INFO
+
+ describe "#available?" do
+ it "returns true versions are available" do
+ expect(parser).to be_available
+ end
+
+ it "returns true when versions has only one gem" do
+ compact_index.versions = +"a 1.0.0 aaa1\n"
+ expect(parser).to be_available
+ end
+
+ it "returns true when versions has a gem and a header" do
+ compact_index.versions = +"---\na 1.0.0 aaa1\n"
+ expect(parser).to be_available
+ end
+
+ it "returns true when versions has a gem and a header with header data" do
+ compact_index.versions = +"created_at: 2024-05-01T00:00:04Z\n---\na 1.0.0 aaa1\n"
+ expect(parser).to be_available
+ end
+
+ it "returns false when versions has only the header" do
+ compact_index.versions = +"---\n"
+ expect(parser).not_to be_available
+ end
+
+ it "returns false when versions has only the header with header data" do
+ compact_index.versions = +"created_at: 2024-05-01T00:00:04Z\n---\n"
+ expect(parser).not_to be_available
+ end
+
+ it "returns false when versions index is not available" do
+ compact_index.versions = nil
+ expect(parser).not_to be_available
+ end
+
+ it "returns false when versions is empty" do
+ compact_index.versions = +""
+ expect(parser).not_to be_available
+ end
+ end
+
+ describe "#names" do
+ it "returns the names" do
+ expect(parser.names).to eq(%w[a b c])
+ end
+
+ it "returns an empty array when names is empty" do
+ compact_index.names = ""
+ expect(parser.names).to eq([])
+ end
+
+ it "returns an empty array when names is not readable" do
+ compact_index.names = nil
+ expect(parser.names).to eq([])
+ end
+ end
+
+ describe "#versions" do
+ it "returns the versions" do
+ expect(parser.versions).to eq(
+ "a" => [
+ ["a", "1.0.0"],
+ ["a", "1.0.1"],
+ ["a", "1.1.0"],
+ ],
+ "b" => [
+ ["b", "2.0.0"],
+ ["b", "2.0.0", "java"],
+ ],
+ "c" => [
+ ["c", "3.0.0"],
+ ["c", "3.3.3"],
+ ],
+ )
+ end
+
+ it "returns an empty hash when versions is empty" do
+ compact_index.versions = ""
+ expect(parser.versions).to eq({})
+ end
+
+ it "returns an empty hash when versions is not readable" do
+ compact_index.versions = nil
+ expect(parser.versions).to eq({})
+ end
+ end
+
+ describe "#info" do
+ let(:a_result) do
+ [
+ [
+ "a",
+ "1.0.0",
+ nil,
+ [],
+ [["checksum", ["aaa1"]], ["ruby", [">= 3.0.0"]], ["rubygems", [">= 3.2.3"]]],
+ ],
+ [
+ "a",
+ "1.0.1",
+ nil,
+ [],
+ [["checksum", ["aaa2"]], ["ruby", [">= 3.0.0"]], ["rubygems", [">= 3.2.3"]]],
+ ],
+ [
+ "a",
+ "1.1.0",
+ nil,
+ [],
+ [["checksum", ["aaa3"]], ["ruby", [">= 3.0.0"]], ["rubygems", [">= 3.2.3"]]],
+ ],
+ ]
+ end
+ let(:b_result) do
+ [
+ [
+ "b",
+ "2.0.0",
+ nil,
+ [["a", ["~> 1.0", "<= 3.0"]]],
+ [["checksum", ["bbb1"]]],
+ ],
+ [
+ "b",
+ "2.0.0",
+ "java",
+ [["a", ["~> 1.0", "<= 3.0"]]],
+ [["checksum", ["bbb2"]]],
+ ],
+ ]
+ end
+ let(:c_result) do
+ [
+ [
+ "c",
+ "3.0.0",
+ nil,
+ [["a", ["= 1.0.0"]], ["b", ["~> 2.0"]]],
+ [["checksum", ["ccc1"]], ["ruby", [">= 2.7.0"]], ["rubygems", [">= 3.0.0"]]],
+ ],
+ [
+ "c",
+ "3.3.3",
+ nil,
+ [["a", [">= 1.1.0"]], ["b", ["~> 2.0"]]],
+ [["checksum", ["ccc3"]], ["ruby", [">= 3.0.0"]], ["rubygems", [">= 3.2.3"]], ["created_at", ["2026-05-12T10:00:00Z"]]],
+ ],
+ ]
+ end
+
+ it "returns the info for example gem 'a' which has no deps" do
+ expect(parser.info("a")).to eq(a_result)
+ end
+
+ it "returns the info for example gem 'b' which has platform and compound deps" do
+ expect(parser.info("b")).to eq(b_result)
+ end
+
+ it "returns the info for example gem 'c' which has deps and yanked version (requires use of correct info checksum)" do
+ expect(parser.info("c")).to eq(c_result)
+ end
+
+ it "returns an empty array when the info is empty" do
+ compact_index.set_info_data("a", {})
+ expect(parser.info("a")).to eq([])
+ end
+
+ it "returns an empty array when the info is not readable" do
+ expect(parser.info("d")).to eq([])
+ end
+
+ it "handles empty lines in the versions file (Artifactory bug that they have yet to fix)" do
+ compact_index.versions = +<<~VERSIONS
+ created_at: 2024-05-01T00:00:04Z
+ ---
+ a 1.0.0,1.0.1,1.1.0 aaa111
+ b 2.0.0,2.0.0-java bbb222
+
+ c 3.0.0,3.0.3,3.3.3 ccc333
+ c -3.0.3 ccc333yanked
+ VERSIONS
+ expect(parser.info("a")).to eq(a_result)
+ end
+
+ it "handles lines without a checksum" do
+ compact_index.versions = <<~VERSIONS
+ created_at: 2024-05-01T00:00:04Z
+ ---
+ a 1.0.0,1.0.1,1.1.0 aaa111
+ b 2.0.0,2.0.0-java
+ c 3.0.0,3.0.3,3.3.3 ccc333
+ VERSIONS
+
+ expect(parser.info("a")).to eq(a_result)
+ end
+ 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 26159dccd8..fd63a652a4 100644
--- a/spec/bundler/bundler/compact_index_client/updater_spec.rb
+++ b/spec/bundler/bundler/compact_index_client/updater_spec.rb
@@ -1,53 +1,243 @@
# frozen_string_literal: true
-require "net/http"
+require "bundler/vendored_net_http"
require "bundler/compact_index_client"
require "bundler/compact_index_client/updater"
+require "tmpdir"
RSpec.describe Bundler::CompactIndexClient::Updater do
+ subject(:updater) { described_class.new(fetcher) }
+
let(:fetcher) { double(:fetcher) }
- let(:local_path) { Pathname("/tmp/localpath") }
+ let(:local_path) { Pathname.new(Dir.mktmpdir("localpath")).join("versions") }
+ let(:etag_path) { Pathname.new(Dir.mktmpdir("localpath-etags")).join("versions.etag") }
let(:remote_path) { double(:remote_path) }
- let!(:updater) { described_class.new(fetcher) }
+ let(:full_body) { "abc123" }
+ let(:response) { double(:response, body: full_body, is_a?: false) }
+ let(:digest) { Digest::SHA256.base64digest(full_body) }
- context "when the ETag header is missing" do
- # Regression test for https://github.com/rubygems/bundler/issues/5463
+ context "when the local path does not exist" do
+ before do
+ allow(response).to receive(:[]).with("Repr-Digest") { nil }
+ allow(response).to receive(:[]).with("Digest") { nil }
+ allow(response).to receive(:[]).with("ETag") { '"thisisanetag"' }
+ end
- let(:response) { double(:response, :body => "") }
+ it "downloads the file without attempting append" do
+ expect(fetcher).to receive(:call).once.with(remote_path, {}) { response }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("thisisanetag")
+ end
- it "MisMatchedChecksumError is raised" do
- # Twice: #update retries on failure
- expect(response).to receive(:[]).with("Content-Encoding").twice { "" }
- expect(response).to receive(:[]).with("ETag").twice { nil }
- expect(fetcher).to receive(:call).twice { response }
+ it "fails immediately on bad checksum" do
+ expect(fetcher).to receive(:call).once.with(remote_path, {}) { response }
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:baddigest:" }
expect do
- updater.update(local_path, remote_path)
- end.to raise_error(Bundler::CompactIndexClient::Updater::MisMatchedChecksumError)
+ updater.update(remote_path, local_path, etag_path)
+ end.to raise_error(Bundler::CompactIndexClient::Updater::MismatchedChecksumError)
+ end
+ end
+
+ context "when the local path exists" do
+ let(:local_body) { "abc" }
+
+ before do
+ local_path.open("w") {|f| f.write(local_body) }
+ end
+
+ context "with an etag" do
+ before do
+ etag_path.open("w") {|f| f.write("LocalEtag") }
+ end
+
+ let(:headers) do
+ {
+ "If-None-Match" => '"LocalEtag"',
+ "Range" => "bytes=2-",
+ }
+ end
+
+ it "does nothing if etags match" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { false }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { true }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq("abc")
+ expect(etag_path.read).to eq("LocalEtag")
+ end
+
+ it "appends the file if etags do not match" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(response).to receive(:[]).with("ETag") { '"NewEtag"' }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { false }
+ allow(response).to receive(:body) { "c123" }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("NewEtag")
+ end
+
+ it "replaces the file if response ignores range" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(response).to receive(:[]).with("ETag") { '"NewEtag"' }
+ allow(response).to receive(:body) { full_body }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("NewEtag")
+ end
+
+ it "tries the request again if the partial response fails digest check" do
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:baddigest:" }
+ allow(response).to receive(:body) { "the beginning of the file changed" }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true }
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+
+ full_response = double(:full_response, body: full_body, is_a?: false)
+ allow(full_response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(full_response).to receive(:[]).with("ETag") { '"NewEtag"' }
+ expect(fetcher).to receive(:call).once.with(remote_path, { "If-None-Match" => '"LocalEtag"' }).and_return(full_response)
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("NewEtag")
+ end
+
+ it "tries the request again if the partial response is blank" do
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:baddigest:" }
+ allow(response).to receive(:body) { "" }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true }
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+
+ full_response = double(:full_response, body: full_body, is_a?: false)
+ allow(full_response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(full_response).to receive(:[]).with("ETag") { '"NewEtag"' }
+ expect(fetcher).to receive(:call).once.with(remote_path, { "If-None-Match" => '"LocalEtag"' }).and_return(full_response)
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("NewEtag")
+ end
+ end
+
+ context "without an etag file" do
+ let(:headers) do
+ { "Range" => "bytes=2-" }
+ end
+
+ it "appends the file" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(response).to receive(:[]).with("ETag") { '"OpaqueEtag"' }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { false }
+ allow(response).to receive(:body) { "c123" }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("OpaqueEtag")
+ end
+
+ it "replaces the file on full file response that ignores range request" do
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+ allow(response).to receive(:[]).with("Repr-Digest") { nil }
+ allow(response).to receive(:[]).with("Digest") { nil }
+ allow(response).to receive(:[]).with("ETag") { '"OpaqueEtag"' }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { false }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { false }
+ allow(response).to receive(:body) { full_body }
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("OpaqueEtag")
+ end
+
+ it "tries the request again if the partial response fails digest check" do
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:baddigest:" }
+ allow(response).to receive(:body) { "the beginning of the file changed" }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true }
+ expect(fetcher).to receive(:call).once.with(remote_path, headers) do
+ # During the failed first request, we simulate another process writing the etag.
+ # This ensures the second request doesn't generate the md5 etag again but just uses whatever is written.
+ etag_path.open("w") {|f| f.write("LocalEtag") }
+ response
+ end
+
+ full_response = double(:full_response, body: full_body, is_a?: false)
+ allow(full_response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(full_response).to receive(:[]).with("ETag") { '"NewEtag"' }
+ expect(fetcher).to receive(:call).once.with(remote_path, { "If-None-Match" => '"LocalEtag"' }).and_return(full_response)
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("NewEtag")
+ end
+ end
+ end
+
+ context "when the ETag header is missing" do
+ # Regression test for https://github.com/rubygems/bundler/issues/5463
+ let(:response) { double(:response, body: full_body) }
+
+ it "treats the response as an update" do
+ allow(response).to receive(:[]).with("Repr-Digest") { nil }
+ allow(response).to receive(:[]).with("Digest") { nil }
+ allow(response).to receive(:[]).with("ETag") { nil }
+ expect(fetcher).to receive(:call) { response }
+
+ updater.update(remote_path, local_path, etag_path)
end
end
context "when the download is corrupt" do
- let(:response) { double(:response, :body => "") }
+ let(:response) { double(:response, body: "") }
it "raises HTTPError" do
- expect(response).to receive(:[]).with("Content-Encoding") { "gzip" }
- expect(fetcher).to receive(:call) { response }
+ expect(fetcher).to receive(:call).and_raise(Zlib::GzipFile::Error)
expect do
- updater.update(local_path, remote_path)
+ updater.update(remote_path, local_path, etag_path)
end.to raise_error(Bundler::HTTPError)
end
end
- context "when bundler doesn't have permissions on Dir.tmpdir" do
- it "Errno::EACCES is raised" do
- allow(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) }
- expect do
- updater.update(local_path, remote_path)
- end.to raise_error(Bundler::PermissionError)
+ it "works just fine" do
+ old_verbose = $VERBOSE
+ previous_internal_encoding = Encoding.default_internal
+
+ begin
+ $VERBOSE = false
+ Encoding.default_internal = "ASCII"
+ allow(response).to receive(:[]).with("Repr-Digest") { nil }
+ allow(response).to receive(:[]).with("Digest") { nil }
+ allow(response).to receive(:[]).with("ETag") { nil }
+ expect(fetcher).to receive(:call) { response }
+
+ updater.update(remote_path, local_path, etag_path)
+ ensure
+ Encoding.default_internal = previous_internal_encoding
+ $VERBOSE = old_verbose
+ end
end
end
end
diff --git a/spec/bundler/bundler/current_ruby_spec.rb b/spec/bundler/bundler/current_ruby_spec.rb
new file mode 100644
index 0000000000..79eb802aa5
--- /dev/null
+++ b/spec/bundler/bundler/current_ruby_spec.rb
@@ -0,0 +1,157 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::CurrentRuby do
+ describe "PLATFORM_MAP" do
+ subject { described_class::PLATFORM_MAP }
+
+ # rubocop:disable Naming/VariableNumber
+ let(:platforms) do
+ { ruby: Gem::Platform::RUBY,
+ ruby_18: Gem::Platform::RUBY,
+ ruby_19: Gem::Platform::RUBY,
+ ruby_20: Gem::Platform::RUBY,
+ ruby_21: Gem::Platform::RUBY,
+ ruby_22: Gem::Platform::RUBY,
+ ruby_23: Gem::Platform::RUBY,
+ ruby_24: Gem::Platform::RUBY,
+ ruby_25: Gem::Platform::RUBY,
+ ruby_26: Gem::Platform::RUBY,
+ ruby_27: Gem::Platform::RUBY,
+ ruby_30: Gem::Platform::RUBY,
+ ruby_31: Gem::Platform::RUBY,
+ ruby_32: Gem::Platform::RUBY,
+ ruby_33: Gem::Platform::RUBY,
+ ruby_34: Gem::Platform::RUBY,
+ ruby_40: Gem::Platform::RUBY,
+ ruby_41: Gem::Platform::RUBY,
+ mri: Gem::Platform::RUBY,
+ mri_18: Gem::Platform::RUBY,
+ mri_19: Gem::Platform::RUBY,
+ mri_20: Gem::Platform::RUBY,
+ mri_21: Gem::Platform::RUBY,
+ mri_22: Gem::Platform::RUBY,
+ mri_23: Gem::Platform::RUBY,
+ mri_24: Gem::Platform::RUBY,
+ mri_25: Gem::Platform::RUBY,
+ mri_26: Gem::Platform::RUBY,
+ mri_27: Gem::Platform::RUBY,
+ mri_30: Gem::Platform::RUBY,
+ mri_31: Gem::Platform::RUBY,
+ mri_32: Gem::Platform::RUBY,
+ mri_33: Gem::Platform::RUBY,
+ mri_34: Gem::Platform::RUBY,
+ mri_40: Gem::Platform::RUBY,
+ mri_41: Gem::Platform::RUBY,
+ rbx: Gem::Platform::RUBY,
+ truffleruby: Gem::Platform::RUBY,
+ jruby: Gem::Platform::JAVA,
+ jruby_18: Gem::Platform::JAVA,
+ jruby_19: Gem::Platform::JAVA,
+ windows: Gem::Platform::WINDOWS,
+ windows_18: Gem::Platform::WINDOWS,
+ windows_19: Gem::Platform::WINDOWS,
+ windows_20: Gem::Platform::WINDOWS,
+ windows_21: Gem::Platform::WINDOWS,
+ windows_22: Gem::Platform::WINDOWS,
+ windows_23: Gem::Platform::WINDOWS,
+ windows_24: Gem::Platform::WINDOWS,
+ windows_25: Gem::Platform::WINDOWS,
+ windows_26: Gem::Platform::WINDOWS,
+ windows_27: Gem::Platform::WINDOWS,
+ windows_30: Gem::Platform::WINDOWS,
+ windows_31: Gem::Platform::WINDOWS,
+ windows_32: Gem::Platform::WINDOWS,
+ windows_33: Gem::Platform::WINDOWS,
+ windows_34: Gem::Platform::WINDOWS,
+ windows_40: Gem::Platform::WINDOWS,
+ windows_41: Gem::Platform::WINDOWS }
+ end
+
+ let(:deprecated) do
+ { mswin: Gem::Platform::MSWIN,
+ mswin_18: Gem::Platform::MSWIN,
+ mswin_19: Gem::Platform::MSWIN,
+ mswin_20: Gem::Platform::MSWIN,
+ mswin_21: Gem::Platform::MSWIN,
+ mswin_22: Gem::Platform::MSWIN,
+ mswin_23: Gem::Platform::MSWIN,
+ mswin_24: Gem::Platform::MSWIN,
+ mswin_25: Gem::Platform::MSWIN,
+ mswin_26: Gem::Platform::MSWIN,
+ mswin_27: Gem::Platform::MSWIN,
+ mswin_30: Gem::Platform::MSWIN,
+ mswin_31: Gem::Platform::MSWIN,
+ mswin_32: Gem::Platform::MSWIN,
+ mswin_33: Gem::Platform::MSWIN,
+ mswin_34: Gem::Platform::MSWIN,
+ mswin_40: Gem::Platform::MSWIN,
+ mswin_41: Gem::Platform::MSWIN,
+ mswin64: Gem::Platform::MSWIN64,
+ mswin64_19: Gem::Platform::MSWIN64,
+ mswin64_20: Gem::Platform::MSWIN64,
+ mswin64_21: Gem::Platform::MSWIN64,
+ mswin64_22: Gem::Platform::MSWIN64,
+ mswin64_23: Gem::Platform::MSWIN64,
+ mswin64_24: Gem::Platform::MSWIN64,
+ mswin64_25: Gem::Platform::MSWIN64,
+ mswin64_26: Gem::Platform::MSWIN64,
+ mswin64_27: Gem::Platform::MSWIN64,
+ mswin64_30: Gem::Platform::MSWIN64,
+ mswin64_31: Gem::Platform::MSWIN64,
+ mswin64_32: Gem::Platform::MSWIN64,
+ mswin64_33: Gem::Platform::MSWIN64,
+ mswin64_34: Gem::Platform::MSWIN64,
+ mswin64_40: Gem::Platform::MSWIN64,
+ mswin64_41: Gem::Platform::MSWIN64,
+ mingw: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_18: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_19: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_20: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_21: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_22: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_23: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_24: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_25: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_26: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_27: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_30: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_31: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_32: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_33: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_34: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_40: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_41: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_20: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_21: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_22: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_23: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_24: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_25: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_26: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_27: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_30: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_31: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_32: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_33: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_34: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_40: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_41: Gem::Platform::UNIVERSAL_MINGW }
+ end
+ # rubocop:enable Naming/VariableNumber
+
+ it "includes all platforms" do
+ expect(subject).to eq(platforms.merge(deprecated))
+ end
+ end
+
+ describe "Deprecated platform" do
+ it "outputs an error and aborts when calling maglev?" do
+ expect { Bundler.current_ruby.maglev? }.to raise_error(Bundler::RemovedError, /`CurrentRuby#maglev\?` was removed with no replacement./)
+ end
+
+ it "outputs an error and aborts when calling maglev_31?" do
+ expect { Bundler.current_ruby.maglev_31? }.to raise_error(Bundler::RemovedError, /`CurrentRuby#maglev_31\?` was removed with no replacement./)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/definition_spec.rb b/spec/bundler/bundler/definition_spec.rb
index d0ebb37933..8c4a5a0331 100644
--- a/spec/bundler/bundler/definition_spec.rb
+++ b/spec/bundler/bundler/definition_spec.rb
@@ -3,190 +3,216 @@
require "bundler/definition"
RSpec.describe Bundler::Definition do
+ describe "#overrides" do
+ before do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile) { bundled_app_gemfile }
+ end
+
+ subject { Bundler::Definition.new(bundled_app_lock, [], Bundler::SourceList.new, {}) }
+
+ it "defaults to an empty array" do
+ expect(subject.overrides).to eq([])
+ end
+
+ it "is writable" do
+ override = Bundler::Override.new("rails", :version, ">= 8.0")
+ subject.overrides = [override]
+ expect(subject.overrides).to eq([override])
+ end
+ end
+
describe "#lock" do
before do
- allow(Bundler).to receive(:settings) { Bundler::Settings.new(".") }
- allow(Bundler::SharedHelpers).to receive(:find_gemfile) { Pathname.new("Gemfile") }
- allow(Bundler).to receive(:ui) { double("UI", :info => "", :debug => "") }
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile) { bundled_app_gemfile }
+ allow(Bundler).to receive(:ui) { double("UI", info: "", debug: "") }
end
- context "when it's not possible to write to the file" do
- subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
+ subject { Bundler::Definition.new(bundled_app_lock, [], Bundler::SourceList.new, {}) }
+
+ context "when it's not possible to write to the file" do
it "raises an PermissionError with explanation" do
allow(File).to receive(:open).and_call_original
- expect(File).to receive(:open).with("Gemfile.lock", "wb").
- and_raise(Errno::EACCES)
- expect { subject.lock("Gemfile.lock") }.
+ expect(File).to receive(:open).with(bundled_app_lock, "wb").
+ and_raise(Errno::EACCES.new(bundled_app_lock.to_s))
+ expect { subject.lock }.
to raise_error(Bundler::PermissionError, /Gemfile\.lock/)
end
end
context "when a temporary resource access issue occurs" do
- subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
-
it "raises a TemporaryResourceError with explanation" do
allow(File).to receive(:open).and_call_original
- expect(File).to receive(:open).with("Gemfile.lock", "wb").
+ expect(File).to receive(:open).with(bundled_app_lock, "wb").
and_raise(Errno::EAGAIN)
- expect { subject.lock("Gemfile.lock") }.
+ expect { subject.lock }.
to raise_error(Bundler::TemporaryResourceError, /temporarily unavailable/)
end
end
+ context "when Bundler::Definition.no_lock is set to true" do
+ before { Bundler::Definition.no_lock = true }
+ after { Bundler::Definition.no_lock = false }
+
+ it "does not create a lockfile" do
+ subject.lock
+ expect(bundled_app_lock).not_to be_file
+ end
+ end
end
describe "detects changes" do
- it "for a path gem with changes", :bundler => "< 3" do
- build_lib "foo", "1.0", :path => lib_path("foo")
+ it "for a path gem with changes" do
+ build_lib "foo", "1.0", path: lib_path("foo")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo")}"
G
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
- s.add_dependency "rack", "1.0"
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack", "1.0"
end
- bundle :install, :env => { "DEBUG" => "1" }
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo1, "myrack", "1.0.0"
+ end
+
+ 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:
foo (1.0)
- rack (= 1.0)
+ myrack (= 1.0)
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "for a path gem with changes", :bundler => "3" do
- build_lib "foo", "1.0", :path => lib_path("foo")
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "foo", :path => "#{lib_path("foo")}"
- G
-
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
- s.add_dependency "rack", "1.0"
+ it "with an explicit update" do
+ build_repo4 do
+ build_gem("ffi", "1.9.23") {|s| s.platform = "java" }
+ build_gem("ffi", "1.9.23")
end
- bundle :install, :env => { "DEBUG" => "1" }
-
- expect(out).to match(/re-resolving dependencies/)
- lockfile_should_be <<-G
- PATH
- remote: #{lib_path("foo")}
- specs:
- foo (1.0)
- rack (= 1.0)
-
- GEM
- remote: #{file_uri_for(gem_repo1)}/
- specs:
- rack (1.0.0)
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "ffi"
+ G
- PLATFORMS
- #{lockfile_platforms}
+ bundle "lock --add-platform java"
- DEPENDENCIES
- foo!
+ bundle "update ffi", env: { "DEBUG" => "1" }
- BUNDLED WITH
- #{Bundler::VERSION}
- G
+ expect(out).to match(/because bundler is unlocking gems: \(ffi\)/)
end
it "for a path gem with deps and no changes" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
- s.add_dependency "rack", "1.0"
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack", "1.0"
s.add_development_dependency "net-ssh", "1.0"
end
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo1, "myrack", "1.0.0"
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo")}"
G
- bundle :check, :env => { "DEBUG" => "1" }
-
- expect(out).to match(/using resolution from the lockfile/)
- lockfile_should_be <<-G
+ expected_lockfile = <<~G
PATH
remote: #{lib_path("foo")}
specs:
foo (1.0)
- rack (= 1.0)
+ myrack (= 1.0)
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
+
+ expect(lockfile).to eq(expected_lockfile)
+
+ bundle :check, env: { "DEBUG" => "1" }
+
+ expect(out).to match(/using resolution from the lockfile/)
+ expect(lockfile).to eq(expected_lockfile)
end
it "for a locked gem for another platform" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "only_java", platform: :jruby
G
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo1, "only_java", "1.1", "java"
+ end
+
bundle "lock --add-platform java"
- bundle :check, :env => { "DEBUG" => "1" }
+ 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)}/
+ remote: https://gem.repo1/
specs:
only_java (1.1-java)
PLATFORMS
- java
- #{lockfile_platforms}
+ #{lockfile_platforms("java")}
DEPENDENCIES
only_java
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "for a rubygems gem" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo1, "foo", "1.0"
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo"
G
- bundle :check, :env => { "DEBUG" => "1" }
+ 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)}/
+ remote: https://gem.repo1/
specs:
foo (1.0)
@@ -195,50 +221,25 @@ RSpec.describe Bundler::Definition do
DEPENDENCIES
foo
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
end
describe "initialize" do
context "gem version promoter" do
- context "with lockfile" do
- before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "foo"
- G
-
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- end
-
- it "should get a locked specs list when updating all" do
- definition = Bundler::Definition.new(bundled_app_lock, [], Bundler::SourceList.new, true)
- locked_specs = definition.gem_version_promoter.locked_specs
- expect(locked_specs.to_a.map(&:name)).to eq ["foo"]
- expect(definition.instance_variable_get("@locked_specs").empty?).to eq true
- end
- end
-
- context "without gemfile or lockfile" do
- it "should not attempt to parse empty lockfile contents" do
- definition = Bundler::Definition.new(nil, [], mock_source_list, true)
- expect(definition.gem_version_promoter.locked_specs.to_a).to eq []
- end
- end
-
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("https://gem.repo4")
end
end
before do
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem 'isolated_owner'
gem 'shared_owner_a'
@@ -247,7 +248,7 @@ RSpec.describe Bundler::Definition do
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo4)}
+ remote: https://gem.repo4
specs:
isolated_dep (2.0.1)
isolated_owner (1.0.1)
@@ -296,7 +297,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]
@@ -306,31 +307,55 @@ 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"
+ describe "#precompute_source_requirements_for_indirect_dependencies?" do
+ before do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile) { Pathname.new("Gemfile") }
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) }
+ let(:sources) { Bundler::SourceList.new }
+ subject { Bundler::Definition.new(nil, [], sources, []) }
- 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
+ before do
+ allow(sources).to receive(:non_global_rubygems_sources).and_return(non_global_rubygems_sources)
end
- end
- def build_stub_spec(name, version)
- Bundler::StubSpecification.new(name, version, nil, nil)
+ context "when all the scoped sources implement a dependency API" do
+ let(:non_global_rubygems_sources) do
+ [
+ double("non-global-source-0", "dependency_api_available?":true, to_s:"a"),
+ double("non-global-source-1", "dependency_api_available?":true, to_s:"b"),
+ ]
+ end
+
+ it "returns true without warning" do
+ expect(subject).not_to receive(:non_dependency_api_warning)
+
+ expect(subject.send(:precompute_source_requirements_for_indirect_dependencies?)).to be_truthy
+ end
+ end
+
+ context "when some scoped sources do not implement a dependency API" do
+ let(:non_global_rubygems_sources) do
+ [
+ double("non-global-source-0", "dependency_api_available?":true, to_s:"a"),
+ double("non-global-source-1", "dependency_api_available?":false, to_s:"b"),
+ double("non-global-source-2", "dependency_api_available?":false, to_s:"c"),
+ ]
+ end
+
+ it "returns false and warns about the non-API sources" do
+ expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
+Your Gemfile contains scoped sources that don't implement a dependency API, namely:
+
+ * b
+ * c
+
+Using the above gem servers may result in installing unexpected gems. To resolve this warning, make sure you use gem servers that implement dependency APIs, such as gemstash or geminabox gem servers.
+ W
+
+ expect(subject.send(:precompute_source_requirements_for_indirect_dependencies?)).to be_falsy
+ end
+ end
end
def mock_source_list
@@ -343,10 +368,6 @@ RSpec.describe Bundler::Definition do
[]
end
- def rubygems_remotes
- []
- end
-
def replace_sources!(arg)
nil
end
diff --git a/spec/bundler/bundler/dep_proxy_spec.rb b/spec/bundler/bundler/dep_proxy_spec.rb
deleted file mode 100644
index 0f8d6b1076..0000000000
--- a/spec/bundler/bundler/dep_proxy_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe Bundler::DepProxy do
- let(:dep) { Bundler::Dependency.new("rake", ">= 0") }
- subject { described_class.new(dep, Gem::Platform::RUBY) }
- let(:same) { subject }
- let(:other) { subject.dup }
- let(:different) { described_class.new(dep, Gem::Platform::JAVA) }
-
- describe "#eql?" do
- it { expect(subject.eql?(same)).to be true }
- it { expect(subject.eql?(other)).to be true }
- it { expect(subject.eql?(different)).to be false }
- it { expect(subject.eql?(nil)).to be false }
- it { expect(subject.eql?("foobar")).to be false }
- end
-
- describe "#hash" do
- it { expect(subject.hash).to eq(same.hash) }
- it { expect(subject.hash).to eq(other.hash) }
- end
-end
diff --git a/spec/bundler/bundler/dependency_spec.rb b/spec/bundler/bundler/dependency_spec.rb
new file mode 100644
index 0000000000..f930459571
--- /dev/null
+++ b/spec/bundler/bundler/dependency_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Dependency do
+ let(:options) do
+ {}
+ end
+ let(:dependency) do
+ described_class.new(
+ "test_gem",
+ "1.0.0",
+ options
+ )
+ end
+
+ describe "to_lock" do
+ it "returns formatted string" do
+ expect(dependency.to_lock).to eq(" test_gem (= 1.0.0)")
+ end
+
+ it "matches format of Gem::Dependency#to_lock" do
+ gem_dependency = Gem::Dependency.new("test_gem", "1.0.0")
+ expect(dependency.to_lock).to eq(gem_dependency.to_lock)
+ end
+
+ context "when source is passed" do
+ let(:options) do
+ {
+ "source" => Bundler::Source::Git.new({}),
+ }
+ end
+
+ it "returns formatted string with exclamation mark" do
+ expect(dependency.to_lock).to eq(" test_gem (= 1.0.0)!")
+ end
+ end
+ end
+
+ it "is on the current platform" do
+ engine = Gem.win_platform? ? "windows" : RUBY_ENGINE
+
+ dep = described_class.new(
+ "test_gem",
+ "1.0.0",
+ { "platforms" => "#{engine}_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}" },
+ )
+
+ expect(dep.current_platform?).to be_truthy
+ end
+end
diff --git a/spec/bundler/bundler/digest_spec.rb b/spec/bundler/bundler/digest_spec.rb
new file mode 100644
index 0000000000..f876827964
--- /dev/null
+++ b/spec/bundler/bundler/digest_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require "openssl"
+require "bundler/digest"
+
+RSpec.describe Bundler::Digest do
+ context "SHA1" do
+ subject { Bundler::Digest }
+ let(:stdlib) { OpenSSL::Digest::SHA1 }
+
+ it "is compatible with stdlib" do
+ random_strings = ["foo", "skfjsdlkfjsdf", "3924m", "ldskfj"]
+
+ # https://www.rfc-editor.org/rfc/rfc3174#section-7.3
+ rfc3174_test_cases = ["abc", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "a", "01234567" * 8]
+
+ (random_strings + rfc3174_test_cases).each do |payload|
+ sha1 = subject.sha1(payload)
+ sha1_stdlib = stdlib.hexdigest(payload)
+ expect(sha1).to be == sha1_stdlib, "#{payload}'s sha1 digest (#{sha1}) did not match stlib's result (#{sha1_stdlib})"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb
index ff87b57886..b6e67a312c 100644
--- a/spec/bundler/bundler/dsl_spec.rb
+++ b/spec/bundler/bundler/dsl_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Bundler::Dsl do
it "registers custom hosts" do
subject.git_source(:example) {|repo_name| "git@git.example.com:#{repo_name}.git" }
subject.git_source(:foobar) {|repo_name| "git@foobar.com:#{repo_name}.git" }
- subject.gem("dobry-pies", :example => "strzalek/dobry-pies")
+ subject.gem("dobry-pies", example: "strzalek/dobry-pies")
example_uri = "git@git.example.com:strzalek/dobry-pies.git"
expect(subject.dependencies.first.source.uri).to eq(example_uri)
end
@@ -25,54 +25,144 @@ 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.ref).to eq("refs/pull/5/head")
+ end
+
+ it "converts :gitlab PR to URI using https" do
+ subject.gem("sparks", gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5")
+ gitlab_uri = "https://gitlab.com/indirect/sparks.git"
+ expect(subject.dependencies.first.source.uri).to eq(gitlab_uri)
+ expect(subject.dependencies.first.source.ref).to eq("refs/merge-requests/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 :gitlab PR URI with a branch, ref or tag" do
+ expect do
+ subject.gem("sparks", gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5", branch: "foo")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :branch option can't be used with `gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5"`),
+ )
+
+ expect do
+ subject.gem("sparks", gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5", ref: "foo")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :ref option can't be used with `gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5"`),
+ )
+
+ expect do
+ subject.gem("sparks", gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/5", tag: "foo")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :tag option can't be used with `gitlab: "https://gitlab.com/indirect/sparks/-/merge_requests/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
+
+ it "rejects :gitlab with :git" do
+ expect do
+ subject.gem("sparks", gitlab: "indirect/sparks", git: "https://gitlab.com/indirect/sparks.git")
+ end.to raise_error(
+ Bundler::GemfileError,
+ %(The :git option can't be used with `gitlab: "indirect/sparks"`),
+ )
+ end
+
+ context "default hosts" do
it "converts :github to URI using https" do
- subject.gem("sparks", :github => "indirect/sparks")
+ subject.gem("sparks", github: "indirect/sparks")
github_uri = "https://github.com/indirect/sparks.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
it "converts :github shortcut to URI using https" do
- subject.gem("sparks", :github => "rails")
+ subject.gem("sparks", github: "rails")
github_uri = "https://github.com/rails/rails.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
+ it "converts :gitlab to URI using https" do
+ subject.gem("sparks", gitlab: "indirect/sparks")
+ gitlab_uri = "https://gitlab.com/indirect/sparks.git"
+ expect(subject.dependencies.first.source.uri).to eq(gitlab_uri)
+ end
+
+ it "converts :gitlab shortcut to URI using https" do
+ subject.gem("sparks", gitlab: "rails")
+ gitlab_uri = "https://gitlab.com/rails/rails.git"
+ expect(subject.dependencies.first.source.uri).to eq(gitlab_uri)
+ end
+
it "converts numeric :gist to :git" do
- subject.gem("not-really-a-gem", :gist => 2_859_988)
+ subject.gem("not-really-a-gem", gist: 2_859_988)
github_uri = "https://gist.github.com/2859988.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
it "converts :gist to :git" do
- subject.gem("not-really-a-gem", :gist => "2859988")
+ subject.gem("not-really-a-gem", gist: "2859988")
github_uri = "https://gist.github.com/2859988.git"
expect(subject.dependencies.first.source.uri).to eq(github_uri)
end
it "converts :bitbucket to :git" do
- subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails")
+ subject.gem("not-really-a-gem", bitbucket: "mcorp/flatlab-rails")
bitbucket_uri = "https://mcorp@bitbucket.org/mcorp/flatlab-rails.git"
expect(subject.dependencies.first.source.uri).to eq(bitbucket_uri)
end
it "converts 'mcorp' to 'mcorp/mcorp'" do
- subject.gem("not-really-a-gem", :bitbucket => "mcorp")
+ subject.gem("not-really-a-gem", bitbucket: "mcorp")
bitbucket_uri = "https://mcorp@bitbucket.org/mcorp/mcorp.git"
expect(subject.dependencies.first.source.uri).to eq(bitbucket_uri)
end
end
context "default git sources" do
- it "has bitbucket, gist, and github" do
- expect(subject.instance_variable_get(:@git_sources).keys.sort).to eq(%w[bitbucket gist github])
+ it "has bitbucket, gist, github, and gitlab" do
+ expect(subject.instance_variable_get(:@git_sources).keys.sort).to eq(%w[bitbucket gist github gitlab])
end
end
end
describe "#method_missing" do
it "raises an error for unknown DSL methods" do
- expect(Bundler).to receive(:read_file).with(source_root.join("Gemfile").to_s).
+ expect(Bundler).to receive(:read_file).with(git_root.join("Gemfile").to_s).
and_return("unknown")
error_msg = "There was an error parsing `Gemfile`: Undefined local variable or method `unknown' for Gemfile. Bundler cannot continue."
@@ -83,33 +173,59 @@ RSpec.describe Bundler::Dsl do
describe "#eval_gemfile" do
it "handles syntax errors with a useful message" do
- expect(Bundler).to receive(:read_file).with(source_root.join("Gemfile").to_s).and_return("}")
+ expect(Bundler).to receive(:read_file).with(git_root.join("Gemfile").to_s).and_return("}")
expect { subject.eval_gemfile("Gemfile") }.
- to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`: (syntax error, unexpected tSTRING_DEND|(compile error - )?syntax error, unexpected '\}'). Bundler cannot continue./)
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`: (syntax error, unexpected tSTRING_DEND|(compile error - )?syntax error, unexpected '\}'|.+?unexpected '}', ignoring it\n). Bundler cannot continue./m)
end
it "distinguishes syntax errors from evaluation errors" do
- expect(Bundler).to receive(:read_file).with(source_root.join("Gemfile").to_s).and_return(
+ expect(Bundler).to receive(:read_file).with(git_root.join("Gemfile").to_s).and_return(
"ruby '2.1.5', :engine => 'ruby', :engine_version => '1.2.4'"
)
expect { subject.eval_gemfile("Gemfile") }.
to raise_error(Bundler::GemfileError, /There was an error evaluating `Gemfile`: ruby_version must match the :engine_version for MRI/)
end
+
+ it "populates __dir__ and __FILE__ correctly" do
+ abs_path = git_root.join("../fragment.rb").to_s
+ expect(Bundler).to receive(:read_file).with(abs_path).and_return(<<~RUBY)
+ @fragment_dir = __dir__
+ @fragment_file = __FILE__
+ RUBY
+ subject.eval_gemfile("../fragment.rb")
+ expect(subject.instance_variable_get(:@fragment_dir)).to eq(git_root.dirname.to_s)
+ expect(subject.instance_variable_get(:@fragment_file)).to eq(abs_path)
+ end
end
describe "#gem" do
- [:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24, :ruby_25, :ruby_26, :mri, :mri_18, :mri_19,
- :mri_20, :mri_21, :mri_22, :mri_23, :mri_24, :mri_25, :mri_26, :jruby, :rbx, :truffleruby].each do |platform|
+ # rubocop:disable Naming/VariableNumber
+ [:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24, :ruby_25, :ruby_26, :ruby_27,
+ :ruby_30, :ruby_31, :ruby_32, :ruby_33, :ruby_34, :ruby_40, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24,
+ :mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :mri_34, :mri_40, :jruby, :rbx, :truffleruby].each do |platform|
it "allows #{platform} as a valid platform" do
- subject.gem("foo", :platform => platform)
+ subject.gem("foo", platform: platform)
end
end
+ # rubocop:enable Naming/VariableNumber
+
+ it "allows platforms matching the running Ruby version" do
+ platform = "ruby_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}"
+
+ expect { subject.gem("foo", platform: platform) }.not_to raise_error
+ expect(Bundler.current_ruby.respond_to?("#{platform}?")).to be_truthy
+ end
it "rejects invalid platforms" do
- expect { subject.gem("foo", :platform => :bogus) }.
+ expect { subject.gem("foo", platform: :bogus) }.
to raise_error(Bundler::GemfileError, /is not a valid platform/)
end
+ it "warn for legacy windows platforms" do
+ expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with(/\APlatform :mswin, :x64_mingw will be removed in the future./)
+ subject.gem("foo", platforms: [:mswin, :jruby, :x64_mingw])
+ end
+
it "rejects empty gem name" do
expect { subject.gem("") }.
to raise_error(Bundler::GemfileError, /an empty gem name is not valid/)
@@ -156,24 +272,33 @@ RSpec.describe Bundler::Dsl do
end
it "rejects branch option on non-git gems" do
- expect { subject.gem("foo", :branch => "test") }.
+ expect { subject.gem("foo", branch: "test") }.
to raise_error(Bundler::GemfileError, /The `branch` option for `gem 'foo'` is not allowed. Only gems with a git source can specify a branch/)
end
it "allows specifying a branch on git gems" do
- subject.gem("foo", :branch => "test", :git => "http://mytestrepo")
+ subject.gem("foo", branch: "test", git: "http://mytestrepo")
dep = subject.dependencies.last
expect(dep.name).to eq "foo"
end
it "allows specifying a branch on git gems with a git_source" do
subject.git_source(:test_source) {|n| "https://github.com/#{n}" }
- subject.gem("foo", :branch => "test", :test_source => "bundler/bundler")
+ subject.gem("foo", branch: "test", test_source: "bundler/bundler")
dep = subject.dependencies.last
expect(dep.name).to eq "foo"
end
end
+ describe "#platforms" do
+ it "warn for legacy windows platforms" do
+ expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with(/\APlatform :mswin64, :mingw will be removed in the future./)
+ subject.platforms(:mswin64, :jruby, :mingw) do
+ subject.gem("foo")
+ end
+ end
+ end
+
context "can bundle groups of gems with" do
# git "https://github.com/rails/rails.git" do
# gem "railties"
@@ -195,19 +320,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]
@@ -226,7 +338,7 @@ RSpec.describe Bundler::Dsl do
it "will raise a Bundler::GemfileError" do
gemfile "gem 'foo', :path => /unquoted/string/syntax/error"
expect { Bundler::Dsl.evaluate(bundled_app_gemfile, nil, true) }.
- to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`:( compile error -)? unknown regexp options - trg.+ Bundler cannot continue./)
+ to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`:( compile error -)?.+?unknown regexp options - trg.+ Bundler cannot continue./m)
end
end
@@ -246,7 +358,7 @@ RSpec.describe Bundler::Dsl do
allow(Bundler).to receive(:default_gemfile).and_return(Pathname.new("./Gemfile"))
subject.source("https://other-source.org") do
- subject.gem("dobry-pies", :path => "foo")
+ subject.gem("dobry-pies", path: "foo")
subject.gem("foo")
end
@@ -254,4 +366,193 @@ RSpec.describe Bundler::Dsl do
end
end
end
+
+ describe "#source with cooldown" do
+ before do
+ allow(@rubygems).to receive(:add_remote)
+ end
+
+ it "accepts a non-negative integer" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: 7)
+ end.not_to raise_error
+ end
+
+ it "accepts 0 as an explicit disable" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: 0)
+ end.not_to raise_error
+ end
+
+ it "rejects a string" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: "7")
+ end.to raise_error(Bundler::InvalidOption, /non-negative integer/)
+ end
+
+ it "rejects a float" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: 7.5)
+ end.to raise_error(Bundler::InvalidOption, /non-negative integer/)
+ end
+
+ it "rejects a negative integer" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: -7)
+ end.to raise_error(Bundler::InvalidOption, /non-negative integer/)
+ end
+
+ it "rejects an array" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: [7])
+ end.to raise_error(Bundler::InvalidOption, /non-negative integer/)
+ end
+ end
+
+ describe "#override" do
+ it "stores an Override for a gem with a version: operation" do
+ subject.override("rails", version: ">= 8.0")
+
+ expect(subject.overrides.size).to eq(1)
+ override = subject.overrides.first
+ expect(override.target).to eq("rails")
+ expect(override.field).to eq(:version)
+ expect(override.operation).to eq(">= 8.0")
+ end
+
+ it "accepts :ignore_upper as the operation" do
+ subject.override("nokogiri", version: :ignore_upper)
+ expect(subject.overrides.first.operation).to eq(:ignore_upper)
+ end
+
+ it "accepts nil as the operation" do
+ subject.override("legacy", version: nil)
+ expect(subject.overrides.first.operation).to be_nil
+ end
+
+ it "appends to overrides across multiple statements" do
+ subject.override("rails", version: ">= 8.0")
+ subject.override("nokogiri", version: :ignore_upper)
+ expect(subject.overrides.map(&:target)).to eq(["rails", "nokogiri"])
+ end
+
+ it "is empty by default" do
+ expect(subject.overrides).to eq([])
+ end
+
+ it "raises ArgumentError when target is :all and version: is given" do
+ expect do
+ subject.override(:all, version: ">= 8.0")
+ end.to raise_error(ArgumentError, /`override :all, version:` is not allowed/)
+ end
+
+ it "rejects :all + version: even when other fields are also given" do
+ expect do
+ subject.override(:all, required_ruby_version: :ignore_upper, version: ">= 8.0")
+ end.to raise_error(ArgumentError, /`override :all, version:` is not allowed/)
+ end
+
+ it "does not record any override when :all + version: is rejected" do
+ expect do
+ subject.override(:all, version: ">= 8.0")
+ end.to raise_error(ArgumentError)
+ expect(subject.overrides).to eq([])
+ end
+
+ it "raises ArgumentError when target is neither :all nor a string" do
+ expect do
+ subject.override(:rails, version: ">= 8.0")
+ end.to raise_error(ArgumentError, /target must be :all or a gem name string/)
+ end
+
+ it "raises ArgumentError for an unsupported field" do
+ expect do
+ subject.override("rails", as: "y")
+ end.to raise_error(ArgumentError, /unsupported override field `as:`/)
+ end
+
+ it "stores an Override for a gem with a required_ruby_version: operation" do
+ subject.override("rails", required_ruby_version: :ignore_upper)
+ override = subject.overrides.first
+ expect(override.target).to eq("rails")
+ expect(override.field).to eq(:required_ruby_version)
+ expect(override.operation).to eq(:ignore_upper)
+ end
+
+ it "stores an Override for a gem with a required_rubygems_version: operation" do
+ subject.override("rails", required_rubygems_version: nil)
+ override = subject.overrides.first
+ expect(override.field).to eq(:required_rubygems_version)
+ expect(override.operation).to be_nil
+ end
+
+ it "stores an Override targeting :all with a metadata field" do
+ subject.override(:all, required_ruby_version: :ignore_upper)
+ override = subject.overrides.first
+ expect(override.target).to eq(:all)
+ expect(override.field).to eq(:required_ruby_version)
+ expect(override.operation).to eq(:ignore_upper)
+ end
+
+ it "stores an Override targeting :all with required_rubygems_version" do
+ subject.override(:all, required_rubygems_version: nil)
+ override = subject.overrides.first
+ expect(override.target).to eq(:all)
+ expect(override.field).to eq(:required_rubygems_version)
+ end
+
+ it "raises ArgumentError for a non-string, non-symbol, non-nil operation" do
+ expect do
+ subject.override("rails", version: 42)
+ end.to raise_error(ArgumentError, /override operation must be a String, Symbol, or nil/)
+ end
+
+ it "raises ArgumentError for an unsupported symbol operation" do
+ expect do
+ subject.override("rails", version: :explode)
+ end.to raise_error(ArgumentError, /unsupported override operation/)
+ end
+
+ it "raises ArgumentError for an unparsable version string" do
+ expect do
+ subject.override("rails", version: "not a version")
+ end.to raise_error(ArgumentError, /invalid override version requirement/)
+ end
+
+ it "does not record an override when the version string is invalid" do
+ expect do
+ subject.override("rails", version: "not a version")
+ end.to raise_error(ArgumentError)
+ expect(subject.overrides).to eq([])
+ end
+
+ it "rejects atomically when one field in a multi-field call is invalid" do
+ expect do
+ subject.override("rails", version: ">= 8.0", as: "y")
+ end.to raise_error(ArgumentError, /unsupported override field/)
+ expect(subject.overrides).to eq([])
+ end
+
+ it "raises ArgumentError when the same target and field are overridden twice" do
+ subject.override("rails", version: ">= 8.0")
+ expect do
+ subject.override("rails", version: :ignore_upper)
+ end.to raise_error(ArgumentError, /duplicate override for "rails" `version:`/)
+ end
+
+ it "keeps the original override when a duplicate is rejected" do
+ subject.override("rails", version: ">= 8.0")
+ expect do
+ subject.override("rails", version: :ignore_upper)
+ end.to raise_error(ArgumentError)
+ expect(subject.overrides.size).to eq(1)
+ expect(subject.overrides.first.operation).to eq(">= 8.0")
+ end
+
+ it "allows different targets with the same field" do
+ subject.override("rails", version: ">= 8.0")
+ subject.override("nokogiri", version: :ignore_upper)
+ expect(subject.overrides.size).to eq(2)
+ end
+ end
end
diff --git a/spec/bundler/bundler/endpoint_specification_spec.rb b/spec/bundler/bundler/endpoint_specification_spec.rb
index a9371f6617..4fbd59d48f 100644
--- a/spec/bundler/bundler/endpoint_specification_spec.rb
+++ b/spec/bundler/bundler/endpoint_specification_spec.rb
@@ -5,9 +5,10 @@ RSpec.describe Bundler::EndpointSpecification do
let(:version) { "1.0.0" }
let(:platform) { Gem::Platform::RUBY }
let(:dependencies) { [] }
+ let(:spec_fetcher) { double(:spec_fetcher) }
let(:metadata) { nil }
- subject(:spec) { described_class.new(name, version, platform, dependencies, metadata) }
+ subject(:spec) { described_class.new(name, version, platform, spec_fetcher, dependencies, metadata) }
describe "#build_dependency" do
let(:name) { "foo" }
@@ -32,22 +33,6 @@ RSpec.describe Bundler::EndpointSpecification do
)
end
end
-
- context "when there is an ill formed requirement" do
- before do
- allow(Gem::Dependency).to receive(:new).with(name, [requirement1, requirement2]) {
- raise ArgumentError.new("Ill-formed requirement [\"#<YAML::Syck::DefaultKey")
- }
- # Eliminate extra line break in rspec output due to `puts` in `#build_dependency`
- allow(subject).to receive(:puts) {}
- end
-
- it "should raise a Bundler::GemspecError with invalid gemspec message" do
- expect { subject.send(:build_dependency, name, [requirement1, requirement2]) }.to raise_error(
- Bundler::GemspecError, /Unfortunately, the gem foo \(1\.0\.0\) has an invalid gemspec/
- )
- end
- end
end
describe "#parse_metadata" do
@@ -57,14 +42,81 @@ RSpec.describe Bundler::EndpointSpecification do
expect { subject }.to raise_error(
Bundler::GemspecError,
a_string_including("There was an error parsing the metadata for the gem foo (1.0.0)").
- and(a_string_including('The metadata was {"rubygems"=>">\n"}'))
+ and(a_string_including("The metadata was #{{ "rubygems" => ">\n" }.inspect}"))
)
end
end
+
+ context "when the metadata has created_at" do
+ let(:metadata) { { "created_at" => ["2026-05-12T10:00:00Z"] } }
+
+ it "parses created_at as a Time" do
+ expect(subject.created_at).to eq(Time.utc(2026, 5, 12, 10, 0, 0))
+ end
+ end
+
+ context "when the metadata has a string created_at (older rubygems shape)" do
+ let(:metadata) { { "created_at" => "2026-05-12T10:00:00Z" } }
+
+ it "still parses created_at" do
+ expect(subject.created_at).to eq(Time.utc(2026, 5, 12, 10, 0, 0))
+ end
+ end
+
+ context "when created_at is truncated (older rubygems splits on colons)" do
+ let(:metadata) { { "created_at" => "2026-05-12T10" } }
+
+ it "leaves created_at as nil instead of raising" do
+ expect(subject.created_at).to be_nil
+ end
+ end
+
+ context "when the metadata has no created_at" do
+ let(:metadata) { { "checksum" => ["abc"] } }
+ let(:spec_fetcher) { double(:spec_fetcher, uri: "https://rubygems.org") }
+
+ it "leaves created_at as nil" do
+ allow(Bundler::Checksum).to receive(:from_api).and_return(nil)
+ expect(subject.created_at).to be_nil
+ end
+ end
+
+ context "when the metadata is nil" do
+ it "leaves created_at as nil" do
+ expect(subject.created_at).to be_nil
+ end
+ end
+ end
+
+ describe "#required_ruby_version" do
+ context "required_ruby_version is already set on endpoint specification" do
+ existing_value = "already set value"
+ let(:required_ruby_version) { existing_value }
+
+ it "should return the current value when already set on endpoint specification" do
+ expect(spec.required_ruby_version). eql?(existing_value)
+ end
+ end
+
+ it "should return the remote spec value when not set on endpoint specification and remote spec has one" do
+ remote_value = "remote_value"
+ remote_spec = double(:remote_spec, required_ruby_version: remote_value, required_rubygems_version: nil)
+ allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
+
+ expect(spec.required_ruby_version). eql?(remote_value)
+ end
+
+ it "should use the default Gem Requirement value when not set on endpoint specification and not set on remote spec" do
+ remote_spec = double(:remote_spec, required_ruby_version: nil, required_rubygems_version: nil)
+ allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
+ expect(spec.required_ruby_version). eql?(Gem::Requirement.default)
+ end
end
it "supports equality comparison" do
- other_spec = described_class.new("bar", version, platform, dependencies, metadata)
+ remote_spec = double(:remote_spec, required_ruby_version: nil, required_rubygems_version: nil)
+ allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
+ other_spec = described_class.new("bar", version, platform, spec_fetcher, dependencies, metadata)
expect(spec).to eql(spec)
expect(spec).to_not eql(other_spec)
end
diff --git a/spec/bundler/bundler/env_spec.rb b/spec/bundler/bundler/env_spec.rb
index fb593639bd..2b7dbde217 100644
--- a/spec/bundler/bundler/env_spec.rb
+++ b/spec/bundler/bundler/env_spec.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
-require "openssl"
require "bundler/settings"
+require "openssl"
RSpec.describe Bundler::Env do
- let(:git_proxy_stub) { Bundler::Source::Git::GitProxy.new(nil, nil, nil) }
+ let(:git_proxy_stub) { Bundler::Source::Git::GitProxy.new(nil, nil) }
describe "#report" do
it "prints the environment" do
@@ -34,8 +34,6 @@ RSpec.describe Bundler::Env do
end
it "prints user home" do
- skip "needs to use a valid HOME" if Gem.win_platform? && RUBY_VERSION < "2.6.0"
-
with_clear_paths("HOME", "/a/b/c") do
out = described_class.report
expect(out).to include("User Home /a/b/c")
@@ -43,8 +41,6 @@ RSpec.describe Bundler::Env do
end
it "prints user path" do
- skip "needs to use a valid HOME" if Gem.win_platform? && RUBY_VERSION < "2.6.0"
-
with_clear_paths("HOME", "/a/b/c") do
allow(File).to receive(:exist?)
allow(File).to receive(:exist?).with("/a/b/c/.gem").and_return(true)
@@ -60,7 +56,7 @@ RSpec.describe Bundler::Env do
end
end
- private
+ private
def with_clear_paths(env_var, env_value)
old_env_var = ENV[env_var]
@@ -74,16 +70,16 @@ 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 'https://gem.repo1'; gem 'myrack', '1.0.0'"
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
DEPENDENCIES
- rack
+ myrack
BUNDLED WITH
1.10.0
@@ -92,30 +88,58 @@ RSpec.describe Bundler::Env do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
end
- let(:output) { described_class.report(:print_gemfile => true) }
+ let(:output) { described_class.report(print_gemfile: true) }
it "prints the Gemfile" do
expect(output).to include("Gemfile")
- expect(output).to include("'rack', '1.0.0'")
+ expect(output).to include("'myrack', '1.0.0'")
end
it "prints the lockfile" do
expect(output).to include("Gemfile.lock")
- expect(output).to include("rack (1.0.0)")
+ expect(output).to include("myrack (1.0.0)")
end
end
context "when there no Gemfile and print_gemfile is true" do
- let(:output) { described_class.report(:print_gemfile => true) }
+ let(:output) { described_class.report(print_gemfile: true) }
it "prints the environment" do
expect(output).to start_with("## Environment")
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)
+ <<~GEMSPEC
Gem::Specification.new do |gem|
gem.name = "foo"
gem.author = "Fumofu"
@@ -124,9 +148,9 @@ RSpec.describe Bundler::Env do
end
before do
- gemfile("gemspec")
+ gemfile("source 'https://gem.repo1'; gemspec")
- File.open(bundled_app.join("foo.gemspec"), "wb") do |f|
+ File.open(bundled_app("foo.gemspec"), "wb") do |f|
f.write(gemspec)
end
@@ -134,7 +158,7 @@ RSpec.describe Bundler::Env do
end
it "prints the gemspec" do
- output = described_class.report(:print_gemspecs => true)
+ output = described_class.report(print_gemspecs: true)
expect(output).to include("foo.gemspec")
expect(output).to include(gemspec)
@@ -143,18 +167,18 @@ RSpec.describe Bundler::Env do
context "when eval_gemfile is used" do
it "prints all gemfiles" do
- create_file bundled_app("other/Gemfile-other"), "gem 'rack'"
- create_file bundled_app("other/Gemfile"), "eval_gemfile 'Gemfile-other'"
- create_file bundled_app("Gemfile-alt"), <<-G
- source "#{file_uri_for(gem_repo1)}"
+ gemfile bundled_app("other/Gemfile-other"), "gem 'myrack'"
+ gemfile bundled_app("other/Gemfile"), "eval_gemfile 'Gemfile-other'"
+ gemfile bundled_app("Gemfile-alt"), <<-G
+ source "https://gem.repo1"
eval_gemfile "other/Gemfile"
G
gemfile "eval_gemfile #{bundled_app("Gemfile-alt").to_s.dump}"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
allow(Bundler::SharedHelpers).to receive(:pwd).and_return(bundled_app)
- output = described_class.report(:print_gemspecs => true)
- expect(output).to include(strip_whitespace(<<-ENV))
+ output = described_class.report(print_gemspecs: true)
+ expect(output).to include(<<~ENV)
## Gemfile
### Gemfile
@@ -166,7 +190,7 @@ RSpec.describe Bundler::Env do
### Gemfile-alt
```ruby
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
eval_gemfile "other/Gemfile"
```
@@ -179,7 +203,7 @@ RSpec.describe Bundler::Env do
### other/Gemfile-other
```ruby
- gem 'rack'
+ gem 'myrack'
```
### Gemfile.lock
@@ -193,20 +217,21 @@ RSpec.describe Bundler::Env do
context "when the git version is OS specific" do
it "includes OS specific information with the version number" do
- expect(git_proxy_stub).to receive(:git).with("--version").
- and_return("git version 1.2.3 (Apple Git-BS)")
+ status = double("success?" => true)
+ expect(Open3).to receive(:capture3).with("git", "--version").
+ and_return(["git version 1.2.3 (Apple Git-BS)", "", status])
expect(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub)
- expect(described_class.report).to include("Git 1.2.3 (Apple Git-BS)")
+ expect(described_class.report).to include("Git 1.2.3 (Apple Git-BS)")
end
end
- end
- describe ".version_of" do
- let(:parsed_version) { described_class.send(:version_of, "ruby") }
-
- it "strips version of new line characters" do
- expect(parsed_version).to_not end_with("\n")
+ it "no longer reports the Tools section or external tool versions" do
+ report = described_class.report
+ expect(report).not_to include("Tools")
+ ["rbenv", "RVM", "chruby"].each do |tool|
+ expect(report).not_to include(tool)
+ end
end
end
end
diff --git a/spec/bundler/bundler/environment_preserver_spec.rb b/spec/bundler/bundler/environment_preserver_spec.rb
index 530ca6f835..6c7066d0c6 100644
--- a/spec/bundler/bundler/environment_preserver_spec.rb
+++ b/spec/bundler/bundler/environment_preserver_spec.rb
@@ -27,8 +27,12 @@ RSpec.describe Bundler::EnvironmentPreserver do
context "when a key is empty" do
let(:env) { { "foo" => "" } }
- it "should not create backup entries" do
- expect(subject).not_to have_key "BUNDLER_ORIG_foo"
+ it "should keep the original entry" do
+ expect(subject["foo"]).to be_empty
+ end
+
+ it "should still create backup entries" do
+ expect(subject).to have_key "BUNDLER_ORIG_foo"
end
end
@@ -71,8 +75,12 @@ RSpec.describe Bundler::EnvironmentPreserver do
context "when the original key is empty" do
let(:env) { { "foo" => "my-foo", "BUNDLER_ORIG_foo" => "" } }
- it "should keep the current value" do
- expect(subject["foo"]).to eq("my-foo")
+ it "should restore the original value" do
+ expect(subject["foo"]).to be_empty
+ end
+
+ it "should delete the backup value" do
+ expect(subject.key?("BUNDLER_ORIG_foo")).to eq(false)
end
end
end
diff --git a/spec/bundler/bundler/errors_spec.rb b/spec/bundler/bundler/errors_spec.rb
new file mode 100644
index 0000000000..b62d85d32b
--- /dev/null
+++ b/spec/bundler/bundler/errors_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::IncorrectLockfileDependencies do
+ describe "#message" do
+ let(:spec) do
+ double("LazySpecification", full_name: "rubocop-1.82.0")
+ end
+
+ context "without dependency details" do
+ subject { described_class.new(spec) }
+
+ it "provides a basic error message" do
+ expect(subject.message).to include("Bundler found incorrect dependencies in the lockfile for rubocop-1.82.0")
+ expect(subject.message).to include("Please run `bundle install` to regenerate the lockfile.")
+ end
+ end
+
+ context "with dependency details" do
+ let(:actual_dependencies) do
+ [
+ Gem::Dependency.new("json", [">= 2.3", "< 4.0"]),
+ Gem::Dependency.new("parallel", ["~> 1.10"]),
+ Gem::Dependency.new("parser", [">= 3.3.0.2"]),
+ ]
+ end
+
+ let(:lockfile_dependencies) do
+ [
+ Gem::Dependency.new("json", [">= 2.3", "< 3.0"]),
+ Gem::Dependency.new("parallel", ["~> 1.10"]),
+ Gem::Dependency.new("parser", [">= 3.2.0.0"]),
+ ]
+ end
+
+ subject { described_class.new(spec, actual_dependencies, lockfile_dependencies) }
+
+ it "shows only mismatched dependencies" do
+ message = subject.message
+
+ expect(message).to include("json: gemspec specifies")
+ expect(message).to include("parser: gemspec specifies")
+ expect(message).not_to include("parallel")
+ end
+ end
+
+ context "when gemspec has dependencies but lockfile has none" do
+ let(:actual_dependencies) do
+ [
+ Gem::Dependency.new("myrack-test", ["~> 1.0"]),
+ ]
+ end
+
+ let(:lockfile_dependencies) { [] }
+
+ subject { described_class.new(spec, actual_dependencies, lockfile_dependencies) }
+
+ it "shows the dependency as not in lockfile" do
+ message = subject.message
+
+ expect(message).to include("myrack-test: gemspec specifies ~> 1.0, not in lockfile")
+ end
+ end
+
+ context "when gemspec has no dependencies but lockfile has some" do
+ let(:actual_dependencies) { [] }
+
+ let(:lockfile_dependencies) do
+ [
+ Gem::Dependency.new("unexpected", ["~> 1.0"]),
+ ]
+ end
+
+ subject { described_class.new(spec, actual_dependencies, lockfile_dependencies) }
+
+ it "shows the dependency as not in gemspec" do
+ message = subject.message
+
+ expect(message).to include("unexpected: not in gemspec, lockfile has ~> 1.0")
+ end
+ end
+ end
+
+ describe "#status_code" do
+ let(:spec) { double("LazySpecification", full_name: "test-1.0.0") }
+ subject { described_class.new(spec) }
+
+ it "returns 41" do
+ expect(subject.status_code).to eq(41)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/base_spec.rb b/spec/bundler/bundler/fetcher/base_spec.rb
index 02506591f3..b8c6b57b10 100644
--- a/spec/bundler/bundler/fetcher/base_spec.rb
+++ b/spec/bundler/bundler/fetcher/base_spec.rb
@@ -4,15 +4,16 @@ RSpec.describe Bundler::Fetcher::Base do
let(:downloader) { double(:downloader) }
let(:remote) { double(:remote) }
let(:display_uri) { "http://sample_uri.com" }
+ let(:gem_remote_fetcher) { nil }
class TestClass < described_class; end
- subject { TestClass.new(downloader, remote, display_uri) }
+ subject { TestClass.new(downloader, remote, display_uri, gem_remote_fetcher) }
describe "#initialize" do
context "with the abstract Base class" do
it "should raise an error" do
- expect { described_class.new(downloader, remote, display_uri) }.to raise_error(RuntimeError, "Abstract class")
+ expect { described_class.new(downloader, remote, display_uri, gem_remote_fetcher) }.to raise_error(RuntimeError, "Abstract class")
end
end
@@ -36,7 +37,7 @@ RSpec.describe Bundler::Fetcher::Base do
end
describe "#fetch_uri" do
- let(:remote_uri_obj) { Bundler::URI("http://rubygems.org") }
+ let(:remote_uri_obj) { Gem::URI("http://rubygems.org") }
before { allow(subject).to receive(:remote_uri).and_return(remote_uri_obj) }
@@ -49,10 +50,10 @@ RSpec.describe Bundler::Fetcher::Base do
end
context "when the remote uri's host is not rubygems.org" do
- let(:remote_uri_obj) { Bundler::URI("http://otherhost.org") }
+ let(:remote_uri_obj) { Gem::URI("http://otherhost.org") }
it "should return the remote uri" do
- expect(subject.fetch_uri).to eq(Bundler::URI("http://otherhost.org"))
+ expect(subject.fetch_uri).to eq(Gem::URI("http://otherhost.org"))
end
end
diff --git a/spec/bundler/bundler/fetcher/compact_index_spec.rb b/spec/bundler/bundler/fetcher/compact_index_spec.rb
index 00eb27edea..aa536673d9 100644
--- a/spec/bundler/bundler/fetcher/compact_index_spec.rb
+++ b/spec/bundler/bundler/fetcher/compact_index_spec.rb
@@ -4,13 +4,18 @@
require "bundler/compact_index_client"
RSpec.describe Bundler::Fetcher::CompactIndex do
- let(:downloader) { double(:downloader) }
- let(:display_uri) { Bundler::URI("http://sampleuri.com") }
- let(:remote) { double(:remote, :cache_slug => "lsjdf", :uri => display_uri) }
- let(:compact_index) { described_class.new(downloader, remote, display_uri) }
+ let(:response) { double(:response) }
+ let(:downloader) { double(:downloader, fetch: response) }
+ let(:display_uri) { Gem::URI("http://sampleuri.com") }
+ let(:remote) { double(:remote, cache_slug: "lsjdf", uri: display_uri) }
+ let(:gem_remote_fetcher) { nil }
+ let(:compact_index) { described_class.new(downloader, remote, display_uri, gem_remote_fetcher) }
+ let(:compact_index_client) { double(:compact_index_client, available?: true, info: [["lskdjf", "1", nil, [], []]]) }
before do
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified).and_return(true)
allow(compact_index).to receive(:log_specs) {}
+ allow(compact_index).to receive(:compact_index_client).and_return(compact_index_client)
end
describe "#specs_for_names" do
@@ -31,11 +36,6 @@ RSpec.describe Bundler::Fetcher::CompactIndex do
end
describe "#available?" do
- before do
- allow(compact_index).to receive(:compact_index_client).
- and_return(double(:compact_index_client, :update_and_parse_checksums! => true))
- end
-
it "returns true" do
expect(compact_index).to be_available
end
diff --git a/spec/bundler/bundler/fetcher/dependency_spec.rb b/spec/bundler/bundler/fetcher/dependency_spec.rb
index 53249116cd..501bc269a5 100644
--- a/spec/bundler/bundler/fetcher/dependency_spec.rb
+++ b/spec/bundler/bundler/fetcher/dependency_spec.rb
@@ -2,10 +2,11 @@
RSpec.describe Bundler::Fetcher::Dependency do
let(:downloader) { double(:downloader) }
- let(:remote) { double(:remote, :uri => Bundler::URI("http://localhost:5000")) }
+ let(:remote) { double(:remote, uri: Gem::URI("http://localhost:5000")) }
let(:display_uri) { "http://sample_uri.com" }
+ let(:gem_remote_fetcher) { nil }
- subject { described_class.new(downloader, remote, display_uri) }
+ subject { described_class.new(downloader, remote, display_uri, gem_remote_fetcher) }
describe "#available?" do
let(:dependency_api_uri) { double(:dependency_api_uri) }
@@ -155,9 +156,9 @@ RSpec.describe Bundler::Fetcher::Dependency do
end
end
- shared_examples_for "the error suggests retrying with the full index" do
- it "should log the inability to fetch from API at debug level" do
- expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API\nit's suggested to retry using the full index via `bundle install --full-index`")
+ shared_examples_for "the error is logged" do
+ it "should log the inability to fetch from API at debug level, and mention retrying" do
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API, trying the full index")
subject.specs(gem_names, full_dependency_list, last_spec_list)
end
end
@@ -166,25 +167,21 @@ RSpec.describe Bundler::Fetcher::Dependency do
before { allow(subject).to receive(:dependency_specs) { raise Bundler::HTTPError.new } }
it_behaves_like "the error is properly handled"
- it_behaves_like "the error suggests retrying with the full index"
+ it_behaves_like "the error is logged"
end
context "when a GemspecError occurs" do
before { allow(subject).to receive(:dependency_specs) { raise Bundler::GemspecError.new } }
it_behaves_like "the error is properly handled"
- it_behaves_like "the error suggests retrying with the full index"
+ it_behaves_like "the error is logged"
end
context "when a MarshalError occurs" do
before { allow(subject).to receive(:dependency_specs) { raise Bundler::MarshalError.new } }
it_behaves_like "the error is properly handled"
-
- it "should log the inability to fetch from API and mention retrying" do
- expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API, trying the full index")
- subject.specs(gem_names, full_dependency_list, last_spec_list)
- end
+ it_behaves_like "the error is logged"
end
end
@@ -214,37 +211,49 @@ RSpec.describe Bundler::Fetcher::Dependency do
let(:gem_names) { [%w[foo bar], %w[bundler rubocop]] }
let(:dep_api_uri) { double(:dep_api_uri) }
let(:unmarshalled_gems) { double(:unmarshalled_gems) }
- let(:fetch_response) { double(:fetch_response, :body => double(:body)) }
- let(:rubygems_limit) { 50 }
+ let(:fetch_response) { double(:fetch_response, body: double(:body)) }
+ let(:rubygems_limit) { 100 }
before { allow(subject).to receive(:dependency_api_uri).with(gem_names).and_return(dep_api_uri) }
it "should fetch dependencies from RubyGems and unmarshal them" do
expect(gem_names).to receive(:each_slice).with(rubygems_limit).and_call_original
expect(downloader).to receive(:fetch).with(dep_api_uri).and_return(fetch_response)
- expect(Bundler).to receive(:load_marshal).with(fetch_response.body).and_return([unmarshalled_gems])
+ expect(Bundler).to receive(:safe_load_marshal).with(fetch_response.body).and_return([unmarshalled_gems])
expect(subject.unmarshalled_dep_gems(gem_names)).to eq([unmarshalled_gems])
end
+
+ it "should fetch as many dependencies as specified" do
+ allow(subject).to receive(:dependency_api_uri).with([%w[foo bar]]).and_return(dep_api_uri)
+ allow(subject).to receive(:dependency_api_uri).with([%w[bundler rubocop]]).and_return(dep_api_uri)
+
+ expect(downloader).to receive(:fetch).twice.with(dep_api_uri).and_return(fetch_response)
+ expect(Bundler).to receive(:safe_load_marshal).twice.with(fetch_response.body).and_return([unmarshalled_gems])
+
+ Bundler.settings.temporary(api_request_size: 1) do
+ expect(subject.unmarshalled_dep_gems(gem_names)).to eq([unmarshalled_gems, unmarshalled_gems])
+ end
+ end
end
describe "#get_formatted_specs_and_deps" do
let(:gem_list) do
[
{
- :dependencies => {
+ dependencies: {
"resque" => "req3,req4",
},
- :name => "typhoeus",
- :number => "1.0.1",
- :platform => "ruby",
+ name: "typhoeus",
+ number: "1.0.1",
+ platform: "ruby",
},
{
- :dependencies => {
+ dependencies: {
"faraday" => "req1,req2",
},
- :name => "grape",
- :number => "2.0.2",
- :platform => "jruby",
+ name: "grape",
+ number: "2.0.2",
+ platform: "jruby",
},
]
end
@@ -258,7 +267,7 @@ RSpec.describe Bundler::Fetcher::Dependency do
end
describe "#dependency_api_uri" do
- let(:uri) { Bundler::URI("http://gem-api.com") }
+ let(:uri) { Gem::URI("http://gem-api.com") }
context "with gem names" do
let(:gem_names) { %w[foo bar bundler rubocop] }
diff --git a/spec/bundler/bundler/fetcher/downloader_spec.rb b/spec/bundler/bundler/fetcher/downloader_spec.rb
index ba8451d9fa..edf426328a 100644
--- a/spec/bundler/bundler/fetcher/downloader_spec.rb
+++ b/spec/bundler/bundler/fetcher/downloader_spec.rb
@@ -3,7 +3,7 @@
RSpec.describe Bundler::Fetcher::Downloader do
let(:connection) { double(:connection) }
let(:redirect_limit) { 5 }
- let(:uri) { Bundler::URI("http://www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("http://www.uri-to-fetch.com/api/v2/endpoint") }
let(:options) { double(:options) }
subject { described_class.new(connection, redirect_limit) }
@@ -27,7 +27,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
context "logging" do
- let(:http_response) { Net::HTTPSuccess.new("1.1", 200, "Success") }
+ let(:http_response) { Gem::Net::HTTPSuccess.new("1.1", 200, "Success") }
it "should log the HTTP response code and message to debug" do
expect(Bundler).to receive_message_chain(:ui, :debug).with("HTTP 200 Success #{uri}")
@@ -35,56 +35,61 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
- context "when the request response is a Net::HTTPRedirection" do
- let(:http_response) { Net::HTTPRedirection.new(httpv, 308, "Moved") }
+ context "when the request response is a Gem::Net::HTTPRedirection" do
+ let(:http_response) { Gem::Net::HTTPRedirection.new(httpv, 308, "Moved") }
before { http_response["location"] = "http://www.redirect-uri.com/api/v2/endpoint" }
it "should try to fetch the redirect uri and iterate the # requests counter" do
- expect(subject).to receive(:fetch).with(Bundler::URI("http://www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
- expect(subject).to receive(:fetch).with(Bundler::URI("http://www.redirect-uri.com/api/v2/endpoint"), options, 1)
+ expect(subject).to receive(:fetch).with(Gem::URI("http://www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
+ expect(subject).to receive(:fetch).with(Gem::URI("http://www.redirect-uri.com/api/v2/endpoint"), options, 1)
subject.fetch(uri, options, counter)
end
context "when the redirect uri and original uri are the same" do
- let(:uri) { Bundler::URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
before { http_response["location"] = "ssh://www.uri-to-fetch.com/api/v1/endpoint" }
it "should set the same user and password for the redirect uri" do
- expect(subject).to receive(:fetch).with(Bundler::URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
- expect(subject).to receive(:fetch).with(Bundler::URI("ssh://username:password@www.uri-to-fetch.com/api/v1/endpoint"), options, 1)
+ expect(subject).to receive(:fetch).with(Gem::URI("ssh://username:password@www.uri-to-fetch.com/api/v2/endpoint"), options, 0).and_call_original
+ expect(subject).to receive(:fetch).with(Gem::URI("ssh://username:password@www.uri-to-fetch.com/api/v1/endpoint"), options, 1)
subject.fetch(uri, options, counter)
end
end
end
- context "when the request response is a Net::HTTPSuccess" do
- let(:http_response) { Net::HTTPSuccess.new("1.1", 200, "Success") }
+ context "when the request response is a Gem::Net::HTTPSuccess" do
+ let(:http_response) { Gem::Net::HTTPSuccess.new("1.1", 200, "Success") }
it "should return the response body" do
expect(subject.fetch(uri, options, counter)).to eq(http_response)
end
end
- context "when the request response is a Net::HTTPRequestEntityTooLarge" do
- let(:http_response) { Net::HTTPRequestEntityTooLarge.new("1.1", 413, "Too Big") }
+ context "when the request response is a Gem::Net::HTTPRequestEntityTooLarge" do
+ let(:http_response) { Gem::Net::HTTPRequestEntityTooLarge.new("1.1", 413, "Too Big") }
it "should raise a Bundler::Fetcher::FallbackError with the response body" do
expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::FallbackError, "Body with info")
end
end
- context "when the request response is a Net::HTTPUnauthorized" do
- let(:http_response) { Net::HTTPUnauthorized.new("1.1", 401, "Unauthorized") }
+ context "when the request response is a Gem::Net::HTTPUnauthorized" do
+ let(:http_response) { Gem::Net::HTTPUnauthorized.new("1.1", 401, "Unauthorized") }
it "should raise a Bundler::Fetcher::AuthenticationRequiredError with the uri host" do
expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
/Authentication is required for www.uri-to-fetch.com/)
end
- context "when the there are credentials provided in the request" do
- let(:uri) { Bundler::URI("http://user:password@www.uri-to-fetch.com") }
+ it "should raise a Bundler::Fetcher::AuthenticationRequiredError with advice" 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 there are credentials provided in the request" do
+ let(:uri) { Gem::URI("http://user:password@www.uri-to-fetch.com") }
it "should raise a Bundler::Fetcher::BadAuthenticationError that doesn't contain the password" do
expect { subject.fetch(uri, options, counter) }.
@@ -93,29 +98,71 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
- context "when the request response is a Net::HTTPNotFound" do
- let(:http_response) { Net::HTTPNotFound.new("1.1", 404, "Not Found") }
+ context "when the request response is a Gem::Net::HTTPForbidden" do
+ let(:http_response) { Gem::Net::HTTPForbidden.new("1.1", 403, "Forbidden") }
+ let(:uri) { Gem::URI("http://user:password@www.uri-to-fetch.com") }
+
+ it "should raise a Bundler::Fetcher::AuthenticationForbiddenError with the uri host" do
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationForbiddenError,
+ /Access token could not be authenticated for www.uri-to-fetch.com/)
+ end
+ end
+
+ context "when the request response is a Gem::Net::HTTPNotFound" do
+ let(:http_response) { Gem::Net::HTTPNotFound.new("1.1", 404, "Not Found") }
- it "should raise a Bundler::Fetcher::FallbackError with Net::HTTPNotFound" do
+ it "should raise a Bundler::Fetcher::FallbackError with Gem::Net::HTTPNotFound" do
expect { subject.fetch(uri, options, counter) }.
- to raise_error(Bundler::Fetcher::FallbackError, "Net::HTTPNotFound: http://www.uri-to-fetch.com/api/v2/endpoint")
+ to raise_error(Bundler::Fetcher::FallbackError, "Gem::Net::HTTPNotFound: http://www.uri-to-fetch.com/api/v2/endpoint")
end
- context "when the there are credentials provided in the request" do
- let(:uri) { Bundler::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ context "when there are credentials provided in the request" do
+ let(:uri) { Gem::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
it "should raise a Bundler::Fetcher::FallbackError that doesn't contain the password" do
expect { subject.fetch(uri, options, counter) }.
- to raise_error(Bundler::Fetcher::FallbackError, "Net::HTTPNotFound: http://username@www.uri-to-fetch.com/api/v2/endpoint")
+ to raise_error(Bundler::Fetcher::FallbackError, "Gem::Net::HTTPNotFound: http://username@www.uri-to-fetch.com/api/v2/endpoint")
end
end
end
+ context "when the request response is a Gem::Net::HTTPRequestedRangeNotSatisfiable" do
+ let(:http_response) { Gem::Net::HTTPRequestedRangeNotSatisfiable.new("1.1", 416, "Range Not Satisfiable") }
+ let(:success_response) { Gem::Net::HTTPSuccess.new("1.1", 200, "Success") }
+ let(:options) { { "Range" => "bytes=1000-", "If-None-Match" => "some-etag" } }
+
+ before do
+ # First request returns 416, retry request returns success
+ allow(subject).to receive(:request).with(uri, options).and_return(http_response)
+ allow(subject).to receive(:request).with(uri, { "If-None-Match" => "some-etag" }).and_return(success_response)
+ end
+
+ # The 416 handler removes the Range header and retries without incrementing the counter.
+ # Importantly, it does NOT add Accept-Encoding header, which would break Ruby's
+ # automatic gzip decompression (see issue #9271 for details on that bug).
+ it "should retry the request without the Range header" do
+ expect(subject).to receive(:request).with(uri, options).ordered
+ expect(subject).to receive(:request).with(uri, hash_excluding("Range", "Accept-Encoding")).ordered
+ subject.fetch(uri, options, counter)
+ end
+
+ it "should preserve other headers on retry" do
+ expect(subject).to receive(:request).with(uri, options).ordered
+ expect(subject).to receive(:request).with(uri, hash_including("If-None-Match" => "some-etag")).ordered
+ subject.fetch(uri, options, counter)
+ end
+
+ it "should return the successful response" do
+ result = subject.fetch(uri, options, counter)
+ expect(result).to eq(success_response)
+ end
+ end
+
context "when the request response is some other type" do
- let(:http_response) { Net::HTTPBadGateway.new("1.1", 500, "Fatal Error") }
+ let(:http_response) { Gem::Net::HTTPBadGateway.new("1.1", 500, "Fatal Error") }
it "should raise a Bundler::HTTPError with the response class and body" do
- expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::HTTPError, "Net::HTTPBadGateway: Body with info")
+ expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::HTTPError, "Gem::Net::HTTPBadGateway: Body with info")
end
end
end
@@ -125,7 +172,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
let(:response) { double(:response) }
before do
- allow(Net::HTTP::Get).to receive(:new).with("/api/v2/endpoint", options).and_return(net_http_get)
+ allow(Gem::Net::HTTP::Get).to receive(:new).with("/api/v2/endpoint", options).and_return(net_http_get)
allow(connection).to receive(:request).with(uri, net_http_get).and_return(response)
end
@@ -137,62 +184,46 @@ RSpec.describe Bundler::Fetcher::Downloader do
context "when there is a user provided in the request" do
context "and there is also a password provided" do
context "that contains cgi escaped characters" do
- let(:uri) { Bundler::URI("http://username:password%24@www.uri-to-fetch.com/api/v2/endpoint") }
+ let(:uri) { Gem::URI("http://username:password%24@www.uri-to-fetch.com/api/v2/endpoint") }
- it "should request basic authentication with the username and password" do
+ it "should request basic authentication with the username and password, and log the HTTP GET request to debug, without the password" do
expect(net_http_get).to receive(:basic_auth).with("username", "password$")
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("HTTP GET http://username@www.uri-to-fetch.com/api/v2/endpoint")
subject.request(uri, options)
end
end
context "that is all unescaped characters" do
- let(:uri) { Bundler::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
- it "should request basic authentication with the username and proper cgi compliant password" do
+ let(:uri) { Gem::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ it "should request basic authentication with the username and proper cgi compliant password, and log the HTTP GET request to debug, without the password" do
expect(net_http_get).to receive(:basic_auth).with("username", "password")
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("HTTP GET http://username@www.uri-to-fetch.com/api/v2/endpoint")
subject.request(uri, options)
end
end
end
- context "and there is no password provided" do
- let(:uri) { Bundler::URI("http://username@www.uri-to-fetch.com/api/v2/endpoint") }
+ context "and it's used as the authentication token" do
+ let(:uri) { Gem::URI("http://username@www.uri-to-fetch.com/api/v2/endpoint") }
- it "should request basic authentication with just the user" do
+ it "should request basic authentication with just the user, and log the HTTP GET request to debug, without the token" do
expect(net_http_get).to receive(:basic_auth).with("username", nil)
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("HTTP GET http://www.uri-to-fetch.com/api/v2/endpoint")
subject.request(uri, options)
end
end
- context "that contains cgi escaped characters" do
- let(:uri) { Bundler::URI("http://username%24@www.uri-to-fetch.com/api/v2/endpoint") }
+ context "and it's used as the authentication token, and contains cgi escaped characters" do
+ let(:uri) { Gem::URI("http://username%24@www.uri-to-fetch.com/api/v2/endpoint") }
- it "should request basic authentication with the proper cgi compliant password user" do
+ it "should request basic authentication with the proper cgi compliant password user, and log the HTTP GET request to debug, without the token" do
expect(net_http_get).to receive(:basic_auth).with("username$", nil)
+ expect(Bundler).to receive_message_chain(:ui, :debug).with("HTTP GET http://www.uri-to-fetch.com/api/v2/endpoint")
subject.request(uri, options)
end
end
end
- context "when the request response causes a NoMethodError" do
- before { allow(connection).to receive(:request).with(uri, net_http_get) { raise NoMethodError.new(message) } }
-
- context "and the error message is about use_ssl=" do
- let(:message) { "undefined method 'use_ssl='" }
-
- it "should raise a LoadError about openssl" do
- expect { subject.request(uri, options) }.to raise_error(LoadError, "cannot load such file -- openssl")
- end
- end
-
- context "and the error message is not about use_ssl=" 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'")
- end
- end
- end
-
context "when the request response causes a OpenSSL::SSL::SSLError" do
before { allow(connection).to receive(:request).with(uri, net_http_get) { raise OpenSSL::SSL::SSLError.new } }
@@ -202,66 +233,68 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
- context "when the request response causes an error included in HTTP_ERRORS" do
- let(:message) { nil }
- let(:error) { RuntimeError.new(message) }
+ context "when the request response causes an HTTP error" do
+ let(:message) { "error about network" }
+ let(:error) { error_class.new(message) }
before do
- stub_const("Bundler::Fetcher::HTTP_ERRORS", [RuntimeError])
allow(connection).to receive(:request).with(uri, net_http_get) { raise error }
end
- it "should trace log the error" do
- allow(Bundler).to receive_message_chain(:ui, :debug)
- expect(Bundler).to receive_message_chain(:ui, :trace).with(error)
- expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError)
- end
-
- context "when error message is about the host being down" do
- let(:message) { "host down: 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 getaddrinfo issues" do
- let(:message) { "getaddrinfo: nodename nor servname provided for http://www.uri-to-fetch.com" }
+ context "that it's retryable" do
+ let(:error_class) { Gem::Timeout::Error }
- 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/)
+ it "should trace log the error" do
+ allow(Bundler).to receive_message_chain(:ui, :debug)
+ expect(Bundler).to receive_message_chain(:ui, :trace).with(error)
+ expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError)
end
- end
-
- context "when error message is about neither host down or getaddrinfo" do
- let(:message) { "other error about network" }
it "should raise a Bundler::HTTPError" do
expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
- "Network error while fetching http://www.uri-to-fetch.com/api/v2/endpoint (other error about network)")
+ "Network error while fetching http://www.uri-to-fetch.com/api/v2/endpoint (error about network)")
end
- context "when the there are credentials provided in the request" do
- let(:uri) { Bundler::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
+ context "when there are credentials provided in the request" do
+ let(:uri) { Gem::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
before do
allow(net_http_get).to receive(:basic_auth).with("username", "password")
end
it "should raise a Bundler::HTTPError that doesn't contain the password" do
expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
- "Network error while fetching http://username@www.uri-to-fetch.com/api/v2/endpoint (other error about network)")
+ "Network error while fetching http://username@www.uri-to-fetch.com/api/v2/endpoint (error about network)")
end
end
end
- context "when error message is about no route to host" do
+ context "when error is about the host being down" do
+ let(:error_class) { Gem::Net::HTTP::Persistent::Error }
+ let(:message) { "host down: 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 is about connection refused" do
+ let(:error_class) { Gem::Net::HTTP::Persistent::Error }
+ let(:message) { "connection refused down: 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 is about no route to host" do
+ let(:error_class) { SocketError }
let(:message) { "Failed to open TCP connection to www.uri-to-fetch.com:443 " }
- it "should raise a Bundler::Fetcher::HTTPError" do
- expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
- "Network error while fetching http://www.uri-to-fetch.com/api/v2/endpoint (#{message})")
+ 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
end
diff --git a/spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb b/spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb
new file mode 100644
index 0000000000..df1a58d843
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require "rubygems/remote_fetcher"
+require "bundler/fetcher/gem_remote_fetcher"
+require_relative "../../support/artifice/helpers/artifice"
+require "bundler/vendored_persistent.rb"
+
+RSpec.describe Bundler::Fetcher::GemRemoteFetcher do
+ describe "Parallel download" do
+ it "download using multiple connections from the pool" do
+ unless Bundler.rubygems.provides?(">= 4.0.0.dev")
+ skip "This example can only run when RubyGems supports multiple http connection pool"
+ end
+
+ require_relative "../../support/artifice/helpers/endpoint"
+ concurrent_ruby_path = Dir[scoped_base_system_gem_path.join("gems/concurrent-ruby-*/lib/concurrent-ruby")].first
+ $LOAD_PATH.unshift(concurrent_ruby_path)
+ require "concurrent-ruby"
+
+ require_rack_test
+ responses = []
+
+ latch1 = Concurrent::CountDownLatch.new
+ latch2 = Concurrent::CountDownLatch.new
+ previous_client = Gem::Request::ConnectionPools.client
+ dummy_endpoint = Class.new(Endpoint) do
+ get "/foo" do
+ latch2.count_down
+ latch1.wait
+
+ responses << "foo"
+ end
+
+ get "/bar" do
+ responses << "bar"
+
+ latch1.count_down
+ end
+ end
+
+ Artifice.activate_with(dummy_endpoint)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+
+ first_request = Thread.new do
+ subject.fetch_path("https://example.org/foo")
+ end
+ second_request = Thread.new do
+ latch2.wait
+ subject.fetch_path("https://example.org/bar")
+ end
+
+ [first_request, second_request].each(&:join)
+
+ expect(responses).to eq(["bar", "foo"])
+ ensure
+ Artifice.deactivate
+ Gem::Request::ConnectionPools.client = previous_client
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/index_spec.rb b/spec/bundler/bundler/fetcher/index_spec.rb
index 5ecd7d9e05..a6a18efd98 100644
--- a/spec/bundler/bundler/fetcher/index_spec.rb
+++ b/spec/bundler/bundler/fetcher/index_spec.rb
@@ -1,13 +1,18 @@
# frozen_string_literal: true
+require "rubygems/remote_fetcher"
+
RSpec.describe Bundler::Fetcher::Index do
let(:downloader) { nil }
- let(:remote) { nil }
+ let(:remote) { double(:remote, uri: remote_uri) }
+ let(:remote_uri) { Gem::URI("http://#{userinfo}remote-uri.org") }
+ let(:userinfo) { "" }
let(:display_uri) { "http://sample_uri.com" }
let(:rubygems) { double(:rubygems) }
let(:gem_names) { %w[foo bar] }
+ let(:gem_remote_fetcher) { nil }
- subject { described_class.new(downloader, remote, display_uri) }
+ subject { described_class.new(downloader, remote, display_uri, gem_remote_fetcher) }
before { allow(Bundler).to receive(:rubygems).and_return(rubygems) }
@@ -17,100 +22,54 @@ RSpec.describe Bundler::Fetcher::Index do
end
context "error handling" do
- shared_examples_for "the error is properly handled" do
- let(:remote_uri) { Bundler::URI("http://remote-uri.org") }
- before do
- allow(subject).to receive(:remote_uri).and_return(remote_uri)
- end
-
- context "when certificate verify failed" do
- let(:error_message) { "certificate verify failed" }
-
- it "should raise a Bundler::Fetcher::CertificateFailureError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::CertificateFailureError,
- %r{Could not verify the SSL certificate for http://sample_uri.com})
- end
- end
-
- context "when a 401 response occurs" do
- let(:error_message) { "401" }
-
- before do
- allow(remote_uri).to receive(:userinfo).and_return(userinfo)
- end
-
- context "and there was userinfo" do
- let(:userinfo) { double(:userinfo) }
-
- it "should raise a Bundler::Fetcher::BadAuthenticationError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::BadAuthenticationError,
- %r{Bad username or password for http://remote-uri.org})
- end
- end
+ before do
+ allow(rubygems).to receive(:fetch_all_remote_specs) { raise Gem::RemoteFetcher::FetchError.new(error_message, display_uri) }
+ end
- context "and there was no userinfo" do
- let(:userinfo) { nil }
+ context "when certificate verify failed" do
+ let(:error_message) { "certificate verify failed" }
- it "should raise a Bundler::Fetcher::AuthenticationRequiredError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
- %r{Authentication is required for http://remote-uri.org})
- end
- end
+ it "should raise a Bundler::Fetcher::CertificateFailureError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::CertificateFailureError,
+ %r{Could not verify the SSL certificate for http://sample_uri.com})
end
+ end
- context "when a 403 response occurs" do
- let(:error_message) { "403" }
-
- before do
- allow(remote_uri).to receive(:userinfo).and_return(userinfo)
- end
-
- context "and there was userinfo" do
- let(:userinfo) { double(:userinfo) }
-
- it "should raise a Bundler::Fetcher::BadAuthenticationError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::BadAuthenticationError,
- %r{Bad username or password for http://remote-uri.org})
- end
- end
-
- context "and there was no userinfo" do
- let(:userinfo) { nil }
+ context "when a 401 response occurs" do
+ let(:error_message) { "401" }
- it "should raise a Bundler::Fetcher::AuthenticationRequiredError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
- %r{Authentication is required for http://remote-uri.org})
- end
- end
+ it "should raise a Bundler::Fetcher::AuthenticationRequiredError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
+ %r{Authentication is required for http://remote-uri.org})
end
- context "any other message is returned" do
- let(:error_message) { "You get an error, you get an error!" }
-
- before { allow(Bundler).to receive(:ui).and_return(double(:trace => nil)) }
+ context "and there was userinfo" do
+ let(:userinfo) { "user:pass@" }
- it "should raise a Bundler::HTTPError" do
- expect { subject.specs(gem_names) }.to raise_error(Bundler::HTTPError, "Could not fetch specs from http://sample_uri.com")
+ it "should raise a Bundler::Fetcher::BadAuthenticationError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::BadAuthenticationError,
+ %r{Bad username or password for http://user@remote-uri.org})
end
end
end
- context "when a Gem::RemoteFetcher::FetchError occurs" do
- before { allow(rubygems).to receive(:fetch_all_remote_specs) { raise Gem::RemoteFetcher::FetchError.new(error_message, nil) } }
+ context "when a 403 response occurs" do
+ let(:error_message) { "403" }
- it_behaves_like "the error is properly handled"
+ it "should raise a Bundler::Fetcher::AuthenticationForbiddenError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::Fetcher::AuthenticationForbiddenError,
+ %r{Access token could not be authenticated for http://remote-uri.org})
+ end
end
- context "when a OpenSSL::SSL::SSLError occurs" do
- before { allow(rubygems).to receive(:fetch_all_remote_specs) { raise OpenSSL::SSL::SSLError.new(error_message) } }
+ context "any other message is returned" do
+ let(:error_message) { "You get an error, you get an error!" }
- it_behaves_like "the error is properly handled"
- end
-
- context "when a Net::HTTPFatalError occurs" do
- before { allow(rubygems).to receive(:fetch_all_remote_specs) { raise Net::HTTPFatalError.new(error_message, 404) } }
+ before { allow(Bundler).to receive(:ui).and_return(double(trace: nil)) }
- it_behaves_like "the error is properly handled"
+ it "should raise a Bundler::HTTPError" do
+ expect { subject.specs(gem_names) }.to raise_error(Bundler::HTTPError, "Could not fetch specs from http://sample_uri.com due to underlying error <You get an error, you get an error! (http://sample_uri.com)>")
+ end
end
end
end
diff --git a/spec/bundler/bundler/fetcher_spec.rb b/spec/bundler/bundler/fetcher_spec.rb
index 256d342775..e20f7e7c48 100644
--- a/spec/bundler/bundler/fetcher_spec.rb
+++ b/spec/bundler/bundler/fetcher_spec.rb
@@ -3,8 +3,8 @@
require "bundler/fetcher"
RSpec.describe Bundler::Fetcher do
- let(:uri) { Bundler::URI("https://example.com") }
- let(:remote) { double("remote", :uri => uri, :original_uri => nil) }
+ let(:uri) { Gem::URI("https://example.com") }
+ let(:remote) { double("remote", uri: uri, original_uri: nil) }
subject(:fetcher) { Bundler::Fetcher.new(remote) }
@@ -26,7 +26,7 @@ RSpec.describe Bundler::Fetcher do
context "when Gem.configuration specifies http_proxy " do
let(:proxy) { "http://proxy-example2.com" }
before do
- allow(Bundler.rubygems.configuration).to receive(:[]).with(:http_proxy).and_return(proxy)
+ allow(Gem.configuration).to receive(:[]).with(:http_proxy).and_return(proxy)
end
it "consider Gem.configuration when determine proxy" do
expect(fetcher.http_proxy).to match("http://proxy-example2.com")
@@ -45,9 +45,9 @@ RSpec.describe Bundler::Fetcher do
end
context "when a rubygems source mirror is set" do
- let(:orig_uri) { Bundler::URI("http://zombo.com") }
+ let(:orig_uri) { Gem::URI("http://zombo.com") }
let(:remote_with_mirror) do
- double("remote", :uri => uri, :original_uri => orig_uri, :anonymized_uri => uri)
+ double("remote", uri: uri, original_uri: orig_uri, anonymized_uri: uri)
end
let(:fetcher) { Bundler::Fetcher.new(remote_with_mirror) }
@@ -61,7 +61,7 @@ RSpec.describe Bundler::Fetcher do
context "when there is no rubygems source mirror set" do
let(:remote_no_mirror) do
- double("remote", :uri => uri, :original_uri => nil, :anonymized_uri => uri)
+ double("remote", uri: uri, original_uri: nil, anonymized_uri: uri)
end
let(:fetcher) { Bundler::Fetcher.new(remote_no_mirror) }
@@ -113,10 +113,10 @@ RSpec.describe Bundler::Fetcher do
context "when gem ssl configuration is set" do
before do
- allow(Bundler.rubygems.configuration).to receive_messages(
- :http_proxy => nil,
- :ssl_client_cert => "cert",
- :ssl_ca_cert => "ca"
+ allow(Gem.configuration).to receive_messages(
+ http_proxy: nil,
+ ssl_client_cert: "cert",
+ ssl_ca_cert: "ca"
)
expect(File).to receive(:read).and_return("")
expect(OpenSSL::X509::Certificate).to receive(:new).and_return("cert")
@@ -143,20 +143,118 @@ RSpec.describe Bundler::Fetcher do
describe "include CI information" do
it "from one CI" do
- with_env_vars("JENKINS_URL" => "foo") do
+ with_env_vars("CI" => nil, "JENKINS_URL" => "foo") do
ci_part = fetcher.user_agent.split(" ").find {|x| x.start_with?("ci/") }
- expect(ci_part).to match("jenkins")
+ cis = ci_part.split("/").last.split(",")
+ expect(cis).to include("jenkins")
+ expect(cis).not_to include("ci")
end
end
it "from many CI" do
- with_env_vars("TRAVIS" => "foo", "GITLAB_CI" => "gitlab", "CI_NAME" => "my_ci") do
+ with_env_vars("CI" => "true", "SEMAPHORE" => nil, "TRAVIS" => "foo", "GITLAB_CI" => "gitlab", "CI_NAME" => "MY_ci") do
ci_part = fetcher.user_agent.split(" ").find {|x| x.start_with?("ci/") }
- expect(ci_part).to match("travis")
- expect(ci_part).to match("gitlab")
- expect(ci_part).to match("my_ci")
+ cis = ci_part.split("/").last.split(",")
+ expect(cis).to include("ci", "gitlab", "my_ci", "travis")
+ expect(cis).not_to include("semaphore")
end
end
end
end
+
+ describe "#fetch_spec" do
+ let(:name) { "name" }
+ let(:version) { "1.3.17" }
+ let(:platform) { "platform" }
+ let(:downloader) { double("downloader") }
+ let(:body) { double(Gem::Net::HTTP::Get, body: downloaded_data) }
+
+ context "when attempting to load a Gem::Specification" do
+ let(:spec) { Gem::Specification.new(name, version) }
+ let(:downloaded_data) { Zlib::Deflate.deflate(Marshal.dump(spec)) }
+
+ it "returns the spec" do
+ expect(Bundler::Fetcher::Downloader).to receive(:new).and_return(downloader)
+ expect(downloader).to receive(:fetch).once.and_return(body)
+ result = fetcher.fetch_spec([name, version, platform])
+ expect(result).to eq(spec)
+ end
+ end
+
+ context "when attempting to load an unexpected class" do
+ let(:downloaded_data) { Zlib::Deflate.deflate(Marshal.dump(3)) }
+
+ it "raises a HTTPError error" do
+ expect(Bundler::Fetcher::Downloader).to receive(:new).and_return(downloader)
+ expect(downloader).to receive(:fetch).once.and_return(body)
+ expect { fetcher.fetch_spec([name, version, platform]) }.to raise_error(Bundler::HTTPError, /Gemspec .* contained invalid data/i)
+ end
+ end
+ end
+
+ describe "#specs_with_retry" do
+ let(:downloader) { double(:downloader) }
+ let(:remote) { double(:remote, cache_slug: "slug", uri: uri, original_uri: nil, anonymized_uri: uri) }
+ let(:compact_index) { double(Bundler::Fetcher::CompactIndex, available?: true, api_fetcher?: true) }
+ let(:dependency) { double(Bundler::Fetcher::Dependency, available?: true, api_fetcher?: true) }
+ let(:index) { double(Bundler::Fetcher::Index, available?: true, api_fetcher?: false) }
+
+ before do
+ allow(Bundler::Fetcher::CompactIndex).to receive(:new).and_return(compact_index)
+ allow(Bundler::Fetcher::Dependency).to receive(:new).and_return(dependency)
+ allow(Bundler::Fetcher::Index).to receive(:new).and_return(index)
+ end
+
+ it "picks the first fetcher that works" do
+ expect(compact_index).to receive(:specs).with("name").and_return([["name", "1.2.3", "ruby"]])
+ expect(dependency).not_to receive(:specs)
+ expect(index).not_to receive(:specs)
+ fetcher.specs_with_retry("name", double(Bundler::Source::Rubygems))
+ end
+
+ context "when APIs are not available" do
+ before do
+ allow(compact_index).to receive(:available?).and_return(false)
+ allow(dependency).to receive(:available?).and_return(false)
+ end
+
+ it "uses the index" do
+ expect(compact_index).not_to receive(:specs)
+ expect(dependency).not_to receive(:specs)
+ expect(index).to receive(:specs).with("name").and_return([["name", "1.2.3", "ruby"]])
+
+ fetcher.specs_with_retry("name", double(Bundler::Source::Rubygems))
+ end
+ end
+ end
+
+ describe "#api_fetcher?" do
+ let(:downloader) { double(:downloader) }
+ let(:remote) { double(:remote, cache_slug: "slug", uri: uri, original_uri: nil, anonymized_uri: uri) }
+ let(:compact_index) { double(Bundler::Fetcher::CompactIndex, available?: false, api_fetcher?: true) }
+ let(:dependency) { double(Bundler::Fetcher::Dependency, available?: false, api_fetcher?: true) }
+ let(:index) { double(Bundler::Fetcher::Index, available?: true, api_fetcher?: false) }
+
+ before do
+ allow(Bundler::Fetcher::CompactIndex).to receive(:new).and_return(compact_index)
+ allow(Bundler::Fetcher::Dependency).to receive(:new).and_return(dependency)
+ allow(Bundler::Fetcher::Index).to receive(:new).and_return(index)
+ end
+
+ context "when an api fetcher is available" do
+ before do
+ allow(compact_index).to receive(:available?).and_return(true)
+ end
+
+ it "is truthy" do
+ expect(fetcher).to be_api_fetcher
+ end
+ end
+
+ context "when only the index fetcher is available" do
+ it "is falsey" do
+ expect(fetcher).not_to be_api_fetcher
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/friendly_errors_spec.rb b/spec/bundler/bundler/friendly_errors_spec.rb
index d6496db6ae..426e3c856d 100644
--- a/spec/bundler/bundler/friendly_errors_spec.rb
+++ b/spec/bundler/bundler/friendly_errors_spec.rb
@@ -2,7 +2,8 @@
require "bundler"
require "bundler/friendly_errors"
-require "cgi"
+require "cgi/escape"
+require "cgi/util" unless defined?(CGI::EscapeExt)
RSpec.describe Bundler, "friendly errors" do
context "with invalid YAML in .gemrc" do
@@ -18,11 +19,11 @@ RSpec.describe Bundler, "friendly errors" do
it "reports a relevant friendly error message" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle :install, :env => { "DEBUG" => "true" }
+ bundle :install, env: { "DEBUG" => "true" }
expect(err).to include("Failed to load #{home(".gemrc")}")
end
@@ -101,29 +102,15 @@ RSpec.describe Bundler, "friendly errors" do
context "BundlerError" do
it "Bundler.ui receive error" do
error = Bundler::BundlerError.new
- expect(Bundler.ui).to receive(:error).with(error.message, :wrap => true)
+ expect(Bundler.ui).to receive(:error).with(error.message, wrap: true)
Bundler::FriendlyErrors.log_error(error)
end
- it_behaves_like "Bundler.ui receive trace", Bundler::BundlerError.new
end
context "Thor::Error" do
it_behaves_like "Bundler.ui receive error", Bundler::Thor::Error.new
end
- context "LoadError" do
- let(:error) { LoadError.new("cannot load such file -- openssl") }
-
- before do
- allow(error).to receive(:backtrace).and_return(["backtrace"])
- end
-
- it "Bundler.ui receive error" do
- expect(Bundler.ui).to receive(:error).with("\nCould not load OpenSSL. LoadError: cannot load such file -- openssl\nbacktrace")
- Bundler::FriendlyErrors.log_error(error)
- end
- end
-
context "Interrupt" do
it "Bundler.ui receive error" do
expect(Bundler.ui).to receive(:error).with("\nQuitting...")
@@ -135,7 +122,7 @@ RSpec.describe Bundler, "friendly errors" do
context "Gem::InvalidSpecificationException" do
it "Bundler.ui receive error" do
error = Gem::InvalidSpecificationException.new
- expect(Bundler.ui).to receive(:error).with(error.message, :wrap => true)
+ expect(Bundler.ui).to receive(:error).with(error.message, wrap: true)
Bundler::FriendlyErrors.log_error(error)
end
end
@@ -144,17 +131,13 @@ RSpec.describe Bundler, "friendly errors" do
# Does nothing
end
- context "Java::JavaLang::OutOfMemoryError" do
- module Java
- module JavaLang
- class OutOfMemoryError < StandardError; end
- end
- end
-
+ context "Java::JavaLang::OutOfMemoryError", :jruby_only do
it "Bundler.ui receive error" do
- error = Java::JavaLang::OutOfMemoryError.new
- expect(Bundler.ui).to receive(:error).with(/JVM has run out of memory/)
- Bundler::FriendlyErrors.log_error(error)
+ install_gemfile <<-G, raise_on_error: false, env: { "JRUBY_OPTS" => "-J-Xmx32M" }, artifice: nil
+ source "https://gem.repo1"
+ G
+
+ expect(err).to include("JVM has run out of memory")
end
end
@@ -193,9 +176,9 @@ RSpec.describe Bundler, "friendly errors" do
describe "#request_issue_report_for" do
it "calls relevant methods for Bundler.ui" do
- expect(Bundler.ui).to receive(:info)
- expect(Bundler.ui).to receive(:error)
- expect(Bundler.ui).to receive(:warn)
+ expect(Bundler.ui).not_to receive(:info)
+ expect(Bundler.ui).to receive(:error).exactly(3).times
+ expect(Bundler.ui).not_to receive(:warn)
Bundler::FriendlyErrors.request_issue_report_for(StandardError.new)
end
@@ -214,7 +197,7 @@ RSpec.describe Bundler, "friendly errors" do
it "generates a search URL for the exception message" do
exception = Exception.new("Exception message")
- expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/rubygems/rubygems/search?q=Exception+message&type=Issues")
+ expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/ruby/rubygems/search?q=Exception+message&type=Issues")
end
it "generates a search URL for only the first line of a multi-line exception message" do
@@ -223,7 +206,7 @@ First line of the exception message
Second line of the exception message
END
- expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/rubygems/rubygems/search?q=First+line+of+the+exception+message&type=Issues")
+ expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/ruby/rubygems/search?q=First+line+of+the+exception+message&type=Issues")
end
it "generates the url without colons" do
@@ -232,7 +215,7 @@ Exception ::: with ::: colons :::
END
issues_url = Bundler::FriendlyErrors.issues_url(exception)
expect(issues_url).not_to include("%3A")
- expect(issues_url).to eq("https://github.com/rubygems/rubygems/search?q=#{CGI.escape("Exception with colons ")}&type=Issues")
+ expect(issues_url).to eq("https://github.com/ruby/rubygems/search?q=#{CGI.escape("Exception with colons ")}&type=Issues")
end
it "removes information after - for Errono::EACCES" do
@@ -242,7 +225,7 @@ END
allow(exception).to receive(:is_a?).with(Errno).and_return(true)
issues_url = Bundler::FriendlyErrors.issues_url(exception)
expect(issues_url).not_to include("/Users/foo/bar")
- expect(issues_url).to eq("https://github.com/rubygems/rubygems/search?q=#{CGI.escape("Errno EACCES Permission denied @ dir_s_mkdir ")}&type=Issues")
+ expect(issues_url).to eq("https://github.com/ruby/rubygems/search?q=#{CGI.escape("Errno EACCES Permission denied @ dir_s_mkdir ")}&type=Issues")
end
end
end
diff --git a/spec/bundler/bundler/gem_helper_spec.rb b/spec/bundler/bundler/gem_helper_spec.rb
index b91a2c26cc..b4ae2abdc5 100644
--- a/spec/bundler/bundler/gem_helper_spec.rb
+++ b/spec/bundler/bundler/gem_helper_spec.rb
@@ -9,7 +9,13 @@ 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", "BUNDLE_GEM__CI" => "false"
+ bundle_config_global "gem.mit false"
+ bundle_config_global "gem.test false"
+ bundle_config_global "gem.coc false"
+ bundle_config_global "gem.linter false"
+ bundle_config_global "gem.ci false"
+ bundle_config_global "gem.changelog false"
+ git("config --global init.defaultBranch main")
bundle "gem #{app_name}"
prepare_gemspec(app_gemspec_path)
end
@@ -60,10 +66,20 @@ 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
+
+ def sha512_hexdigest(path)
+ Digest::SHA512.file(path).hexdigest
+ 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
@@ -97,6 +113,7 @@ RSpec.describe Bundler::GemHelper do
context "before installation" do
it "raises an error with appropriate message" do
task_names.each do |name|
+ skip "Rake::FileTask '#{name}' exists" if File.exist?(name)
expect { Rake.application[name] }.
to raise_error(/^Don't know how to build task '#{name}'/)
end
@@ -160,6 +177,48 @@ RSpec.describe Bundler::GemHelper do
end
end
+ describe "#build_checksum" do
+ it "calculates SHA512 of the content" do
+ FileUtils.mkdir_p(app_gem_dir)
+ File.write(app_gem_path, "")
+ mock_checksum_message app_name, app_version
+ subject.build_checksum(app_gem_path)
+ expect(File.read(app_sha_path).chomp).to eql(Digest::SHA512.hexdigest(""))
+ end
+
+ 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
+ expect(File.read(app_sha_path).chomp).to eql(sha512_hexdigest(app_gem_path))
+ 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
+ expect(File.read(app_sha_path).chomp).to eql(sha512_hexdigest(app_gem_path))
+ 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
+ expect(File.read(app_sha_path).chomp).to eql(sha512_hexdigest(app_gem_path))
+ end
+ end
+ end
+
describe "#install_gem" do
context "when installation was successful" do
it "gem is installed" do
@@ -167,7 +226,7 @@ RSpec.describe Bundler::GemHelper do
mock_confirm_message "#{app_name} (#{app_version}) installed."
subject.install_gem(nil, :local)
expect(app_gem_path).to exist
- gem_command :list
+ installed_gems_list
expect(out).to include("#{app_name} (#{app_version})")
end
end
@@ -180,7 +239,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
@@ -198,11 +257,11 @@ RSpec.describe Bundler::GemHelper do
end
before do
- sys_exec("git init", :dir => app_path)
- sys_exec("git config user.email \"you@example.com\"", :dir => app_path)
- sys_exec("git config user.name \"name\"", :dir => app_path)
- sys_exec("git config commit.gpgsign false", :dir => app_path)
- sys_exec("git config push.default simple", :dir => app_path)
+ git("init", app_path)
+ git("config user.email \"you@example.com\"", app_path)
+ git("config user.name \"name\"", app_path)
+ git("config commit.gpgsign false", app_path)
+ git("config push.default simple", app_path)
# silence messages
allow(Bundler.ui).to receive(:confirm)
@@ -216,32 +275,32 @@ RSpec.describe Bundler::GemHelper do
end
it "when there are uncommitted files" do
- sys_exec("git add .", :dir => app_path)
+ git("add .", app_path)
expect { Rake.application["release"].invoke }.
to raise_error("There are files that need to be committed first.")
end
it "when there is no git remote" do
- sys_exec("git commit -a -m \"initial commit\"", :dir => app_path)
+ git("commit -a -m \"initial commit\"", app_path)
expect { Rake.application["release"].invoke }.to raise_error(RuntimeError)
end
end
context "succeeds" do
- let(:repo) { build_git("foo", :bare => true) }
+ let(:repo) { build_git("foo", bare: true) }
before do
- sys_exec("git remote add origin #{file_uri_for(repo.path)}", :dir => app_path)
- sys_exec('git commit -a -m "initial commit"', :dir => app_path)
+ git("remote add origin #{repo.path}", app_path)
+ git('commit -a -m "initial commit"', app_path)
end
context "on releasing" do
before do
mock_build_message app_name, app_version
mock_confirm_message "Tagged v#{app_version}."
- mock_confirm_message "Pushed git commits and tags."
+ mock_confirm_message "Pushed git commits and release tag."
- sys_exec("git push -u origin master", :dir => app_path)
+ git("push -u origin main", app_path)
end
it "calls rubygem_push with proper arguments" do
@@ -256,15 +315,33 @@ RSpec.describe Bundler::GemHelper do
Rake.application["release"].invoke
end
+
+ it "also works when releasing from an ambiguous reference" do
+ # Create a branch with the same name as the tag
+ git("checkout -b v#{app_version}", app_path)
+ git("push -u origin v#{app_version}", app_path)
+
+ expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
+
+ Rake.application["release"].invoke
+ end
+
+ it "also works with releasing from a branch not yet pushed" do
+ git("checkout -b module_function", app_path)
+
+ expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
+
+ Rake.application["release"].invoke
+ end
end
context "on releasing with a custom tag prefix" do
before do
Bundler::GemHelper.tag_prefix = "foo-"
mock_build_message app_name, app_version
- mock_confirm_message "Pushed git commits and tags."
+ mock_confirm_message "Pushed git commits and release tag."
- sys_exec("git push -u origin master", :dir => app_path)
+ git("push -u origin main", app_path)
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
end
@@ -280,7 +357,7 @@ RSpec.describe Bundler::GemHelper do
mock_confirm_message "Tag v#{app_version} has already been created."
expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s)
- sys_exec("git tag -a -m \"Version #{app_version}\" v#{app_version}", :dir => app_path)
+ git("tag -a -m \"Version #{app_version}\" v#{app_version}", app_path)
Rake.application["release"].invoke
end
@@ -301,10 +378,10 @@ RSpec.describe Bundler::GemHelper do
end
before do
- sys_exec("git init", :dir => app_path)
- sys_exec("git config user.email \"you@example.com\"", :dir => app_path)
- sys_exec("git config user.name \"name\"", :dir => app_path)
- sys_exec("git config push.gpgsign simple", :dir => app_path)
+ git("init", app_path)
+ git("config user.email \"you@example.com\"", app_path)
+ git("config user.name \"name\"", app_path)
+ git("config push.gpgsign simple", app_path)
# silence messages
allow(Bundler.ui).to receive(:confirm)
@@ -313,6 +390,7 @@ RSpec.describe Bundler::GemHelper do
credentials = double("credentials", "file?" => true)
allow(Bundler.user_home).to receive(:join).
with(".gem/credentials").and_return(credentials)
+ allow(Bundler.user_home).to receive(:join).and_call_original
end
describe "success messaging" do
diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb
index 01e0232fba..0e1b7c9cc8 100644
--- a/spec/bundler/bundler/gem_version_promoter_spec.rb
+++ b/spec/bundler/bundler/gem_version_promoter_spec.rb
@@ -1,178 +1,162 @@
# frozen_string_literal: true
RSpec.describe Bundler::GemVersionPromoter do
- context "conservative resolver" do
- def versions(result)
- result.flatten.map(&:version).map(&:to_s)
+ let(:gvp) { described_class.new }
+
+ # Rightmost (highest array index) in result is most preferred.
+ # Leftmost (lowest array index) in result is least preferred.
+ # `build_candidates` has all versions of gem in index.
+ # `build_spec` is the version currently in the .lock file.
+ #
+ # In default (not strict) mode, all versions in the index will
+ # be returned, allowing Bundler the best chance to resolve all
+ # dependencies, but sometimes resulting in upgrades that some
+ # would not consider conservative.
+
+ describe "#sort_versions" do
+ def build_candidates(versions)
+ versions.map do |v|
+ Bundler::Resolver::Candidate.new(v)
+ end
end
- def make_instance(*args)
- @gvp = Bundler::GemVersionPromoter.new(*args).tap do |gvp|
- gvp.class.class_eval { public :filter_dep_specs, :sort_dep_specs }
- end
+ def build_package(name, version, unlock)
+ Bundler::Resolver::Package.new(name, [], locked_specs: Bundler::SpecSet.new(build_spec(name, version)), unlock: unlock)
end
- def unlocking(options)
- make_instance(Bundler::SpecSet.new([]), ["foo"]).tap do |p|
- p.level = options[:level] if options[:level]
- p.strict = options[:strict] if options[:strict]
- end
+ def sorted_versions(candidates:, current:, unlock: true)
+ gvp.sort_versions(
+ build_package("foo", current, unlock),
+ build_candidates(candidates)
+ ).flatten.map(&:version).map(&:to_s)
end
- def keep_locked(options)
- make_instance(Bundler::SpecSet.new([]), ["bar"]).tap do |p|
- p.level = options[:level] if options[:level]
- p.strict = options[:strict] if options[:strict]
- end
+ it "numerically sorts versions" do
+ versions = sorted_versions(candidates: %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0], current: "1.7.8")
+ expect(versions).to eq %w[1.8.0 1.7.15 1.7.9 1.7.8 1.7.7]
end
- def build_spec_groups(name, versions)
- versions.map do |v|
- Bundler::Resolver::SpecGroup.new(build_spec(name, v))
+ context "with no options" do
+ it "defaults to level=:major, strict=false, pre=false" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[2.1.0 2.0.1 1.0.0 0.9.0 0.3.1 0.3.0 0.2.0]
end
end
- # Rightmost (highest array index) in result is most preferred.
- # Leftmost (lowest array index) in result is least preferred.
- # `build_spec_groups` has all versions of gem in index.
- # `build_spec` is the version currently in the .lock file.
- #
- # In default (not strict) mode, all versions in the index will
- # be returned, allowing Bundler the best chance to resolve all
- # dependencies, but sometimes resulting in upgrades that some
- # would not consider conservative.
- context "filter specs (strict) level patch" do
- it "when keeping build_spec, keep current, next release" do
- keep_locked(:level => :patch)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[1.7.8 1.7.9 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.9 1.7.8]
- end
+ context "when strict" do
+ before { gvp.strict = true }
- it "when unlocking prefer next release first" do
- unlocking(:level => :patch)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[1.7.8 1.7.9 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.8 1.7.9]
- end
+ context "when level is major" do
+ before { gvp.level = :major }
- it "when unlocking keep current when already at latest release" do
- unlocking(:level => :patch)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[1.7.9 1.8.0 2.0.0]),
- build_spec("foo", "1.7.9").first
- )
- expect(versions(res)).to eq %w[1.7.9]
+ it "keeps downgrades" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[2.1.0 2.0.1 1.0.0 0.9.0 0.3.1 0.3.0 0.2.0]
+ end
end
- end
- context "filter specs (strict) level minor" do
- it "when unlocking favor next releases, remove minor and major increases" do
- unlocking(:level => :minor)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
- build_spec("foo", "0.2.0").first
- )
- expect(versions(res)).to eq %w[0.2.0 0.3.0 0.3.1 0.9.0]
+ context "when level is minor" do
+ before { gvp.level = :minor }
+
+ it "sorts highest minor within same major in first position" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[0.9.0 0.3.1 0.3.0 1.0.0 2.1.0 2.0.1 0.2.0]
+ end
end
- it "when keep locked, keep current, then favor next release, remove minor and major increases" do
- keep_locked(:level => :minor)
- res = @gvp.filter_dep_specs(
- build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
- build_spec("foo", "0.2.0").first
- )
- expect(versions(res)).to eq %w[0.3.0 0.3.1 0.9.0 0.2.0]
+ context "when level is patch" do
+ before { gvp.level = :patch }
+
+ it "sorts highest patch within same minor in first position" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[0.3.1 0.3.0 0.9.0 1.0.0 2.0.1 2.1.0 0.2.0]
+ end
end
end
- context "sort specs (not strict) level patch" do
- it "when not unlocking, same order but make sure build_spec version is most preferred to stay put" do
- keep_locked(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.5.4 1.6.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 2.0.0 2.0.1]),
- build_spec("foo", "1.7.7").first
- )
- expect(versions(res)).to eq %w[1.5.4 1.6.5 1.7.6 2.0.0 2.0.1 1.8.0 1.8.1 1.7.8 1.7.9 1.7.7]
- end
+ context "when not strict" do
+ before { gvp.strict = false }
- it "when unlocking favor next release, then current over minor increase" do
- unlocking(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.7.7 1.7.8 1.7.9 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.7 1.8.0 1.7.8 1.7.9]
+ context "when level is major" do
+ before { gvp.level = :major }
+
+ it "orders by version" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[2.1.0 2.0.1 1.0.0 0.9.0 0.3.1 0.3.0 0.2.0]
+ end
end
- it "when unlocking do proper integer comparison, not string" do
- unlocking(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.7.7 1.7.8 1.7.9 1.7.15 1.8.0]),
- build_spec("foo", "1.7.8").first
- )
- expect(versions(res)).to eq %w[1.7.7 1.8.0 1.7.8 1.7.9 1.7.15]
+ context "when level is minor" do
+ before { gvp.level = :minor }
+
+ it "favors minor upgrades, then patch upgrades, then major upgrades, then downgrades" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[0.9.0 0.3.1 0.3.0 1.0.0 2.1.0 2.0.1 0.2.0]
+ end
end
- it "leave current when unlocking but already at latest release" do
- unlocking(:level => :patch)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[1.7.9 1.8.0 2.0.0]),
- build_spec("foo", "1.7.9").first
- )
- expect(versions(res)).to eq %w[2.0.0 1.8.0 1.7.9]
+ context "when level is patch" do
+ before { gvp.level = :patch }
+
+ it "favors patch upgrades, then minor upgrades, then major upgrades, then downgrades" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.1 2.1.0], current: "0.3.0")
+ expect(versions).to eq %w[0.3.1 0.3.0 0.9.0 1.0.0 2.0.1 2.1.0 0.2.0]
+ end
end
end
- context "sort specs (not strict) level minor" do
- it "when unlocking favor next release, then minor increase over current" do
- unlocking(:level => :minor)
- res = @gvp.sort_dep_specs(
- build_spec_groups("foo", %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.0.0 2.0.1]),
- build_spec("foo", "0.2.0").first
- )
- expect(versions(res)).to eq %w[2.0.0 2.0.1 1.0.0 0.2.0 0.3.0 0.3.1 0.9.0]
+ context "when pre" do
+ before { gvp.pre = true }
+
+ it "sorts regardless of prerelease status" do
+ versions = sorted_versions(candidates: %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0], current: "1.8.0")
+ expect(versions).to eq %w[2.0.0 2.0.0.pre 1.8.1 1.8.1.pre 1.8.0 1.7.7.pre]
end
end
- context "level error handling" do
- subject { Bundler::GemVersionPromoter.new }
+ context "when not pre" do
+ before { gvp.pre = false }
- it "should raise if not major, minor or patch is passed" do
- expect { subject.level = :minjor }.to raise_error ArgumentError
+ it "deprioritizes prerelease gems" do
+ versions = sorted_versions(candidates: %w[1.7.7.pre 1.8.0 1.8.1.pre 1.8.1 2.0.0.pre 2.0.0], current: "1.8.0")
+ expect(versions).to eq %w[2.0.0 1.8.1 1.8.0 2.0.0.pre 1.8.1.pre 1.7.7.pre]
end
+ end
- it "should raise if invalid classes passed" do
- [123, nil].each do |value|
- expect { subject.level = value }.to raise_error ArgumentError
- end
+ context "when locking and not major" do
+ before { gvp.level = :minor }
+
+ it "keeps the current version first" do
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.1.0 2.0.1], current: "0.3.0", unlock: [])
+ expect(versions.first).to eq("0.3.0")
end
+ end
+ end
- it "should accept major, minor patch symbols" do
- [:major, :minor, :patch].each do |value|
- subject.level = value
- expect(subject.level).to eq value
- end
+ describe "#level=" do
+ subject { described_class.new }
+
+ it "should raise if not major, minor or patch is passed" do
+ expect { subject.level = :minjor }.to raise_error ArgumentError
+ end
+
+ it "should raise if invalid classes passed" do
+ [123, nil].each do |value|
+ expect { subject.level = value }.to raise_error ArgumentError
end
+ end
- it "should accept major, minor patch strings" do
- %w[major minor patch].each do |value|
- subject.level = value
- expect(subject.level).to eq value.to_sym
- end
+ it "should accept major, minor patch symbols" do
+ [:major, :minor, :patch].each do |value|
+ subject.level = value
+ expect(subject.level).to eq value
end
end
- context "debug output" do
- it "should not kerblooie on its own debug output" do
- gvp = unlocking(:level => :patch)
- dep = Bundler::DepProxy.new(dep("foo", "1.2.0").first, "ruby")
- result = gvp.send(:debug_format_result, dep, build_spec_groups("foo", %w[1.2.0 1.3.0]))
- expect(result.class).to eq Array
+ it "should accept major, minor patch strings" do
+ %w[major minor patch].each do |value|
+ subject.level = value
+ expect(subject.level).to eq value.to_sym
end
end
end
diff --git a/spec/bundler/bundler/installer/gem_installer_spec.rb b/spec/bundler/bundler/installer/gem_installer_spec.rb
index 8f8d1c6d15..dbd4e1d2c8 100644
--- a/spec/bundler/bundler/installer/gem_installer_spec.rb
+++ b/spec/bundler/bundler/installer/gem_installer_spec.rb
@@ -3,37 +3,44 @@
require "bundler/installer/gem_installer"
RSpec.describe Bundler::GemInstaller do
- let(:installer) { instance_double("Installer") }
+ let(:definition) { instance_double("Definition", locked_gems: nil) }
+ let(:installer) { instance_double("Installer", definition: definition) }
let(:spec_source) { instance_double("SpecSource") }
- let(:spec) { instance_double("Specification", :name => "dummy", :version => "0.0.1", :loaded_from => "dummy", :source => spec_source) }
+ let(:spec) { instance_double("Specification", name: "dummy", version: "0.0.1", loaded_from: "dummy", source: spec_source) }
+ let(:base_options) { { force: false, local: false, previous_spec: nil } }
subject { described_class.new(spec, installer) }
context "spec_settings is nil" do
it "invokes install method with empty build_args" do
- allow(spec_source).to receive(:install).with(spec, :force => false, :ensure_builtin_gems_cached => false, :build_args => [])
+ allow(spec_source).to receive(:install).with(
+ spec,
+ base_options.merge(build_args: [])
+ )
subject.install_from_spec
end
end
context "spec_settings is build option" do
it "invokes install method with build_args" do
- allow(Bundler.settings).to receive(:[]).with(:bin)
- allow(Bundler.settings).to receive(:[]).with(:inline)
- allow(Bundler.settings).to receive(:[]).with(:forget_cli_options)
+ allow(Bundler.settings).to receive(:[])
allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy")
- expect(spec_source).to receive(:install).with(spec, :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy"])
+ expect(spec_source).to receive(:install).with(
+ spec,
+ base_options.merge(build_args: ["--with-dummy-config=dummy"])
+ )
subject.install_from_spec
end
end
context "spec_settings is build option with spaces" do
it "invokes install method with build_args" do
- allow(Bundler.settings).to receive(:[]).with(:bin)
- allow(Bundler.settings).to receive(:[]).with(:inline)
- allow(Bundler.settings).to receive(:[]).with(:forget_cli_options)
+ allow(Bundler.settings).to receive(:[])
allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy --with-another-dummy-config")
- expect(spec_source).to receive(:install).with(spec, :force => false, :ensure_builtin_gems_cached => false, :build_args => ["--with-dummy-config=dummy", "--with-another-dummy-config"])
+ expect(spec_source).to receive(:install).with(
+ spec,
+ base_options.merge(build_args: ["--with-dummy-config=dummy", "--with-another-dummy-config"])
+ )
subject.install_from_spec
end
end
diff --git a/spec/bundler/bundler/installer/parallel_installer_spec.rb b/spec/bundler/bundler/installer/parallel_installer_spec.rb
index ace5c1a23a..49bcb5310b 100644
--- a/spec/bundler/bundler/installer/parallel_installer_spec.rb
+++ b/spec/bundler/bundler/installer/parallel_installer_spec.rb
@@ -1,47 +1,79 @@
# frozen_string_literal: true
require "bundler/installer/parallel_installer"
+require "bundler/rubygems_gem_installer"
+require "rubygems/remote_fetcher"
+require "bundler"
RSpec.describe Bundler::ParallelInstaller do
- let(:installer) { instance_double("Installer") }
- let(:all_specs) { [] }
- let(:size) { 1 }
- let(:standalone) { false }
- let(:force) { false }
-
- subject { described_class.new(installer, all_specs, size, standalone, force) }
-
- context "when dependencies that are not on the overall installation list are the only ones not installed" do
- let(:all_specs) do
- [
- build_spec("alpha", "1.0") {|s| s.runtime "a", "1" },
- ].flatten
+ describe "priority queue" do
+ before do
+ require "support/artifice/compact_index"
+
+ @previous_client = Gem::Request::ConnectionPools.client
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+ Gem::RemoteFetcher.fetcher.close_all
+
+ build_repo2 do
+ build_gem "gem_with_extension", &:add_c_extension
+ build_gem "gem_without_extension"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "gem_with_extension"
+ gem "gem_without_extension"
+ G
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ gem_with_extension (1.0)
+ gem_without_extension (1.0)
+
+ DEPENDENCIES
+ gem_with_extension
+ gem_without_extension
+ L
+
+ @old_ui = Bundler.ui
+ Bundler.ui = Bundler::UI::Silent.new
end
- it "prints a warning" do
- expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
-Your lockfile was created by an old Bundler that left some things out.
-You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile.
-The missing gems are:
-* a depended upon by alpha
- W
- subject.check_for_corrupt_lockfile
+ after do
+ Bundler.ui = @old_ui
+ Gem::Request::ConnectionPools.client = @previous_client
+ Artifice.deactivate
end
- context "when size > 1" do
- let(:size) { 500 }
-
- it "prints a warning and sets size to 1" do
- expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
-Your lockfile was created by an old Bundler that left some things out.
-Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing 500 at a time.
-You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile.
-The missing gems are:
-* a depended upon by alpha
- W
- subject.check_for_corrupt_lockfile
- expect(subject.size).to eq(1)
+ let(:definition) do
+ allow(Bundler).to receive(:root) { bundled_app }
+
+ definition = Bundler::Definition.build(bundled_app.join("Gemfile"), bundled_app.join("Gemfile.lock"), false)
+ definition.tap(&:setup_domain!)
+ end
+ let(:installer) { Bundler::Installer.new(bundled_app, definition) }
+
+ it "queues native extensions in priority" do
+ parallel_installer = Bundler::ParallelInstaller.new(installer, definition.specs, 2, false, true)
+ worker_pool = parallel_installer.send(:worker_pool)
+ expected = 6 # Enqueue to download bundler and the 2 gems. Enqueue to install Bundler and the 2 gems.
+
+ expect(worker_pool).to receive(:enq).exactly(expected).times.and_wrap_original do |original_enq, spec, opts|
+ unless opts.nil? # Enqueued for download, no priority
+ if spec.name == "gem_with_extension"
+ expect(opts).to eq({ priority: true })
+ else
+ expect(opts).to eq({ priority: false })
+ end
+ end
+
+ opts ||= {}
+ original_enq.call(spec, **opts)
end
+
+ parallel_installer.call
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..57868766d9 100644
--- a/spec/bundler/bundler/installer/spec_installation_spec.rb
+++ b/spec/bundler/bundler/installer/spec_installation_spec.rb
@@ -3,14 +3,17 @@
require "bundler/installer/parallel_installer"
RSpec.describe Bundler::ParallelInstaller::SpecInstallation do
- let!(:dep) do
- a_spec = Object.new
- def a_spec.name
- "I like tests"
- end
- a_spec
+ def build_spec(name, extensions: [])
+ spec = Object.new
+ spec.define_singleton_method(:name) { name }
+ spec.define_singleton_method(:full_name) { "#{name}-1.0" }
+ spec.define_singleton_method(:extensions) { extensions }
+ spec.define_singleton_method(:dependencies) { [] }
+ spec
end
+ let!(:dep) { build_spec("I like tests") }
+
describe "#ready_to_enqueue?" do
context "when in enqueued state" do
it "is falsey" do
@@ -35,27 +38,51 @@ RSpec.describe Bundler::ParallelInstaller::SpecInstallation do
end
describe "#dependencies_installed?" do
- context "when all dependencies are installed" do
- it "returns true" do
- dependencies = []
- dependencies << instance_double("SpecInstallation", :spec => "alpha", :name => "alpha", :installed? => true, :all_dependencies => [], :type => :production)
- dependencies << instance_double("SpecInstallation", :spec => "beta", :name => "beta", :installed? => true, :all_dependencies => [], :type => :production)
- all_specs = dependencies + [instance_double("SpecInstallation", :spec => "gamma", :name => "gamma", :installed? => false, :all_dependencies => [], :type => :production)]
+ it "returns true when all dependencies are installed" do
+ alpha = described_class.new(build_spec("alpha"))
+ alpha.dependencies = []
+
+ beta = described_class.new(build_spec("beta"))
+ beta.dependencies = [alpha]
+
+ gamma = described_class.new(build_spec("gamma"))
+ gamma.dependencies = [beta]
+
+ expect(gamma.dependencies_installed?({})).to be_falsey
+ expect(gamma.dependencies_installed?({ "beta" => true })).to be_falsey
+ expect(gamma.dependencies_installed?({ "alpha" => true, "beta" => true })).to be_truthy
+ end
+ end
+
+ describe "#ready_to_install?" do
+ context "when spec has no extensions" do
+ it "returns true regardless of dependencies" do
+ beta = described_class.new(build_spec("beta"))
+ beta.dependencies = []
+
spec = described_class.new(dep)
- allow(spec).to receive(:all_dependencies).and_return(dependencies)
- expect(spec.dependencies_installed?(all_specs)).to be_truthy
+ spec.state = :downloaded
+ spec.dependencies = [beta]
+
+ expect(spec.ready_to_install?({})).to be_truthy
end
end
- context "when all dependencies are not installed" do
- it "returns false" do
- dependencies = []
- dependencies << instance_double("SpecInstallation", :spec => "alpha", :name => "alpha", :installed? => false, :all_dependencies => [], :type => :production)
- dependencies << instance_double("SpecInstallation", :spec => "beta", :name => "beta", :installed? => true, :all_dependencies => [], :type => :production)
- all_specs = dependencies + [instance_double("SpecInstallation", :spec => "gamma", :name => "gamma", :installed? => false, :all_dependencies => [], :type => :production)]
- spec = described_class.new(dep)
- allow(spec).to receive(:all_dependencies).and_return(dependencies)
- expect(spec.dependencies_installed?(all_specs)).to be_falsey
+ context "when spec has extensions" do
+ it "returns true when all dependencies are installed" do
+ alpha = described_class.new(build_spec("alpha"))
+ alpha.dependencies = []
+
+ beta = described_class.new(build_spec("beta"))
+ beta.dependencies = [alpha]
+
+ gamma = described_class.new(build_spec("gamma", extensions: ["ext/Rakefile"]))
+ gamma.state = :downloaded
+ gamma.dependencies = [beta]
+
+ expect(gamma.ready_to_install?({})).to be_falsey
+ expect(gamma.ready_to_install?({ "beta" => true })).to be_falsey
+ expect(gamma.ready_to_install?({ "alpha" => true, "beta" => true })).to be_truthy
end
end
end
diff --git a/spec/bundler/bundler/lockfile_parser_spec.rb b/spec/bundler/bundler/lockfile_parser_spec.rb
index 3a6d61336f..7364ab98e5 100644
--- a/spec/bundler/bundler/lockfile_parser_spec.rb
+++ b/spec/bundler/bundler/lockfile_parser_spec.rb
@@ -3,7 +3,7 @@
require "bundler/lockfile_parser"
RSpec.describe Bundler::LockfileParser do
- let(:lockfile_contents) { strip_whitespace(<<-L) }
+ let(:lockfile_contents) { <<~L }
GIT
remote: https://github.com/alloy/peiji-san.git
revision: eca485d8dc95f12aaec1a434b49d295c7e91844b
@@ -22,6 +22,9 @@ RSpec.describe Bundler::LockfileParser do
peiji-san!
rake
+ CHECKSUMS
+ rake (10.3.2) sha256=814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8
+
RUBY VERSION
ruby 2.1.3p242
@@ -33,13 +36,13 @@ RSpec.describe Bundler::LockfileParser do
it "returns the attributes" do
attributes = described_class.sections_in_lockfile(lockfile_contents)
expect(attributes).to contain_exactly(
- "BUNDLED WITH", "DEPENDENCIES", "GEM", "GIT", "PLATFORMS", "RUBY VERSION"
+ "BUNDLED WITH", "CHECKSUMS", "DEPENDENCIES", "GEM", "GIT", "PLATFORMS", "RUBY VERSION"
)
end
end
describe ".unknown_sections_in_lockfile" do
- let(:lockfile_contents) { strip_whitespace(<<-L) }
+ let(:lockfile_contents) { <<~L }
UNKNOWN ATTR
UNKNOWN ATTR 2
@@ -60,7 +63,7 @@ RSpec.describe Bundler::LockfileParser do
it "returns the same as > 1.0" do
expect(subject).to contain_exactly(
- described_class::BUNDLED, described_class::RUBY, described_class::PLUGIN
+ described_class::BUNDLED, described_class::CHECKSUMS, described_class::RUBY, described_class::PLUGIN
)
end
end
@@ -70,7 +73,7 @@ RSpec.describe Bundler::LockfileParser do
it "returns the same as for the release version" do
expect(subject).to contain_exactly(
- described_class::RUBY, described_class::PLUGIN
+ described_class::CHECKSUMS, described_class::RUBY, described_class::PLUGIN
)
end
end
@@ -108,16 +111,25 @@ RSpec.describe Bundler::LockfileParser do
end
let(:specs) do
[
- Bundler::LazySpecification.new("peiji-san", v("1.2.0"), rb),
- Bundler::LazySpecification.new("rake", v("10.3.2"), rb),
+ Bundler::LazySpecification.new("peiji-san", v("1.2.0"), Gem::Platform::RUBY),
+ Bundler::LazySpecification.new("rake", v("10.3.2"), Gem::Platform::RUBY),
]
end
- let(:platforms) { [rb] }
+ let(:platforms) { [Gem::Platform::RUBY] }
let(:bundler_version) { Gem::Version.new("1.12.0.rc.2") }
let(:ruby_version) { "ruby 2.1.3p242" }
+ let(:lockfile_path) { Bundler.default_lockfile.relative_path_from(Dir.pwd) }
+ let(:rake_sha256_checksum) do
+ Bundler::Checksum.from_lock(
+ "sha256=814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8",
+ "#{lockfile_path}:20:17"
+ )
+ end
+ let(:rake_checksums) { [rake_sha256_checksum] }
shared_examples_for "parsing" do
it "parses correctly" do
+ expect(subject.valid?).to be(true)
expect(subject.sources).to eq sources
expect(subject.dependencies).to eq dependencies
expect(subject.specs).to eq specs
@@ -125,6 +137,9 @@ RSpec.describe Bundler::LockfileParser do
expect(subject.platforms).to eq platforms
expect(subject.bundler_version).to eq bundler_version
expect(subject.ruby_version).to eq ruby_version
+ rake_spec = specs.last
+ checksums = subject.sources.last.checksum_store.to_lock(specs.last)
+ expect(checksums).to eq("#{rake_spec.lock_name} #{rake_checksums.map(&:to_lock).sort.join(",")}")
end
end
@@ -149,5 +164,140 @@ RSpec.describe Bundler::LockfileParser do
let(:lockfile_contents) { super().sub("peiji-san!", "peiji-san!\n foo: bar") }
include_examples "parsing"
end
+
+ context "when the checksum is urlsafe base64 encoded" do
+ let(:lockfile_contents) do
+ super().sub(
+ "sha256=814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8",
+ "sha256=gUgow08TFdfnt-gpUYRXfMTpabrWFWrAadAtY_WNgug="
+ )
+ end
+ include_examples "parsing"
+ end
+
+ context "when the checksum is of an unknown algorithm" do
+ let(:rake_sha512_checksum) do
+ Bundler::Checksum.from_lock(
+ "sha512=pVDn9GLmcFkz8vj1ueiVxj5uGKkAyaqYjEX8zG6L5O4BeVg3wANaKbQdpj/B82Nd/MHVszy6polHcyotUdwilQ==",
+ "#{lockfile_path}:20:17"
+ )
+ end
+ let(:lockfile_contents) do
+ super().sub(
+ "sha256=",
+ "sha512=pVDn9GLmcFkz8vj1ueiVxj5uGKkAyaqYjEX8zG6L5O4BeVg3wANaKbQdpj/B82Nd/MHVszy6polHcyotUdwilQ==,sha256="
+ )
+ end
+ let(:rake_checksums) { [rake_sha256_checksum, rake_sha512_checksum] }
+ include_examples "parsing"
+ end
+
+ context "when the content does not contain any recognized lockfile sections" do
+ let(:lockfile_contents) { "hello world\nlorem ipsum\n" }
+
+ it "does not raise, is not valid, and deprecates" do
+ expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with(
+ /does not appear to be a valid lockfile.*future version of Bundler/m
+ )
+ parser = described_class.new(lockfile_contents)
+ expect(parser.valid?).to be(false)
+ expect(parser.specs).to eq([])
+ expect(parser.dependencies).to eq({})
+ end
+
+ it "does not raise when strict: true, and still deprecates" do
+ expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with(
+ /does not appear to be a valid lockfile.*future version of Bundler/m
+ )
+ parser = described_class.new(lockfile_contents, strict: true)
+ expect(parser.valid?).to be(false)
+ expect(parser.specs).to eq([])
+ expect(parser.dependencies).to eq({})
+ end
+ end
+
+ context "when the content looks like a Gemfile DSL" do
+ let(:lockfile_contents) { <<~G }
+ source "https://rubygems.org"
+ gem "rake"
+ G
+
+ it "does not raise, is not valid, and deprecates" do
+ expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with(
+ /does not appear to be a valid lockfile.*future version of Bundler/m
+ )
+ parser = described_class.new(lockfile_contents)
+ expect(parser.valid?).to be(false)
+ expect(parser.specs).to eq([])
+ expect(parser.dependencies).to eq({})
+ end
+
+ it "does not raise when strict: true, and still deprecates" do
+ expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with(
+ /does not appear to be a valid lockfile.*future version of Bundler/m
+ )
+ parser = described_class.new(lockfile_contents, strict: true)
+ expect(parser.valid?).to be(false)
+ expect(parser.specs).to eq([])
+ expect(parser.dependencies).to eq({})
+ end
+ end
+
+ context "when the content is empty" do
+ let(:lockfile_contents) { "" }
+
+ it "does not raise and is valid" do
+ expect { subject }.not_to raise_error
+ expect(subject.valid?).to be(true)
+ end
+ end
+
+ context "when lockfile_path is given" do
+ it "uses the provided path in error messages instead of looking up Bundler.default_lockfile" do
+ expect(Bundler::SharedHelpers).not_to receive(:relative_lockfile_path)
+ parser = described_class.new(lockfile_contents, lockfile_path: "custom/path.lock")
+ expect(parser.valid?).to be(true)
+ rake_spec = parser.specs.last
+ checksums = parser.sources.last.checksum_store.to_lock(rake_spec)
+ expected_checksum = Bundler::Checksum.from_lock(
+ "sha256=814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8",
+ "custom/path.lock:20:17"
+ )
+ expect(checksums).to eq("#{rake_spec.lock_name} #{expected_checksum.to_lock}")
+ end
+
+ it "raises with the provided path when the lockfile contains merge conflicts" do
+ expect do
+ described_class.new("<<<<<<<\n", lockfile_path: "custom/path.lock")
+ end.to raise_error(Bundler::LockfileError, %r{custom/path\.lock contains merge conflicts})
+ end
+ end
+
+ context "when CHECKSUMS has duplicate checksums in the lockfile that don't match" do
+ let(:bad_checksum) { "sha256=c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11" }
+ let(:lockfile_contents) { super().split(/(?<=CHECKSUMS\n)/m).insert(1, " rake (10.3.2) #{bad_checksum}\n").join }
+
+ it "raises a security error" do
+ expect { subject }.to raise_error(Bundler::SecurityError) do |e|
+ expect(e.message).to match <<~MESSAGE
+ Bundler found mismatched checksums. This is a potential security risk.
+ rake (10.3.2) #{bad_checksum}
+ from the lockfile CHECKSUMS at #{lockfile_path}:20:17
+ rake (10.3.2) #{rake_sha256_checksum.to_lock}
+ from the lockfile CHECKSUMS at #{lockfile_path}:21:17
+
+ To resolve this issue you can either:
+ 1. remove the matching checksum in #{lockfile_path}:21:17
+ 2. run `bundle install`
+ or if you are sure that the new checksum from the lockfile CHECKSUMS at #{lockfile_path}:21:17 is correct:
+ 1. remove the matching checksum in #{lockfile_path}:20:17
+ 2. run `bundle install`
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ MESSAGE
+ end
+ end
+ end
end
end
diff --git a/spec/bundler/bundler/mirror_spec.rb b/spec/bundler/bundler/mirror_spec.rb
index 1eaf1e9a8e..ba1c6ed413 100644
--- a/spec/bundler/bundler/mirror_spec.rb
+++ b/spec/bundler/bundler/mirror_spec.rb
@@ -36,12 +36,12 @@ RSpec.describe Bundler::Settings::Mirror do
it "takes a string for the uri but returns an uri object" do
mirror.uri = "http://localhost:9292"
- expect(mirror.uri).to eq(Bundler::URI("http://localhost:9292"))
+ expect(mirror.uri).to eq(Gem::URI("http://localhost:9292"))
end
it "takes an uri object for the uri" do
- mirror.uri = Bundler::URI("http://localhost:9293")
- expect(mirror.uri).to eq(Bundler::URI("http://localhost:9293"))
+ mirror.uri = Gem::URI("http://localhost:9293")
+ expect(mirror.uri).to eq(Gem::URI("http://localhost:9293"))
end
context "without a uri" do
@@ -145,7 +145,7 @@ RSpec.describe Bundler::Settings::Mirror do
end
RSpec.describe Bundler::Settings::Mirrors do
- let(:localhost_uri) { Bundler::URI("http://localhost:9292") }
+ let(:localhost_uri) { Gem::URI("http://localhost:9292") }
context "with a just created mirror" do
let(:mirrors) do
@@ -260,7 +260,7 @@ RSpec.describe Bundler::Settings::Mirrors do
before { mirrors.parse("mirror.all.fallback_timeout", "true") }
it "returns the source uri, not localhost" do
- expect(mirrors.for("http://whatever.com").uri).to eq(Bundler::URI("http://whatever.com/"))
+ expect(mirrors.for("http://whatever.com").uri).to eq(Gem::URI("http://whatever.com/"))
end
end
end
@@ -270,7 +270,7 @@ RSpec.describe Bundler::Settings::Mirrors do
context "without a fallback timeout" do
it "returns the uri that is not mirrored" do
- expect(mirrors.for("http://whatever.com").uri).to eq(Bundler::URI("http://whatever.com/"))
+ expect(mirrors.for("http://whatever.com").uri).to eq(Gem::URI("http://whatever.com/"))
end
it "returns localhost for rubygems.org" do
@@ -282,11 +282,11 @@ RSpec.describe Bundler::Settings::Mirrors do
before { mirrors.parse("mirror.http://rubygems.org/.fallback_timeout", "true") }
it "returns the uri that is not mirrored" do
- expect(mirrors.for("http://whatever.com").uri).to eq(Bundler::URI("http://whatever.com/"))
+ expect(mirrors.for("http://whatever.com").uri).to eq(Gem::URI("http://whatever.com/"))
end
it "returns rubygems.org for rubygems.org" do
- expect(mirrors.for("http://rubygems.org/").uri).to eq(Bundler::URI("http://rubygems.org/"))
+ expect(mirrors.for("http://rubygems.org/").uri).to eq(Gem::URI("http://rubygems.org/"))
end
end
end
diff --git a/spec/bundler/bundler/override_spec.rb b/spec/bundler/bundler/override_spec.rb
new file mode 100644
index 0000000000..ad8be75520
--- /dev/null
+++ b/spec/bundler/bundler/override_spec.rb
@@ -0,0 +1,175 @@
+# frozen_string_literal: true
+
+RSpec.describe "MatchMetadata override-aware checks" do
+ let(:spec_class) do
+ Class.new do
+ include Bundler::MatchMetadata
+ attr_accessor :name
+ def initialize(name, ruby_req, rubygems_req)
+ @name = name
+ @required_ruby_version = ruby_req
+ @required_rubygems_version = rubygems_req
+ end
+ end
+ end
+
+ it "matches_current_metadata? ignores overrides (strict path)" do
+ spec = spec_class.new("rails", Gem::Requirement.new("< #{Gem.ruby_version}"), Gem::Requirement.default)
+ overrides = [Bundler::Override.new("rails", :required_ruby_version, :ignore_upper)]
+ # Strict method MUST NOT apply overrides; guards SelfManager and other generic callers.
+ expect(spec.matches_current_metadata?).to be(false)
+ expect(spec.matches_current_metadata_with_overrides?(overrides)).to be(true)
+ end
+
+ it "matches_current_ruby_with_overrides? returns the strict result for an empty override list" do
+ spec = spec_class.new("rails", Gem::Requirement.new(">= #{Gem.ruby_version}"), Gem::Requirement.default)
+ expect(spec.matches_current_ruby_with_overrides?([])).to be(true)
+ expect(spec.matches_current_ruby_with_overrides?(nil)).to be(true)
+ end
+
+ it "matches_current_rubygems_with_overrides? honors :all override" do
+ spec = spec_class.new("rails", Gem::Requirement.default, Gem::Requirement.new("< #{Gem.rubygems_version}"))
+ overrides = [Bundler::Override.new(:all, :required_rubygems_version, :ignore_upper)]
+ expect(spec.matches_current_rubygems_with_overrides?(overrides)).to be(true)
+ end
+end
+
+RSpec.describe "LazySpecification override propagation" do
+ let(:overrides) { [Bundler::Override.new("rails", :required_ruby_version, :ignore_upper)] }
+
+ it "carries overrides forward from a source LazySpec via from_spec" do
+ src = Bundler::LazySpecification.new("rails", "8.0", Gem::Platform::RUBY)
+ src.overrides = overrides
+ derived = Bundler::LazySpecification.from_spec(src)
+ expect(derived.overrides).to eq(overrides)
+ end
+
+ it "does not call respond_to? on the source spec, avoiding gemspec lazy load" do
+ # If from_spec used respond_to?(:overrides), a RemoteSpec source would
+ # force-load the backing gemspec. Use a stand-in object whose
+ # respond_to? raises to prove it is never asked.
+ src = Object.new
+ src.define_singleton_method(:name) { "rails" }
+ src.define_singleton_method(:version) { Gem::Version.new("8.0") }
+ src.define_singleton_method(:platform) { Gem::Platform::RUBY }
+ src.define_singleton_method(:source) { nil }
+ src.define_singleton_method(:runtime_dependencies) { [] }
+ src.define_singleton_method(:required_ruby_version) { Gem::Requirement.default }
+ src.define_singleton_method(:required_rubygems_version) { Gem::Requirement.default }
+ src.define_singleton_method(:respond_to?) {|*| raise "from_spec must not call respond_to?" }
+ expect { Bundler::LazySpecification.from_spec(src) }.not_to raise_error
+ end
+end
+
+RSpec.describe Bundler::Override do
+ describe ".find_for" do
+ it "returns the matching override by target and field" do
+ a = described_class.new("rails", :version, ">= 8.0")
+ b = described_class.new("nokogiri", :version, :ignore_upper)
+ expect(described_class.find_for([a, b], "rails", :version)).to be(a)
+ end
+
+ it "returns nil when no override matches the target" do
+ a = described_class.new("rails", :version, ">= 8.0")
+ expect(described_class.find_for([a], "sinatra", :version)).to be_nil
+ end
+
+ it "returns nil when no override matches the field" do
+ a = described_class.new("rails", :version, ">= 8.0")
+ expect(described_class.find_for([a], "rails", :required_ruby_version)).to be_nil
+ end
+
+ it "returns nil for an empty overrides list" do
+ expect(described_class.find_for([], "rails", :version)).to be_nil
+ end
+
+ it "falls back to an :all override on the same field" do
+ a = described_class.new(:all, :required_ruby_version, :ignore_upper)
+ expect(described_class.find_for([a], "rails", :required_ruby_version)).to be(a)
+ end
+
+ it "prefers a per-gem override over a matching :all override" do
+ per_gem = described_class.new("rails", :required_ruby_version, ">= 3.4")
+ all_target = described_class.new(:all, :required_ruby_version, :ignore_upper)
+ expect(described_class.find_for([all_target, per_gem], "rails", :required_ruby_version)).to be(per_gem)
+ end
+
+ it "does not fall back to :all when the field differs" do
+ a = described_class.new(:all, :required_ruby_version, :ignore_upper)
+ expect(described_class.find_for([a], "rails", :required_rubygems_version)).to be_nil
+ end
+ end
+
+ describe "#apply_to" do
+ context "when operation is a version spec string" do
+ it "replaces the existing requirement entirely" do
+ override = described_class.new("rails", :version, ">= 8.0")
+ result = override.apply_to(Gem::Requirement.new(">= 1.0", "< 2.0"))
+ expect(result).to eq(Gem::Requirement.new(">= 8.0"))
+ end
+
+ it "ignores the existing requirement regardless of its content" do
+ override = described_class.new("rails", :version, "= 1.0")
+ result = override.apply_to(Gem::Requirement.new(">= 99.0"))
+ expect(result).to eq(Gem::Requirement.new("= 1.0"))
+ end
+ end
+
+ context "when operation is :ignore_upper" do
+ it "removes < and <= operators" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ result = override.apply_to(Gem::Requirement.new(">= 1.0", "< 2.0"))
+ expect(result).to eq(Gem::Requirement.new(">= 1.0"))
+ end
+
+ it "keeps >, >=, = operators" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ result = override.apply_to(Gem::Requirement.new("> 1.0", "<= 2.0"))
+ expect(result).to eq(Gem::Requirement.new("> 1.0"))
+ end
+
+ it "converts ~> to >= preserving the lower bound" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ result = override.apply_to(Gem::Requirement.new("~> 1.5"))
+ expect(result).to eq(Gem::Requirement.new(">= 1.5"))
+ end
+
+ it "preserves != exclusion constraints" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ result = override.apply_to(Gem::Requirement.new(">= 1.0", "!= 1.5.0", "< 2.0"))
+ expect(result).to eq(Gem::Requirement.new(">= 1.0", "!= 1.5.0"))
+ end
+
+ it "returns the default requirement when only upper bounds remain" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ result = override.apply_to(Gem::Requirement.new("< 2.0"))
+ expect(result).to eq(Gem::Requirement.default)
+ end
+
+ it "returns the default requirement when the input is nil" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ expect(override.apply_to(nil)).to eq(Gem::Requirement.default)
+ end
+
+ it "returns the default requirement when the input is already the default" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ expect(override.apply_to(Gem::Requirement.default)).to eq(Gem::Requirement.default)
+ end
+ end
+
+ context "when operation is nil" do
+ it "returns the default requirement" do
+ override = described_class.new("rails", :version, nil)
+ result = override.apply_to(Gem::Requirement.new(">= 1.0", "< 2.0"))
+ expect(result).to eq(Gem::Requirement.default)
+ end
+ end
+
+ context "when operation is unsupported" do
+ it "raises ArgumentError" do
+ override = described_class.new("rails", :version, 42)
+ expect { override.apply_to(Gem::Requirement.default) }.to raise_error(ArgumentError, /unsupported override operation/)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/api/source_spec.rb b/spec/bundler/bundler/plugin/api/source_spec.rb
index 2c50ff56a4..ae02e08bea 100644
--- a/spec/bundler/bundler/plugin/api/source_spec.rb
+++ b/spec/bundler/bundler/plugin/api/source_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe Bundler::Plugin::API::Source do
context "to_lock" do
it "returns the string with remote and type" do
- expected = strip_whitespace <<-L
+ expected = <<~L
PLUGIN SOURCE
remote: #{uri}
type: #{type}
@@ -67,7 +67,7 @@ RSpec.describe Bundler::Plugin::API::Source do
end
it "includes them" do
- expected = strip_whitespace <<-L
+ expected = <<~L
PLUGIN SOURCE
remote: #{uri}
type: #{type}
@@ -79,4 +79,10 @@ RSpec.describe Bundler::Plugin::API::Source do
end
end
end
+
+ describe "to_s" do
+ it "returns the string with type and uri" do
+ expect(source.to_s).to eq("plugin source for spec_type with uri uri://to/test")
+ end
+ end
end
diff --git a/spec/bundler/bundler/plugin/dsl_spec.rb b/spec/bundler/bundler/plugin/dsl_spec.rb
index be23db3bba..235a549735 100644
--- a/spec/bundler/bundler/plugin/dsl_spec.rb
+++ b/spec/bundler/bundler/plugin/dsl_spec.rb
@@ -23,16 +23,16 @@ RSpec.describe Bundler::Plugin::DSL do
it "adds #source with :type to list and also inferred_plugins list" do
expect(dsl).to receive(:plugin).with("bundler-source-news").once
- dsl.source("some_random_url", :type => "news") {}
+ dsl.source("some_random_url", type: "news") {}
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") {}
- dsl.source("another_random_url", :type => "news") {}
+ dsl.source("some_random_url", type: "news") {}
+ dsl.source("another_random_url", type: "news") {}
end
end
end
diff --git a/spec/bundler/bundler/plugin/events_spec.rb b/spec/bundler/bundler/plugin/events_spec.rb
index 28d70c6fdd..77e5fdb74c 100644
--- a/spec/bundler/bundler/plugin/events_spec.rb
+++ b/spec/bundler/bundler/plugin/events_spec.rb
@@ -2,7 +2,17 @@
RSpec.describe Bundler::Plugin::Events do
context "plugin events" do
- before { Bundler::Plugin::Events.send :reset }
+ before do
+ @old_constants = Bundler::Plugin::Events.constants.map {|name| [name, Bundler::Plugin::Events.const_get(name)] }
+ Bundler::Plugin::Events.send :reset
+ end
+
+ after do
+ Bundler::Plugin::Events.send(:reset)
+ Hash[@old_constants].each do |name, value|
+ Bundler::Plugin::Events.send(:define, name, value)
+ end
+ end
describe "#define" do
it "raises when redefining a constant" do
diff --git a/spec/bundler/bundler/plugin/index_spec.rb b/spec/bundler/bundler/plugin/index_spec.rb
index 925dc558ac..a28934269b 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 'https://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
@@ -134,7 +140,7 @@ RSpec.describe Bundler::Plugin::Index do
describe "after conflict" do
let(:commands) { ["foo"] }
let(:sources) { ["bar"] }
- let(:hooks) { ["hoook"] }
+ let(:hooks) { ["thehook"] }
shared_examples "it cleans up" do
it "the path" do
@@ -150,7 +156,7 @@ RSpec.describe Bundler::Plugin::Index do
end
it "the hook" do
- expect(index.hook_plugins("xhoook")).to be_empty
+ expect(index.hook_plugins("xthehook")).to be_empty
end
end
@@ -158,7 +164,7 @@ RSpec.describe Bundler::Plugin::Index do
before do
expect do
path = lib_path("cplugin")
- index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["xbar"], ["xhoook"])
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["xbar"], ["xthehook"])
end.to raise_error(Index::CommandConflict)
end
@@ -169,7 +175,7 @@ RSpec.describe Bundler::Plugin::Index do
before do
expect do
path = lib_path("cplugin")
- index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["xfoo"], ["bar"], ["xhoook"])
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["xfoo"], ["bar"], ["xthehook"])
end.to raise_error(Index::SourceConflict)
end
@@ -180,7 +186,7 @@ RSpec.describe Bundler::Plugin::Index do
before do
expect do
path = lib_path("cplugin")
- index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["bar"], ["xhoook"])
+ index.register_plugin("cplugin", path.to_s, [path.join("lib").to_s], ["foo"], ["bar"], ["xthehook"])
end.to raise_error(Index::CommandConflict)
end
@@ -188,11 +194,82 @@ RSpec.describe Bundler::Plugin::Index do
end
end
- describe "readonly disk without home" do
- it "ignores being unable to create temp home dir" do
- expect_any_instance_of(Bundler::Plugin::Index).to receive(:global_index_file).
- and_raise(Bundler::GenericSystemCallError.new("foo", "bar"))
- Bundler::Plugin::Index.new
+ describe "relative plugin paths" do
+ let(:plugin_name) { "relative-plugin" }
+
+ before do
+ Bundler::Plugin.reset!
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+
+ plugin_root = Bundler::Plugin.root
+ FileUtils.mkdir_p(plugin_root)
+
+ path = plugin_root.join(plugin_name)
+ FileUtils.mkdir_p(path.join("lib"))
+
+ index.register_plugin(plugin_name, path.to_s, [path.join("lib").to_s], [], [], [])
+ end
+
+ it "stores plugin paths relative to the plugin root" do
+ require "yaml"
+ data = YAML.load_file(index.index_file)
+
+ expect(data["plugin_paths"][plugin_name]).to eq(plugin_name)
+ expect(data["load_paths"][plugin_name]).to eq([File.join(plugin_name, "lib")])
+ end
+
+ it "expands relative paths to absolute on load" do
+ require "bundler/yaml_serializer"
+
+ plugin_root = Bundler::Plugin.root
+
+ relative_index = {
+ "commands" => {},
+ "hooks" => {},
+ "load_paths" => { plugin_name => [File.join(plugin_name, "lib")] },
+ "plugin_paths" => { plugin_name => plugin_name },
+ "sources" => {},
+ }
+
+ File.open(index.index_file, "w") {|f| f.puts Bundler::YAMLSerializer.dump(relative_index) }
+
+ new_index = Index.new
+ expect(new_index.plugin_path(plugin_name)).to eq(plugin_root.join(plugin_name))
+ expect(new_index.load_paths(plugin_name)).to eq([plugin_root.join(plugin_name, "lib").to_s])
+ end
+
+ it "keeps paths outside the plugin root as absolute" do
+ outside_path = tmp.join("outside", "external-plugin")
+ FileUtils.mkdir_p(outside_path.join("lib"))
+
+ index.register_plugin("external-plugin", outside_path.to_s, [outside_path.join("lib").to_s], [], [], [])
+
+ require "yaml"
+ data = YAML.load_file(index.index_file)
+
+ expect(data["plugin_paths"]["external-plugin"]).to eq(outside_path.to_s)
+ expect(data["load_paths"]["external-plugin"]).to eq([outside_path.join("lib").to_s])
+ end
+
+ it "reads legacy index files with absolute paths" do
+ require "bundler/yaml_serializer"
+
+ plugin_root = Bundler::Plugin.root
+ absolute_path = plugin_root.join(plugin_name).to_s
+
+ legacy_index = {
+ "commands" => {},
+ "hooks" => {},
+ "load_paths" => { plugin_name => [File.join(absolute_path, "lib")] },
+ "plugin_paths" => { plugin_name => absolute_path },
+ "sources" => {},
+ }
+
+ File.open(index.index_file, "w") {|f| f.puts Bundler::YAMLSerializer.dump(legacy_index) }
+
+ new_index = Index.new
+ expect(new_index.plugin_path(plugin_name)).to eq(Pathname.new(absolute_path))
+ expect(new_index.load_paths(plugin_name)).to eq([File.join(absolute_path, "lib")])
end
end
end
diff --git a/spec/bundler/bundler/plugin/installer_spec.rb b/spec/bundler/bundler/plugin/installer_spec.rb
index e89720f6f7..c200a98afa 100644
--- a/spec/bundler/bundler/plugin/installer_spec.rb
+++ b/spec/bundler/bundler/plugin/installer_spec.rb
@@ -6,8 +6,7 @@ RSpec.describe Bundler::Plugin::Installer do
describe "cli install" do
it "uses Gem.sources when non of the source is provided" do
sources = double(:sources)
- Bundler.settings # initialize it before we have to touch rubygems.ext_lock
- allow(Bundler).to receive_message_chain("rubygems.sources") { sources }
+ allow(Gem).to receive(:sources) { sources }
allow(installer).to receive(:install_rubygems).
with("new-plugin", [">= 0"], sources).once
@@ -21,15 +20,15 @@ RSpec.describe Bundler::Plugin::Installer do
allow(installer).to receive(:install_git).
and_return("new-plugin" => spec)
- expect(installer.install(["new-plugin"], :git => "https://some.ran/dom")).
+ expect(installer.install(["new-plugin"], git: "https://some.ran/dom")).
to eq("new-plugin" => spec)
end
it "returns the installed spec after installing local git plugins" do
- allow(installer).to receive(:install_local_git).
+ allow(installer).to receive(:install_git).
and_return("new-plugin" => spec)
- expect(installer.install(["new-plugin"], :local_git => "/phony/path/repo")).
+ expect(installer.install(["new-plugin"], git: "/phony/path/repo")).
to eq("new-plugin" => spec)
end
@@ -37,7 +36,7 @@ RSpec.describe Bundler::Plugin::Installer do
allow(installer).to receive(:install_rubygems).
and_return("new-plugin" => spec)
- expect(installer.install(["new-plugin"], :source => "https://some.ran/dom")).
+ expect(installer.install(["new-plugin"], source: "https://some.ran/dom")).
to eq("new-plugin" => spec)
end
end
@@ -48,17 +47,24 @@ RSpec.describe Bundler::Plugin::Installer do
build_plugin "re-plugin"
build_plugin "ma-plugin"
end
+
+ @previous_ui = Bundler.ui
+ Bundler.ui = Bundler::UI::Silent.new
+ end
+
+ after do
+ Bundler.ui = @previous_ui
end
context "git plugins" do
before do
- build_git "ga-plugin", :path => lib_path("ga-plugin") do |s|
+ build_git "ga-plugin", path: lib_path("ga-plugin") do |s|
s.write "plugins.rb"
end
end
let(:result) do
- installer.install(["ga-plugin"], :git => file_uri_for(lib_path("ga-plugin")))
+ installer.install(["ga-plugin"], git: lib_path("ga-plugin").to_s)
end
it "returns the installed spec after installing" do
@@ -75,13 +81,13 @@ RSpec.describe Bundler::Plugin::Installer do
context "local git plugins" do
before do
- build_git "ga-plugin", :path => lib_path("ga-plugin") do |s|
+ build_git "ga-plugin", path: lib_path("ga-plugin") do |s|
s.write "plugins.rb"
end
end
let(:result) do
- installer.install(["ga-plugin"], :local_git => lib_path("ga-plugin").to_s)
+ installer.install(["ga-plugin"], git: lib_path("ga-plugin").to_s)
end
it "returns the installed spec after installing" do
@@ -98,7 +104,7 @@ RSpec.describe Bundler::Plugin::Installer do
context "rubygems plugins" do
let(:result) do
- installer.install(["re-plugin"], :source => file_uri_for(gem_repo2))
+ installer.install(["re-plugin"], source: file_uri_for(gem_repo2))
end
it "returns the installed spec after installing " do
@@ -113,7 +119,7 @@ RSpec.describe Bundler::Plugin::Installer do
context "multiple plugins" do
let(:result) do
- installer.install(["re-plugin", "ma-plugin"], :source => file_uri_for(gem_repo2))
+ installer.install(["re-plugin", "ma-plugin"], source: file_uri_for(gem_repo2))
end
it "returns the installed spec after installing " do
diff --git a/spec/bundler/bundler/plugin_spec.rb b/spec/bundler/bundler/plugin_spec.rb
index 8c95723bcc..b379594c6f 100644
--- a/spec/bundler/bundler/plugin_spec.rb
+++ b/spec/bundler/bundler/plugin_spec.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require_relative "../support/streams"
-
RSpec.describe Bundler::Plugin do
Plugin = Bundler::Plugin
@@ -11,11 +9,11 @@ RSpec.describe Bundler::Plugin do
let(:spec2) { double(:spec2) }
before do
- build_lib "new-plugin", :path => lib_path("new-plugin") do |s|
+ build_lib "new-plugin", path: lib_path("new-plugin") do |s|
s.write "plugins.rb"
end
- build_lib "another-plugin", :path => lib_path("another-plugin") do |s|
+ build_lib "another-plugin", path: lib_path("another-plugin") do |s|
s.write "plugins.rb"
end
@@ -67,6 +65,8 @@ RSpec.describe Bundler::Plugin do
end
it "passes the name and options to installer" do
+ allow(index).to receive(:up_to_date?).
+ with(spec)
allow(installer).to receive(:install).with(["new-plugin"], opts) do
{ "new-plugin" => spec }
end.once
@@ -75,6 +75,8 @@ RSpec.describe Bundler::Plugin do
end
it "validates the installed plugin" do
+ allow(index).to receive(:up_to_date?).
+ with(spec)
allow(subject).
to receive(:validate_plugin!).with(lib_path("new-plugin")).once
@@ -82,6 +84,8 @@ RSpec.describe Bundler::Plugin do
end
it "registers the plugin with index" do
+ allow(index).to receive(:up_to_date?).
+ with(spec)
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 +102,7 @@ RSpec.describe Bundler::Plugin do
end.once
allow(subject).to receive(:validate_plugin!).twice
+ allow(index).to receive(:up_to_date?).twice
allow(index).to receive(:register_plugin).twice
subject.install ["new-plugin", "another-plugin"], opts
end
@@ -112,6 +117,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
@@ -132,7 +138,7 @@ RSpec.describe Bundler::Plugin do
end
before do
- allow(index).to receive(:installed?) { nil }
+ allow(index).to receive(:up_to_date?) { nil }
allow(definition).to receive(:dependencies) { [Bundler::Dependency.new("new-plugin", ">=0"), Bundler::Dependency.new("another-plugin", ">=0")] }
allow(installer).to receive(:install_definition) { plugin_specs }
end
@@ -219,7 +225,7 @@ RSpec.describe Bundler::Plugin do
end
end
- describe "#source_from_lock" do
+ describe "#from_lock" do
it "returns instance of registered class initialized with locked opts" do
opts = { "type" => "l_source", "remote" => "xyz", "other" => "random" }
allow(index).to receive(:source_plugin).with("l_source") { "plugin_name" }
@@ -230,7 +236,7 @@ RSpec.describe Bundler::Plugin do
expect(SClass).to receive(:new).
with(hash_including("type" => "l_source", "uri" => "xyz", "other" => "random")) { s_instance }
- expect(subject.source_from_lock(opts)).to be(s_instance)
+ expect(subject.from_lock(opts)).to be(s_instance)
end
end
@@ -241,7 +247,7 @@ RSpec.describe Bundler::Plugin do
end
it "returns plugin dir in app .bundle path" do
- expect(subject.root).to eq(bundled_app.join(".bundle/plugin"))
+ expect(subject.root).to eq(bundled_app(".bundle/plugin"))
end
end
@@ -251,7 +257,7 @@ RSpec.describe Bundler::Plugin do
end
it "returns plugin dir in global bundle path" do
- expect(subject.root).to eq(home.join(".bundle/plugin"))
+ expect(subject.root).to eq(home(".bundle/plugin"))
end
end
end
@@ -269,22 +275,30 @@ RSpec.describe Bundler::Plugin do
describe "#hook" do
before do
path = lib_path("foo-plugin")
- build_lib "foo-plugin", :path => path do |s|
+ build_lib "foo-plugin", path: path do |s|
s.write "plugins.rb", code
end
+ @old_constants = Bundler::Plugin::Events.constants.map {|name| [name, Bundler::Plugin::Events.const_get(name)] }
Bundler::Plugin::Events.send(:reset)
- Bundler::Plugin::Events.send(:define, :EVENT_1, "event-1")
- Bundler::Plugin::Events.send(:define, :EVENT_2, "event-2")
+ Bundler::Plugin::Events.send(:define, :EVENT1, "event-1")
+ Bundler::Plugin::Events.send(:define, :EVENT2, "event-2")
- allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_1).
- and_return(["foo-plugin"])
- allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_2).
+ allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT1).
+ and_return(["foo-plugin", "", nil])
+ allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT2).
and_return(["foo-plugin"])
allow(index).to receive(:plugin_path).with("foo-plugin").and_return(path)
allow(index).to receive(:load_paths).with("foo-plugin").and_return([])
end
+ after do
+ Bundler::Plugin::Events.send(:reset)
+ Hash[@old_constants].each do |name, value|
+ Bundler::Plugin::Events.send(:define, name, value)
+ end
+ end
+
let(:code) { <<-RUBY }
Bundler::Plugin::API.hook("event-1") { puts "hook for event 1" }
RUBY
@@ -296,41 +310,58 @@ RSpec.describe Bundler::Plugin do
end
it "executes the hook" do
- out = capture(:stdout) do
- Plugin.hook(Bundler::Plugin::Events::EVENT_1)
- end.strip
-
- expect(out).to eq("hook for event 1")
+ expect do
+ Plugin.hook(Bundler::Plugin::Events::EVENT1)
+ end.to output("hook for event 1\n").to_stdout
end
context "single plugin declaring more than one hook" do
let(:code) { <<-RUBY }
- Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_1) {}
- Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_2) {}
+ Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT1) {}
+ Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT2) {}
puts "loaded"
RUBY
it "evals plugins.rb once" do
- out = capture(:stdout) do
- Plugin.hook(Bundler::Plugin::Events::EVENT_1)
- Plugin.hook(Bundler::Plugin::Events::EVENT_2)
- end.strip
-
- expect(out).to eq("loaded")
+ expect do
+ Plugin.hook(Bundler::Plugin::Events::EVENT1)
+ Plugin.hook(Bundler::Plugin::Events::EVENT2)
+ end.to output("loaded\n").to_stdout
end
end
context "a block is passed" do
let(:code) { <<-RUBY }
- Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_1) { |&blk| blk.call }
+ Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT1) { |&blk| blk.call }
RUBY
it "is passed to the hook" do
- out = capture(:stdout) do
- Plugin.hook(Bundler::Plugin::Events::EVENT_1) { puts "win" }
- end.strip
+ expect do
+ Plugin.hook(Bundler::Plugin::Events::EVENT1) { puts "win" }
+ end.to output("win\n").to_stdout
+ end
+ end
+
+ context "the plugin load_path is invalid" do
+ before do
+ allow(index).to receive(:load_paths).with("foo-plugin").
+ and_return(["invalid-file-name1", "invalid-file-name2"])
+ end
+
+ it "outputs a useful warning" do
+ msg =
+ "The following plugin paths don't exist: invalid-file-name1, invalid-file-name2.\n\n" \
+ "This can happen if the plugin was " \
+ "installed with a different version of Ruby that has since been uninstalled.\n\n" \
+ "If you would like to reinstall the plugin, run:\n\n" \
+ "bundler plugin uninstall foo-plugin && bundler plugin install foo-plugin\n\n" \
+ "Continuing without installing plugin foo-plugin.\n"
+
+ expect(Bundler.ui).to receive(:warn).with(msg)
+
+ Plugin.hook(Bundler::Plugin::Events::EVENT1)
- expect(out).to eq("win")
+ expect(subject.loaded?("foo-plugin")).to be_falsey
end
end
end
diff --git a/spec/bundler/bundler/psyched_yaml_spec.rb b/spec/bundler/bundler/psyched_yaml_spec.rb
deleted file mode 100644
index d5d68c5cc3..0000000000
--- a/spec/bundler/bundler/psyched_yaml_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-require "bundler/psyched_yaml"
-
-RSpec.describe "Bundler::YamlLibrarySyntaxError" do
- it "is raised on YAML parse errors" do
- expect { YAML.parse "{foo" }.to raise_error(Bundler::YamlLibrarySyntaxError)
- end
-end
diff --git a/spec/bundler/bundler/remote_specification_spec.rb b/spec/bundler/bundler/remote_specification_spec.rb
index 8115e026d8..f35b231d58 100644
--- a/spec/bundler/bundler/remote_specification_spec.rb
+++ b/spec/bundler/bundler/remote_specification_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Bundler::RemoteSpecification do
end
describe "#fetch_platform" do
- let(:remote_spec) { double(:remote_spec, :platform => "jruby") }
+ let(:remote_spec) { double(:remote_spec, platform: "jruby") }
before { allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec) }
@@ -45,7 +45,7 @@ RSpec.describe Bundler::RemoteSpecification do
let(:platform) { "jruby" }
it "should return the spec name, version, and platform" do
- expect(subject.full_name).to eq("foo-1.0.0-jruby")
+ expect(subject.full_name).to eq("foo-1.0.0-java")
end
end
end
@@ -113,7 +113,7 @@ RSpec.describe Bundler::RemoteSpecification do
context "comparing a non sortable object" do
let(:other) { Object.new }
- let(:remote_spec) { double(:remote_spec, :platform => "jruby") }
+ let(:remote_spec) { double(:remote_spec, platform: "jruby") }
before do
allow(spec_fetcher).to receive(:fetch_spec).and_return(remote_spec)
@@ -127,8 +127,8 @@ RSpec.describe Bundler::RemoteSpecification do
end
describe "#__swap__" do
- let(:spec) { double(:spec, :dependencies => []) }
- let(:new_spec) { double(:new_spec, :dependencies => [], :runtime_dependencies => []) }
+ let(:spec) { double(:spec, dependencies: []) }
+ let(:new_spec) { double(:new_spec, dependencies: [], runtime_dependencies: []) }
before { subject.instance_variable_set(:@_remote_specification, spec) }
@@ -157,7 +157,7 @@ RSpec.describe Bundler::RemoteSpecification do
describe "method missing" do
context "and is present in Gem::Specification" do
- let(:remote_spec) { double(:remote_spec, :authors => "abcd") }
+ let(:remote_spec) { double(:remote_spec, authors: "abcd") }
before do
allow(subject).to receive(:_remote_specification).and_return(remote_spec)
@@ -172,7 +172,7 @@ RSpec.describe Bundler::RemoteSpecification do
describe "respond to missing?" do
context "and is present in Gem::Specification" do
- let(:remote_spec) { double(:remote_spec, :authors => "abcd") }
+ let(:remote_spec) { double(:remote_spec, authors: "abcd") }
before do
allow(subject).to receive(:_remote_specification).and_return(remote_spec)
diff --git a/spec/bundler/bundler/resolver/candidate_spec.rb b/spec/bundler/bundler/resolver/candidate_spec.rb
new file mode 100644
index 0000000000..aefad3316e
--- /dev/null
+++ b/spec/bundler/bundler/resolver/candidate_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Resolver::Candidate do
+ it "compares fine" do
+ version1 = described_class.new("1.12.5", priority: -1)
+ version2 = described_class.new("1.12.5", priority: 1)
+
+ expect(version2 > version1).to be true
+
+ version1 = described_class.new("1.12.5")
+ version2 = described_class.new("1.12.5")
+
+ expect(version2 == version1).to be true
+
+ version1 = described_class.new("1.12.5", priority: 1)
+ version2 = described_class.new("1.12.5", priority: -1)
+
+ expect(version2 < version1).to be true
+ end
+end
diff --git a/spec/bundler/bundler/resolver/cooldown_spec.rb b/spec/bundler/bundler/resolver/cooldown_spec.rb
new file mode 100644
index 0000000000..37ec158cba
--- /dev/null
+++ b/spec/bundler/bundler/resolver/cooldown_spec.rb
@@ -0,0 +1,148 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Resolver do
+ let(:resolver) { described_class.allocate }
+
+ def remote(cooldown:)
+ instance_double(Bundler::Source::Rubygems::Remote, effective_cooldown: cooldown)
+ end
+
+ def spec(created_at:, remote:, name: "myrack", version: "1.0.0")
+ Struct.new(:name, :version, :created_at, :remote).new(name, Gem::Version.new(version), created_at, remote)
+ end
+
+ describe "#filter_cooldown" do
+ let(:now) { Time.now }
+
+ context "with a 7-day cooldown" do
+ let(:r) { remote(cooldown: 7) }
+
+ it "rejects versions published within the window" do
+ recent = spec(version: "1.1.0", created_at: now - (2 * 86_400), remote: r)
+ old = spec(version: "1.0.0", created_at: now - (30 * 86_400), remote: r)
+
+ expect(resolver.send(:filter_cooldown, [recent, old])).to eq([old])
+ end
+
+ it "keeps versions published exactly at the threshold" do
+ boundary = spec(created_at: now - (7 * 86_400), remote: r)
+
+ expect(resolver.send(:filter_cooldown, [boundary])).to eq([boundary])
+ end
+
+ it "leaves rolling-delay history intact" do
+ # 7-day cooldown with frequent releases must still expose an older candidate.
+ in_cooldown = spec(version: "1.2.0", created_at: now - 86_400, remote: r)
+ also_in_cooldown = spec(version: "1.1.0", created_at: now - (3 * 86_400), remote: r)
+ eligible = spec(version: "1.0.0", created_at: now - (10 * 86_400), remote: r)
+
+ result = resolver.send(:filter_cooldown, [in_cooldown, also_in_cooldown, eligible])
+
+ expect(result).to eq([eligible])
+ end
+
+ it "drops every spec sharing an excluded [name, version] tuple" do
+ # The cooldown check is by version, not per-spec: a StubSpecification for an
+ # in-cooldown release would otherwise slip through on local install paths.
+ endpoint = spec(version: "2.0.0", created_at: now - 86_400, remote: r)
+ local_stub = Struct.new(:name, :version).new("myrack", Gem::Version.new("2.0.0"))
+ eligible = spec(version: "1.0.0", created_at: now - (30 * 86_400), remote: r)
+
+ result = resolver.send(:filter_cooldown, [endpoint, local_stub, eligible])
+
+ expect(result).to eq([eligible])
+ end
+
+ it "keeps stub-only versions that no endpoint marks as in cooldown" do
+ # If no remote spec carries created_at for a version, cooldown cannot judge it;
+ # the stub stays in.
+ local_only = Struct.new(:name, :version).new("myrack", Gem::Version.new("2.0.0"))
+ eligible = spec(version: "1.0.0", created_at: now - (30 * 86_400), remote: r)
+
+ result = resolver.send(:filter_cooldown, [local_only, eligible])
+
+ expect(result).to eq([local_only, eligible])
+ end
+ end
+
+ context "when created_at is missing (blank metadata)" do
+ it "keeps the spec regardless of cooldown" do
+ s = spec(created_at: nil, remote: remote(cooldown: 7))
+
+ expect(resolver.send(:filter_cooldown, [s])).to eq([s])
+ end
+ end
+
+ context "when the remote has no cooldown" do
+ it "keeps every spec" do
+ s = spec(created_at: now - 3600, remote: remote(cooldown: nil))
+
+ expect(resolver.send(:filter_cooldown, [s])).to eq([s])
+ end
+ end
+
+ context "when cooldown is 0" do
+ it "keeps every spec (escape hatch)" do
+ s = spec(created_at: now - 3600, remote: remote(cooldown: 0))
+
+ expect(resolver.send(:filter_cooldown, [s])).to eq([s])
+ end
+ end
+
+ context "when the spec does not respond to created_at" do
+ it "keeps the spec" do
+ bare = Struct.new(:version).new("1.0.0")
+
+ expect(resolver.send(:filter_cooldown, [bare])).to eq([bare])
+ end
+ end
+
+ context "when the spec has no remote" do
+ it "keeps the spec" do
+ s = spec(created_at: now - 86_400, remote: nil)
+
+ expect(resolver.send(:filter_cooldown, [s])).to eq([s])
+ end
+ end
+
+ it "returns the same array when input is empty" do
+ expect(resolver.send(:filter_cooldown, [])).to eq([])
+ end
+ end
+
+ describe "#cooldown_hint" do
+ let(:now) { Time.now }
+ let(:r) { remote(cooldown: 7) }
+
+ it "returns nil when no spec is excluded" do
+ expect(resolver.send(:cooldown_hint, [])).to be_nil
+ end
+
+ it "returns nil when every spec is outside the cooldown window" do
+ eligible = [spec(created_at: now - (30 * 86_400), remote: r)]
+
+ expect(resolver.send(:cooldown_hint, eligible)).to be_nil
+ end
+
+ it "mentions the count and the bypass flag for one excluded version" do
+ excluded = [spec(created_at: now - 86_400, remote: r)]
+
+ hint = resolver.send(:cooldown_hint, excluded)
+
+ expect(hint).to match(/1 version excluded by the cooldown setting/)
+ expect(hint).to match(/--cooldown 0/)
+ end
+
+ it "uses plural wording when multiple versions are excluded" do
+ excluded = %w[1.0.0 1.1.0 1.2.0].map {|v| spec(version: v, created_at: now - 86_400, remote: r) }
+
+ expect(resolver.send(:cooldown_hint, excluded)).to match(/3 versions excluded/)
+ end
+
+ it "counts each unique version once even when multiple spec instances share it" do
+ duplicates = Array.new(3) { spec(created_at: now - 86_400, remote: r) }
+
+ expect(resolver.send(:cooldown_hint, duplicates)).to match(/1 version excluded/)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/retry_spec.rb b/spec/bundler/bundler/retry_spec.rb
index b893580d72..5c84d0bea5 100644
--- a/spec/bundler/bundler/retry_spec.rb
+++ b/spec/bundler/bundler/retry_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Bundler::Retry do
end
it "returns the first valid result" do
- jobs = [proc { raise "foo" }, proc { :bar }, proc { raise "foo" }]
+ jobs = [proc { raise "job 1 failed" }, proc { :bar }, proc { raise "job 2 failed" }]
attempts = 0
result = Bundler::Retry.new(nil, nil, 3).attempt do
attempts += 1
@@ -68,7 +68,7 @@ RSpec.describe Bundler::Retry do
it "print error message with newlines" do
allow(Bundler.ui).to receive(:debug?).and_return(false)
expect(Bundler.ui).to receive(:info).with("").twice
- expect(Bundler.ui).to receive(:warn).with(failure_message, false)
+ expect(Bundler.ui).to receive(:warn).with(failure_message, true)
expect do
Bundler::Retry.new("test", [], 1).attempt do
@@ -78,4 +78,113 @@ RSpec.describe Bundler::Retry do
end
end
end
+
+ context "exponential backoff" do
+ it "can be disabled by setting base_delay to 0" do
+ attempts = 0
+ expect do
+ Bundler::Retry.new("test", [], 2, base_delay: 0).attempt do
+ attempts += 1
+ raise "error"
+ end
+ end.to raise_error(StandardError)
+
+ # Verify no sleep was called (implicitly - if sleep was called, timing would be different)
+ expect(attempts).to eq(3)
+ end
+
+ it "is enabled by default with 1 second base delay" do
+ original_base_delay = Bundler::Retry.default_base_delay
+ Bundler::Retry.default_base_delay = 1.0
+
+ attempts = 0
+ sleep_times = []
+
+ allow_any_instance_of(Bundler::Retry).to receive(:sleep) do |_instance, delay|
+ sleep_times << delay
+ end
+
+ expect do
+ Bundler::Retry.new("test", [], 2, jitter: 0).attempt do
+ attempts += 1
+ raise "error"
+ end
+ end.to raise_error(StandardError)
+
+ expect(attempts).to eq(3)
+ expect(sleep_times.length).to eq(2)
+ # First retry: 1.0 * 2^0 = 1.0
+ expect(sleep_times[0]).to eq(1.0)
+ # Second retry: 1.0 * 2^1 = 2.0
+ expect(sleep_times[1]).to eq(2.0)
+ ensure
+ Bundler::Retry.default_base_delay = original_base_delay
+ end
+
+ it "sleeps with exponential backoff when base_delay is set" do
+ attempts = 0
+ sleep_times = []
+
+ allow_any_instance_of(Bundler::Retry).to receive(:sleep) do |_instance, delay|
+ sleep_times << delay
+ end
+
+ expect do
+ Bundler::Retry.new("test", [], 2, base_delay: 1.0, jitter: 0).attempt do
+ attempts += 1
+ raise "error"
+ end
+ end.to raise_error(StandardError)
+
+ expect(attempts).to eq(3)
+ expect(sleep_times.length).to eq(2)
+ # First retry: 1.0 * 2^0 = 1.0
+ expect(sleep_times[0]).to eq(1.0)
+ # Second retry: 1.0 * 2^1 = 2.0
+ expect(sleep_times[1]).to eq(2.0)
+ end
+
+ it "respects max_delay" do
+ sleep_times = []
+
+ allow_any_instance_of(Bundler::Retry).to receive(:sleep) do |_instance, delay|
+ sleep_times << delay
+ end
+
+ expect do
+ Bundler::Retry.new("test", [], 3, base_delay: 10.0, max_delay: 15.0, jitter: 0).attempt do
+ raise "error"
+ end
+ end.to raise_error(StandardError)
+
+ # First retry: 10.0 * 2^0 = 10.0
+ expect(sleep_times[0]).to eq(10.0)
+ # Second retry: 10.0 * 2^1 = 20.0, capped at 15.0
+ expect(sleep_times[1]).to eq(15.0)
+ # Third retry: 10.0 * 2^2 = 40.0, capped at 15.0
+ expect(sleep_times[2]).to eq(15.0)
+ end
+
+ it "adds jitter to delay" do
+ sleep_times = []
+
+ allow_any_instance_of(Bundler::Retry).to receive(:sleep) do |_instance, delay|
+ sleep_times << delay
+ end
+
+ expect do
+ Bundler::Retry.new("test", [], 2, base_delay: 1.0, jitter: 0.5).attempt do
+ raise "error"
+ end
+ end.to raise_error(StandardError)
+
+ expect(sleep_times.length).to eq(2)
+ # First retry should be between 1.0 and 1.5 (base + jitter)
+ expect(sleep_times[0]).to be >= 1.0
+ expect(sleep_times[0]).to be <= 1.5
+ # Second retry should be between 2.0 and 2.5
+ expect(sleep_times[1]).to be >= 2.0
+ expect(sleep_times[1]).to be <= 2.5
+ end
+ end
end
diff --git a/spec/bundler/bundler/ruby_dsl_spec.rb b/spec/bundler/bundler/ruby_dsl_spec.rb
index bc1ca98457..45a37c5795 100644
--- a/spec/bundler/bundler/ruby_dsl_spec.rb
+++ b/spec/bundler/bundler/ruby_dsl_spec.rb
@@ -7,28 +7,37 @@ RSpec.describe Bundler::RubyDsl do
include Bundler::RubyDsl
attr_reader :ruby_version
+ attr_accessor :gemfile
end
let(:dsl) { MockDSL.new }
let(:ruby_version) { "2.0.0" }
+ let(:ruby_version_arg) { ruby_version }
let(:version) { "2.0.0" }
let(:engine) { "jruby" }
let(:engine_version) { "9000" }
let(:patchlevel) { "100" }
let(:options) do
- { :patchlevel => patchlevel,
- :engine => engine,
- :engine_version => engine_version }
+ { patchlevel: patchlevel,
+ engine: engine,
+ engine_version: engine_version }
end
+ let(:project_root) { Pathname.new("/path/to/project") }
+ let(:gemfile) { project_root.join("Gemfile") }
+ before { allow(Bundler).to receive(:root).and_return(project_root) }
let(:invoke) do
proc do
- args = Array(ruby_version) + [options]
+ args = []
+ args << ruby_version_arg if ruby_version_arg
+ args << options
+
dsl.ruby(*args)
end
end
subject do
+ dsl.gemfile = gemfile
invoke.call
dsl.ruby_version
end
@@ -59,10 +68,19 @@ RSpec.describe Bundler::RubyDsl do
it_behaves_like "it stores the ruby version"
end
+ context "with a preview version" do
+ let(:ruby_version) { "3.3.0-preview2" }
+
+ it "stores the version" do
+ expect(subject.versions).to eq(Array("3.3.0.preview2"))
+ expect(subject.gem_version.version).to eq("3.3.0.preview2")
+ end
+ end
+
context "with two requirements in the same string" do
let(:ruby_version) { ">= 2.0.0, < 3.0" }
it "raises an error" do
- expect { subject }.to raise_error(ArgumentError)
+ expect { subject }.to raise_error(Bundler::InvalidArgumentError)
end
end
@@ -91,5 +109,140 @@ RSpec.describe Bundler::RubyDsl do
it_behaves_like "it stores the ruby version"
end
end
+
+ context "with a file option" do
+ let(:file) { ".ruby-version" }
+ let(:ruby_version_file_path) { gemfile.dirname.join(file) }
+ let(:options) do
+ { file: file,
+ patchlevel: patchlevel,
+ engine: engine,
+ engine_version: engine_version }
+ end
+ let(:ruby_version_arg) { nil }
+ let(:file_content) { "#{version}\n" }
+
+ before do
+ allow(Bundler).to receive(:read_file) do |path|
+ raise Errno::ENOENT, <<~ERROR unless path == ruby_version_file_path
+ #{file} not found in specs:
+ expected: #{ruby_version_file_path}
+ received: #{path}
+ ERROR
+ file_content
+ end
+ end
+
+ it_behaves_like "it stores the ruby version"
+
+ context "with the Gemfile ruby file: path is relative to the Gemfile in a subdir" do
+ let(:gemfile) { project_root.join("subdir", "Gemfile") }
+ let(:file) { "../.ruby-version" }
+ let(:ruby_version_file_path) { gemfile.dirname.join(file) }
+
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with bundler root in a subdir of the project" do
+ let(:project_root) { Pathname.new("/path/to/project/subdir") }
+ let(:gemfile) { project_root.parent.join("Gemfile") }
+
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with the ruby- prefix in the file" do
+ let(:file_content) { "ruby-#{version}\n" }
+
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "and a version" do
+ let(:ruby_version_arg) { version }
+
+ it "raises an error" do
+ expect { subject }.to raise_error(Bundler::GemfileError, "Do not pass version argument when using :file option")
+ end
+ end
+
+ context "with a @gemset" do
+ let(:file_content) { "ruby-#{version}@gemset\n" }
+
+ it "raises an error" do
+ expect { subject }.to raise_error(Bundler::InvalidArgumentError, "2.0.0@gemset is not a valid requirement on the Ruby version")
+ end
+ end
+
+ context "with a mise.toml file format" do
+ let(:file) { "mise.toml" }
+ let(:ruby_version_arg) { nil }
+ let(:file_content) do
+ <<~TOML
+ [tools]
+ ruby = #{quote}#{version}#{quote}
+ TOML
+ end
+
+ context "with double quotes" do
+ let(:quote) { '"' }
+
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with single quotes" do
+ let(:quote) { "'" }
+
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with mismatched quotes" do
+ let(:file_content) do
+ <<~TOML
+ [tools]
+ ruby = "#{version}'
+ TOML
+ end
+
+ it "raises an error" do
+ expect { subject }.to raise_error(Bundler::InvalidArgumentError, "= is not a valid requirement on the Ruby version")
+ end
+ end
+ end
+
+ context "with a .tool-versions file format" do
+ let(:file) { ".tool-versions" }
+ let(:ruby_version_arg) { nil }
+ let(:file_content) do
+ <<~TOOLS
+ nodejs 18.16.0
+ ruby #{version} # This is a comment
+ pnpm 8.6.12
+ TOOLS
+ end
+
+ it_behaves_like "it stores the ruby version"
+
+ context "with extra spaces and a very cozy comment" do
+ let(:file_content) do
+ <<~TOOLS
+ nodejs 18.16.0
+ ruby #{version}# This is a cozy comment
+ pnpm 8.6.12
+ TOOLS
+ end
+
+ it_behaves_like "it stores the ruby version"
+ end
+ end
+
+ context "when the file does not exist" do
+ let(:ruby_version_file_path) { nil }
+ let(:ruby_version_arg) { nil }
+ let(:file) { "nonexistent.txt" }
+
+ it "raises an error" do
+ expect { subject }.to raise_error(Bundler::GemfileError, /Could not find version file nonexistent.txt/)
+ end
+ end
+ end
end
end
diff --git a/spec/bundler/bundler/ruby_version_spec.rb b/spec/bundler/bundler/ruby_version_spec.rb
index 8c6c071d7f..0d41ec9901 100644
--- a/spec/bundler/bundler/ruby_version_spec.rb
+++ b/spec/bundler/bundler/ruby_version_spec.rb
@@ -100,7 +100,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
describe "#to_s" do
it "should return info string with the ruby version, patchlevel, engine, and engine version" do
- expect(subject.to_s).to eq("ruby 2.0.0p645 (jruby 2.0.1)")
+ expect(subject.to_s).to eq("ruby 2.0.0 (jruby 2.0.1)")
end
context "no patchlevel" do
@@ -115,7 +115,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
let(:engine) { "ruby" }
it "should return info string with the ruby version and patchlevel" do
- expect(subject.to_s).to eq("ruby 2.0.0p645")
+ expect(subject.to_s).to eq("ruby 2.0.0")
end
end
@@ -137,7 +137,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
end
end
- context "the versions, pathlevels, engines, and engine_versions match" do
+ shared_examples_for "the versions, engines, and engine_versions match" do
it "should return true" do
expect(subject).to eq(other_ruby_version)
end
@@ -152,7 +152,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
context "the patchlevels do not match" do
let(:other_patchlevel) { "21" }
- it_behaves_like "two ruby versions are not equal"
+ it_behaves_like "the versions, engines, and engine_versions match"
end
context "the engines do not match" do
@@ -228,9 +228,9 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
end
end
- shared_examples_for "there is a difference in the patchlevels" do
- it "should return a tuple with :patchlevel and the two different patchlevels" do
- expect(ruby_version.diff(other_ruby_version)).to eq([:patchlevel, patchlevel, other_patchlevel])
+ shared_examples_for "even there is a difference in the patchlevels" do
+ it "should return nil" do
+ expect(ruby_version.diff(other_ruby_version)).to be_nil
end
end
@@ -287,10 +287,10 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
it_behaves_like "there is a difference in the engine versions"
end
- context "detects patchlevel discrepancies last" do
+ context "ignores patchlevel discrepancies last" do
let(:other_patchlevel) { "643" }
- it_behaves_like "there is a difference in the patchlevels"
+ it_behaves_like "even there is a difference in the patchlevels"
end
context "successfully matches gem requirements" do
@@ -355,7 +355,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
let(:other_engine) { "ruby" }
let(:other_engine_version) { "2.0.5" }
- it_behaves_like "there is a difference in the patchlevels"
+ it_behaves_like "even there is a difference in the patchlevels"
end
context "successfully detects bad gem requirements with engine versions" do
@@ -389,7 +389,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
context "and comparing with a patchlevel that is not -1" do
let(:other_patchlevel) { "642" }
- it_behaves_like "there is a difference in the patchlevels"
+ it_behaves_like "even there is a difference in the patchlevels"
end
end
end
@@ -400,19 +400,19 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
let(:bundler_system_ruby_version) { subject }
around do |example|
- if Bundler::RubyVersion.instance_variable_defined?("@ruby_version")
+ if Bundler::RubyVersion.instance_variable_defined?("@system")
begin
- old_ruby_version = Bundler::RubyVersion.instance_variable_get("@ruby_version")
- Bundler::RubyVersion.remove_instance_variable("@ruby_version")
+ old_ruby_version = Bundler::RubyVersion.instance_variable_get("@system")
+ Bundler::RubyVersion.remove_instance_variable("@system")
example.run
ensure
- Bundler::RubyVersion.instance_variable_set("@ruby_version", old_ruby_version)
+ Bundler::RubyVersion.instance_variable_set("@system", old_ruby_version)
end
else
begin
example.run
ensure
- Bundler::RubyVersion.remove_instance_variable("@ruby_version")
+ Bundler::RubyVersion.remove_instance_variable("@system")
end
end
end
@@ -427,9 +427,8 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
end
describe "#version" do
- it "should return a copy of the value of RUBY_VERSION" do
- expect(subject.versions).to eq([RUBY_VERSION])
- expect(subject.versions.first).to_not be(RUBY_VERSION)
+ it "should return the value of Gem.ruby_version as a string" do
+ expect(subject.versions).to eq([Gem.ruby_version.to_s])
end
end
@@ -446,13 +445,12 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
describe "#engine_version" do
context "engine is ruby" do
before do
- stub_const("RUBY_ENGINE_VERSION", "2.2.4")
+ allow(Gem).to receive(:ruby_version).and_return(Gem::Version.new("2.2.4"))
stub_const("RUBY_ENGINE", "ruby")
end
- it "should return a copy of the value of RUBY_ENGINE_VERSION" do
+ it "should return the value of Gem.ruby_version as a string" do
expect(bundler_system_ruby_version.engine_versions).to eq(["2.2.4"])
- expect(bundler_system_ruby_version.engine_versions.first).to_not be(RUBY_ENGINE_VERSION)
end
end
@@ -498,31 +496,5 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
end
end
end
-
- describe "#to_gem_version_with_patchlevel" do
- shared_examples_for "the patchlevel is omitted" do
- it "does not include a patch level" do
- expect(subject.to_gem_version_with_patchlevel.to_s).to eq(version)
- end
- end
-
- context "with nil patch number" do
- let(:patchlevel) { nil }
-
- it_behaves_like "the patchlevel is omitted"
- end
-
- context "with negative patch number" do
- let(:patchlevel) { -1 }
-
- it_behaves_like "the patchlevel is omitted"
- end
-
- context "with a valid patch number" do
- it "uses the specified patchlevel as patchlevel" do
- expect(subject.to_gem_version_with_patchlevel.to_s).to eq("#{version}.#{patchlevel}")
- end
- end
- end
end
end
diff --git a/spec/bundler/bundler/rubygems_ext_spec.rb b/spec/bundler/bundler/rubygems_ext_spec.rb
new file mode 100644
index 0000000000..0fc528f78c
--- /dev/null
+++ b/spec/bundler/bundler/rubygems_ext_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "bundler/rubygems_ext"
+
+RSpec.describe Gem::SplitCompactIndexEntryOnFirstColon do
+ # Reproduces the RubyGems < 4.0.13 `Gem::Resolver::APISet::GemParser` that
+ # split each compact index entry on every colon, corrupting metadata values
+ # that themselves contain colons.
+ let(:legacy_parser_class) do
+ Class.new do
+ def parse_dependency(string)
+ dependency = string.split(":")
+ dependency[-1] = dependency[-1].split("&") if dependency.size > 1
+ dependency[0] = -dependency[0]
+ dependency
+ end
+ end
+ end
+
+ before { legacy_parser_class.prepend(described_class) }
+
+ it "preserves colon-bearing metadata values such as created_at timestamps" do
+ parser = legacy_parser_class.new
+
+ expect(parser.send(:parse_dependency, "created_at:2026-05-12T10:00:00Z")).to eq(["created_at", ["2026-05-12T10:00:00Z"]])
+ end
+
+ it "still parses ordinary name:requirement entries" do
+ parser = legacy_parser_class.new
+
+ expect(parser.send(:parse_dependency, "myrack:>= 1.0")).to eq(["myrack", [">= 1.0"]])
+ end
+
+ it "keeps parse_dependency private" do
+ parser = legacy_parser_class.new
+
+ expect { parser.parse_dependency("created_at:x") }.to raise_error(NoMethodError, /private method/)
+ end
+end
diff --git a/spec/bundler/bundler/rubygems_integration_spec.rb b/spec/bundler/bundler/rubygems_integration_spec.rb
index 11fa2f4e0d..a2c63a7ca0 100644
--- a/spec/bundler/bundler/rubygems_integration_spec.rb
+++ b/spec/bundler/bundler/rubygems_integration_spec.rb
@@ -1,10 +1,6 @@
# frozen_string_literal: true
RSpec.describe Bundler::RubygemsIntegration do
- it "uses the same chdir lock as rubygems" do
- expect(Bundler.rubygems.ext_lock).to eq(Gem::Ext::Builder::CHDIR_MONITOR)
- end
-
context "#validate" do
let(:spec) do
Gem::Specification.new do |s|
@@ -15,14 +11,14 @@ RSpec.describe Bundler::RubygemsIntegration do
end
subject { Bundler.rubygems.validate(spec) }
- it "validates with packaging mode disabled" do
- expect(spec).to receive(:validate).with(false)
+ it "validates for resolution" do
+ expect(spec).to receive(:validate_for_resolution)
subject
end
context "with an invalid spec" do
before do
- expect(spec).to receive(:validate).with(false).
+ expect(spec).to receive(:validate_for_resolution).
and_raise(Gem::InvalidSpecificationException.new("TODO is not an author"))
end
@@ -34,35 +30,57 @@ RSpec.describe Bundler::RubygemsIntegration do
end
end
- describe "#configuration" do
- it "handles Gem::SystemExitException errors" do
- allow(Gem).to receive(:configuration) { raise Gem::SystemExitException.new(1) }
- expect { Bundler.rubygems.configuration }.to raise_error(Gem::SystemExitException)
- end
- end
-
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(: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
let(:fetcher) { double("gem_remote_fetcher") }
- 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(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)
+ context "when uri is public" do
+ let(:uri) { Gem::URI.parse("https://foo.bar") }
+
+ it "successfully downloads gem with retries" do
+ 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(:cache_update_path)
- Bundler.rubygems.download_gem(spec, uri, path)
+ Bundler.rubygems.download_gem(spec, uri, cache_dir, fetcher)
+ end
+ end
+
+ context "when uri contains userinfo part" do
+ let(:uri) { Gem::URI.parse("https://#{userinfo}@foo.bar") }
+
+ context "with user and password" do
+ let(:userinfo) { "user:password" }
+
+ it "successfully downloads gem with retries with filtered log" do
+ expect(Bundler::Retry).to receive(:new).with("download gem from https://user:REDACTED@foo.bar/").
+ and_return(bundler_retry)
+ expect(bundler_retry).to receive(:attempts).and_yield
+ expect(fetcher).to receive(:cache_update_path)
+
+ Bundler.rubygems.download_gem(spec, uri, cache_dir, fetcher)
+ end
+ end
+
+ context "with token [as user]" do
+ let(:userinfo) { "token" }
+
+ it "successfully downloads gem with retries with filtered log" do
+ expect(Bundler::Retry).to receive(:new).with("download gem from https://REDACTED@foo.bar/").
+ and_return(bundler_retry)
+ expect(bundler_retry).to receive(:attempts).and_yield
+ expect(fetcher).to receive(:cache_update_path)
+
+ Bundler.rubygems.download_gem(spec, uri, cache_dir, fetcher)
+ end
+ end
end
end
@@ -73,30 +91,36 @@ RSpec.describe Bundler::RubygemsIntegration do
let(:prerelease_specs_response) { Marshal.dump(["prerelease_specs"]) }
context "when a rubygems source mirror is set" do
- let(:orig_uri) { Bundler::URI("http://zombo.com") }
- let(:remote_with_mirror) { double("remote", :uri => uri, :original_uri => orig_uri) }
+ let(:orig_uri) { Gem::URI("http://zombo.com") }
+ let(:remote_with_mirror) { double("remote", uri: uri, original_uri: orig_uri) }
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(: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)
+ result = Bundler.rubygems.fetch_all_remote_specs(remote_with_mirror, fetcher)
expect(result).to eq(%w[specs prerelease_specs])
end
end
context "when there is no rubygems source mirror set" do
- let(:remote_no_mirror) { double("remote", :uri => uri, :original_uri => nil) }
+ let(:remote_no_mirror) { double("remote", uri: uri, original_uri: nil) }
it "does not set the 'X-Gemfile-Source' header" do
- expect(Bundler.rubygems).to receive(:gem_remote_fetcher).twice.and_return(fetcher)
- expect(fetcher).to_not receive(:headers=)
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_no_mirror)
+ result = Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror, fetcher)
expect(result).to eq(%w[specs prerelease_specs])
end
end
+
+ context "when loading an unexpected class" do
+ let(:remote_no_mirror) { double("remote", uri: uri, original_uri: nil) }
+ let(:unexpected_specs_response) { Marshal.dump(3) }
+
+ it "raises a MarshalError error" do
+ expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(unexpected_specs_response)
+ expect { Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror, fetcher) }.to raise_error(Bundler::MarshalError, /unexpected class/i)
+ end
+ end
end
end
diff --git a/spec/bundler/bundler/settings/validator_spec.rb b/spec/bundler/bundler/settings/validator_spec.rb
index e4ffd89435..b252ba59a0 100644
--- a/spec/bundler/bundler/settings/validator_spec.rb
+++ b/spec/bundler/bundler/settings/validator_spec.rb
@@ -44,14 +44,14 @@ RSpec.describe Bundler::Settings::Validator do
validate!("without", "b:c", "BUNDLE_WITH" => "a")
end.not_to raise_error
- expect { validate!("with", "b:c", "BUNDLE_WITHOUT" => "c:d") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ expect { validate!("with", "b:c", "BUNDLE_WITHOUT" => "c:d") }.to raise_error Bundler::InvalidOption, <<~EOS.strip
Setting `with` to "b:c" failed:
- a group cannot be in both `with` & `without` simultaneously
- `without` is current set to [:c, :d]
- the `c` groups conflict
EOS
- expect { validate!("without", "b:c", "BUNDLE_WITH" => "c:d") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ expect { validate!("without", "b:c", "BUNDLE_WITH" => "c:d") }.to raise_error Bundler::InvalidOption, <<~EOS.strip
Setting `without` to "b:c" failed:
- a group cannot be in both `with` & `without` simultaneously
- `with` is current set to [:c, :d]
@@ -74,7 +74,7 @@ RSpec.describe Bundler::Settings::Validator do
describe "#fail!" do
it "raises with a helpful message" do
- expect { subject.fail!("key", "value", "reason1", "reason2") }.to raise_error Bundler::InvalidOption, strip_whitespace(<<-EOS).strip
+ expect { subject.fail!("key", "value", "reason1", "reason2") }.to raise_error Bundler::InvalidOption, <<~EOS.strip
Setting `key` to "value" failed:
- rule description
- reason1
diff --git a/spec/bundler/bundler/settings_spec.rb b/spec/bundler/bundler/settings_spec.rb
index 116a038445..5e1aaaa555 100644
--- a/spec/bundler/bundler/settings_spec.rb
+++ b/spec/bundler/bundler/settings_spec.rb
@@ -6,12 +6,18 @@ RSpec.describe Bundler::Settings do
subject(:settings) { described_class.new(bundled_app) }
describe "#set_local" do
- context "when the local config file is not found" do
+ context "root is nil" do
subject(:settings) { described_class.new(nil) }
- it "raises a GemfileNotFound error with explanation" do
- expect { subject.set_local("foo", "bar") }.
- to raise_error(Bundler::GemfileNotFound, "Could not locate Gemfile")
+ before do
+ allow(Pathname).to receive(:new).and_call_original
+ allow(Pathname).to receive(:new).with(".bundle").and_return home(".bundle")
+ end
+
+ it "works" do
+ subject.set_local("foo", "bar")
+
+ expect(subject["foo"]).to eq("bar")
end
end
end
@@ -27,7 +33,7 @@ RSpec.describe Bundler::Settings do
"gem.mit" => "false",
"gem.test" => "minitest",
"thingy" => <<-EOS.tr("\n", " "),
---asdf --fdsa --ty=oh man i hope this doesnt break bundler because
+--asdf --fdsa --ty=oh man i hope this doesn't break bundler because
that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
--very-important-option=DontDeleteRoo
--very-important-option=DontDeleteRoo
@@ -64,13 +70,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
@@ -116,14 +119,20 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
settings.set_local :ssl_verify_mode, "1"
expect(settings[:ssl_verify_mode]).to be 1
end
+
+ it "coerces cooldown to integer" do
+ settings.set_local :cooldown, "7"
+ expect(settings[:cooldown]).to be 7
+ end
end
- context "when it's not possible to write to the file" do
+ context "when it's not possible to create the settings directory" do
it "raises an PermissionError with explanation" do
- expect(::Bundler::FileUtils).to receive(:mkdir_p).with(settings.send(:local_config_file).dirname).
- and_raise(Errno::EACCES)
+ settings_dir = settings.send(:local_config_file).dirname
+ expect(::Bundler::FileUtils).to receive(:mkdir_p).with(settings_dir).
+ and_raise(Errno::EACCES.new(settings_dir.to_s))
expect { settings.set_local :frozen, "1" }.
- to raise_error(Bundler::PermissionError, /config/)
+ to raise_error(Bundler::PermissionError, /#{settings_dir}/)
end
end
end
@@ -134,7 +143,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
Bundler.settings.set_command_option :no_install, true
- Bundler.settings.temporary(:no_install => false) do
+ Bundler.settings.temporary(no_install: false) do
expect(Bundler.settings[:no_install]).to eq false
end
@@ -150,23 +159,24 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
context "when called without a block" do
it "leaves the setting changed" do
- Bundler.settings.temporary(:foo => :random)
+ Bundler.settings.temporary(foo: :random)
expect(Bundler.settings[:foo]).to eq "random"
end
it "returns nil" do
- expect(Bundler.settings.temporary(:foo => :bar)).to be_nil
+ expect(Bundler.settings.temporary(foo: :bar)).to be_nil
end
end
end
describe "#set_global" do
- context "when it's not possible to write to the file" do
+ context "when it's not possible to write to create the settings directory" do
it "raises an PermissionError with explanation" do
- expect(::Bundler::FileUtils).to receive(:mkdir_p).with(settings.send(:global_config_file).dirname).
- and_raise(Errno::EACCES)
+ settings_dir = settings.send(:global_config_file).dirname
+ expect(::Bundler::FileUtils).to receive(:mkdir_p).with(settings_dir).
+ and_raise(Errno::EACCES.new(settings_dir.to_s))
expect { settings.set_global(:frozen, "1") }.
- to raise_error(Bundler::PermissionError, %r{\.bundle/config})
+ to raise_error(Bundler::PermissionError, /#{settings_dir}/)
end
end
end
@@ -182,7 +192,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
describe "#mirror_for" do
- let(:uri) { Bundler::URI("https://rubygems.org/") }
+ let(:uri) { Gem::URI("https://rubygems.org/") }
context "with no configured mirror" do
it "returns the original URI" do
@@ -195,7 +205,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
context "with a configured mirror" do
- let(:mirror_uri) { Bundler::URI("https://rubygems-mirror.org/") }
+ let(:mirror_uri) { Gem::URI("https://example-mirror.rubygems.org/") }
before { settings.set_local "mirror.https://rubygems.org/", mirror_uri.to_s }
@@ -216,7 +226,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
context "with a file URI" do
- let(:mirror_uri) { Bundler::URI("file:/foo/BAR/baz/qUx/") }
+ let(:mirror_uri) { Gem::URI("file:/foo/BAR/baz/qUx/") }
it "returns the mirror URI" do
expect(settings.mirror_for(uri)).to eq(mirror_uri)
@@ -234,7 +244,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
describe "#credentials_for" do
- let(:uri) { Bundler::URI("https://gemserver.example.org/") }
+ let(:uri) { Gem::URI("https://gemserver.example.org/") }
let(:credentials) { "username:password" }
context "with no configured credentials" do
@@ -272,12 +282,12 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
it "normalizes HTTP URIs in mirror configuration" do
- settings.set_local "mirror.http://rubygems.org", "http://rubygems-mirror.org"
+ settings.set_local "mirror.http://rubygems.org", "http://example-mirror.rubygems.org"
expect(settings.all).to include("mirror.http://rubygems.org/")
end
it "normalizes HTTPS URIs in mirror configuration" do
- settings.set_local "mirror.https://rubygems.org", "http://rubygems-mirror.org"
+ settings.set_local "mirror.https://rubygems.org", "http://example-mirror.rubygems.org"
expect(settings.all).to include("mirror.https://rubygems.org/")
end
@@ -292,9 +302,9 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
it "reads older keys without trailing slashes" do
- settings.set_local "mirror.https://rubygems.org", "http://rubygems-mirror.org"
+ settings.set_local "mirror.https://rubygems.org", "http://example-mirror.rubygems.org"
expect(settings.mirror_for("https://rubygems.org/")).to eq(
- Bundler::URI("http://rubygems-mirror.org/")
+ Gem::URI("http://example-mirror.rubygems.org/")
)
end
@@ -312,19 +322,59 @@ 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
- config("BUNDLE_MY__PERSONAL.RACK" => "~/Work/git/rack")
- expect(settings["my.personal.rack"]).to eq("~/Work/git/rack")
+ it "converts older keys without double underscore" do
+ bundle_config("BUNDLE_MY__PERSONAL.MYRACK" => "~/Work/git/myrack")
+ expect(settings["my.personal.myrack"]).to eq("~/Work/git/myrack")
+ end
+
+ it "converts older keys without trailing slashes and double underscore" do
+ bundle_config("BUNDLE_MIRROR__HTTPS://RUBYGEMS.ORG" => "http://example-mirror.rubygems.org")
+ expect(settings["mirror.https://rubygems.org/"]).to eq("http://example-mirror.rubygems.org")
+ end
+
+ it "ignores commented out keys" do
+ create_file bundled_app(".bundle/config"), <<~C
+ # BUNDLE_MY-PERSONAL-SERVER__ORG: my-personal-server.org
+ C
+
+ expect(Bundler.ui).not_to receive(:warn)
+ expect(settings.all).to be_empty
end
- it "converts older keys without trailing slashes and double dashes" do
- config("BUNDLE_MIRROR__HTTPS://RUBYGEMS.ORG" => "http://rubygems-mirror.org")
- expect(settings["mirror.https://rubygems.org/"]).to eq("http://rubygems-mirror.org")
+ it "converts older keys with dashes" do
+ bundle_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")
+ bundle_config("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://example-mirror.rubygems.org")
+ expect(settings["mirror.https://rubygems.org/"]).to eq("http://example-mirror.rubygems.org")
+ end
+ end
+
+ describe "default_cli_command validation" do
+ it "accepts 'install' as a valid value" do
+ expect { settings.set_local("default_cli_command", "install") }.not_to raise_error
+ end
+
+ it "accepts 'cli_help' as a valid value" do
+ expect { settings.set_local("default_cli_command", "cli_help") }.not_to raise_error
+ end
+
+ it "rejects invalid values" do
+ expect { settings.set_local("default_cli_command", "invalid") }.to raise_error(
+ Bundler::InvalidOption,
+ /Setting `default_cli_command` to "invalid" failed:\n - default_cli_command must be either 'install' or 'cli_help'\n - must be one of: install, cli_help/
+ )
+ end
+
+ it "accepts nil values" do
+ expect { settings.set_local("default_cli_command", nil) }.not_to raise_error
end
end
end
diff --git a/spec/bundler/bundler/shared_helpers_spec.rb b/spec/bundler/bundler/shared_helpers_spec.rb
index 97647dc6eb..41115aa667 100644
--- a/spec/bundler/bundler/shared_helpers_spec.rb
+++ b/spec/bundler/bundler/shared_helpers_spec.rb
@@ -1,12 +1,8 @@
# frozen_string_literal: true
RSpec.describe Bundler::SharedHelpers do
- let(:ext_lock_double) { double(:ext_lock) }
-
before do
pwd_stub
- allow(Bundler.rubygems).to receive(:ext_lock).and_return(ext_lock_double)
- allow(ext_lock_double).to receive(:synchronize) {|&block| block.call }
end
let(:pwd_stub) { allow(subject).to receive(:pwd).and_return(bundled_app) }
@@ -63,7 +59,7 @@ RSpec.describe Bundler::SharedHelpers do
before { allow(subject).to receive(:default_gemfile).and_return(gemfile_path) }
- it "returns the lock file path" do
+ it "returns the lockfile path" do
expect(subject.default_lockfile).to eq(expected_lockfile_path)
end
end
@@ -163,7 +159,7 @@ RSpec.describe Bundler::SharedHelpers do
let(:pwd_stub) { nil }
it "returns the current absolute path" do
- expect(subject.pwd).to eq(source_root)
+ expect(subject.pwd).to eq(git_root.to_s)
end
end
@@ -242,7 +238,14 @@ RSpec.describe Bundler::SharedHelpers do
shared_examples_for "ENV['RUBYOPT'] gets set correctly" do
it "ensures -rbundler/setup is at the beginning of ENV['RUBYOPT']" do
subject.set_bundle_environment
- expect(ENV["RUBYOPT"].split(" ")).to start_with("-r#{source_lib_dir}/bundler/setup")
+ expect(ENV["RUBYOPT"].split(" ")).to start_with("-r#{install_path}/bundler/setup")
+ end
+ end
+
+ shared_examples_for "ENV['BUNDLER_SETUP'] gets set correctly" do
+ it "ensures bundler/setup is set in ENV['BUNDLER_SETUP']" do
+ subject.set_bundle_environment
+ expect(ENV["BUNDLER_SETUP"]).to eq("#{source_lib_dir}/bundler/setup")
end
end
@@ -255,8 +258,7 @@ RSpec.describe Bundler::SharedHelpers do
it "ensures bundler's ruby version lib path is in ENV['RUBYLIB']" do
subject.set_bundle_environment
- paths = (ENV["RUBYLIB"]).split(File::PATH_SEPARATOR)
- expect(paths).to include(ruby_lib_path)
+ expect(rubylib).to include(ruby_lib_path)
end
end
@@ -273,15 +275,14 @@ RSpec.describe Bundler::SharedHelpers do
subject.set_bundle_environment
- paths = (ENV["RUBYLIB"]).split(File::PATH_SEPARATOR)
- expect(paths.count(RbConfig::CONFIG["rubylibdir"])).to eq(0)
+ expect(rubylib.count(RbConfig::CONFIG["rubylibdir"])).to eq(0)
end
it "exits if bundle path contains the unix-like path separator" do
if Gem.respond_to?(:path_separator)
allow(Gem).to receive(:path_separator).and_return(":")
else
- stub_const("File::PATH_SEPARATOR", ":".freeze)
+ stub_const("File::PATH_SEPARATOR", ":")
end
allow(Bundler).to receive(:bundle_path) { Pathname.new("so:me/dir/bin") }
expect { subject.send(:validate_bundle_path) }.to raise_error(
@@ -353,25 +354,46 @@ RSpec.describe Bundler::SharedHelpers do
it "ENV['PATH'] should only contain one instance of bundle bin path" do
subject.set_bundle_environment
- paths = (ENV["PATH"]).split(File::PATH_SEPARATOR)
+ paths = ENV["PATH"].split(File::PATH_SEPARATOR)
expect(paths.count(bundle_path)).to eq(1)
end
end
- context "ENV['RUBYOPT'] does not exist" do
- before { ENV.delete("RUBYOPT") }
+ context "when bundler install path is standard" do
+ let(:install_path) { source_lib_dir }
- it_behaves_like "ENV['RUBYOPT'] gets set correctly"
- end
+ context "ENV['RUBYOPT'] does not exist" do
+ before { ENV.delete("RUBYOPT") }
- context "ENV['RUBYOPT'] exists without -rbundler/setup" do
- before { ENV["RUBYOPT"] = "-I/some_app_path/lib" }
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
- it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ context "ENV['RUBYOPT'] exists without -rbundler/setup" do
+ before { ENV["RUBYOPT"] = "-I/some_app_path/lib" }
+
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
+
+ context "ENV['RUBYOPT'] exists and contains -rbundler/setup" do
+ before { ENV["RUBYOPT"] = "-rbundler/setup" }
+
+ it_behaves_like "ENV['RUBYOPT'] gets set correctly"
+ end
end
- context "ENV['RUBYOPT'] exists and contains -rbundler/setup" do
- before { ENV["RUBYOPT"] = "-rbundler/setup" }
+ context "when bundler install path contains special characters" do
+ let(:install_path) { "/opt/ruby3.3.0-preview2/lib/ruby/3.3.0+0" }
+
+ before do
+ ENV["RUBYOPT"] = "-r#{install_path}/bundler/setup"
+ allow(File).to receive(:expand_path).and_return("#{install_path}/bundler/setup")
+ allow(Gem).to receive(:bin_path).and_return("#{install_path}/bundler/setup")
+ end
+
+ it "ensures -rbundler/setup is not duplicated" do
+ subject.set_bundle_environment
+ expect(ENV["RUBYOPT"].split(" ").grep(%r{-r.*/bundler/setup}).length).to eq(1)
+ end
it_behaves_like "ENV['RUBYOPT'] gets set correctly"
end
@@ -401,7 +423,7 @@ RSpec.describe Bundler::SharedHelpers do
it "sets BUNDLE_BIN_PATH to the bundle executable file" do
subject.set_bundle_environment
bin_path = ENV["BUNDLE_BIN_PATH"]
- expect(bin_path).to eq(bindir.join("bundle").to_s)
+ expect(bin_path).to eq(exedir.join("bundle").to_s)
expect(File.exist?(bin_path)).to be true
end
end
@@ -417,8 +439,7 @@ RSpec.describe Bundler::SharedHelpers do
it "ENV['RUBYLIB'] should only contain one instance of bundler's ruby version lib path" do
subject.set_bundle_environment
- paths = (ENV["RUBYLIB"]).split(File::PATH_SEPARATOR)
- expect(paths.count(ruby_lib_path)).to eq(1)
+ expect(rubylib.count(ruby_lib_path)).to eq(1)
end
end
end
@@ -434,7 +455,7 @@ RSpec.describe Bundler::SharedHelpers do
end
context "system throws Errno::EACESS" do
- let(:file_op_block) { proc {|_path| raise Errno::EACCES } }
+ let(:file_op_block) { proc {|_path| raise Errno::EACCES.new("/path") } }
it "raises a PermissionError" do
expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
@@ -489,33 +510,9 @@ RSpec.describe Bundler::SharedHelpers do
it "raises a GenericSystemCallError" do
expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
- Bundler::GenericSystemCallError, /error accessing.+underlying.+Shields down/m
+ Bundler::GenericSystemCallError, /error creating.+underlying.+Shields down/m
)
end
end
end
-
- describe "#const_get_safely" do
- module TargetNamespace
- VALID_CONSTANT = 1
- end
-
- context "when the namespace does have the requested constant" do
- it "returns the value of the requested constant" do
- expect(subject.const_get_safely(:VALID_CONSTANT, TargetNamespace)).to eq(1)
- end
- end
-
- context "when the requested constant is passed as a string" do
- it "returns the value of the requested constant" do
- expect(subject.const_get_safely("VALID_CONSTANT", TargetNamespace)).to eq(1)
- end
- end
-
- context "when the namespace does not have the requested constant" do
- it "returns nil" do
- expect(subject.const_get_safely("INVALID_CONSTANT", TargetNamespace)).to be_nil
- end
- end
- end
end
diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb
index 169d7234b4..1f10ca4b07 100644
--- a/spec/bundler/bundler/source/git/git_proxy_spec.rb
+++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb
@@ -2,40 +2,98 @@
RSpec.describe Bundler::Source::Git::GitProxy do
let(:path) { Pathname("path") }
- let(:uri) { "https://github.com/bundler/bundler.git" }
- let(:ref) { "HEAD" }
+ let(:uri) { "https://github.com/ruby/rubygems.git" }
+ let(:ref) { nil }
+ let(:branch) { nil }
+ let(:tag) { nil }
+ let(:options) { { "ref" => ref, "branch" => branch, "tag" => tag }.compact }
let(:revision) { nil }
let(:git_source) { nil }
- subject { described_class.new(path, uri, ref, revision, git_source) }
+ let(:clone_result) { double(Process::Status, success?: true) }
+ let(:fail_result) { double(Process::Status, success?: false) }
+ let(:base_clone_args) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch"] }
+ let(:base_fetch_args) { ["fetch", "--force", "--quiet", "--no-tags", "--depth", "1"] }
+ subject(:git_proxy) { described_class.new(path, uri, options, revision, git_source) }
+
+ context "with explicit ref" do
+ context "with branch only" do
+ let(:branch) { "main" }
+ it "sets explicit ref to branch" do
+ expect(git_proxy.explicit_ref).to eq(branch)
+ end
+ end
+
+ context "with ref only" do
+ let(:ref) { "HEAD" }
+ it "sets explicit ref to ref" do
+ expect(git_proxy.explicit_ref).to eq(ref)
+ end
+ end
+
+ context "with tag only" do
+ let(:tag) { "v1.0" }
+ it "sets explicit ref to ref" do
+ expect(git_proxy.explicit_ref).to eq(tag)
+ end
+ end
+
+ context "with tag and branch" do
+ let(:tag) { "v1.0" }
+ let(:branch) { "main" }
+ it "raises error" do
+ expect { git_proxy }.to raise_error(Bundler::Source::Git::AmbiguousGitReference)
+ end
+ end
+
+ context "with tag and ref" do
+ let(:tag) { "v1.0" }
+ let(:ref) { "HEAD" }
+ it "raises error" do
+ expect { git_proxy }.to raise_error(Bundler::Source::Git::AmbiguousGitReference)
+ end
+ end
+
+ context "with branch and ref" do
+ let(:branch) { "main" }
+ let(:ref) { "HEAD" }
+ it "honors ref over branch" do
+ expect(git_proxy.explicit_ref).to eq(ref)
+ end
+ end
+ end
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(match("https://u:p@github.com/bundler/bundler.git"))
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/ruby/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
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(match("https://u:p@github.com/bundler/bundler.git"))
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/ruby/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
it "does not add username and password to mismatched URI" do
- Bundler.settings.temporary("https://u:p@github.com/bundler/bundler-mismatch.git" => "u:p") do
- expect(subject).to receive(:git_retry).with(match(uri))
+ Bundler.settings.temporary("https://u:p@github.com/ruby/rubygems-mismatch.git" => "u:p") do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
it "keeps original userinfo" do
Bundler.settings.temporary("github.com" => "u:p") do
- original = "https://orig:info@github.com/bundler/bundler.git"
- subject = described_class.new(Pathname("path"), original, "HEAD")
- expect(subject).to receive(:git_retry).with(match(original))
- subject.checkout
+ original = "https://orig:info@github.com/ruby/rubygems.git"
+ git_proxy = described_class.new(Pathname("path"), original, options)
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", original, path.to_s], nil).and_return(["", "", clone_result])
+ git_proxy.checkout
end
end
end
@@ -43,46 +101,46 @@ RSpec.describe Bundler::Source::Git::GitProxy do
describe "#version" do
context "with a normal version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(described_class).to receive(:full_version).
and_return("git version 1.2.3")
end
it "returns the git version number" do
- expect(subject.version).to eq("1.2.3")
+ expect(git_proxy.version).to eq("1.2.3")
end
it "does not raise an error when passed into Gem::Version.create" do
- expect { Gem::Version.create subject.version }.not_to raise_error
+ expect { Gem::Version.create git_proxy.version }.not_to raise_error
end
end
context "with a OSX version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(described_class).to receive(:full_version).
and_return("git version 1.2.3 (Apple Git-BS)")
end
it "strips out OSX specific additions in the version string" do
- expect(subject.version).to eq("1.2.3")
+ expect(git_proxy.version).to eq("1.2.3")
end
it "does not raise an error when passed into Gem::Version.create" do
- expect { Gem::Version.create subject.version }.not_to raise_error
+ expect { Gem::Version.create git_proxy.version }.not_to raise_error
end
end
context "with a msysgit version number" do
before do
- expect(subject).to receive(:git).with("--version").
+ expect(described_class).to receive(:full_version).
and_return("git version 1.2.3.msysgit.0")
end
it "strips out msysgit specific additions in the version string" do
- expect(subject.version).to eq("1.2.3")
+ expect(git_proxy.version).to eq("1.2.3")
end
it "does not raise an error when passed into Gem::Version.create" do
- expect { Gem::Version.create subject.version }.not_to raise_error
+ expect { Gem::Version.create git_proxy.version }.not_to raise_error
end
end
end
@@ -90,59 +148,206 @@ RSpec.describe Bundler::Source::Git::GitProxy do
describe "#full_version" do
context "with a normal version number" do
before do
- expect(subject).to receive(:git).with("--version").
- and_return("git version 1.2.3")
+ status = double("success?" => true)
+ expect(Open3).to receive(:capture3).with("git", "--version").
+ and_return(["git version 1.2.3", "", status])
end
it "returns the git version number" do
- expect(subject.full_version).to eq("1.2.3")
+ expect(git_proxy.full_version).to eq("1.2.3")
end
end
context "with a OSX version number" do
before do
- expect(subject).to receive(:git).with("--version").
- and_return("git version 1.2.3 (Apple Git-BS)")
+ status = double("success?" => true)
+ expect(Open3).to receive(:capture3).with("git", "--version").
+ and_return(["git version 1.2.3 (Apple Git-BS)", "", status])
end
it "does not strip out OSX specific additions in the version string" do
- expect(subject.full_version).to eq("1.2.3 (Apple Git-BS)")
+ expect(git_proxy.full_version).to eq("1.2.3 (Apple Git-BS)")
end
end
context "with a msysgit version number" do
before do
- expect(subject).to receive(:git).with("--version").
- and_return("git version 1.2.3.msysgit.0")
+ status = double("success?" => true)
+ expect(Open3).to receive(:capture3).with("git", "--version").
+ and_return(["git version 1.2.3.msysgit.0", "", status])
end
it "does not strip out msysgit specific additions in the version string" do
- expect(subject.full_version).to eq("1.2.3.msysgit.0")
+ expect(git_proxy.full_version).to eq("1.2.3.msysgit.0")
end
end
end
- describe "#copy_to" do
- let(:cache) { tmpdir("cache_path") }
- let(:destination) { tmpdir("copy_to_path") }
- let(:submodules) { false }
-
- context "when given a SHA as a revision" do
- let(:revision) { "abcd" * 10 }
- let(:command) { "reset --hard #{revision}" }
-
- it "fails gracefully when resetting to the revision fails" do
- expect(subject).to receive(:git_retry).with(start_with("clone ")) { destination.mkpath }
- expect(subject).to receive(:git_retry).with(start_with("fetch "), :dir => destination)
- expect(subject).to receive(:git).with(command, :dir => destination).and_raise(Bundler::Source::Git::GitCommandError.new(command, cache, destination))
- expect(subject).not_to receive(:git)
-
- expect { subject.copy_to(destination, submodules) }.
- to raise_error(
- Bundler::Source::Git::MissingGitRevisionError,
- "Git error: command `git #{command}` in directory #{destination} has failed.\n" \
- "Revision #{revision} does not exist in the repository #{uri}. Maybe you misspelled it?" \
- )
+ 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
+
+ context "URI is HTTP" do
+ let(:uri) { "http://github.com/ruby/rubygems.git" }
+ let(:clone_args_without_depth) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--single-branch"] }
+
+ it "retries clone without --depth when dumb http transport fails" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "dumb http transport does not support shallow capabilities", fail_result])
+ expect(git_proxy).to receive(:capture).with([*clone_args_without_depth, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
+
+ subject.checkout
+ end
+ end
+
+ describe "#installed_to?" do
+ let(:destination) { "install/dir" }
+ let(:destination_dir_exists) { true }
+ let(:children) { ["gem.gemspec", "README.me", ".git", "Rakefile"] }
+
+ before do
+ allow(Dir).to receive(:exist?).with(destination).and_return(destination_dir_exists)
+ allow(Dir).to receive(:children).with(destination).and_return(children)
+ end
+
+ context "when destination dir exists with children other than just .git" do
+ it "returns true" do
+ expect(git_proxy.installed_to?(destination)).to be true
+ end
+ end
+
+ context "when destination dir does not exist" do
+ let(:destination_dir_exists) { false }
+
+ it "returns false" do
+ expect(git_proxy.installed_to?(destination)).to be false
+ end
+ end
+
+ context "when destination dir is empty" do
+ let(:children) { [] }
+
+ it "returns false" do
+ expect(git_proxy.installed_to?(destination)).to be false
+ end
+ end
+
+ context "when destination dir has only .git directory" do
+ let(:children) { [".git"] }
+
+ it "returns false" do
+ expect(git_proxy.installed_to?(destination)).to be false
+ end
+ end
+ end
+
+ describe "#checkout" do
+ context "when the repository isn't cloned" do
+ before do
+ allow(path).to receive(:exist?).and_return(false)
+ end
+
+ it "clones the repository" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+
+ context "when the repository is cloned" do
+ before do
+ allow(path).to receive(:exist?).and_return(true)
+ end
+
+ context "with a locked revision" do
+ let(:revision) { Digest::SHA1.hexdigest("ruby") }
+
+ context "when the revision exists locally" do
+ it "uses the cached revision" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:git).with("cat-file", "-e", revision, dir: path).and_return(true)
+ subject.checkout
+ end
+ end
+
+ context "when the revision doesn't exist locally" do
+ it "fetches the specific revision" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:git).with("cat-file", "-e", revision, dir: path).and_raise(Bundler::GitError)
+ expect(git_proxy).to receive(:capture).with(["fetch", "--force", "--quiet", "--no-tags", "--depth", "1", "--", uri, "#{revision}:refs/#{revision}-sha"], path).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+ end
+
+ context "with no explicit ref" do
+ it "fetches the HEAD revision" do
+ parsed_revision = Digest::SHA1.hexdigest("ruby")
+ allow(git_proxy).to receive(:git_local).with("rev-parse", "--abbrev-ref", "HEAD", dir: path).and_return(parsed_revision)
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with(["fetch", "--force", "--quiet", "--no-tags", "--depth", "1", "--", uri, "refs/heads/#{parsed_revision}:refs/heads/#{parsed_revision}"], path).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+
+ context "with a commit ref" do
+ let(:ref) { Digest::SHA1.hexdigest("ruby") }
+
+ context "when the revision exists locally" do
+ it "uses the cached revision" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:git).with("cat-file", "-e", ref, dir: path).and_return(true)
+ subject.checkout
+ end
+ end
+
+ context "when the revision doesn't exist locally" do
+ it "fetches the specific revision" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:git).with("cat-file", "-e", ref, dir: path).and_raise(Bundler::GitError)
+ expect(git_proxy).to receive(:capture).with(["fetch", "--force", "--quiet", "--no-tags", "--depth", "1", "--", uri, "#{ref}:refs/#{ref}-sha"], path).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+ end
+
+ context "with a non-commit ref" do
+ let(:ref) { "HEAD" }
+
+ it "fetches all revisions" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with(["fetch", "--force", "--quiet", "--no-tags", "--", uri, "refs/*:refs/*"], path).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+
+ context "URI is HTTP" do
+ let(:uri) { "http://github.com/ruby/rubygems.git" }
+
+ it "retries fetch without --depth when dumb http transport fails" do
+ parsed_revision = Digest::SHA1.hexdigest("ruby")
+ allow(git_proxy).to receive(:git_local).with("rev-parse", "--abbrev-ref", "HEAD", dir: path).and_return(parsed_revision)
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_fetch_args, "--", uri, "refs/heads/#{parsed_revision}:refs/heads/#{parsed_revision}"], path).and_return(["", "dumb http transport does not support shallow capabilities", fail_result])
+ expect(git_proxy).to receive(:capture).with(["fetch", "--force", "--quiet", "--no-tags", "--", uri, "refs/heads/#{parsed_revision}:refs/heads/#{parsed_revision}"], path).and_return(["", "", clone_result])
+ subject.checkout
+ end
end
end
end
diff --git a/spec/bundler/bundler/source/git_spec.rb b/spec/bundler/bundler/source/git_spec.rb
index f7475a35aa..14e91c6bdc 100644
--- a/spec/bundler/bundler/source/git_spec.rb
+++ b/spec/bundler/bundler/source/git_spec.rb
@@ -14,14 +14,109 @@ RSpec.describe Bundler::Source::Git do
describe "#to_s" do
it "returns a description" do
- expect(subject.to_s).to eq "https://github.com/foo/bar.git (at master)"
+ expect(subject.to_s).to eq "https://github.com/foo/bar.git"
end
context "when the URI contains credentials" do
let(:uri) { "https://my-secret-token:x-oauth-basic@github.com/foo/bar.git" }
it "filters credentials" do
- expect(subject.to_s).to eq "https://x-oauth-basic@github.com/foo/bar.git (at master)"
+ 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
+
+ describe "#locked_revision_checked_out?" do
+ let(:revision) { "abc" }
+ let(:git_proxy_revision) { revision }
+ let(:git_proxy_installed) { true }
+ let(:git_proxy) { subject.send(:git_proxy) }
+ let(:options) do
+ {
+ "uri" => uri,
+ "revision" => revision,
+ }
+ end
+
+ before do
+ allow(git_proxy).to receive(:revision).and_return(git_proxy_revision)
+ allow(git_proxy).to receive(:installed_to?).with(subject.install_path).and_return(git_proxy_installed)
+ end
+
+ context "when the locked revision is checked out" do
+ it "returns true" do
+ expect(subject.send(:locked_revision_checked_out?)).to be true
+ end
+ end
+
+ context "when no revision is provided" do
+ let(:options) do
+ { "uri" => uri }
+ end
+
+ it "returns falsey value" do
+ expect(subject.send(:locked_revision_checked_out?)).to be_falsey
+ end
+ end
+
+ context "when the git proxy revision is different than the git revision" do
+ let(:git_proxy_revision) { revision.next }
+
+ it "returns falsey value" do
+ expect(subject.send(:locked_revision_checked_out?)).to be_falsey
+ end
+ end
+
+ context "when the gem hasn't been installed" do
+ let(:git_proxy_installed) { false }
+
+ it "returns falsey value" do
+ expect(subject.send(:locked_revision_checked_out?)).to be_falsey
end
end
end
diff --git a/spec/bundler/bundler/source/rubygems/remote_spec.rb b/spec/bundler/bundler/source/rubygems/remote_spec.rb
index 07ce4f968e..27430d4a3b 100644
--- a/spec/bundler/bundler/source/rubygems/remote_spec.rb
+++ b/spec/bundler/bundler/source/rubygems/remote_spec.rb
@@ -11,8 +11,8 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
allow(Digest(:MD5)).to receive(:hexdigest).with(duck_type(:to_s)) {|string| "MD5HEX(#{string})" }
end
- let(:uri_no_auth) { Bundler::URI("https://gems.example.com") }
- let(:uri_with_auth) { Bundler::URI("https://#{credentials}@gems.example.com") }
+ let(:uri_no_auth) { Gem::URI("https://gems.example.com") }
+ let(:uri_with_auth) { Gem::URI("https://#{credentials}@gems.example.com") }
let(:credentials) { "username:password" }
context "when the original URI has no credentials" do
@@ -89,11 +89,11 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
end
context "when the original URI has only a username" do
- let(:uri) { Bundler::URI("https://SeCrEt-ToKeN@gem.fury.io/me/") }
+ let(:uri) { Gem::URI("https://SeCrEt-ToKeN@gem.fury.io/me/") }
describe "#anonymized_uri" do
it "returns the URI without username and password" do
- expect(remote(uri).anonymized_uri).to eq(Bundler::URI("https://gem.fury.io/me/"))
+ expect(remote(uri).anonymized_uri).to eq(Gem::URI("https://gem.fury.io/me/"))
end
end
@@ -105,9 +105,9 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
end
context "when a mirror with inline credentials is configured for the URI" do
- let(:uri) { Bundler::URI("https://rubygems.org/") }
- let(:mirror_uri_with_auth) { Bundler::URI("https://username:password@rubygems-mirror.org/") }
- let(:mirror_uri_no_auth) { Bundler::URI("https://rubygems-mirror.org/") }
+ let(:uri) { Gem::URI("https://rubygems.org/") }
+ let(:mirror_uri_with_auth) { Gem::URI("https://username:password@example-mirror.rubygems.org/") }
+ let(:mirror_uri_no_auth) { Gem::URI("https://example-mirror.rubygems.org/") }
before { Bundler.settings.temporary("mirror.https://rubygems.org/" => mirror_uri_with_auth.to_s) }
@@ -131,9 +131,9 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
end
context "when a mirror with configured credentials is configured for the URI" do
- let(:uri) { Bundler::URI("https://rubygems.org/") }
- let(:mirror_uri_with_auth) { Bundler::URI("https://#{credentials}@rubygems-mirror.org/") }
- let(:mirror_uri_no_auth) { Bundler::URI("https://rubygems-mirror.org/") }
+ let(:uri) { Gem::URI("https://rubygems.org/") }
+ let(:mirror_uri_with_auth) { Gem::URI("https://#{credentials}@example-mirror.rubygems.org/") }
+ let(:mirror_uri_no_auth) { Gem::URI("https://example-mirror.rubygems.org/") }
before do
Bundler.settings.temporary("mirror.https://rubygems.org/" => mirror_uri_no_auth.to_s)
@@ -169,4 +169,39 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
end
end
end
+
+ describe "#cooldown" do
+ it "is nil by default" do
+ expect(remote(uri_no_auth).cooldown).to be_nil
+ end
+
+ it "returns the value passed to the constructor" do
+ r = Bundler::Source::Rubygems::Remote.new(uri_no_auth, cooldown: 7)
+ expect(r.cooldown).to eq(7)
+ end
+ end
+
+ describe "#effective_cooldown" do
+ it "returns the per-remote value when no override is set" do
+ r = Bundler::Source::Rubygems::Remote.new(uri_no_auth, cooldown: 7)
+ expect(r.effective_cooldown).to eq(7)
+ end
+
+ it "returns nil when neither override nor per-remote value is set" do
+ expect(remote(uri_no_auth).effective_cooldown).to be_nil
+ end
+
+ it "settings override per-remote value" do
+ r = Bundler::Source::Rubygems::Remote.new(uri_no_auth, cooldown: 7)
+ Bundler.settings.temporary(cooldown: 14) do
+ expect(r.effective_cooldown).to eq(14)
+ end
+ end
+
+ it "settings override even when per-remote value is absent" do
+ Bundler.settings.temporary(cooldown: 14) do
+ expect(remote(uri_no_auth).effective_cooldown).to eq(14)
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/source/rubygems_spec.rb b/spec/bundler/bundler/source/rubygems_spec.rb
index 7c457a7265..feb787498e 100644
--- a/spec/bundler/bundler/source/rubygems_spec.rb
+++ b/spec/bundler/bundler/source/rubygems_spec.rb
@@ -30,4 +30,75 @@ 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
+
+ describe "#clear_cache" do
+ it "invalidates memoized indexes so subsequent reads rebuild them" do
+ source = described_class.new
+
+ first_specs = source.specs
+ first_installed = source.send(:installed_specs)
+ first_default = source.send(:default_specs)
+ first_cached = source.send(:cached_specs)
+
+ expect(source.specs).to equal(first_specs)
+ expect(source.send(:installed_specs)).to equal(first_installed)
+ expect(source.send(:default_specs)).to equal(first_default)
+ expect(source.send(:cached_specs)).to equal(first_cached)
+
+ source.clear_cache
+
+ expect(source.specs).not_to equal(first_specs)
+ expect(source.send(:installed_specs)).not_to equal(first_installed)
+ expect(source.send(:default_specs)).not_to equal(first_default)
+ expect(source.send(:cached_specs)).not_to equal(first_cached)
+ end
+
+ it "reflects newly-discovered installed gems after clear_cache" do
+ source = described_class.new
+ foo = Gem::Specification.new("foo", "1.0.0")
+ bar = Gem::Specification.new("bar", "1.0.0")
+
+ allow(Bundler.rubygems).to receive(:installed_specs).and_return([foo])
+ expect(source.send(:installed_specs).search("bar")).to be_empty
+
+ allow(Bundler.rubygems).to receive(:installed_specs).and_return([foo, bar])
+ expect(source.send(:installed_specs).search("bar")).to be_empty
+
+ source.clear_cache
+
+ expect(source.send(:installed_specs).search("bar")).not_to be_empty
+ end
+ end
+
+ describe "log debug information" do
+ it "log the time spent downloading and installing a gem" do
+ build_repo2 do
+ build_gem "warning"
+ end
+
+ gemfile_content = <<~G
+ source "https://gem.repo2"
+ gem "warning"
+ G
+
+ stdout = install_gemfile(gemfile_content, env: { "DEBUG" => "1" })
+
+ expect(stdout).to match(/Downloaded warning in: \d+\.\d+s/)
+ expect(stdout).to match(/Installed warning in: \d+\.\d+s/)
+ end
+ end
end
diff --git a/spec/bundler/bundler/source_list_spec.rb b/spec/bundler/bundler/source_list_spec.rb
index 93159998c6..61bd99b063 100644
--- a/spec/bundler/bundler/source_list_spec.rb
+++ b/spec/bundler/bundler/source_list_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Bundler::SourceList do
subject(:source_list) { Bundler::SourceList.new }
- let(:rubygems_aggregate) { Bundler::Source::Rubygems.new }
+ let(:global_rubygems_source) { Bundler::Source::Rubygems.new }
let(:metadata_source) { Bundler::Source::Metadata.new }
describe "adding sources" do
@@ -75,7 +75,7 @@ RSpec.describe Bundler::SourceList do
let(:msg) do
"The git source `git://existing-git.org/path.git` " \
"uses the `git` protocol, which transmits data without encryption. " \
- "Disable this warning with `bundle config set git.allow_insecure true`, " \
+ "Disable this warning with `bundle config set --local git.allow_insecure true`, " \
"or switch to the `https` protocol to keep your data secure."
end
@@ -85,7 +85,7 @@ RSpec.describe Bundler::SourceList do
end
it "ignores git protocols on request" do
- Bundler.settings.temporary(:"git.allow_insecure" => true)
+ Bundler.settings.temporary("git.allow_insecure": true)
expect(Bundler.ui).to_not receive(:warn).with(msg)
source_list.add_git_source("uri" => "git://existing-git.org/path.git")
end
@@ -115,20 +115,26 @@ 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
+ it "returns the global 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")
+ it "adds the provided remote to the beginning of the global source" do
+ 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/"),
+ Gem::URI("https://othersource.org/"),
+ Gem::URI("https://rubygems.org/"),
]
end
+
+ it "records the per-remote cooldown when supplied" do
+ source_list.add_global_rubygems_remote("https://othersource.org", cooldown: 7)
+ expect(returned_source.cooldown_for(Gem::URI("https://othersource.org/"))).to eq(7)
+ expect(returned_source.cooldown_for(Gem::URI("https://rubygems.org/"))).to be_nil
+ end
end
describe "#add_plugin_source" do
@@ -156,21 +162,21 @@ RSpec.describe Bundler::SourceList do
end
describe "#all_sources" do
- it "includes the aggregate rubygems source when rubygems sources have been added" do
+ it "includes the global rubygems source when rubygems sources have been added" do
source_list.add_git_source("uri" => "git://host/path.git")
source_list.add_rubygems_source("remotes" => ["https://rubygems.org"])
source_list.add_path_source("path" => "/path/to/gem")
source_list.add_plugin_source("new_source", "uri" => "https://some.url/a")
- expect(source_list.all_sources).to include rubygems_aggregate
+ expect(source_list.all_sources).to include global_rubygems_source
end
- it "includes the aggregate rubygems source when no rubygems sources have been added" do
+ it "includes the global rubygems source when no rubygems sources have been added" do
source_list.add_git_source("uri" => "git://host/path.git")
source_list.add_path_source("path" => "/path/to/gem")
source_list.add_plugin_source("new_source", "uri" => "https://some.url/a")
- expect(source_list.all_sources).to include rubygems_aggregate
+ expect(source_list.all_sources).to include global_rubygems_source
end
it "returns sources of the same type in the reverse order that they were added" do
@@ -204,7 +210,7 @@ RSpec.describe Bundler::SourceList do
Bundler::Source::Rubygems.new("remotes" => ["https://third-rubygems.org"]),
Bundler::Source::Rubygems.new("remotes" => ["https://fourth-rubygems.org"]),
Bundler::Source::Rubygems.new("remotes" => ["https://fifth-rubygems.org"]),
- rubygems_aggregate,
+ global_rubygems_source,
metadata_source,
]
end
@@ -212,22 +218,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 +246,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 +254,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 +275,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 +285,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 [
@@ -297,19 +303,19 @@ RSpec.describe Bundler::SourceList do
end
describe "#rubygems_sources" do
- it "includes the aggregate rubygems source when rubygems sources have been added" do
+ it "includes the global rubygems source when rubygems sources have been added" do
source_list.add_git_source("uri" => "git://host/path.git")
source_list.add_rubygems_source("remotes" => ["https://rubygems.org"])
source_list.add_path_source("path" => "/path/to/gem")
- expect(source_list.rubygems_sources).to include rubygems_aggregate
+ expect(source_list.rubygems_sources).to include global_rubygems_source
end
- it "returns only the aggregate rubygems source when no rubygems sources have been added" do
+ it "returns only the global rubygems source when no rubygems sources have been added" do
source_list.add_git_source("uri" => "git://host/path.git")
source_list.add_path_source("path" => "/path/to/gem")
- expect(source_list.rubygems_sources).to eq [rubygems_aggregate]
+ expect(source_list.rubygems_sources).to eq [global_rubygems_source]
end
it "returns rubygems sources in the reverse order that they were added" do
@@ -331,7 +337,7 @@ RSpec.describe Bundler::SourceList do
Bundler::Source::Rubygems.new("remotes" => ["https://third-rubygems.org"]),
Bundler::Source::Rubygems.new("remotes" => ["https://fourth-rubygems.org"]),
Bundler::Source::Rubygems.new("remotes" => ["https://fifth-rubygems.org"]),
- rubygems_aggregate,
+ global_rubygems_source,
]
end
end
@@ -339,7 +345,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 +378,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 +447,29 @@ RSpec.describe Bundler::SourceList do
source_list.remote!
end
end
+
+ describe "#clear_cache" do
+ let(:rubygems_source) { source_list.add_rubygems_source("remotes" => ["https://rubygems.org"]) }
+
+ it "calls #clear_cache on all rubygems sources" do
+ expect(rubygems_source).to receive(:clear_cache)
+ expect(source_list.global_rubygems_source).to receive(:clear_cache)
+ source_list.clear_cache
+ 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/source_spec.rb b/spec/bundler/bundler/source_spec.rb
index 0c35c27fdf..01b57ce9e8 100644
--- a/spec/bundler/bundler/source_spec.rb
+++ b/spec/bundler/bundler/source_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Bundler::Source do
end
describe "#version_message" do
- let(:spec) { double(:spec, :name => "nokogiri", :version => ">= 1.6", :platform => rb) }
+ let(:spec) { double(:spec, name: "nokogiri", version: ">= 1.6", platform: Gem::Platform::RUBY) }
shared_examples_for "the lockfile specs are not relevant" do
it "should return a string with the spec name and version" do
@@ -30,31 +30,21 @@ RSpec.describe Bundler::Source do
end
context "when there are locked gems" do
- let(:locked_gems) { double(:locked_gems) }
-
- before { allow(Bundler).to receive(:locked_gems).and_return(locked_gems) }
-
context "that contain the relevant gem spec" do
- before do
- specs = double(:specs)
- allow(locked_gems).to receive(:specs).and_return(specs)
- allow(specs).to receive(:find).and_return(locked_gem)
- end
-
context "without a version" do
- let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => nil) }
+ let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: nil) }
it_behaves_like "the lockfile specs are not relevant"
end
context "with the same version" do
- let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => ">= 1.6") }
+ let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: ">= 1.6") }
it_behaves_like "the lockfile specs are not relevant"
end
context "with a different version" do
- let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "< 1.5") }
+ let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "< 1.5") }
context "with color", :no_color_tty do
before do
@@ -62,7 +52,7 @@ RSpec.describe Bundler::Source do
end
it "should return a string with the spec name and version and locked spec version" do
- expect(subject.version_message(spec)).to eq("nokogiri >= 1.6\e[32m (was < 1.5)\e[0m")
+ expect(subject.version_message(spec, locked_gem)).to eq("nokogiri >= 1.6\e[32m (was < 1.5)\e[0m")
end
end
@@ -74,14 +64,14 @@ RSpec.describe Bundler::Source do
end
it "should return a string with the spec name and version and locked spec version" do
- expect(subject.version_message(spec)).to eq("nokogiri >= 1.6 (was < 1.5)")
+ expect(subject.version_message(spec, locked_gem)).to eq("nokogiri >= 1.6 (was < 1.5)")
end
end
end
context "with a more recent version" do
- let(:spec) { double(:spec, :name => "nokogiri", :version => "1.6.1", :platform => rb) }
- let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "1.7.0") }
+ let(:spec) { double(:spec, name: "nokogiri", version: "1.6.1", platform: Gem::Platform::RUBY) }
+ let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
context "with color", :no_color_tty do
before do
@@ -89,7 +79,7 @@ RSpec.describe Bundler::Source do
end
it "should return a string with the locked spec version in yellow" do
- expect(subject.version_message(spec)).to eq("nokogiri 1.6.1\e[33m (was 1.7.0)\e[0m")
+ expect(subject.version_message(spec, locked_gem)).to eq("nokogiri 1.6.1\e[33m (was 1.7.0)\e[0m")
end
end
@@ -101,14 +91,14 @@ RSpec.describe Bundler::Source do
end
it "should return a string with the locked spec version in yellow" do
- expect(subject.version_message(spec)).to eq("nokogiri 1.6.1 (was 1.7.0)")
+ expect(subject.version_message(spec, locked_gem)).to eq("nokogiri 1.6.1 (was 1.7.0)")
end
end
end
context "with an older version" do
- let(:spec) { double(:spec, :name => "nokogiri", :version => "1.7.1", :platform => rb) }
- let(:locked_gem) { double(:locked_gem, :name => "nokogiri", :version => "1.7.0") }
+ let(:spec) { double(:spec, name: "nokogiri", version: "1.7.1", platform: Gem::Platform::RUBY) }
+ let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
context "with color", :no_color_tty do
before do
@@ -116,7 +106,7 @@ RSpec.describe Bundler::Source do
end
it "should return a string with the locked spec version in green" do
- expect(subject.version_message(spec)).to eq("nokogiri 1.7.1\e[32m (was 1.7.0)\e[0m")
+ expect(subject.version_message(spec, locked_gem)).to eq("nokogiri 1.7.1\e[32m (was 1.7.0)\e[0m")
end
end
@@ -128,33 +118,17 @@ RSpec.describe Bundler::Source do
end
it "should return a string with the locked spec version in yellow" do
- expect(subject.version_message(spec)).to eq("nokogiri 1.7.1 (was 1.7.0)")
+ expect(subject.version_message(spec, locked_gem)).to eq("nokogiri 1.7.1 (was 1.7.0)")
end
end
end
end
-
- context "that do not contain the relevant gem spec" do
- before do
- specs = double(:specs)
- allow(locked_gems).to receive(:specs).and_return(specs)
- allow(specs).to receive(:find).and_return(nil)
- end
-
- it_behaves_like "the lockfile specs are not relevant"
- end
- end
-
- context "when there are no locked gems" do
- before { allow(Bundler).to receive(:locked_gems).and_return(nil) }
-
- it_behaves_like "the lockfile specs are not relevant"
end
end
describe "#can_lock?" do
context "when the passed spec's source is equivalent" do
- let(:spec) { double(:spec, :source => subject) }
+ let(:spec) { double(:spec, source: subject) }
it "should return true" do
expect(subject.can_lock?(spec)).to be_truthy
@@ -162,7 +136,7 @@ RSpec.describe Bundler::Source do
end
context "when the passed spec's source is not equivalent" do
- let(:spec) { double(:spec, :source => double(:other_source)) }
+ let(:spec) { double(:spec, source: double(:other_source)) }
it "should return false" do
expect(subject.can_lock?(spec)).to be_falsey
@@ -188,7 +162,7 @@ RSpec.describe Bundler::Source do
end
end
-private
+ private
def with_ui(ui)
old_ui = Bundler.ui
diff --git a/spec/bundler/bundler/spec_set_spec.rb b/spec/bundler/bundler/spec_set_spec.rb
index 6fedd38b50..1e1ceadf26 100644
--- a/spec/bundler/bundler/spec_set_spec.rb
+++ b/spec/bundler/bundler/spec_set_spec.rb
@@ -43,23 +43,29 @@ RSpec.describe Bundler::SpecSet do
spec = described_class.new(specs).find_by_name_and_platform("b", platform)
expect(spec).to eq platform_spec
end
- end
- describe "#merge" do
- let(:other_specs) do
- [
- build_spec("f", "1.0"),
- build_spec("g", "2.0"),
- ].flatten
+ it "returns nil when the name is not present" do
+ spec = described_class.new(specs).find_by_name_and_platform("missing", platform)
+ expect(spec).to be_nil
end
- let(:other_spec_set) { described_class.new(other_specs) }
+ it "returns nil when the name exists but no spec is installable on the requested platform" do
+ incompatible_platform = Gem::Platform.new("java")
+ incompatible_spec = build_spec("a", "1.0", incompatible_platform).first
- it "merges the items in each gemspec" do
- new_spec_set = subject.merge(other_spec_set)
- specs = new_spec_set.to_a.map(&:full_name)
- expect(specs).to include("a-1.0")
- expect(specs).to include("f-1.0")
+ spec = described_class.new([incompatible_spec]).find_by_name_and_platform("a", platform)
+ expect(spec).to be_nil
+ end
+
+ it "returns the first installable spec for the given name in insertion order" do
+ later_platform_spec = build_spec("b", "3.0", platform).first
+ specs = [
+ platform_spec,
+ later_platform_spec,
+ ]
+
+ spec = described_class.new(specs).find_by_name_and_platform("b", platform)
+ expect(spec).to eq platform_spec
end
end
@@ -73,5 +79,70 @@ RSpec.describe Bundler::SpecSet do
d-2.0
]
end
+
+ it "puts rake first when present" do
+ specs = [
+ build_spec("a", "1.0") {|s| s.dep "rake", ">= 0" },
+ build_spec("rake", "13.0"),
+ ].flatten
+
+ expect(described_class.new(specs).to_a.map(&:full_name)).to eq %w[
+ rake-13.0
+ a-1.0
+ ]
+ end
+ end
+
+ describe "#complete_platform" do
+ let(:platform) { Gem::Platform.new("x86_64-linux") }
+
+ let(:platform_variant) do
+ build_spec("needs_old_ruby", "1.0", platform).first.tap do |s|
+ s.required_ruby_version = Gem::Requirement.new("< #{Gem.ruby_version}")
+ end
+ end
+
+ let(:lazy_spec) do
+ lazy = Bundler::LazySpecification.new("needs_old_ruby", Gem::Version.new("1.0"), Gem::Platform::RUBY)
+ lazy.required_ruby_version = Gem::Requirement.new("< #{Gem.ruby_version}")
+ source = double("source")
+ source_specs = double("source_specs")
+ allow(source).to receive(:specs).and_return(source_specs)
+ allow(source_specs).to receive(:search).
+ with(["needs_old_ruby", Gem::Version.new("1.0")]).and_return([platform_variant])
+ lazy.source = source
+ lazy
+ end
+
+ it "rejects a platform variant whose strict metadata is incompatible when no override is attached" do
+ set = described_class.new([lazy_spec])
+ expect(set.send(:complete_platform, platform)).to be(false)
+ end
+
+ it "accepts a platform variant when the LazySpec carries an override that allows it" do
+ lazy_spec.overrides = [Bundler::Override.new("needs_old_ruby", :required_ruby_version, :ignore_upper)]
+ set = described_class.new([lazy_spec])
+ expect(set.send(:complete_platform, platform)).to be(true)
+ end
+
+ it "carries overrides onto a synthesized LazySpec so a follow-up complete_platform still honors them" do
+ override = Bundler::Override.new("needs_old_ruby", :required_ruby_version, :ignore_upper)
+ lazy_spec.overrides = [override]
+ second_platform = Gem::Platform.new("aarch64-linux")
+ second_variant = build_spec("needs_old_ruby", "1.0", second_platform).first.tap do |s|
+ s.required_ruby_version = Gem::Requirement.new("< #{Gem.ruby_version}")
+ end
+ allow(lazy_spec.source.specs).to receive(:search).
+ with(["needs_old_ruby", Gem::Version.new("1.0")]).and_return([platform_variant, second_variant])
+
+ set = described_class.new([lazy_spec])
+ expect(set.send(:complete_platform, platform)).to be(true)
+ # The synthesized x86_64-linux variant is now in the set. If lookup
+ # picks it as exemplar for the next platform check, the override list
+ # must still be reachable via its overrides accessor.
+ synthesized = set.to_a.find {|s| s.platform == platform }
+ expect(synthesized.overrides).to eq([override])
+ expect(set.send(:complete_platform, second_platform)).to be(true)
+ end
end
end
diff --git a/spec/bundler/bundler/specifications/foo.gemspec b/spec/bundler/bundler/specifications/foo.gemspec
new file mode 100644
index 0000000000..19b7724e81
--- /dev/null
+++ b/spec/bundler/bundler/specifications/foo.gemspec
@@ -0,0 +1,13 @@
+# rubocop:disable Style/FrozenStringLiteralComment
+# stub: foo 1.0.0 ruby lib
+
+# The first line would be '# -*- encoding: utf-8 -*-' in a real stub gemspec
+
+Gem::Specification.new do |s|
+ s.name = "foo"
+ s.version = "1.0.0"
+ s.loaded_from = __FILE__
+ s.extensions = "ext/foo"
+ s.required_ruby_version = ">= 3.2.0"
+end
+# rubocop:enable Style/FrozenStringLiteralComment
diff --git a/spec/bundler/bundler/stub_specification_spec.rb b/spec/bundler/bundler/stub_specification_spec.rb
index 0a54f9d7b5..f2faa2ea64 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,65 @@ RSpec.describe Bundler::StubSpecification do
expect(stub).to be(with_bundler_stub_spec)
end
end
+
+ describe "#gem_build_complete_path" do
+ it "StubSpecification should have equal gem_build_complete_path as Specification" do
+ spec_path = File.join(File.dirname(__FILE__), "specifications", "foo.gemspec")
+ spec = Gem::Specification.load(spec_path)
+ gem_stub = Gem::StubSpecification.new(spec_path, File.dirname(__FILE__),"","")
+
+ stub = described_class.from_stub(gem_stub)
+ expect(stub.gem_build_complete_path).to eq spec.gem_build_complete_path
+ 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 #{RUBY_ENGINE == "jruby" ? "false" : "true"} if not manually_installed?" do
+ stub = described_class.from_stub(with_bundler_stub_spec)
+ stub.installed_by_version = Gem::Version.new(1)
+ if RUBY_ENGINE == "jruby"
+ expect(stub.missing_extensions?).to be false
+ else
+ expect(stub.missing_extensions?).to be true
+ end
+ end
+ end
+
+ describe "#activated?" do
+ it "returns true after activation" do
+ stub = described_class.from_stub(with_bundler_stub_spec)
+
+ expect(stub.activated?).to be_falsey
+ stub.activated = true
+ expect(stub.activated?).to be true
+ end
+
+ it "returns true after activation if the underlying stub is a `Gem::StubSpecification`" do
+ spec_path = File.join(File.dirname(__FILE__), "specifications", "foo.gemspec")
+ gem_stub = Gem::StubSpecification.new(spec_path, File.dirname(__FILE__),"","")
+ stub = described_class.from_stub(gem_stub)
+
+ expect(stub.activated?).to be_falsey
+ stub.activated = true
+ expect(stub.activated?).to be true
+ end
+ end
end
diff --git a/spec/bundler/bundler/ui/shell_spec.rb b/spec/bundler/bundler/ui/shell_spec.rb
index 536014c6aa..83f147191e 100644
--- a/spec/bundler/bundler/ui/shell_spec.rb
+++ b/spec/bundler/bundler/ui/shell_spec.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require_relative "../../support/streams"
-
RSpec.describe Bundler::UI::Shell do
subject { described_class.new }
@@ -12,6 +10,13 @@ RSpec.describe Bundler::UI::Shell do
it "prints to stdout" do
expect { subject.info("info") }.to output("info\n").to_stdout
end
+
+ context "when output_stream is :stderr" do
+ before { subject.output_stream = :stderr }
+ it "prints to stderr" do
+ expect { subject.info("info") }.to output("info\n").to_stderr
+ end
+ end
end
describe "#confirm" do
@@ -19,19 +24,36 @@ RSpec.describe Bundler::UI::Shell do
it "prints to stdout" do
expect { subject.confirm("confirm") }.to output("confirm\n").to_stdout
end
+
+ context "when output_stream is :stderr" do
+ before { subject.output_stream = :stderr }
+ it "prints to stderr" do
+ expect { subject.confirm("confirm") }.to output("confirm\n").to_stderr
+ end
+ end
end
describe "#warn" do
before { subject.level = "warn" }
- it "prints to stderr" do
+ it "prints to stderr, implicitly adding a newline" do
expect { subject.warn("warning") }.to output("warning\n").to_stderr
end
+ it "can be told not to emit a newline" do
+ expect { subject.warn("warning", false) }.to output("warning").to_stderr
+ end
end
describe "#debug" do
it "prints to stdout" do
expect { subject.debug("debug") }.to output("debug\n").to_stdout
end
+
+ context "when output_stream is :stderr" do
+ before { subject.output_stream = :stderr }
+ it "prints to stderr" do
+ expect { subject.debug("debug") }.to output("debug\n").to_stderr
+ end
+ end
end
describe "#error" do
@@ -43,11 +65,48 @@ RSpec.describe Bundler::UI::Shell do
context "when stderr is closed" do
it "doesn't report anything" do
- output = capture(:stderr, :closed => true) do
- subject.error("Something went wrong")
+ output = begin
+ result = StringIO.new
+ result.close
+
+ $stderr = result
+
+ subject.error("Something went wrong")
+
+ result.string
+ ensure
+ $stderr = STDERR
+ end
+ expect(output).to_not eq("Something went wrong")
+ end
+ end
+ end
+
+ describe "threads" do
+ it "is thread safe when using with_level" do
+ stop_thr1 = false
+ stop_thr2 = false
+
+ expect(subject.level).to eq("debug")
+
+ thr1 = Thread.new do
+ subject.silence do
+ sleep(0.1) until stop_thr1
+ end
+
+ stop_thr2 = true
+ end
+
+ thr2 = Thread.new do
+ subject.silence do
+ stop_thr1 = true
+ sleep(0.1) until stop_thr2
end
- expect(output).to_not eq("Something went wrong\n")
end
+
+ [thr1, thr2].each(&:join)
+
+ expect(subject.level).to eq("debug")
end
end
end
diff --git a/spec/bundler/bundler/uri_credentials_filter_spec.rb b/spec/bundler/bundler/uri_credentials_filter_spec.rb
index 466c1b8594..641f0addb4 100644
--- a/spec/bundler/bundler/uri_credentials_filter_spec.rb
+++ b/spec/bundler/bundler/uri_credentials_filter_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Bundler::URICredentialsFilter do
let(:credentials) { "oauth_token:x-oauth-basic@" }
it "returns the uri without the oauth token" do
- expect(subject.credential_filtered_uri(uri).to_s).to eq(Bundler::URI("https://x-oauth-basic@github.com/company/private-repo").to_s)
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(Gem::URI("https://x-oauth-basic@github.com/company/private-repo").to_s)
end
it_behaves_like "original type of uri is maintained"
@@ -26,7 +26,17 @@ RSpec.describe Bundler::URICredentialsFilter do
let(:credentials) { "oauth_token:x@" }
it "returns the uri without the oauth token" do
- expect(subject.credential_filtered_uri(uri).to_s).to eq(Bundler::URI("https://x@github.com/company/private-repo").to_s)
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(Gem::URI("https://x@github.com/company/private-repo").to_s)
+ end
+
+ it_behaves_like "original type of uri is maintained"
+ end
+
+ context "specified without empty username" do
+ let(:credentials) { "oauth_token@" }
+
+ it "returns the uri without the oauth token" do
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(Gem::URI("https://github.com/company/private-repo").to_s)
end
it_behaves_like "original type of uri is maintained"
@@ -37,7 +47,7 @@ RSpec.describe Bundler::URICredentialsFilter do
let(:credentials) { "username1:hunter3@" }
it "returns the uri without the password" do
- expect(subject.credential_filtered_uri(uri).to_s).to eq(Bundler::URI("https://username1@github.com/company/private-repo").to_s)
+ expect(subject.credential_filtered_uri(uri).to_s).to eq(Gem::URI("https://username1@github.com/company/private-repo").to_s)
end
it_behaves_like "original type of uri is maintained"
@@ -55,7 +65,7 @@ RSpec.describe Bundler::URICredentialsFilter do
end
context "uri is a uri object" do
- let(:uri) { Bundler::URI("https://#{credentials}github.com/company/private-repo") }
+ let(:uri) { Gem::URI("https://#{credentials}github.com/company/private-repo") }
it_behaves_like "sensitive credentials in uri are filtered out"
end
@@ -90,7 +100,7 @@ RSpec.describe Bundler::URICredentialsFilter do
describe "#credential_filtered_string" do
let(:str_to_filter) { "This is a git message containing a uri #{uri}!" }
let(:credentials) { "" }
- let(:uri) { Bundler::URI("https://#{credentials}github.com/company/private-repo") }
+ let(:uri) { Gem::URI("https://#{credentials}github.com/company/private-repo") }
context "with a uri that contains credentials" do
let(:credentials) { "oauth_token:x-oauth-basic@" }
diff --git a/spec/bundler/bundler/uri_normalizer_spec.rb b/spec/bundler/bundler/uri_normalizer_spec.rb
new file mode 100644
index 0000000000..1308e86014
--- /dev/null
+++ b/spec/bundler/bundler/uri_normalizer_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::URINormalizer do
+ describe ".normalize_suffix" do
+ context "when trailing_slash is true" do
+ it "adds a trailing slash when missing" do
+ expect(described_class.normalize_suffix("https://example.com", trailing_slash: true)).to eq("https://example.com/")
+ end
+
+ it "keeps the trailing slash when present" do
+ expect(described_class.normalize_suffix("https://example.com/", trailing_slash: true)).to eq("https://example.com/")
+ end
+ end
+
+ context "when trailing_slash is false" do
+ it "removes a trailing slash when present" do
+ expect(described_class.normalize_suffix("https://example.com/", trailing_slash: false)).to eq("https://example.com")
+ end
+
+ it "keeps the value unchanged when no trailing slash exists" do
+ expect(described_class.normalize_suffix("https://example.com", trailing_slash: false)).to eq("https://example.com")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/vendored_persistent_spec.rb b/spec/bundler/bundler/vendored_persistent_spec.rb
deleted file mode 100644
index 3ed899dbcf..0000000000
--- a/spec/bundler/bundler/vendored_persistent_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-
-require "bundler/vendored_persistent"
-
-RSpec.describe Bundler::PersistentHTTP do
- describe "#warn_old_tls_version_rubygems_connection" do
- let(:uri) { "https://index.rubygems.org" }
- let(:connection) { instance_double(Bundler::Persistent::Net::HTTP::Persistent::Connection) }
- let(:tls_version) { "TLSv1.2" }
- let(:socket) { double("Socket") }
- let(:socket_io) { double("SocketIO") }
-
- before do
- allow(connection).to receive_message_chain(:http, :use_ssl?).and_return(!tls_version.nil?)
- allow(socket).to receive(:io).and_return(socket_io) if socket
- connection.instance_variable_set(:@socket, socket)
-
- if tls_version
- allow(socket_io).to receive(:ssl_version).and_return(tls_version)
- end
- end
-
- shared_examples_for "does not warn" do
- it "does not warn" do
- allow(Bundler.ui).to receive(:warn).never
- subject.warn_old_tls_version_rubygems_connection(Bundler::URI(uri), connection)
- end
- end
-
- shared_examples_for "does warn" do |*expected|
- it "warns" do
- expect(Bundler.ui).to receive(:warn).with(*expected)
- subject.warn_old_tls_version_rubygems_connection(Bundler::URI(uri), connection)
- end
- end
-
- context "an HTTPS uri with TLSv1.2" do
- include_examples "does not warn"
- end
-
- context "without SSL" do
- let(:tls_version) { nil }
-
- include_examples "does not warn"
- end
-
- context "without a socket" do
- let(:socket) { nil }
-
- include_examples "does not warn"
- end
-
- context "with a different TLD" do
- let(:uri) { "https://foo.bar" }
- include_examples "does not warn"
-
- context "and an outdated TLS version" do
- let(:tls_version) { "TLSv1" }
- include_examples "does not warn"
- end
- end
-
- context "with a nonsense TLS version" do
- let(:tls_version) { "BlahBlah2.0Blah" }
- include_examples "does not warn"
- end
-
- context "with an outdated TLS version" do
- let(:tls_version) { "TLSv1" }
- include_examples "does warn",
- "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \
- "Starting in January 2018, RubyGems.org will refuse connection requests from these very old versions of OpenSSL. " \
- "If you will need to continue installing gems after January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.",
- :wrap => true
- end
- end
-end
diff --git a/spec/bundler/bundler/version_ranges_spec.rb b/spec/bundler/bundler/version_ranges_spec.rb
deleted file mode 100644
index bca044b0c0..0000000000
--- a/spec/bundler/bundler/version_ranges_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-require "bundler/version_ranges"
-
-RSpec.describe Bundler::VersionRanges do
- describe ".empty?" do
- shared_examples_for "empty?" do |exp, *req|
- it "returns #{exp} for #{req}" do
- r = Gem::Requirement.new(*req)
- ranges = described_class.for(r)
- expect(described_class.empty?(*ranges)).to eq(exp), "expected `#{r}` #{exp ? "" : "not "}to be empty"
- end
- end
-
- include_examples "empty?", false
- include_examples "empty?", false, "!= 1"
- include_examples "empty?", false, "!= 1", "= 2"
- include_examples "empty?", false, "!= 1", "> 1"
- include_examples "empty?", false, "!= 1", ">= 1"
- include_examples "empty?", false, "= 1", ">= 0.1", "<= 1.1"
- include_examples "empty?", false, "= 1", ">= 1", "<= 1"
- include_examples "empty?", false, "= 1", "~> 1"
- include_examples "empty?", false, ">= 0.z", "= 0"
- include_examples "empty?", false, ">= 0"
- include_examples "empty?", false, ">= 1.0.0", "< 2.0.0"
- include_examples "empty?", false, "~> 1"
- include_examples "empty?", false, "~> 2.0", "~> 2.1"
- include_examples "empty?", true, ">= 4.1.0", "< 5.0", "= 5.2.1"
- include_examples "empty?", true, "< 5.0", "< 5.3", "< 6.0", "< 6", "= 5.2.0", "> 2", ">= 3.0", ">= 3.1", ">= 3.2", ">= 4.0.0", ">= 4.1.0", ">= 4.2.0", ">= 4.2", ">= 4"
- include_examples "empty?", true, "!= 1", "< 2", "> 2"
- include_examples "empty?", true, "!= 1", "<= 1", ">= 1"
- include_examples "empty?", true, "< 2", "> 2"
- include_examples "empty?", true, "< 2", "> 2", "= 2"
- include_examples "empty?", true, "= 1", "!= 1"
- include_examples "empty?", true, "= 1", "= 2"
- include_examples "empty?", true, "= 1", "~> 2"
- include_examples "empty?", true, ">= 0", "<= 0.a"
- include_examples "empty?", true, "~> 2.0", "~> 3"
- end
-end
diff --git a/spec/bundler/bundler/worker_spec.rb b/spec/bundler/bundler/worker_spec.rb
index 2e5642709d..2ad2845e37 100644
--- a/spec/bundler/bundler/worker_spec.rb
+++ b/spec/bundler/bundler/worker_spec.rb
@@ -19,4 +19,71 @@ RSpec.describe Bundler::Worker do
end
end
end
+
+ describe "priority queue" do
+ it "process elements from the priority queue first" do
+ processed_elements = []
+
+ function = proc do |element, _|
+ processed_elements << element
+ end
+
+ worker = described_class.new(1, "Spec Worker", function)
+ worker.instance_variable_set(:@threads, []) # Prevent the enqueueing from starting work.
+ worker.enq("Normal element")
+ worker.enq("Priority element", priority: true)
+ worker.send(:create_threads)
+
+ worker.stop
+
+ expect(processed_elements).to eq(["Priority element", "Normal element"])
+ 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/bundler/yaml_serializer_spec.rb b/spec/bundler/bundler/yaml_serializer_spec.rb
index 1241c74bbf..9ff1579b76 100644
--- a/spec/bundler/bundler/yaml_serializer_spec.rb
+++ b/spec/bundler/bundler/yaml_serializer_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Bundler::YAMLSerializer do
it "works for simple hash" do
hash = { "Q" => "Where does Thursday come before Wednesday? In the dictionary. :P" }
- expected = strip_whitespace <<-YAML
+ expected = <<~YAML
---
Q: "Where does Thursday come before Wednesday? In the dictionary. :P"
YAML
@@ -24,7 +24,7 @@ RSpec.describe Bundler::YAMLSerializer do
},
}
- expected = strip_whitespace <<-YAML
+ expected = <<~YAML
---
nice-one:
read_ahead: "All generalizations are false, including this one"
@@ -45,7 +45,7 @@ RSpec.describe Bundler::YAMLSerializer do
},
}
- expected = strip_whitespace <<-YAML
+ expected = <<~YAML
---
nested_hash:
contains_array:
@@ -57,11 +57,24 @@ RSpec.describe Bundler::YAMLSerializer do
expect(serializer.dump(hash)).to eq(expected)
end
+
+ it "handles empty array" do
+ hash = {
+ "empty_array" => [],
+ }
+
+ expected = <<~YAML
+ ---
+ empty_array: []
+ YAML
+
+ expect(serializer.dump(hash)).to eq(expected)
+ end
end
describe "#load" do
it "works for simple hash" do
- yaml = strip_whitespace <<-YAML
+ yaml = <<~YAML
---
Jon: "Air is free dude!"
Jack: "Yes.. until you buy a bag of chips!"
@@ -76,7 +89,7 @@ RSpec.describe Bundler::YAMLSerializer do
end
it "works for nested hash" do
- yaml = strip_whitespace <<-YAML
+ yaml = <<~YAML
---
baa:
baa: "black sheep"
@@ -98,15 +111,15 @@ RSpec.describe Bundler::YAMLSerializer do
end
it "handles colon in key/value" do
- yaml = strip_whitespace <<-YAML
- BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/: http://rubygems-mirror.org
+ yaml = <<~YAML
+ BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/: http://example-mirror.rubygems.org
YAML
- expect(serializer.load(yaml)).to eq("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://rubygems-mirror.org")
+ expect(serializer.load(yaml)).to eq("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://example-mirror.rubygems.org")
end
it "handles arrays inside hashes" do
- yaml = strip_whitespace <<-YAML
+ yaml = <<~YAML
---
nested_hash:
contains_array:
@@ -127,7 +140,7 @@ RSpec.describe Bundler::YAMLSerializer do
end
it "handles windows-style CRLF line endings" do
- yaml = strip_whitespace(<<-YAML).gsub("\n", "\r\n")
+ yaml = <<~YAML.gsub("\n", "\r\n")
---
nested_hash:
contains_array:
@@ -148,6 +161,34 @@ RSpec.describe Bundler::YAMLSerializer do
expect(serializer.load(yaml)).to eq(hash)
end
+
+ it "handles empty array" do
+ yaml = <<~YAML
+ ---
+ empty_array: []
+ YAML
+
+ hash = {
+ "empty_array" => [],
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
+
+ it "skip commented out words" do
+ yaml = <<~YAML
+ ---
+ foo: bar
+ buzz: foo # bar
+ YAML
+
+ hash = {
+ "foo" => "bar",
+ "buzz" => "foo",
+ }
+
+ expect(serializer.load(yaml)).to eq(hash)
+ end
end
describe "against yaml lib" do
diff --git a/spec/bundler/cache/cache_path_spec.rb b/spec/bundler/cache/cache_path_spec.rb
index 12385427b1..2a280ea858 100644
--- a/spec/bundler/cache/cache_path_spec.rb
+++ b/spec/bundler/cache/cache_path_spec.rb
@@ -3,30 +3,30 @@
RSpec.describe "bundle package" do
before do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
context "with --cache-path" do
it "caches gems at given path" do
bundle :cache, "cache-path" => "vendor/cache-foo"
- expect(bundled_app("vendor/cache-foo/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache-foo/myrack-1.0.0.gem")).to exist
end
end
context "with config cache_path" do
it "caches gems at given path" do
- bundle "config set cache_path vendor/cache-foo"
+ bundle_config "cache_path vendor/cache-foo"
bundle :cache
- expect(bundled_app("vendor/cache-foo/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache-foo/myrack-1.0.0.gem")).to exist
end
end
context "with absolute --cache-path" do
it "caches gems at given path" do
- bundle :cache, "cache-path" => "/tmp/cache-foo"
- expect(bundled_app("/tmp/cache-foo/rack-1.0.0.gem")).to exist
+ bundle :cache, "cache-path" => bundled_app("vendor/cache-foo")
+ expect(bundled_app("vendor/cache-foo/myrack-1.0.0.gem")).to exist
end
end
end
diff --git a/spec/bundler/cache/gems_spec.rb b/spec/bundler/cache/gems_spec.rb
index 2b5ba733f0..198279d84c 100644
--- a/spec/bundler/cache/gems_spec.rb
+++ b/spec/bundler/cache/gems_spec.rb
@@ -4,22 +4,23 @@ RSpec.describe "bundle cache" do
shared_examples_for "when there are only gemsources" do
before :each do
gemfile <<-G
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
- system_gems "rack-1.0.0", :path => path
+ system_gems "myrack-1.0.0", path: path
bundle :cache
end
it "copies the .gem file to vendor/cache" do
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
end
it "uses the cache as a source when installing gems" do
- build_gem "omg", :path => bundled_app("vendor/cache")
+ build_gem "omg", path: bundled_app("vendor/cache")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "omg"
G
@@ -27,44 +28,47 @@ RSpec.describe "bundle cache" do
end
it "uses the cache as a source when installing gems with --local" do
- system_gems [], :path => default_bundle_path
+ system_gems [], path: default_bundle_path
bundle "install --local"
- expect(the_bundle).to include_gems("rack 1.0.0")
+ expect(the_bundle).to include_gems("myrack 1.0.0")
end
it "does not reinstall gems from the cache if they exist on the system" do
- build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
+ build_gem "myrack", "1.0.0", path: bundled_app("vendor/cache") do |s|
+ s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
end
install_gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- expect(the_bundle).to include_gems("rack 1.0.0")
+ expect(the_bundle).to include_gems("myrack 1.0.0")
end
it "does not reinstall gems from the cache if they exist in the bundle" do
- system_gems "rack-1.0.0", :path => default_bundle_path
+ system_gems "myrack-1.0.0", path: default_bundle_path
gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
+ build_gem "myrack", "1.0.0", path: bundled_app("vendor/cache") do |s|
+ s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
end
- bundle :install, :local => true
- expect(the_bundle).to include_gems("rack 1.0.0")
+ bundle :install, local: true
+ expect(the_bundle).to include_gems("myrack 1.0.0")
end
it "creates a lockfile" do
- cache_gems "rack-1.0.0"
+ cache_gems "myrack-1.0.0"
gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
bundle "cache"
@@ -74,86 +78,117 @@ RSpec.describe "bundle cache" do
end
context "using system gems" do
- before { bundle "config set path.system true" }
+ before { bundle_config "path.system true" }
let(:path) { system_gem_path }
it_behaves_like "when there are only gemsources"
end
context "installing into a local path" do
- before { bundle "config set path ./.bundle" }
+ before { bundle_config "path ./.bundle" }
let(:path) { local_gem_path }
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, to_system: true, default: true
+ end
+
+ context "when a remote gem is available for caching" do
+ before do
+ build_repo2 do
+ build_gem "json", default_json_version
+ end
end
- build_gem "builtin_gem", "1.0.2", :to_system => true do |s|
- s.summary = "This builtin_gem is bundled with Ruby"
+ it "uses remote gems when installing" do
+ install_gemfile %(source "https://gem.repo2"; gem 'json', '#{default_json_version}'), verbose: true
+ expect(out).to include("Installing json #{default_json_version}")
end
- FileUtils.rm("#{system_gem_path}/cache/builtin_gem-1.0.2.gem")
- end
+ it "does not use remote gems when installing with --local flag" do
+ install_gemfile %(source "https://gem.repo2"; gem 'json', '#{default_json_version}'), verbose: true, local: true
+ expect(out).to include("Using json #{default_json_version}")
+ 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")
- end
+ it "does not use remote gems when installing with --prefer-local flag" do
+ install_gemfile %(source "https://gem.repo2"; gem 'json', '#{default_json_version}'), verbose: true, "prefer-local": 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 'rack', '1.0.0'
- G
+ it "caches remote and builtin gems" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'json', '#{default_json_version}'
+ gem 'myrack', '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
- end
+ bundle :cache
+ expect(bundled_app("vendor/cache/myrack-1.0.0.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
- build_gem "builtin_gem_2", "1.0.2", :path => bundled_app("vendor/cache") do |s|
- s.summary = "This builtin_gem is bundled with Ruby"
+ it "caches builtin gems when cache_all_platforms is set" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "json"
+ G
+
+ bundle_config "cache_all_platforms true"
+
+ bundle :cache
+ expect(bundled_app("vendor/cache/json-#{default_json_version}.gem")).to exist
end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem 'builtin_gem_2', '1.0.2'
- G
+ it "doesn't make remote request after caching the gem" do
+ build_gem "builtin_gem_2", "1.0.2", path: bundled_app("vendor/cache"), default: true
- bundle "install --local"
- expect(the_bundle).to include_gems("builtin_gem_2 1.0.2")
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'builtin_gem_2', '1.0.2'
+ G
+
+ bundle "install --local"
+ expect(the_bundle).to include_gems("builtin_gem_2 1.0.2")
+ end
end
- it "errors if the builtin gem isn't available to cache" do
- bundle "config set path.system true"
+ context "when a remote gem is not available for caching" do
+ it "warns, but uses builtin gems when installing to system gems" do
+ bundle_config "path.system true"
+ install_gemfile %(source "https://gem.repo1"; gem 'json', '#{default_json_version}'), verbose: true
+ expect(err).to include("json-#{default_json_version} is built in to Ruby, and can't be cached")
+ expect(out).to include("Using json #{default_json_version}")
+ end
- install_gemfile <<-G
- gem 'builtin_gem', '1.0.2'
- G
+ it "errors when explicitly caching" do
+ bundle_config "path.system true"
- 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")
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'json', '#{default_json_version}'
+ G
+
+ bundle :cache, raise_on_error: false
+ expect(last_command).to be_failure
+ expect(err).to include("json-#{default_json_version} is built in to Ruby, and can't be cached")
+ end
end
end
describe "when there are also git sources" do
before do
build_git "foo"
- system_gems "rack-1.0.0"
+ system_gems "myrack-1.0.0"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
- gem 'rack'
+ gem 'myrack'
G
end
@@ -163,7 +198,7 @@ RSpec.describe "bundle cache" do
system_gems []
bundle "install --local"
- expect(the_bundle).to include_gems("rack 1.0.0", "foo 1.0")
+ expect(the_bundle).to include_gems("myrack 1.0.0", "foo 1.0")
end
it "should not explode if the lockfile is not present" do
@@ -176,36 +211,44 @@ RSpec.describe "bundle cache" do
end
describe "when previously cached" do
- before :each do
+ let :setup_main_repo do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
+ source "https://gem.repo2"
+ gem "myrack"
gem "actionpack"
G
bundle :cache
- expect(cached_gem("rack-1.0.0")).to exist
+ expect(cached_gem("myrack-1.0.0")).to exist
expect(cached_gem("actionpack-2.3.2")).to exist
expect(cached_gem("activesupport-2.3.2")).to exist
end
it "re-caches during install" do
- cached_gem("rack-1.0.0").rmtree
+ setup_main_repo
+ FileUtils.rm_rf cached_gem("myrack-1.0.0")
bundle :install
expect(out).to include("Updating files in vendor/cache")
- expect(cached_gem("rack-1.0.0")).to exist
+ expect(cached_gem("myrack-1.0.0")).to exist
end
it "adds and removes when gems are updated" do
- update_repo2
- bundle "update", :all => true
- expect(cached_gem("rack-1.2")).to exist
- expect(cached_gem("rack-1.0.0")).not_to exist
+ setup_main_repo
+ update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+ end
+
+ bundle "update", all: true
+ expect(cached_gem("myrack-1.2")).to exist
+ expect(cached_gem("myrack-1.0.0")).not_to exist
end
it "adds new gems and dependencies" do
+ setup_main_repo
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "rails"
G
expect(cached_gem("rails-2.3.2")).to exist
@@ -213,24 +256,26 @@ RSpec.describe "bundle cache" do
end
it "removes .gems for removed gems and dependencies" do
+ setup_main_repo
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
+ source "https://gem.repo2"
+ gem "myrack"
G
- expect(cached_gem("rack-1.0.0")).to exist
+ expect(cached_gem("myrack-1.0.0")).to exist
expect(cached_gem("actionpack-2.3.2")).not_to exist
expect(cached_gem("activesupport-2.3.2")).not_to exist
end
it "removes .gems when gem changes to git source" do
- build_git "rack"
+ setup_main_repo
+ build_git "myrack"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack", :git => "#{lib_path("rack-1.0")}"
+ source "https://gem.repo2"
+ gem "myrack", :git => "#{lib_path("myrack-1.0")}"
gem "actionpack"
G
- expect(cached_gem("rack-1.0.0")).not_to exist
+ expect(cached_gem("myrack-1.0.0")).not_to exist
expect(cached_gem("actionpack-2.3.2")).to exist
expect(cached_gem("activesupport-2.3.2")).to exist
end
@@ -238,7 +283,7 @@ RSpec.describe "bundle cache" do
it "doesn't remove gems that are for another platform" do
simulate_platform "java" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "platform_specific"
G
@@ -246,37 +291,111 @@ RSpec.describe "bundle cache" do
expect(cached_gem("platform_specific-1.0-java")).to exist
end
- simulate_new_machine
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "platform_specific"
- G
+ pristine_system_gems
+
+ simulate_platform "x86-darwin-100" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
+
+ expect(cached_gem("platform_specific-1.0-x86-darwin-100")).to exist
+ expect(cached_gem("platform_specific-1.0-java")).to exist
+ end
+ end
+
+ it "doesn't remove gems cached gems that don't match their remote counterparts, but also refuses to install and prints an error" do
+ setup_main_repo
+ cached_myrack = cached_gem("myrack-1.0.0")
+ FileUtils.rm_rf cached_myrack
+ build_gem "myrack", "1.0.0",
+ path: cached_myrack.parent,
+ rubygems_version: "1.3.2"
+
+ FileUtils.rm_r default_bundle_path
+ default_system_gems
+
+ FileUtils.rm bundled_app_lock
+ bundle :install, raise_on_error: false
+
+ expect(err).to eq <<~E.strip
+ Bundler found mismatched checksums. This is a potential security risk.
+ #{checksum_to_lock(gem_repo2, "myrack", "1.0.0")}
+ from the API at https://gem.repo2/
+ #{checksum_from_package(cached_myrack, "myrack", "1.0.0")}
+ from the gem at #{cached_myrack}
+
+ If you trust the API at https://gem.repo2/, to resolve this issue you can:
+ 1. remove the gem at #{cached_myrack}
+ 2. run `bundle install`
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
- expect(cached_gem("platform_specific-1.0-#{Bundler.local_platform}")).to exist
- expect(cached_gem("platform_specific-1.0-java")).to exist
+ expect(cached_gem("myrack-1.0.0")).to exist
end
- it "doesn't remove gems with mismatched :rubygems_version or :date" do
- cached_gem("rack-1.0.0").rmtree
- build_gem "rack", "1.0.0",
- :path => bundled_app("vendor/cache"),
- :rubygems_version => "1.3.2"
- simulate_new_machine
+ it "raises an error when a cached gem is altered and produces a different checksum than the remote gem" do
+ setup_main_repo
+ FileUtils.rm_rf cached_gem("myrack-1.0.0")
+ build_gem "myrack", "1.0.0", path: bundled_app("vendor/cache")
+ checksums = checksums_section do |c|
+ c.checksum gem_repo1, "myrack", "1.0.0"
+ end
+
+ FileUtils.rm_r default_bundle_path
+ default_system_gems
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack (1.0.0)
+ #{checksums}
+ L
+
+ bundle :install, raise_on_error: false
+ expect(exitstatus).to eq(37)
+ expect(err).to include("Bundler found mismatched checksums.")
+ expect(err).to include("1. remove the gem at #{cached_gem("myrack-1.0.0")}")
+
+ expect(cached_gem("myrack-1.0.0")).to exist
+ FileUtils.rm_rf cached_gem("myrack-1.0.0")
bundle :install
- expect(cached_gem("rack-1.0.0")).to exist
+ expect(cached_gem("myrack-1.0.0")).to exist
+ end
+
+ it "installs a modified gem with a non-matching checksum when the API implementation does not provide checksums" do
+ setup_main_repo
+ FileUtils.rm_rf cached_gem("myrack-1.0.0")
+ build_gem "myrack", "1.0.0", path: bundled_app("vendor/cache")
+ pristine_system_gems
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack (1.0.0)
+ L
+
+ bundle :install, artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ expect(cached_gem("myrack-1.0.0")).to exist
end
it "handles directories and non .gem files in the cache" do
+ setup_main_repo
bundled_app("vendor/cache/foo").mkdir
File.open(bundled_app("vendor/cache/bar"), "w") {|f| f.write("not a gem") }
bundle :cache
end
it "does not say that it is removing gems when it isn't actually doing so" do
+ setup_main_repo
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
bundle "cache"
bundle "install"
@@ -284,9 +403,10 @@ RSpec.describe "bundle cache" do
end
it "does not warn about all if it doesn't have any git/path dependency" do
+ setup_main_repo
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
bundle "cache"
expect(out).not_to match(/\-\-all/)
@@ -294,9 +414,10 @@ RSpec.describe "bundle cache" do
it "should install gems with the name bundler in them (that aren't bundler)" do
build_gem "foo-bundler", "1.0",
- :path => bundled_app("vendor/cache")
+ path: bundled_app("vendor/cache")
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo-bundler"
G
diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb
index d481e00666..f0976ecac7 100644
--- a/spec/bundler/cache/git_spec.rb
+++ b/spec/bundler/cache/git_spec.rb
@@ -13,41 +13,57 @@ RSpec.describe "git base name" do
end
RSpec.describe "bundle cache with git" do
+ it "does not copy repository to vendor cache when cache_all set to false" do
+ git = build_git "foo"
+ ref = git.ref_for("main", 11)
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle_config "cache_all false"
+ bundle :cache
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).not_to exist
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
it "copies repository to vendor cache and uses it" do
git = build_git "foo"
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.bundlecache")).to be_file
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
expect(the_bundle).to include_gems "foo 1.0"
end
it "copies repository to vendor cache and uses it even when configured with `path`" do
git = build_git "foo"
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
expect(the_bundle).to include_gems "foo 1.0"
end
@@ -55,64 +71,63 @@ RSpec.describe "bundle cache with git" do
build_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
bundle :cache
expect(out).to include "Updating files in vendor/cache"
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
expect(the_bundle).to include_gems "foo 1.0"
end
it "tracks updates" do
git = build_git "foo"
- old_ref = git.ref_for("master", 11)
+ old_ref = git.ref_for("main", 11)
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
update_git "foo" do |s|
s.write "lib/foo.rb", "puts :CACHE"
end
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
expect(ref).not_to eq(old_ref)
- bundle "update", :all => true
- bundle "config set cache_all true"
+ bundle "update", all: true
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
expect(bundled_app("vendor/cache/foo-1.0-#{old_ref}")).not_to exist
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
run "require 'foo'"
expect(out).to eq("CACHE")
end
it "tracks updates when specifying the gem" do
git = build_git "foo"
- old_ref = git.ref_for("master", 11)
+ old_ref = git.ref_for("main", 11)
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
update_git "foo" do |s|
s.write "lib/foo.rb", "puts :CACHE"
end
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
expect(ref).not_to eq(old_ref)
bundle "update foo"
@@ -120,22 +135,22 @@ RSpec.describe "bundle cache with git" do
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
expect(bundled_app("vendor/cache/foo-1.0-#{old_ref}")).not_to exist
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
run "require 'foo'"
expect(out).to eq("CACHE")
end
it "uses the local repository to generate the cache" do
git = build_git "foo"
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
gemfile <<-G
- gem "foo", :git => '#{lib_path("foo-invalid")}', :branch => :master
+ source "https://gem.repo1"
+ gem "foo", :git => '#{lib_path("foo-invalid")}', :branch => :main
G
bundle %(config set local.foo #{lib_path("foo-1.0")})
bundle "install"
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-invalid-#{ref}")).to exist
@@ -149,55 +164,193 @@ RSpec.describe "bundle cache with git" do
expect(out).to eq("LOCAL")
end
- it "copies repository to vendor cache, including submodules" do
- build_git "submodule", "1.0"
+ it "can use gems after copying install folder to a different machine with git not installed" do
+ build_git "foo"
- git = build_git "has_submodule", "1.0" do |s|
- s.add_dependency "submodule"
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle_config "path vendor/bundle"
+ bundle :install
+
+ pristine_system_gems
+ with_path_as "" do
+ bundle_config "deployment true"
+ bundle "install --local"
+ expect(the_bundle).to include_gem "foo 1.0"
end
+ end
- sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0")
- sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
+ it "can install after bundle cache without cloning remote repositories" do
+ build_git "foo"
- install_gemfile <<-G
- git "#{lib_path("has_submodule-1.0")}", :submodules => true do
- gem "has_submodule"
- end
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
G
+ bundle :cache, "all-platforms" => true
- ref = git.ref_for("master", 11)
- bundle "config set cache_all true"
- bundle :cache
+ pristine_system_gems
+ bundle_config "frozen true"
+ bundle "install --local --verbose"
+ expect(out).to_not include("Fetching")
+ expect(the_bundle).to include_gem "foo 1.0"
+ end
- expect(bundled_app("vendor/cache/has_submodule-1.0-#{ref}")).to exist
- expect(bundled_app("vendor/cache/has_submodule-1.0-#{ref}/submodule-1.0")).to exist
- expect(the_bundle).to include_gems "has_submodule 1.0"
+ it "can install after bundle cache without cloning remote repositories even without the original cache" do
+ build_git "foo"
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle :cache, "all-platforms" => true
+
+ pristine_system_gems
+ bundle_config "frozen true"
+ bundle "install --local --verbose"
+ expect(out).to_not include("Fetching")
+ expect(the_bundle).to include_gem "foo 1.0"
end
- it "displays warning message when detecting git repo in Gemfile", :bundler => "< 3" do
+ it "can install after bundle cache without cloning remote repositories with only git tracked files" do
build_git "foo"
- install_gemfile <<-G
+ gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
+ bundle :cache, "all-platforms" => true
- bundle :cache
+ pristine_system_gems
+ bundle_config "frozen true"
+
+ # Remove untracked files (including the empty refs dir in the cache)
+ Dir.chdir(bundled_app) do
+ system(*%W[git init --quiet])
+ system(*%W[git add --all])
+ system(*%W[git clean -d --force --quiet])
+ end
- expect(err).to include("Your Gemfile contains path and git dependencies.")
+ bundle "install --local --verbose"
+ expect(out).to_not include("Fetching")
+ expect(the_bundle).to include_gem "foo 1.0"
end
- it "does not display warning message if cache_all is set in bundle config" do
- build_git "foo"
+ it "installs properly a bundler 2.5.17-2.5.23 cache as a bare repository without cloning remote repositories" do
+ git = build_git "foo"
- install_gemfile <<-G
+ short_ref = git.ref_for("main", 11)
+ cache_dir = bundled_app("vendor/cache/foo-1.0-#{short_ref}")
+
+ gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
+ bundle_config "global_gem_cache false"
+ bundle_config "path vendor/bundle"
+ bundle :install
- bundle "config set cache_all true"
- bundle :cache
+ # Simulate old cache by copying the real cache folder to vendor/cache
+ FileUtils.mkdir_p bundled_app("vendor/cache")
+ FileUtils.cp_r "#{Dir.glob(vendored_gems("cache/bundler/git/foo-1.0-*")).first}/.", cache_dir
+ FileUtils.rm_r bundled_app("vendor/bundle")
+
+ bundle "install --local --verbose"
+ expect(err).to include("Installing from cache in old \"bare repository\" format for compatibility")
+
+ expect(out).to_not include("Fetching")
+
+ # leaves old cache alone
+ expect(cache_dir.join("lib/foo.rb")).not_to exist
+ expect(cache_dir.join("HEAD")).to exist
+
+ expect(the_bundle).to include_gem "foo 1.0"
+ end
+
+ it "migrates a bundler 2.5.17-2.5.23 cache as a bare repository when not running with --local" do
+ git = build_git "foo"
+
+ short_ref = git.ref_for("main", 11)
+ cache_dir = bundled_app("vendor/cache/foo-1.0-#{short_ref}")
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle_config "global_gem_cache false"
+ bundle_config "path vendor/bundle"
+ bundle :install
+
+ # Simulate old cache by copying the real cache folder to vendor/cache
+ FileUtils.mkdir_p bundled_app("vendor/cache")
+ FileUtils.cp_r "#{Dir.glob(vendored_gems("cache/bundler/git/foo-1.0-*")).first}/.", cache_dir
+ FileUtils.rm_r bundled_app("vendor/bundle")
+
+ bundle "install --verbose"
+ expect(out).to include("Fetching")
+
+ # migrates old cache alone
+ expect(cache_dir.join("lib/foo.rb")).to exist
+ expect(cache_dir.join("HEAD")).not_to exist
+
+ expect(the_bundle).to include_gem "foo 1.0"
+ end
+
+ it "migrates a bundler 2.5.17-2.5.23 cache as a bare repository when running `bundle cache`, even if gems already installed" do
+ git = build_git "foo"
+
+ short_ref = git.ref_for("main", 11)
+ cache_dir = bundled_app("vendor/cache/foo-1.0-#{short_ref}")
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle_config "global_gem_cache false"
+ bundle_config "path vendor/bundle"
+ bundle :install
+
+ # Simulate old cache by copying the real cache folder to vendor/cache
+ FileUtils.mkdir_p bundled_app("vendor/cache")
+ FileUtils.cp_r "#{Dir.glob(vendored_gems("cache/bundler/git/foo-1.0-*")).first}/.", cache_dir
+
+ bundle "cache"
+
+ # migrates old cache alone
+ expect(cache_dir.join("lib/foo.rb")).to exist
+ expect(cache_dir.join("HEAD")).not_to exist
+
+ expect(the_bundle).to include_gem "foo 1.0"
+ 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|
+ s.add_dependency "submodule"
+ end
+
+ git "submodule add #{lib_path("submodule-1.0")} submodule-1.0", lib_path("has_submodule-1.0")
+ git "commit -m \"submodulator\"", lib_path("has_submodule-1.0")
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ git "#{lib_path("has_submodule-1.0")}", :submodules => true do
+ gem "has_submodule"
+ end
+ G
+
+ ref = git.ref_for("main", 11)
bundle :cache
- expect(err).not_to include("Your Gemfile contains path and git dependencies.")
+ expect(bundled_app("vendor/cache/has_submodule-1.0-#{ref}")).to exist
+ expect(bundled_app("vendor/cache/has_submodule-1.0-#{ref}/submodule-1.0")).to exist
+ expect(the_bundle).to include_gems "has_submodule 1.0"
end
it "caches pre-evaluated gemspecs" do
@@ -209,12 +362,12 @@ RSpec.describe "bundle cache with git" do
update_git("foo") {|s| s.write "foo.gemspec", spec_lines.join("\n") }
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
gemspec = bundled_app("vendor/cache/foo-1.0-#{ref}/foo.gemspec").read
expect(gemspec).to_not match("`echo bob`")
end
@@ -223,16 +376,136 @@ RSpec.describe "bundle cache with git" do
build_git "foo"
gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
- bundle :cache, "all-platforms" => true, :install => false, :path => "./vendor/cache"
+ bundle :cache, "all-platforms" => true, :install => false
- simulate_new_machine
+ pristine_system_gems
with_path_as "" do
- bundle "config set deployment true"
- bundle :install, :local => true
+ bundle_config "deployment true"
+ bundle :install, local: true
expect(the_bundle).to include_gem "foo 1.0"
end
end
+
+ it "can install after bundle cache generated with an older Bundler that kept checkouts in the cache" do
+ git = build_git("foo")
+ locked_revision = git.ref_for("main")
+ path_revision = git.ref_for("main", 11)
+
+ git_path = lib_path("foo-1.0")
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => '#{git_path}'
+ G
+ lockfile <<~L
+ GIT
+ remote: #{git_path}/
+ revision: #{locked_revision}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ # Simulate an old incorrect situation where vendor/cache would be the install location of git gems
+ FileUtils.mkdir_p bundled_app("vendor/cache")
+ FileUtils.cp_r git_path, bundled_app("vendor/cache/foo-1.0-#{path_revision}")
+ FileUtils.rm_r bundled_app("vendor/cache/foo-1.0-#{path_revision}/.git")
+
+ bundle :install, env: { "BUNDLE_DEPLOYMENT" => "true", "BUNDLE_CACHE_ALL" => "true" }
+ end
+
+ it "respects the --no-install flag" do
+ git = build_git "foo", &:add_c_extension
+ ref = git.ref_for("main", 11)
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ # The algorithm for the cache location for a git checkout is
+ # in Bundle::Source::Git#cache_path
+ cache_path_name = "foo-1.0-#{Digest(:SHA1).hexdigest(lib_path("foo-1.0").to_s)}"
+
+ # Run this test twice. This is because materially different codepaths
+ # will get hit the second time around.
+ # The first time, Bundler::Sources::Git#install_path is set to the system
+ # wide cache directory bundler/gems; the second time, it's set to the
+ # vendor/cache directory. We don't want the native extension to appear in
+ # either of these places, so run the `bundle cache` command twice.
+ 2.times do
+ bundle :cache, "all-platforms" => true, :install => false
+
+ # it did _NOT_ actually install the gem - neither in $GEM_HOME (bundler 2 mode),
+ # nor in .bundle (bundler 4 mode)
+ expect(Pathname.new(File.join(default_bundle_path, "gems/foo-1.0-#{ref}"))).to_not exist
+ # it _did_ cache the gem in vendor/
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
+ # it did _NOT_ build the gems extensions in the vendor/ dir
+ expect(Dir[bundled_app("vendor/cache/foo-1.0-#{ref}/lib/foo_c*")]).to be_empty
+ # it _did_ cache the git checkout
+ expect(default_cache_path("git", cache_path_name)).to exist
+ # And the checkout is a bare checkout
+ expect(default_cache_path("git", cache_path_name, "HEAD")).to exist
+ end
+
+ # Subsequently installing the gem should compile it.
+ # _currently_, the gem gets compiled in vendor/cache, and vendor/cache is added
+ # to the $LOAD_PATH for git extensions, so it all kind of "works". However, in the
+ # future we would like to stop adding vendor/cache to the $LOAD_PATH for git extensions
+ # and instead treat them identically to normal gems (where the gem install location,
+ # not the cache location, is added to $LOAD_PATH).
+ # Verify that the compilation worked and the result is in $LOAD_PATH by simply attempting
+ # to require it; that should make sure this spec does not break if the load path behaviour
+ # is changed.
+ bundle :install, local: true
+ ruby <<~R, raise_on_error: false
+ require 'bundler/setup'
+ require 'foo_c'
+ R
+ expect(last_command).to_not be_failure
+ end
+
+ it "doesn't fail when git gem has extensions and an empty cache folder is present before bundle install" do
+ build_git "puma" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.executables = "puma"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/puma.rb", "w") do |f|
+ f.puts "PUMA = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ FileUtils.mkdir_p(bundled_app("vendor/cache"))
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "puma", :git => "#{lib_path("puma-1.0")}"
+ G
+
+ bundle "exec puma"
+
+ expect(out).to eq("YES")
+ end
end
diff --git a/spec/bundler/cache/path_spec.rb b/spec/bundler/cache/path_spec.rb
index 0c84d242b5..42648aea1f 100644
--- a/spec/bundler/cache/path_spec.rb
+++ b/spec/bundler/cache/path_spec.rb
@@ -2,13 +2,13 @@
RSpec.describe "bundle cache with path" do
it "is no-op when the path is within the bundle" do
- build_lib "foo", :path => bundled_app("lib/foo")
+ build_lib "foo", path: bundled_app("lib/foo")
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :path => '#{bundled_app("lib/foo")}'
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
expect(the_bundle).to include_gems "foo 1.0"
@@ -18,10 +18,10 @@ RSpec.describe "bundle cache with path" do
build_lib "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0")).to exist
expect(bundled_app("vendor/cache/foo-1.0/.bundlecache")).to be_file
@@ -33,13 +33,13 @@ RSpec.describe "bundle cache with path" do
libname = File.basename(bundled_app) + "_gem"
libpath = File.join(File.dirname(bundled_app), libname)
- build_lib libname, :path => libpath
+ build_lib libname, path: libpath
install_gemfile <<-G
+ source "https://gem.repo1"
gem "#{libname}", :path => '#{libpath}'
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/#{libname}")).to exist
expect(bundled_app("vendor/cache/#{libname}/.bundlecache")).to be_file
@@ -51,10 +51,10 @@ RSpec.describe "bundle cache with path" do
build_lib "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
build_lib "foo" do |s|
@@ -73,10 +73,10 @@ RSpec.describe "bundle cache with path" do
build_lib "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0")).to exist
@@ -84,6 +84,7 @@ RSpec.describe "bundle cache with path" do
build_lib "bar"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "bar", :path => '#{lib_path("bar-1.0")}'
G
@@ -91,55 +92,30 @@ RSpec.describe "bundle cache with path" do
expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
end
- it "raises a warning without --all", :bundler => "< 3" do
+ it "does not cache path gems if cache_all is set to false" do
build_lib "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
+ bundle_config "cache_all false"
bundle :cache
- expect(err).to match(/please pass the \-\-all flag/)
+ expect(err).to be_empty
expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
end
- it "stores the given flag" do
+ it "caches path gems by default" do
build_lib "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
- build_lib "bar"
-
- install_gemfile <<-G
- gem "foo", :path => '#{lib_path("foo-1.0")}'
- gem "bar", :path => '#{lib_path("bar-1.0")}'
- G
-
- bundle :cache
- expect(bundled_app("vendor/cache/bar-1.0")).to exist
- end
-
- it "can rewind chosen configuration" do
- build_lib "foo"
-
- install_gemfile <<-G
- gem "foo", :path => '#{lib_path("foo-1.0")}'
- G
-
- bundle "config set cache_all true"
- bundle :cache
- build_lib "baz"
-
- gemfile <<-G
- gem "foo", :path => '#{lib_path("foo-1.0")}'
- gem "baz", :path => '#{lib_path("baz-1.0")}'
- G
-
- bundle "cache --no-all", :raise_on_error => false
- expect(bundled_app("vendor/cache/baz-1.0")).not_to exist
+ expect(err).to be_empty
+ expect(bundled_app("vendor/cache/foo-1.0")).to exist
end
end
diff --git a/spec/bundler/cache/platform_spec.rb b/spec/bundler/cache/platform_spec.rb
index 128278956c..71c0eaee8e 100644
--- a/spec/bundler/cache/platform_spec.rb
+++ b/spec/bundler/cache/platform_spec.rb
@@ -3,10 +3,10 @@
RSpec.describe "bundle cache with multiple platforms" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
platforms :mri, :rbx do
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
end
platforms :jruby do
@@ -16,9 +16,9 @@ RSpec.describe "bundle cache with multiple platforms" do
lockfile <<-G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
activesupport (2.3.5)
PLATFORMS
@@ -26,24 +26,24 @@ RSpec.describe "bundle cache with multiple platforms" do
java
DEPENDENCIES
- rack (1.0.0)
+ myrack (1.0.0)
activesupport (2.3.5)
G
- cache_gems "rack-1.0.0", "activesupport-2.3.5"
+ cache_gems "myrack-1.0.0", "activesupport-2.3.5"
end
it "ensures that a successful bundle install does not delete gems for other platforms" do
bundle "install"
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/activesupport-2.3.5.gem")).to exist
end
it "ensures that a successful bundle update does not delete gems for other platforms" do
- bundle "update", :all => true
+ bundle "update", all: true
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/activesupport-2.3.5.gem")).to exist
end
end
diff --git a/spec/bundler/commands/add_spec.rb b/spec/bundler/commands/add_spec.rb
index 4c533652ca..162650f2e5 100644
--- a/spec/bundler/commands/add_spec.rb
+++ b/spec/bundler/commands/add_spec.rb
@@ -9,48 +9,65 @@ RSpec.describe "bundle add" do
build_gem "bar", "0.12.3"
build_gem "cat", "0.12.3.pre"
build_gem "dog", "1.1.3.pre"
+ build_gem "lemur", "3.1.1.pre.2023.1.1"
end
build_git "foo", "2.0"
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "weakling", "~> 0.0.1"
G
end
context "when no gems are specified" do
it "shows error" do
- bundle "add", :raise_on_error => false
+ bundle "add", raise_on_error: false
expect(err).to include("Please specify gems to add")
end
end
+ context "when Gemfile is empty, and frozen mode is set" do
+ it "shows error" do
+ gemfile 'source "https://gem.repo2"'
+ bundle "add bar", raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" }
+
+ expect(err).to include("Frozen mode is set, but there's no lockfile")
+ end
+ end
+
describe "without version specified" do
- it "version requirement becomes ~> major.minor.patch when resolved version is < 1.0" do
+ it "version requirement becomes >= major.minor.patch when resolved version is < 1.0" do
bundle "add 'bar'"
- expect(bundled_app_gemfile.read).to match(/gem "bar", "~> 0.12.3"/)
+ expect(bundled_app_gemfile.read).to match(/gem "bar", ">= 0.12.3"/)
expect(the_bundle).to include_gems "bar 0.12.3"
end
- it "version requirement becomes ~> major.minor when resolved version is > 1.0" do
+ it "version requirement becomes >= major.minor when resolved version is > 1.0" do
bundle "add 'baz'"
- expect(bundled_app_gemfile.read).to match(/gem "baz", "~> 1.2"/)
+ expect(bundled_app_gemfile.read).to match(/gem "baz", ">= 1.2"/)
expect(the_bundle).to include_gems "baz 1.2.3"
end
- it "version requirement becomes ~> major.minor.patch.pre when resolved version is < 1.0" do
+ it "version requirement becomes >= major.minor.patch.pre when resolved version is < 1.0" do
bundle "add 'cat'"
- expect(bundled_app_gemfile.read).to match(/gem "cat", "~> 0.12.3.pre"/)
+ expect(bundled_app_gemfile.read).to match(/gem "cat", ">= 0.12.3.pre"/)
expect(the_bundle).to include_gems "cat 0.12.3.pre"
end
- it "version requirement becomes ~> major.minor.pre when resolved version is > 1.0.pre" do
+ it "version requirement becomes >= major.minor.pre when resolved version is >= 1.0.pre" do
bundle "add 'dog'"
- expect(bundled_app_gemfile.read).to match(/gem "dog", "~> 1.1.pre"/)
+ expect(bundled_app_gemfile.read).to match(/gem "dog", ">= 1.1.pre"/)
expect(the_bundle).to include_gems "dog 1.1.3.pre"
end
+
+ it "version requirement becomes >= major.minor.pre.tail when resolved version has a very long tail pre version" do
+ bundle "add 'lemur'"
+ # the trailing pre purposely matches the release version to ensure that subbing the release doesn't change the pre.version"
+ expect(bundled_app_gemfile.read).to match(/gem "lemur", ">= 3.1.pre.2023.1.1"/)
+ expect(the_bundle).to include_gems "lemur 3.1.1.pre.2023.1.1"
+ end
end
describe "with --version" do
@@ -63,56 +80,196 @@ RSpec.describe "bundle add" do
it "adds multiple version constraints when specified" do
requirements = ["< 3.0", "> 1.0"]
bundle "add 'foo' --version='#{requirements.join(", ")}'"
- expect(bundled_app_gemfile.read).to match(/gem "foo", #{Gem::Requirement.new(requirements).as_list.map(&:dump).join(', ')}/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", #{Gem::Requirement.new(requirements).as_list.map(&:dump).join(", ")}/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
+ describe "with --require" do
+ it "adds the require param for the gem" do
+ bundle "add 'foo' --require=foo/engine"
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo",(?: .*,) require: "foo\/engine"})
+ end
+
+ it "converts false to a boolean" do
+ bundle "add 'foo' --require=false"
+ expect(bundled_app_gemfile.read).to match(/gem "foo",(?: .*,) require: false/)
+ end
+ end
+
describe "with --group" do
it "adds dependency for the specified group" do
bundle "add 'foo' --group='development'"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :group => :development/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2.0", group: :development/)
expect(the_bundle).to include_gems "foo 2.0"
end
it "adds dependency to more than one group" do
bundle "add 'foo' --group='development, test'"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :groups => \[:development, :test\]/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2.0", groups: \[:development, :test\]/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
describe "with --source" do
it "adds dependency with specified source" do
- bundle "add 'foo' --source='#{file_uri_for(gem_repo2)}'"
+ bundle "add 'foo' --source='https://gem.repo2'"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :source => "#{file_uri_for(gem_repo2)}"/)
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", ">= 2.0", source: "https://gem.repo2"})
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --path" do
+ it "adds dependency with specified path" do
+ bundle "add 'foo' --path='#{lib_path("foo-2.0")}'"
+
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2.0", path: "#{lib_path("foo-2.0")}"/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
describe "with --git" do
- it "adds dependency with specified github source" do
+ it "adds dependency with specified git source" do
bundle "add foo --git=#{lib_path("foo-2.0")}"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}"/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2.0", git: "#{lib_path("foo-2.0")}"/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
describe "with --git and --branch" do
before do
- update_git "foo", "2.0", :branch => "test"
+ update_git "foo", "2.0", branch: "test"
end
- it "adds dependency with specified github source and branch" do
+ it "adds dependency with specified git source and branch" do
bundle "add foo --git=#{lib_path("foo-2.0")} --branch=test"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}", :branch => "test"/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2.0", git: "#{lib_path("foo-2.0")}", branch: "test"/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --git and --ref" do
+ it "adds dependency with specified git source and branch" do
+ bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{revision_for(lib_path("foo-2.0"))}"
+
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2\.0", git: "#{lib_path("foo-2.0")}", ref: "#{revision_for(lib_path("foo-2.0"))}"/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
+ describe "with --github" do
+ before do
+ build_git "rake", "13.0"
+ git("config --global url.file://#{lib_path("rake-13.0")}.insteadOf https://github.com/ruby/rake.git")
+ end
+
+ it "adds dependency with specified github source" do
+ bundle "add rake --github=ruby/rake"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake"})
+ end
+
+ it "adds dependency with specified github source and branch" do
+ bundle "add rake --github=ruby/rake --branch=main"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake", branch: "main"})
+ end
+
+ it "adds dependency with specified github source and ref" do
+ ref = revision_for(lib_path("rake-13.0"))
+ bundle "add rake --github=ruby/rake --ref=#{ref}"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake", ref: "#{ref}"})
+ end
+
+ it "adds dependency with specified github source and glob" do
+ bundle "add rake --github=ruby/rake --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake", glob: "\.\/\*\.gemspec"})
+ end
+
+ it "adds dependency with specified github source, branch and glob" do
+ bundle "add rake --github=ruby/rake --branch=main --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake", branch: "main", glob: "\.\/\*\.gemspec"})
+ end
+
+ it "adds dependency with specified github source, ref and glob" do
+ ref = revision_for(lib_path("rake-13.0"))
+ bundle "add rake --github=ruby/rake --ref=#{ref} --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake", ref: "#{ref}", glob: "\.\/\*\.gemspec"})
+ end
+ end
+
+ describe "with --git and --glob" do
+ it "adds dependency with specified git source" do
+ bundle "add foo --git=#{lib_path("foo-2.0")} --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", ">= 2.0", git: "#{lib_path("foo-2.0")}", glob: "\./\*\.gemspec"})
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --git and --branch and --glob" do
+ before do
+ update_git "foo", "2.0", branch: "test"
+ end
+
+ it "adds dependency with specified git source and branch" do
+ bundle "add foo --git=#{lib_path("foo-2.0")} --branch=test --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", ">= 2.0", git: "#{lib_path("foo-2.0")}", branch: "test", glob: "\./\*\.gemspec"})
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --git and --ref and --glob" do
+ it "adds dependency with specified git source and branch" do
+ bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{revision_for(lib_path("foo-2.0"))} --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", ">= 2\.0", git: "#{lib_path("foo-2.0")}", ref: "#{revision_for(lib_path("foo-2.0"))}", glob: "\./\*\.gemspec"})
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with mismatched pair in --git/--github, --branch/--ref" do
+ describe "with --git and --github" do
+ it "throws error" do
+ bundle "add 'foo' --git x --github y", raise_on_error: false
+
+ expect(err).to include("You cannot specify `--git` and `--github` at the same time.")
+ end
+ end
+
+ describe "with --branch and --ref with --git" do
+ it "throws error" do
+ bundle "add 'foo' --branch x --ref y --git file://git", raise_on_error: false
+
+ expect(err).to include("You cannot specify `--branch` and `--ref` at the same time.")
+ end
+ end
+
+ describe "with --branch but without --git or --github" do
+ it "throws error" do
+ bundle "add 'foo' --branch x", raise_on_error: false
+
+ expect(err).to include("You cannot specify `--branch` unless `--git` or `--github` is specified.")
+ end
+ end
+
+ describe "with --ref but without --git or --github" do
+ it "throws error" do
+ bundle "add 'foo' --ref y", raise_on_error: false
+
+ expect(err).to include("You cannot specify `--ref` unless `--git` or `--github` is specified.")
+ end
+ end
+ end
+
describe "with --skip-install" do
it "adds gem to Gemfile but is not installed" do
bundle "add foo --skip-install --version=2.0"
@@ -123,41 +280,81 @@ RSpec.describe "bundle add" do
end
it "using combination of short form options works like long form" do
- bundle "add 'foo' -s='#{file_uri_for(gem_repo2)}' -g='development' -v='~>1.0'"
- expect(bundled_app_gemfile.read).to include %(gem "foo", "~> 1.0", :group => :development, :source => "#{file_uri_for(gem_repo2)}")
+ bundle "add 'foo' -s='https://gem.repo2' -g='development' -v='~>1.0'"
+ expect(bundled_app_gemfile.read).to include %(gem "foo", "~> 1.0", group: :development, source: "https://gem.repo2")
expect(the_bundle).to include_gems "foo 1.1"
end
it "shows error message when version is not formatted correctly" do
- bundle "add 'foo' -v='~>1 . 0'", :raise_on_error => false
+ bundle "add 'foo' -v='~>1 . 0'", raise_on_error: false
expect(err).to match("Invalid gem requirement pattern '~>1 . 0'")
end
it "shows error message when gem cannot be found" do
- bundle "config set force_ruby_platform true"
- bundle "add 'werk_it'", :raise_on_error => false
+ bundle_config "force_ruby_platform true"
+ bundle "add 'werk_it'", raise_on_error: false
expect(err).to match("Could not find gem 'werk_it' in")
- bundle "add 'werk_it' -s='#{file_uri_for(gem_repo2)}'", :raise_on_error => false
+ bundle "add 'werk_it' -s='https://gem.repo2'", raise_on_error: false
expect(err).to match("Could not find gem 'werk_it' in rubygems repository")
end
it "shows error message when source cannot be reached" do
- bundle "add 'baz' --source='http://badhostasdf'", :raise_on_error => false
+ bundle "add 'baz' --source='http://badhostasdf'", raise_on_error: false, artifice: "fail"
expect(err).to include("Could not reach host badhostasdf. Check your network connection and try again.")
- bundle "add 'baz' --source='file://does/not/exist'", :raise_on_error => false
+ bundle "add 'baz' --source='file://does/not/exist'", raise_on_error: false
expect(err).to include("Could not fetch specs from file://does/not/exist/")
end
describe "with --optimistic" do
- it "adds optimistic version" do
+ it "ignores option" do
bundle "add 'foo' --optimistic"
expect(bundled_app_gemfile.read).to include %(gem "foo", ">= 2.0")
expect(the_bundle).to include_gems "foo 2.0"
end
end
+ describe "with --pessimistic" do
+ it "adds pessimistic version" do
+ bundle "add 'foo' --pessimistic"
+ expect(bundled_app_gemfile.read).to include %(gem "foo", "~> 2.0")
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --quiet option" do
+ it "is quiet when there are no warnings" do
+ bundle "add 'foo' --quiet"
+ expect(out).to be_empty
+ expect(err).to be_empty
+ end
+
+ it "still displays warning and errors" do
+ create_file("add_with_warning.rb", <<~RUBY)
+ require "#{lib_dir}/bundler"
+ require "#{lib_dir}/bundler/cli"
+ require "#{lib_dir}/bundler/cli/add"
+
+ module RunWithWarning
+ def run
+ super
+ rescue
+ Bundler.ui.warn "This is a warning"
+ raise
+ end
+ end
+
+ Bundler::CLI::Add.prepend(RunWithWarning)
+ RUBY
+
+ bundle "add 'non-existing-gem' --quiet", raise_on_error: false, env: { "RUBYOPT" => "-r#{bundled_app("add_with_warning.rb")}" }
+ expect(out).to be_empty
+ expect(err).to include("Could not find gem 'non-existing-gem'")
+ expect(err).to include("This is a warning")
+ end
+ end
+
describe "with --strict option" do
it "adds strict version" do
bundle "add 'foo' --strict"
@@ -167,18 +364,18 @@ RSpec.describe "bundle add" do
end
describe "with no option" do
- it "adds pessimistic version" do
+ it "adds optimistic version" do
bundle "add 'foo'"
- expect(bundled_app_gemfile.read).to include %(gem "foo", "~> 2.0")
+ expect(bundled_app_gemfile.read).to include %(gem "foo", ">= 2.0")
expect(the_bundle).to include_gems "foo 2.0"
end
end
- describe "with --optimistic and --strict" do
+ describe "with --pessimistic and --strict" do
it "throws error" do
- bundle "add 'foo' --strict --optimistic", :raise_on_error => false
+ bundle "add 'foo' --strict --pessimistic", raise_on_error: false
- expect(err).to include("You can not specify `--strict` and `--optimistic` at the same time")
+ expect(err).to include("You cannot specify `--strict` and `--pessimistic` at the same time")
end
end
@@ -186,12 +383,12 @@ RSpec.describe "bundle add" do
it "adds multiple gems to gemfile" do
bundle "add bar baz"
- expect(bundled_app_gemfile.read).to match(/gem "bar", "~> 0.12.3"/)
- expect(bundled_app_gemfile.read).to match(/gem "baz", "~> 1.2"/)
+ expect(bundled_app_gemfile.read).to match(/gem "bar", ">= 0.12.3"/)
+ expect(bundled_app_gemfile.read).to match(/gem "baz", ">= 1.2"/)
end
it "throws error if any of the specified gems are present in the gemfile with different version" do
- bundle "add weakling bar", :raise_on_error => false
+ bundle "add weakling bar", raise_on_error: false
expect(err).to include("You cannot specify the same gem twice with different version requirements")
expect(err).to include("You specified: weakling (~> 0.0.1) and weakling (>= 0).")
@@ -201,41 +398,41 @@ RSpec.describe "bundle add" do
describe "when a gem is added which is already specified in Gemfile with version" do
it "shows an error when added with different version requirement" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack", "1.0"
+ source "https://gem.repo2"
+ gem "myrack", "1.0"
G
- bundle "add 'rack' --version=1.1", :raise_on_error => false
+ bundle "add 'myrack' --version=1.1", raise_on_error: false
expect(err).to include("You cannot specify the same gem twice with different version requirements")
- expect(err).to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
+ expect(err).to include("If you want to update the gem version, run `bundle update myrack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
end
it "shows error when added without version requirements" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack", "1.0"
+ source "https://gem.repo2"
+ gem "myrack", "1.0"
G
- bundle "add 'rack'", :raise_on_error => false
+ bundle "add 'myrack'", raise_on_error: false
expect(err).to include("Gem already added.")
expect(err).to include("You cannot specify the same gem twice with different version requirements")
- expect(err).not_to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
+ expect(err).not_to include("If you want to update the gem version, run `bundle update myrack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
end
end
describe "when a gem is added which is already specified in Gemfile without version" do
it "shows an error when added with different version requirement" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
+ source "https://gem.repo2"
+ gem "myrack"
G
- bundle "add 'rack' --version=1.1", :raise_on_error => false
+ bundle "add 'myrack' --version=1.1", raise_on_error: false
expect(err).to include("You cannot specify the same gem twice with different version requirements")
- expect(err).to include("If you want to update the gem version, run `bundle update rack`.")
+ expect(err).to include("If you want to update the gem version, run `bundle update myrack`.")
expect(err).not_to include("You may also need to change the version requirement specified in the Gemfile if it's too restrictive")
end
end
@@ -244,8 +441,8 @@ RSpec.describe "bundle add" do
it "caches all new dependencies added for the specified gem" do
bundle :cache
- bundle "add 'rack' --version=1.0.0"
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ bundle "add 'myrack' --version=1.0.0"
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
end
end
end
diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb
index 632c521caa..af4d24a9e8 100644
--- a/spec/bundler/commands/binstubs_spec.rb
+++ b/spec/bundler/commands/binstubs_spec.rb
@@ -4,240 +4,95 @@ RSpec.describe "bundle binstubs <gem>" do
context "when the gem exists in the lockfile" do
it "sets up the binstub" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "binstubs rack"
+ bundle "binstubs myrack"
- expect(bundled_app("bin/rackup")).to exist
+ expect(bundled_app("bin/myrackup")).to exist
end
it "does not install other binstubs" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
gem "rails"
G
bundle "binstubs rails"
- expect(bundled_app("bin/rackup")).not_to exist
+ expect(bundled_app("bin/myrackup")).not_to exist
expect(bundled_app("bin/rails")).to exist
end
it "does install multiple binstubs" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
gem "rails"
G
- bundle "binstubs rails rack"
+ bundle "binstubs rails myrack"
- expect(bundled_app("bin/rackup")).to exist
+ expect(bundled_app("bin/myrackup")).to exist
expect(bundled_app("bin/rails")).to exist
end
it "allows installing all binstubs" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
- bundle :binstubs, :all => true
+ bundle :binstubs, all: true
expect(bundled_app("bin/rails")).to exist
expect(bundled_app("bin/rake")).to exist
end
+ it "allows installing binstubs for all platforms" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "binstubs myrack --all-platforms"
+
+ expect(bundled_app("bin/myrackup")).to exist
+ expect(bundled_app("bin/myrackup.cmd")).to exist
+ end
+
it "displays an error when used without any gem" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "binstubs", :raise_on_error => false
+ bundle "binstubs", raise_on_error: false
expect(exitstatus).to eq(1)
expect(err).to include("`bundle binstubs` needs at least one gem to run.")
end
it "displays an error when used with --all and gems" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "binstubs rack", :all => true, :raise_on_error => false
+ bundle "binstubs myrack", all: true, raise_on_error: false
expect(last_command).to be_failure
expect(err).to include("Cannot specify --all with specific gems")
end
- context "when generating bundle binstub outside bundler" do
- it "should abort" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- bundle "binstubs rack"
-
- File.open(bundled_app("bin/bundle"), "wb") do |file|
- file.print "OMG"
- end
-
- sys_exec "bin/rackup", :raise_on_error => false
-
- expect(err).to include("was not generated by Bundler")
- end
- end
-
- context "the bundle binstub" do
- before do
- pristine_system_gems "bundler-#{system_bundler_version}"
- build_repo2 do
- build_gem "prints_loaded_gems", "1.0" do |s|
- 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) }
- puts specs.map(&:full_name).sort.inspect
- R
- end
- end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
- gem "prints_loaded_gems"
- G
- bundle "binstubs bundler rack prints_loaded_gems"
- end
-
- let(:system_bundler_version) { Bundler::VERSION }
-
- it "runs bundler" do
- sys_exec "bin/bundle install", :env => { "DEBUG" => "1" }
- expect(out).to include %(Using bundler #{system_bundler_version}\n)
- end
-
- context "when BUNDLER_VERSION is set" 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'`")
- end
- end
-
- context "when a lockfile exists with a locked bundler version" do
- context "and the version is newer" do
- before do
- lockfile lockfile.gsub(system_bundler_version, "999.999")
- end
-
- it "runs the correct version of bundler" do
- sys_exec "bin/bundle install", :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'`")
- end
- end
-
- context "and the version is older and a different major" do
- let(:system_bundler_version) { "55" }
-
- before do
- lockfile lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 44.0")
- end
-
- it "runs the correct version of bundler" do
- sys_exec "bin/bundle install", :raise_on_error => false
- expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (~> 44.0) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`")
- end
- end
-
- context "and the version is older and the same major" do
- let(:system_bundler_version) { "55.1" }
-
- before do
- lockfile lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 55.0")
- end
-
- it "runs the available version of bundler when the version is older and the same major" do
- sys_exec "bin/bundle install"
- expect(exitstatus).not_to eq(42)
- expect(err).not_to include("Activating bundler (~> 55.0) failed:")
- end
- end
-
- context "and the version is a pre-releaser" do
- let(:system_bundler_version) { "55" }
-
- before do
- lockfile lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 2.12.0.a")
- end
-
- it "runs the correct version of bundler when the version is a pre-release" do
- sys_exec "bin/bundle install", :raise_on_error => false
- expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (~> 2.12.a) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 2.12.a'`")
- end
- end
- end
-
- context "when update --bundler is called" do
- before { lockfile.gsub(system_bundler_version, "1.1.1") }
-
- it "calls through to the latest bundler version" do
- sys_exec "bin/bundle update --bundler", :env => { "DEBUG" => "1" }
- expect(out).to include %(Using bundler #{system_bundler_version}\n)
- end
-
- 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'`")
- end
- end
-
- context "without a lockfile" do
- it "falls back to the latest installed bundler" do
- FileUtils.rm bundled_app_lock
- sys_exec "bin/bundle install", :env => { "DEBUG" => "1" }
- expect(out).to include "Using bundler #{system_bundler_version}\n"
- end
- end
-
- context "using another binstub" do
- it "loads all gems" do
- sys_exec bundled_app("bin/print_loaded_gems").to_s
- expect(out).to eq %(["bundler-#{Bundler::VERSION}", "prints_loaded_gems-1.0", "rack-1.2"])
- end
-
- context "when requesting a different bundler version" do
- before { lockfile lockfile.gsub(Bundler::VERSION, "999.999.999") }
-
- it "attempts to load that version" do
- sys_exec bundled_app("bin/rackup").to_s, :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'`")
- end
- end
- end
- end
-
it "installs binstubs from git gems" do
FileUtils.mkdir_p(lib_path("foo/bin"))
FileUtils.touch(lib_path("foo/bin/foo"))
- build_git "foo", "1.0", :path => lib_path("foo") do |s|
+ build_git "foo", "1.0", path: lib_path("foo") do |s|
s.executables = %w[foo]
end
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo")}"
G
@@ -249,10 +104,11 @@ RSpec.describe "bundle binstubs <gem>" do
it "installs binstubs from path gems" do
FileUtils.mkdir_p(lib_path("foo/bin"))
FileUtils.touch(lib_path("foo/bin/foo"))
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.executables = %w[foo]
end
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo")}"
G
@@ -262,29 +118,27 @@ RSpec.describe "bundle binstubs <gem>" do
end
it "sets correct permissions for binstubs" do
- skip "https://github.com/rubygems/bundler/issues/6895" if Gem.win_platform?
-
with_umask(0o002) do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "binstubs rack"
- binary = bundled_app("bin/rackup")
- expect(File.stat(binary).mode.to_s(8)).to eq("100775")
+ bundle "binstubs myrack"
+ binary = bundled_app("bin/myrackup")
+ expect(File.stat(binary).mode.to_s(8)).to eq(Gem.win_platform? ? "100644" : "100775")
end
end
context "when using --shebang" do
it "sets the specified shebang for the binstub" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "binstubs rack --shebang jruby"
- expect(File.readlines(bundled_app("bin/rackup")).first).to eq("#!/usr/bin/env jruby\n")
+ bundle "binstubs myrack --shebang jruby"
+ expect(File.readlines(bundled_app("bin/myrackup")).first).to eq("#!/usr/bin/env jruby\n")
end
end
end
@@ -292,64 +146,83 @@ RSpec.describe "bundle binstubs <gem>" do
context "when the gem doesn't exist" do
it "displays an error with correct status" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
G
- bundle "binstubs doesnt_exist", :raise_on_error => false
+ bundle "binstubs doesnt_exist", raise_on_error: false
expect(exitstatus).to eq(7)
expect(err).to include("Could not find gem 'doesnt_exist'.")
end
end
- context "--path" do
- it "sets the binstubs dir" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- bundle "binstubs rack --path exec"
-
- expect(bundled_app("exec/rackup")).to exist
+ context "with the binstubs dir configured" do
+ before do
+ bundle_config "bin exec"
end
- it "setting is saved for bundle install", :bundler => "< 3" do
+ it "creates the binstubs in the configured dir" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rails"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "binstubs rack", :path => "exec"
- bundle :install
+ bundle "binstubs myrack"
- expect(bundled_app("exec/rails")).to exist
+ expect(bundled_app("exec/myrackup")).to exist
end
end
context "with --standalone option" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "rails"
G
end
it "generates a standalone binstub" do
- bundle "binstubs rack --standalone"
- expect(bundled_app("bin/rackup")).to exist
+ bundle "binstubs myrack --standalone"
+ expect(bundled_app("bin/myrackup")).to exist
end
it "generates a binstub that does not depend on rubygems or bundler" do
- bundle "binstubs rack --standalone"
- expect(File.read(bundled_app("bin/rackup"))).to_not include("Gem.bin_path")
+ bundle "binstubs myrack --standalone"
+ expect(File.read(bundled_app("bin/myrackup"))).to_not include("Gem.bin_path")
+ end
+
+ it "generates a standalone binstub at the given path when configured" do
+ bundle_config "bin foo"
+ bundle "binstubs myrack --standalone"
+ expect(bundled_app("foo/myrackup")).to exist
end
- context "when specified --path option" do
- it "generates a standalone binstub at the given path" do
- bundle "binstubs rack --standalone --path foo"
- expect(bundled_app("foo/rackup")).to exist
+ context "when specified --all-platforms option" do
+ it "generates standalone binstubs for all platforms" do
+ bundle "binstubs myrack --standalone --all-platforms"
+ expect(bundled_app("bin/myrackup")).to exist
+ expect(bundled_app("bin/myrackup.cmd")).to exist
+ end
+ end
+
+ context "when the gem is bundler" do
+ it "warns without generating a standalone binstub" do
+ bundle "binstubs bundler --standalone"
+ expect(bundled_app("bin/bundle")).not_to exist
+ expect(bundled_app("bin/bundler")).not_to exist
+ expect(err).to include("Sorry, Bundler can only be run via RubyGems.")
+ end
+ end
+
+ context "when specified --all option" do
+ it "generates standalone binstubs for all gems except bundler" do
+ bundle "binstubs --standalone --all"
+ expect(bundled_app("bin/myrackup")).to exist
+ expect(bundled_app("bin/rails")).to exist
+ expect(bundled_app("bin/bundle")).not_to exist
+ expect(bundled_app("bin/bundler")).not_to exist
+ expect(err).not_to include("Sorry, Bundler can only be run via RubyGems.")
end
end
end
@@ -357,39 +230,39 @@ RSpec.describe "bundle binstubs <gem>" do
context "when the bin already exists" do
it "doesn't overwrite and warns" do
FileUtils.mkdir_p(bundled_app("bin"))
- File.open(bundled_app("bin/rackup"), "wb") do |file|
+ File.open(bundled_app("bin/myrackup"), "wb") do |file|
file.print "OMG"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "binstubs rack"
+ bundle "binstubs myrack"
- expect(bundled_app("bin/rackup")).to exist
- expect(File.read(bundled_app("bin/rackup"))).to eq("OMG")
- expect(err).to include("Skipped rackup")
+ expect(bundled_app("bin/myrackup")).to exist
+ expect(File.read(bundled_app("bin/myrackup"))).to eq("OMG")
+ expect(err).to include("Skipped myrackup")
expect(err).to include("overwrite skipped stubs, use --force")
end
context "when using --force" do
it "overwrites the binstub" do
FileUtils.mkdir_p(bundled_app("bin"))
- File.open(bundled_app("bin/rackup"), "wb") do |file|
+ File.open(bundled_app("bin/myrackup"), "wb") do |file|
file.print "OMG"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "binstubs rack --force"
+ bundle "binstubs myrack --force"
- expect(bundled_app("bin/rackup")).to exist
- expect(File.read(bundled_app("bin/rackup"))).not_to eq("OMG")
+ expect(bundled_app("bin/myrackup")).to exist
+ expect(File.read(bundled_app("bin/myrackup"))).not_to eq("OMG")
end
end
end
@@ -397,18 +270,18 @@ RSpec.describe "bundle binstubs <gem>" do
context "when the gem has no bins" do
it "suggests child gems if they have bins" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack-obama"
+ source "https://gem.repo1"
+ gem "myrack-obama"
G
- bundle "binstubs rack-obama"
- expect(err).to include("rack-obama has no executables")
- expect(err).to include("rack has: rackup")
+ bundle "binstubs myrack-obama"
+ expect(err).to include("myrack-obama has no executables")
+ expect(err).to include("myrack has: myrackup")
end
it "works if child gems don't have bins" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "actionpack"
G
@@ -417,8 +290,14 @@ RSpec.describe "bundle binstubs <gem>" do
end
it "works if the gem has development dependencies" do
+ build_repo2 do
+ build_gem "with_development_dependency" do |s|
+ s.add_development_dependency "activesupport", "= 2.3.5"
+ end
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "with_development_dependency"
G
@@ -430,25 +309,25 @@ RSpec.describe "bundle binstubs <gem>" do
context "when BUNDLE_INSTALL is specified" do
it "performs an automatic bundle install" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "config set auto_install 1"
- bundle "binstubs rack"
- expect(out).to include("Installing rack 1.0.0")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle_config "auto_install 1"
+ bundle "binstubs myrack"
+ expect(out).to include("Installing myrack 1.0.0")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "does nothing when already up to date" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "config set auto_install 1"
- bundle "binstubs rack", :env => { "BUNDLE_INSTALL" => "1" }
- expect(out).not_to include("Installing rack 1.0.0")
+ bundle_config "auto_install 1"
+ bundle "binstubs myrack", env: { "BUNDLE_INSTALL" => "1" }
+ expect(out).not_to include("Installing myrack 1.0.0")
end
end
end
diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb
index 173389c068..e223d07f7f 100644
--- a/spec/bundler/commands/cache_spec.rb
+++ b/spec/bundler/commands/cache_spec.rb
@@ -1,33 +1,46 @@
# frozen_string_literal: true
RSpec.describe "bundle cache" do
+ it "doesn't update the cache multiple times, even if it already exists" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle :cache
+ expect(out).to include("Updating files in vendor/cache").once
+
+ bundle :cache
+ expect(out).to include("Updating files in vendor/cache").once
+ end
+
context "with --gemfile" do
it "finds the gemfile" do
gemfile bundled_app("NotGemfile"), <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
bundle "cache --gemfile=NotGemfile"
ENV["BUNDLE_GEMFILE"] = "NotGemfile"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
- context "with --all" do
+ context "with cache_all configured" do
context "without a gemspec" do
it "caches all dependencies except bundler itself" do
gemfile <<-D
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
gem 'bundler'
D
- bundle "config set cache_all true"
+ bundle_config "cache_all true"
bundle :cache
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
end
end
@@ -50,15 +63,15 @@ RSpec.describe "bundle cache" do
it "caches all dependencies except bundler and the gemspec specified gem" do
gemfile <<-D
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
gemspec
D
- bundle "config set cache_all true"
+ bundle_config "cache_all true"
bundle :cache
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/nokogiri-1.4.2.gem")).to exist
expect(bundled_app("vendor/cache/mygem-0.1.1.gem")).to_not exist
expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
@@ -82,15 +95,15 @@ RSpec.describe "bundle cache" do
it "caches all dependencies except bundler and the gemspec specified gem" do
gemfile <<-D
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
gemspec
D
- bundle "config set cache_all true"
+ bundle_config "cache_all true"
bundle :cache
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/nokogiri-1.4.2.gem")).to exist
expect(bundled_app("vendor/cache/mygem-0.1.1.gem")).to_not exist
expect(bundled_app("vendor/cache/bundler-0.9.gem")).to_not exist
@@ -126,16 +139,16 @@ RSpec.describe "bundle cache" do
it "caches all dependencies except bundler and the gemspec specified gems" do
gemfile <<-D
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
gemspec :name => 'mygem'
gemspec :name => 'mygem_test'
D
- bundle "config set cache_all true"
+ bundle_config "cache_all true"
bundle :cache
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/nokogiri-1.4.2.gem")).to exist
expect(bundled_app("vendor/cache/weakling-0.0.3.gem")).to exist
expect(bundled_app("vendor/cache/mygem-0.1.1.gem")).to_not exist
@@ -145,71 +158,64 @@ RSpec.describe "bundle cache" do
end
end
- context "with --path", :bundler => "< 3" do
- it "sets root directory for gems" do
- gemfile <<-D
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
- D
-
- bundle "cache --path #{bundled_app("test")}"
-
- expect(the_bundle).to include_gems "rack 1.0.0"
- expect(bundled_app("test/vendor/cache/")).to exist
- end
- end
-
context "with --no-install" do
it "puts the gems in vendor/cache but does not install them" do
gemfile <<-D
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
D
bundle "cache --no-install"
- expect(the_bundle).not_to include_gems "rack 1.0.0"
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(the_bundle).not_to include_gems "myrack 1.0.0"
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
end
it "does not prevent installing gems with bundle install" do
gemfile <<-D
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
D
bundle "cache --no-install"
bundle "install"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "does not prevent installing gems with bundle update" do
gemfile <<-D
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
D
bundle "cache --no-install"
bundle "update --all"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
context "with --all-platforms" do
- before do
- skip "doesn't put gems where it should" if Gem.win_platform?
- end
-
it "puts the gems in vendor/cache even for other rubies" do
gemfile <<-D
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack', :platforms => :ruby_19
+ source "https://gem.repo1"
+ gem 'myrack', :platforms => [:ruby_20, :windows_20]
D
bundle "cache --all-platforms"
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
+ end
+
+ it "prints a warn when using legacy windows rubies" do
+ gemfile <<-D
+ source "https://gem.repo1"
+ gem 'myrack', :platforms => [:ruby_20, :x64_mingw_20]
+ D
+
+ bundle "cache --all-platforms", raise_on_error: false
+ expect(err).to include("will be removed in the future")
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
end
it "does not attempt to install gems in without groups" do
@@ -221,57 +227,170 @@ RSpec.describe "bundle cache" do
end
end
- bundle "config --local without wo"
- install_gemfile <<-G
- source "file:#{gem_repo1}"
- gem "rack"
+ bundle_config "without wo"
+ install_gemfile <<-G, artifice: "compact_index_extra_api"
+ source "https://main.repo"
+ gem "myrack"
group :wo do
gem "weakling"
- gem "uninstallable", :source => "file:#{gem_repo4}"
+ gem "uninstallable", :source => "https://main.repo/extra"
end
G
- bundle :cache, "all-platforms" => true
+ bundle :cache, "all-platforms" => true, artifice: "compact_index_extra_api"
expect(bundled_app("vendor/cache/weakling-0.0.3.gem")).to exist
expect(bundled_app("vendor/cache/uninstallable-2.0.gem")).to exist
- expect(the_bundle).to include_gem "rack 1.0"
+ expect(the_bundle).to include_gem "myrack 1.0"
expect(the_bundle).not_to include_gems "weakling", "uninstallable"
- bundle "config --local without wo"
- bundle :install
- expect(the_bundle).to include_gem "rack 1.0"
- expect(the_bundle).not_to include_gems "weakling", "uninstallable"
+ bundle_config "without wo"
+ bundle :install, artifice: "compact_index_extra_api"
+ expect(the_bundle).to include_gem "myrack 1.0"
+ expect(the_bundle).not_to include_gems "weakling"
+ end
+
+ it "does not fail to cache gems in excluded groups when there's a lockfile but gems not previously installed" do
+ bundle_config "without wo"
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ group :wo do
+ gem "weakling"
+ end
+ G
+
+ bundle :lock
+ bundle :cache, "all-platforms" => true
+ expect(bundled_app("vendor/cache/weakling-0.0.3.gem")).to exist
end
end
context "with frozen configured" do
+ let(:app_cache) { bundled_app("vendor/cache") }
+
before do
+ bundle_config "frozen true"
+ end
+
+ it "tries to install but fails when the lockfile is out of sync" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "install"
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+ myrack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ bundle :cache, raise_on_error: false
+ expect(exitstatus).to eq(16)
+ expect(err).to include("frozen mode")
+ expect(err).to include("You have deleted from the Gemfile")
+ expect(err).to include("* myrack-obama")
+ bundle "env"
+ expect(out).to include("frozen")
end
- subject do
- bundle "config --local frozen true"
- bundle :cache, :raise_on_error => false
+ it "caches gems without installing when lockfile is in sync, and --no-install is passed, even if vendor/cache directory is initially empty" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ FileUtils.mkdir_p app_cache
+
+ bundle "cache --no-install"
+ expect(out).not_to include("Installing myrack 1.0.0")
+ expect(out).to include("Fetching myrack 1.0.0")
+ expect(app_cache.join("myrack-1.0.0.gem")).to exist
end
- it "tries to install with frozen" do
- bundle "config set deployment true"
+ it "completes a partial cache when lockfile is in sync, even if the already cached gem is no longer available remotely" do
+ build_repo4 do
+ build_gem "foo", "1.0.0"
+ end
+
+ build_gem "bar", "1.0.0", path: bundled_app("vendor/cache")
+
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rack-obama"
+ source "https://gem.repo4"
+ gem "foo"
+ gem "bar"
G
- subject
- expect(exitstatus).to eq(16)
- expect(err).to include("deployment mode")
- expect(err).to include("You have added to the Gemfile")
- expect(err).to include("* rack-obama")
- bundle "env"
- expect(out).to include("frozen").or include("deployment")
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ foo (1.0.0)
+ bar (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ bar
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "cache --no-install"
+ expect(out).to include("Fetching foo 1.0.0")
+ expect(out).not_to include("Fetching bar 1.0.0")
+ expect(app_cache.join("foo-1.0.0.gem")).to exist
+ expect(app_cache.join("bar-1.0.0.gem")).to exist
+ 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 myrack' }"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "racc"
+ G
+ end
+
+ it "installs them properly from cache to a different path" do
+ bundle "cache"
+ bundle_config "path vendor/bundle"
+ bundle "install --local"
end
end
end
@@ -281,76 +400,215 @@ RSpec.describe "bundle install with gem sources" do
it "does not hit the remote at all" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
+ source "https://gem.repo2"
+ gem "myrack"
G
bundle :cache
- simulate_new_machine
- FileUtils.rm_rf gem_repo2
+ pristine_system_gems
+ FileUtils.rm_r gem_repo2
bundle "install --local"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 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 "https://gem.repo2"
+ gem "myrack"
+ G
+
+ bundle :cache
+ pristine_system_gems
+ FileUtils.rm_r gem_repo2
+
+ bundle_config "deployment true"
+ bundle_config "path vendor/bundle"
+ bundle :install
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "does not hit the remote at all in non frozen mode either" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
+ source "https://gem.repo2"
+ gem "myrack"
G
bundle :cache
- simulate_new_machine
- FileUtils.rm_rf gem_repo2
+ pristine_system_gems
+ FileUtils.rm_r gem_repo2
- bundle "config --local deployment true"
- bundle "config --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "does not hit the remote at all when cache_all_platforms configured" do
+ build_repo2
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+
+ bundle :cache
+ pristine_system_gems
+ FileUtils.rm_r gem_repo2
+
+ bundle_config "cache_all_platforms true"
+ bundle_config "path vendor/bundle"
+ bundle "install --local"
+ expect(out).not_to include("Fetching gem metadata")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "uses cached gems for secondary sources when cache_all_platforms configured" do
+ build_repo4 do
+ build_gem "foo", "1.0.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem "foo", "1.0.0" do |s|
+ s.platform = "arm64-darwin"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ source "https://gem.repo4" do
+ gem "foo"
+ end
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ foo (1.0.0-x86_64-linux)
+ foo (1.0.0-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle_config "cache_all_platforms true"
+ bundle_config "path vendor/bundle"
+ bundle :cache, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ # simulate removal of all remote gems
+ empty_repo4
+
+ # delete compact index cache
+ FileUtils.rm_r home(".bundle/cache/compact_index")
+
+ bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+
+ expect(the_bundle).to include_gems "foo 1.0.0 x86_64-linux"
+ end
end
it "does not reinstall already-installed gems" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
bundle :cache
- build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s|
- s.write "lib/rack.rb", "raise 'omg'"
+ build_gem "myrack", "1.0.0", path: bundled_app("vendor/cache") do |s|
+ s.write "lib/myrack.rb", "raise 'omg'"
end
bundle :install
expect(err).to be_empty
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
it "ignores cached gems for the wrong platform" do
simulate_platform "java" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "platform_specific"
G
bundle :cache
end
- simulate_new_machine
+ pristine_system_gems
- simulate_platform "ruby" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "platform_specific"
+ bundle_config "force_ruby_platform true"
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
+ expect(the_bundle).to include_gems("platform_specific 1.0 ruby")
+ end
+
+ it "keeps gems that are locked and cached for the current platform, even if incompatible with the current ruby" do
+ build_repo4 do
+ build_gem "bcrypt_pbkdf", "1.1.1"
+ build_gem "bcrypt_pbkdf", "1.1.1" do |s|
+ s.platform = "arm64-darwin"
+ s.required_ruby_version = "< #{current_ruby_minor}"
+ end
+ end
+
+ app_cache = bundled_app("vendor/cache")
+ FileUtils.mkdir_p app_cache
+ FileUtils.cp gem_repo4("gems/bcrypt_pbkdf-1.1.1-arm64-darwin.gem"), app_cache
+ FileUtils.cp gem_repo4("gems/bcrypt_pbkdf-1.1.1.gem"), app_cache
+
+ bundle_config "cache_all_platforms true"
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ bcrypt_pbkdf (1.1.1)
+ bcrypt_pbkdf (1.1.1-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin
+ ruby
+
+ DEPENDENCIES
+ bcrypt_pbkdf
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "arm64-darwin-23" do
+ install_gemfile <<~G, verbose: true
+ source "https://gem.repo4"
+ gem "bcrypt_pbkdf"
G
- run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
- expect(out).to eq("1.0.0 RUBY")
+
+ expect(out).to include("Updating files in vendor/cache")
+ expect(err).to be_empty
+ expect(app_cache.join("bcrypt_pbkdf-1.1.1-arm64-darwin.gem")).to exist
+ expect(app_cache.join("bcrypt_pbkdf-1.1.1.gem")).to exist
end
end
it "does not update the cache if --no-cache is passed" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
bundled_app("vendor/cache").mkpath
expect(bundled_app("vendor/cache").children).to be_empty
diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb
index dd69172677..7fe6897ae3 100644
--- a/spec/bundler/commands/check_spec.rb
+++ b/spec/bundler/commands/check_spec.rb
@@ -3,7 +3,7 @@
RSpec.describe "bundle check" do
it "returns success when the Gemfile is satisfied" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
@@ -13,17 +13,17 @@ RSpec.describe "bundle check" do
it "works with the --gemfile flag when not in the directory" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
- bundle "check --gemfile bundled_app/Gemfile", :dir => tmp
+ bundle "check --gemfile bundled_app/Gemfile", dir: tmp
expect(out).to include("The Gemfile's dependencies are satisfied")
end
it "creates a Gemfile.lock by default if one does not exist" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
@@ -36,7 +36,7 @@ RSpec.describe "bundle check" do
it "does not create a Gemfile.lock if --dry-run was passed" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
@@ -47,64 +47,88 @@ RSpec.describe "bundle check" do
expect(bundled_app_lock).not_to exist
end
- it "prints a generic error if the missing gems are unresolvable" do
- system_gems ["rails-2.3.2"]
+ it "prints an error that shows missing gems" do
+ system_gems ["rails-2.3.2"], path: default_bundle_path
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
- bundle :check, :raise_on_error => false
- expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.")
+ bundle :check, raise_on_error: false
+ expect(err).to include("The following gems are missing")
+ expect(err).to include(" * rake (#{rake_version})")
+ expect(err).to include(" * actionpack (2.3.2)")
+ expect(err).to include(" * activerecord (2.3.2)")
+ expect(err).to include(" * actionmailer (2.3.2)")
+ expect(err).to include(" * activeresource (2.3.2)")
+ expect(err).to include(" * activesupport (2.3.2)")
+ expect(err).to include("Install missing gems with `bundle install`")
end
- it "prints a generic error if a Gemfile.lock does not exist and a toplevel dependency does not exist" do
+ it "prints an error that shows missing gems if a Gemfile.lock does not exist and a toplevel dependency is missing" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(exitstatus).to be > 0
+ expect(err).to include("The following gems are missing")
+ expect(err).to include(" * rails (2.3.2)")
+ expect(err).to include(" * rake (#{rake_version})")
+ expect(err).to include(" * actionpack (2.3.2)")
+ expect(err).to include(" * activerecord (2.3.2)")
+ expect(err).to include(" * actionmailer (2.3.2)")
+ expect(err).to include(" * activeresource (2.3.2)")
+ expect(err).to include(" * activesupport (2.3.2)")
+ expect(err).to include("Install missing gems with `bundle install`")
+ end
+
+ it "prints a generic error if gem git source is not checked out" do
+ build_git "foo", path: lib_path("foo")
+
+ bundle_config "path vendor/bundle"
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", git: "#{lib_path("foo")}"
+ G
+
+ FileUtils.rm_r bundled_app("vendor/bundle")
+ bundle :check, raise_on_error: false
+ expect(exitstatus).to eq 1
expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.")
end
it "prints a generic message if you changed your lockfile" do
+ build_repo2 do
+ build_gem "rails_pinned_to_old_activesupport" do |s|
+ s.add_dependency "activesupport", "= 1.2.3"
+ end
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem 'rails'
G
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "rails"
gem "rails_pinned_to_old_activesupport"
G
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.")
end
- it "remembers --without option from install", :bundler => "< 3" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- group :foo do
- gem "rack"
- end
- G
-
- bundle "install --without foo"
- bundle "check"
- expect(out).to include("The Gemfile's dependencies are satisfied")
- end
-
it "uses the without setting" do
- bundle "config set without foo"
+ bundle_config "without foo"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
group :foo do
- gem "rack"
+ gem "myrack"
end
G
@@ -114,47 +138,63 @@ RSpec.describe "bundle check" do
it "ensures that gems are actually installed and not just cached" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :group => :foo
+ source "https://gem.repo1"
+ gem "myrack", :group => :foo
G
- bundle "config --local without foo"
+ bundle_config "without foo"
bundle :install
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "check", :raise_on_error => false
- expect(err).to include("* rack (1.0.0)")
+ bundle "check", raise_on_error: false
+ expect(err).to include("* myrack (1.0.0)")
+ expect(exitstatus).to eq(1)
+ end
+
+ it "ensures that gems are actually installed and not just cached in applications' cache" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle_config "path vendor/bundle"
+ bundle :cache
+
+ uninstall_gem("myrack", env: { "GEM_HOME" => vendored_gems.to_s })
+
+ bundle "check", raise_on_error: false
+ expect(err).to include("* myrack (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)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
platforms :#{not_local_tag} do
gem "activesupport"
end
G
- system_gems "rack-1.0.0", :path => default_bundle_path
+ system_gems "myrack-1.0.0", path: default_bundle_path
lockfile <<-G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
activesupport (2.3.5)
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
- #{local}
+ #{generic_local_platform}
#{not_local}
DEPENDENCIES
- rack
+ myrack
activesupport
G
@@ -164,28 +204,28 @@ RSpec.describe "bundle check" do
it "works with env conditionals" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
env :NOT_GOING_TO_BE_SET do
gem "activesupport"
end
G
- system_gems "rack-1.0.0", :path => default_bundle_path
+ system_gems "myrack-1.0.0", path: default_bundle_path
lockfile <<-G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
activesupport (2.3.5)
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
- #{local}
+ #{generic_local_platform}
#{not_local}
DEPENDENCIES
- rack
+ myrack
activesupport
G
@@ -194,120 +234,341 @@ RSpec.describe "bundle check" do
end
it "outputs an error when the default Gemfile is not found" do
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(exitstatus).to eq(10)
expect(err).to include("Could not locate Gemfile")
end
it "does not output fatal error message" do
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(exitstatus).to eq(10)
expect(err).not_to include("Unfortunately, a fatal error has occurred. ")
end
- it "fails when there's no lock file and frozen is set" do
+ it "fails when there's no lockfile and frozen is set" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo"
G
- bundle "config --local deployment true"
+ bundle_config "deployment true"
bundle "install"
FileUtils.rm(bundled_app_lock)
- bundle :check, :raise_on_error => false
+ bundle :check, raise_on_error: false
expect(last_command).to be_failure
end
- context "--path", :bundler => "< 3" do
- context "after installing gems in the proper directory" do
- before do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
- G
- bundle "install --path vendor/bundle"
+ describe "when locked" do
+ before :each do
+ system_gems "myrack-1.0.0"
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+ end
- FileUtils.rm_rf(bundled_app(".bundle"))
- end
+ it "returns success when the Gemfile is satisfied" do
+ bundle :install
+ bundle :check
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
- it "returns success" do
- bundle "check --path vendor/bundle"
- expect(out).to include("The Gemfile's dependencies are satisfied")
- end
+ it "shows what is missing with the current Gemfile if it is not satisfied" do
+ FileUtils.rm_r default_bundle_path
+ default_system_gems
+ bundle :check, raise_on_error: false
+ expect(err).to match(/The following gems are missing/)
+ expect(err).to include("* myrack (1.0")
+ end
+ end
- it "should write to .bundle/config" do
- bundle "check --path vendor/bundle"
- bundle "check"
+ describe "when locked with multiple dependents with different requirements" do
+ before :each do
+ build_repo4 do
+ build_gem "depends_on_myrack" do |s|
+ s.add_dependency "myrack", ">= 1.0"
+ end
+ build_gem "also_depends_on_myrack" do |s|
+ s.add_dependency "myrack", "~> 1.0"
+ end
+ build_gem "myrack"
end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "depends_on_myrack"
+ gem "also_depends_on_myrack"
+ G
+
+ bundle "lock"
end
- context "after installing gems on a different directory" do
- before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
- G
+ 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("* myrack (1.0").once
+ end
+ end
- bundle "check --path vendor/bundle", :raise_on_error => false
+ describe "when locked under multiple platforms" do
+ before :each do
+ build_repo4 do
+ build_gem "myrack"
end
- it "returns false" do
- expect(exitstatus).to eq(1)
- expect(err).to match(/The following gems are missing/)
- end
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (1.0)
+
+ PLATFORMS
+ ruby
+ #{local_platform}
+
+ DEPENDENCIES
+ myrack
+
+ 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("* myrack (1.0").once
end
end
- describe "when locked" do
- before :each do
- system_gems "rack-1.0.0"
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0"
+ describe "when using only scoped rubygems sources" do
+ before do
+ gemfile <<~G
+ source "https://gem.repo2"
+ source "https://gem.repo1" do
+ gem "myrack"
+ end
G
end
it "returns success when the Gemfile is satisfied" do
- bundle :install
- bundle :check
+ system_gems "myrack-1.0.0", path: default_bundle_path
+ bundle :check, artifice: "compact_index"
expect(out).to include("The Gemfile's dependencies are satisfied")
end
+ end
- it "shows what is missing with the current Gemfile if it is not satisfied" do
- simulate_new_machine
- bundle :check, :raise_on_error => false
- expect(err).to match(/The following gems are missing/)
- expect(err).to include("* rack (1.0")
+ describe "when using only scoped rubygems sources with indirect dependencies" do
+ before do
+ build_repo4 do
+ build_gem "depends_on_myrack" do |s|
+ s.add_dependency "myrack"
+ end
+
+ build_gem "myrack"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo1"
+ source "https://gem.repo4" do
+ gem "depends_on_myrack"
+ end
+ G
+ end
+
+ it "returns success when the Gemfile is satisfied and generates a correct lockfile" do
+ system_gems "depends_on_myrack-1.0", "myrack-1.0", gem_repo: gem_repo4, path: default_bundle_path
+ bundle :check, artifice: "compact_index"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "depends_on_myrack", "1.0"
+ c.checksum gem_repo4, "myrack", "1.0"
+ end
+
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ depends_on_myrack (1.0)
+ myrack
+ myrack (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ depends_on_myrack!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with gemspec directive and scoped sources" do
+ before do
+ build_repo4 do
+ build_gem "awesome_print"
+ end
+
+ build_repo2 do
+ build_gem "dex-dispatch-engine"
+ end
+
+ build_lib("bundle-check-issue", path: tmp("bundle-check-issue")) do |s|
+ s.write "Gemfile", <<-G
+ source "https://localgemserver.test"
+
+ gemspec
+
+ source "https://localgemserver.test/extra" do
+ gem "dex-dispatch-engine"
+ end
+ G
+
+ s.add_dependency "awesome_print"
+ end
+
+ bundle "install", artifice: "compact_index_extra", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, dir: tmp("bundle-check-issue")
+ end
+
+ it "does not corrupt lockfile when changing version" do
+ version_file = tmp("bundle-check-issue/bundle-check-issue.gemspec")
+ File.write(version_file, File.read(version_file).gsub(/s\.version = .+/, "s.version = '9999'"))
+
+ bundle "check --verbose", dir: tmp("bundle-check-issue")
+
+ lockfile = File.read(tmp("bundle-check-issue/Gemfile.lock"))
+
+ checksums = checksums_section_when_enabled(lockfile) do |c|
+ c.checksum gem_repo4, "awesome_print", "1.0"
+ c.no_checksum "bundle-check-issue", "9999"
+ c.checksum gem_repo2, "dex-dispatch-engine", "1.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ PATH
+ remote: .
+ specs:
+ bundle-check-issue (9999)
+ awesome_print
+
+ GEM
+ remote: https://localgemserver.test/
+ specs:
+ awesome_print (1.0)
+
+ GEM
+ remote: https://localgemserver.test/extra/
+ specs:
+ dex-dispatch-engine (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ bundle-check-issue!
+ dex-dispatch-engine!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with scoped and unscoped sources" do
+ it "does not corrupt lockfile" do
+ build_repo2 do
+ build_gem "foo"
+ build_gem "wadus"
+ build_gem("baz") {|s| s.add_dependency "wadus" }
+ end
+
+ build_repo4 do
+ build_gem "bar"
+ end
+
+ bundle_config "path.system true"
+
+ # Add all gems to ensure all gems are installed so that a bundle check
+ # would be successful
+ install_gemfile(<<-G, artifice: "compact_index_extra")
+ source "https://gem.repo2"
+
+ source "https://gem.repo4" do
+ gem "bar"
+ end
+
+ gem "foo"
+ gem "baz"
+ G
+
+ original_lockfile = lockfile
+
+ # Remove "baz" gem from the Gemfile, and bundle install again to generate
+ # a functional lockfile with no "baz" dependency or "wadus" transitive
+ # dependency
+ install_gemfile(<<-G, artifice: "compact_index_extra")
+ source "https://gem.repo2"
+
+ source "https://gem.repo4" do
+ gem "bar"
+ end
+
+ gem "foo"
+ G
+
+ # Add back "baz" gem back to the Gemfile, but _crucially_ we do not perform a
+ # bundle install
+ gemfile(gemfile + 'gem "baz"')
+
+ bundle :check, verbose: true
+
+ # Bundle check should succeed and restore the lockfile to its original
+ # state
+ expect(lockfile).to eq(original_lockfile)
end
end
describe "BUNDLED WITH" do
def lock_with(bundler_version = nil)
- lock = <<-L
+ lock = <<~L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack
L
if bundler_version
- lock += "\n BUNDLED WITH\n #{bundler_version}\n"
+ lock += "\nBUNDLED WITH\n #{bundler_version}\n"
end
lock
end
before do
+ bundle_config "path vendor/bundle"
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
@@ -315,24 +576,25 @@ 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
context "is newer" do
- it "does not change the lock but warns" do
+ it "does not change the lock and does not warn" 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(err).to be_empty
+ expect(lockfile).to eq lock_with(Bundler::VERSION.succ)
end
end
context "is older" do
it "does not change the lock" do
- lockfile lock_with("1.10.1")
- bundle :check, :raise_on_error => false
- lockfile_should_be lock_with("1.10.1")
+ system_gems "bundler-1.18.0"
+ lockfile lock_with("1.18.0")
+ bundle :check
+ 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 0ecdf230f9..c77859d378 100644
--- a/spec/bundler/commands/clean_spec.rb
+++ b/spec/bundler/commands/clean_spec.rb
@@ -19,18 +19,18 @@ RSpec.describe "bundle clean" do
it "removes unused gems that are different" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
G
@@ -40,95 +40,95 @@ RSpec.describe "bundle clean" do
expect(out).to include("Removing foo (1.0)")
- should_have_gems "thin-1.0", "rack-1.0.0"
+ should_have_gems "thin-1.0", "myrack-1.0.0"
should_not_have_gems "foo-1.0"
- expect(vendored_gems("bin/rackup")).to exist
+ expect(vendored_gems("bin/myrackup")).to exist
end
it "removes old version of gem if unused" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "0.9.1"
+ gem "myrack", "0.9.1"
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
gem "foo"
G
bundle "install"
bundle :clean
- expect(out).to include("Removing rack (0.9.1)")
+ expect(out).to include("Removing myrack (0.9.1)")
- should_have_gems "foo-1.0", "rack-1.0.0"
- should_not_have_gems "rack-0.9.1"
+ should_have_gems "foo-1.0", "myrack-1.0.0"
+ should_not_have_gems "myrack-0.9.1"
- expect(vendored_gems("bin/rackup")).to exist
+ expect(vendored_gems("bin/myrackup")).to exist
end
it "removes new version of gem if unused" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "0.9.1"
+ gem "myrack", "0.9.1"
gem "foo"
G
- bundle "update rack"
+ bundle "update myrack"
bundle :clean
- expect(out).to include("Removing rack (1.0.0)")
+ expect(out).to include("Removing myrack (1.0.0)")
- should_have_gems "foo-1.0", "rack-0.9.1"
- should_not_have_gems "rack-1.0.0"
+ should_have_gems "foo-1.0", "myrack-0.9.1"
+ should_not_have_gems "myrack-1.0.0"
- expect(vendored_gems("bin/rackup")).to exist
+ expect(vendored_gems("bin/myrackup")).to exist
end
it "removes gems in bundle without groups" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo"
group :test_group do
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
end
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
- bundle "config set without test_group"
+ bundle_config "without test_group"
bundle "install"
bundle :clean
- expect(out).to include("Removing rack (1.0.0)")
+ expect(out).to include("Removing myrack (1.0.0)")
should_have_gems "foo-1.0"
- should_not_have_gems "rack-1.0.0"
+ should_not_have_gems "myrack-1.0.0"
- expect(vendored_gems("bin/rackup")).to_not exist
+ expect(vendored_gems("bin/myrackup")).to_not exist
end
it "does not remove cached git dir if it's being used" do
@@ -137,45 +137,45 @@ RSpec.describe "bundle clean" do
git_path = lib_path("foo-1.0")
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
git "#{git_path}", :ref => "#{revision}" do
gem "foo"
end
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
bundle :clean
digest = Digest(:SHA1).hexdigest(git_path.to_s)
- cache_path = Bundler::VERSION.start_with?("2.") ? vendored_gems("cache/bundler/git/foo-1.0-#{digest}") : home(".bundle/cache/git/foo-1.0-#{digest}")
+ cache_path = vendored_gems("cache/bundler/git/foo-1.0-#{digest}")
expect(cache_path).to exist
end
it "removes unused git gems" do
- build_git "foo", :path => lib_path("foo")
+ build_git "foo", path: lib_path("foo")
git_path = lib_path("foo")
revision = revision_for(git_path)
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
git "#{git_path}", :ref => "#{revision}" do
gem "foo"
end
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
G
bundle "install"
@@ -183,34 +183,34 @@ RSpec.describe "bundle clean" do
expect(out).to include("Removing foo (#{revision[0..11]})")
- expect(vendored_gems("gems/rack-1.0.0")).to exist
+ expect(vendored_gems("gems/myrack-1.0.0")).to exist
expect(vendored_gems("bundler/gems/foo-#{revision[0..11]}")).not_to exist
digest = Digest(:SHA1).hexdigest(git_path.to_s)
expect(vendored_gems("cache/bundler/git/foo-#{digest}")).not_to exist
- expect(vendored_gems("specifications/rack-1.0.0.gemspec")).to exist
+ expect(vendored_gems("specifications/myrack-1.0.0.gemspec")).to exist
- expect(vendored_gems("bin/rackup")).to exist
+ expect(vendored_gems("bin/myrackup")).to exist
end
it "keeps used git gems even if installed to a symlinked location" do
- build_git "foo", :path => lib_path("foo")
+ build_git "foo", path: lib_path("foo")
git_path = lib_path("foo")
revision = revision_for(git_path)
gemfile <<-G
- source "file://#{gem_repo1}"
+ source "https://gem.repo1"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
git "#{git_path}", :ref => "#{revision}" do
gem "foo"
end
G
FileUtils.mkdir_p(bundled_app("real-path"))
- FileUtils.ln_sf(bundled_app("real-path"), bundled_app("symlink-path"))
+ File.symlink(bundled_app("real-path"), bundled_app("symlink-path"))
- bundle "config set path #{bundled_app("symlink-path")}"
+ bundle_config "path #{bundled_app("symlink-path")}"
bundle "install"
bundle :clean
@@ -220,51 +220,52 @@ RSpec.describe "bundle clean" do
expect(bundled_app("symlink-path/#{Bundler.ruby_scope}/bundler/gems/foo-#{revision[0..11]}")).to exist
end
- it "removes old git gems" do
- build_git "foo-bar", :path => lib_path("foo-bar")
+ it "removes old git gems on bundle update" do
+ build_git "foo-bar", path: lib_path("foo-bar")
revision = revision_for(lib_path("foo-bar"))
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
git "#{lib_path("foo-bar")}" do
gem "foo-bar"
end
G
- bundle "config set path vendor/bundle"
+ bundle_config "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
+ bundle "update", all: true
bundle :clean
expect(out).to include("Removing foo-bar (#{revision[0..11]})")
- expect(vendored_gems("gems/rack-1.0.0")).to exist
+ expect(vendored_gems("gems/myrack-1.0.0")).to exist
expect(vendored_gems("bundler/gems/foo-bar-#{revision[0..11]}")).not_to exist
expect(vendored_gems("bundler/gems/foo-bar-#{revision2[0..11]}")).to exist
- expect(vendored_gems("specifications/rack-1.0.0.gemspec")).to exist
+ expect(vendored_gems("specifications/myrack-1.0.0.gemspec")).to exist
- expect(vendored_gems("bin/rackup")).to exist
+ expect(vendored_gems("bin/myrackup")).to exist
end
it "does not remove nested gems in a git repo" do
- build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
- build_git "rails", "3.0", :path => lib_path("rails") do |s|
+ build_lib "activesupport", "3.0", path: lib_path("rails/activesupport")
+ build_git "rails", "3.0", path: lib_path("rails") do |s|
s.add_dependency "activesupport", "= 3.0"
end
revision = revision_for(lib_path("rails"))
gemfile <<-G
+ source "https://gem.repo1"
gem "activesupport", :git => "#{lib_path("rails")}", :ref => '#{revision}'
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
bundle :clean
expect(out).to include("")
@@ -273,22 +274,22 @@ RSpec.describe "bundle clean" do
end
it "does not remove git sources that are in without groups" do
- build_git "foo", :path => lib_path("foo")
+ build_git "foo", path: lib_path("foo")
git_path = lib_path("foo")
revision = revision_for(git_path)
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
group :test do
git "#{git_path}", :ref => "#{revision}" do
gem "foo"
end
end
G
- bundle "config set path vendor/bundle"
- bundle "config set without test"
+ bundle_config "path vendor/bundle"
+ bundle_config "without test"
bundle "install"
bundle :clean
@@ -301,31 +302,31 @@ RSpec.describe "bundle clean" do
it "does not blow up when using without groups" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
group :development do
gem "foo"
end
G
- bundle "config set path vendor/bundle"
- bundle "config set without development"
+ bundle_config "path vendor/bundle"
+ bundle_config "without development"
bundle "install"
bundle :clean
end
it "displays an error when used without --path" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
G
- bundle :clean, :raise_on_error => false
+ bundle :clean, raise_on_error: false
expect(exitstatus).to eq(15)
expect(err).to include("--force")
@@ -334,103 +335,94 @@ RSpec.describe "bundle clean" do
# handling bundle clean upgrade path from the pre's
it "removes .gem/.gemspec file even if there's no corresponding gem dir" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
gem "foo"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo"
G
bundle "install"
- FileUtils.rm(vendored_gems("bin/rackup"))
- FileUtils.rm_rf(vendored_gems("gems/thin-1.0"))
- FileUtils.rm_rf(vendored_gems("gems/rack-1.0.0"))
+ FileUtils.rm(vendored_gems("bin/myrackup"))
+ FileUtils.rm_r(vendored_gems("gems/thin-1.0"))
+ FileUtils.rm_r(vendored_gems("gems/myrack-1.0.0"))
bundle :clean
- should_not_have_gems "thin-1.0", "rack-1.0"
+ should_not_have_gems "thin-1.0", "myrack-1.0"
should_have_gems "foo-1.0"
- expect(vendored_gems("bin/rackup")).not_to exist
+ expect(vendored_gems("bin/myrackup")).not_to exist
end
it "does not call clean automatically when using system gems" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
- gem "rack"
+ gem "myrack"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
G
- gem_command :list
- expect(out).to include("rack (1.0.0)").and include("thin (1.0)")
+ installed_gems_list
+ expect(out).to include("myrack (1.0.0)").and include("thin (1.0)")
end
- it "--clean should override the bundle setting on install", :bundler => "< 3" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
-
- gem "thin"
- gem "rack"
- G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
- bundle "install --clean true"
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
-
- gem "rack"
- G
- bundle "install"
-
- should_have_gems "rack-1.0.0"
- should_not_have_gems "thin-1.0"
- end
-
- it "--clean should override the bundle setting on update", :bundler => "< 3" do
+ it "does not clean on bundle update when path has not been set" do
build_repo2
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ install_gemfile <<-G
+ source "https://gem.repo2"
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
- bundle "install --clean true"
update_repo2 do
build_gem "foo", "1.0.1"
end
- bundle "update", :all => true
+ bundle "update", all: true
- should_have_gems "foo-1.0.1"
- should_not_have_gems "foo-1.0"
+ files = Pathname.glob(default_bundle_path("*", "*"))
+ files.map! {|f| f.to_s.sub(default_bundle_path.to_s, "") }
+ expected_files = %W[
+ /bin/bundle
+ /bin/bundler
+ /cache/bundler-#{Bundler::VERSION}.gem
+ /cache/foo-1.0.1.gem
+ /cache/foo-1.0.gem
+ /gems/bundler-#{Bundler::VERSION}
+ /gems/foo-1.0
+ /gems/foo-1.0.1
+ /specifications/bundler-#{Bundler::VERSION}.gemspec
+ /specifications/foo-1.0.1.gemspec
+ /specifications/foo-1.0.gemspec
+ ]
+ expected_files += ["/bin/bundle.bat", "/bin/bundler.bat"] if Gem.win_platform?
+
+ expect(files.sort).to eq(expected_files.sort)
end
- it "automatically cleans when path has not been set", :bundler => "3" do
+ it "will automatically clean on bundle update when path has not been set", bundler: "5" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "foo"
G
@@ -439,10 +431,10 @@ RSpec.describe "bundle clean" do
build_gem "foo", "1.0.1"
end
- bundle "update", :all => true
+ bundle "update", all: true
- files = Pathname.glob(bundled_app(".bundle", Bundler.ruby_scope, "*", "*"))
- files.map! {|f| f.to_s.sub(bundled_app(".bundle", Bundler.ruby_scope).to_s, "") }
+ files = Pathname.glob(local_gem_path("*", "*"))
+ files.map! {|f| f.to_s.sub(local_gem_path.to_s, "") }
expect(files.sort).to eq %w[
/cache/foo-1.0.1.gem
/gems/foo-1.0.1
@@ -450,52 +442,52 @@ RSpec.describe "bundle clean" do
]
end
- it "does not clean automatically on --path" do
+ it "does not clean automatically when path configured" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
- gem "rack"
+ gem "myrack"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
G
bundle "install"
- should_have_gems "rack-1.0.0", "thin-1.0"
+ should_have_gems "myrack-1.0.0", "thin-1.0"
end
- it "does not clean on bundle update with --path" do
+ it "does not clean on bundle update when path configured" do
build_repo2
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "foo"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
update_repo2 do
build_gem "foo", "1.0.1"
end
- bundle :update, :all => true
+ bundle :update, all: true
should_have_gems "foo-1.0", "foo-1.0.1"
end
- it "does not clean on bundle update when using --system" do
- bundle "config set path.system true"
+ it "does not clean on bundle update when installing to system gems" do
+ bundle_config "path.system true"
build_repo2
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "foo"
G
@@ -504,35 +496,35 @@ RSpec.describe "bundle clean" do
update_repo2 do
build_gem "foo", "1.0.1"
end
- bundle :update, :all => true
+ bundle :update, all: true
- gem_command :list
+ installed_gems_list
expect(out).to include("foo (1.0.1, 1.0)")
end
it "cleans system gems when --force is used" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo"
- gem "rack"
+ gem "myrack"
G
bundle :install
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
G
bundle :install
bundle "clean --force"
expect(out).to include("Removing foo (1.0)")
- gem_command :list
+ installed_gems_list
expect(out).not_to include("foo (1.0)")
- expect(out).to include("rack (1.0.0)")
+ expect(out).to include("myrack (1.0.0)")
end
describe "when missing permissions", :permissions do
@@ -543,30 +535,30 @@ RSpec.describe "bundle clean" do
end
it "returns a helpful error message" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo"
- gem "rack"
+ gem "myrack"
G
bundle :install
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
G
bundle :install
FileUtils.chmod(0o500, system_cache_path)
- bundle :clean, :force => true, :raise_on_error => false
+ bundle :clean, force: true, raise_on_error: false
expect(err).to include(system_gem_path.to_s)
expect(err).to include("grant write permissions")
- gem_command :list
+ installed_gems_list
expect(out).to include("foo (1.0)")
- expect(out).to include("rack (1.0.0)")
+ expect(out).to include("myrack (1.0.0)")
end
end
@@ -575,12 +567,12 @@ RSpec.describe "bundle clean" do
revision = revision_for(lib_path("foo-1.0"))
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
# mimic 7 length git revisions in Gemfile.lock
@@ -590,7 +582,7 @@ RSpec.describe "bundle clean" do
end
lockfile(bundled_app_lock, gemfile_lock.join("\n"))
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
bundle :clean
@@ -601,10 +593,9 @@ RSpec.describe "bundle clean" do
end
it "when using --force on system gems, it doesn't remove binaries" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
- build_repo2
- update_repo2 do
+ build_repo2 do
build_gem "bindir" do |s|
s.bindir = "exe"
s.executables = "foo"
@@ -612,7 +603,7 @@ RSpec.describe "bundle clean" do
end
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "bindir"
G
@@ -625,6 +616,24 @@ RSpec.describe "bundle clean" do
expect(out).to eq("1.0")
end
+ it "when using --force, it doesn't remove default gem binaries" do
+ 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?
+
+ # simulate executable for default gem
+ build_gem "irb", default_irb_version, to_system: true, default: true do |s|
+ s.executables = "irb"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ G
+
+ bundle "clean --force", env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
+
+ expect(out).not_to include("Removing irb")
+ end
+
it "doesn't blow up on path gems without a .gemspec" do
relative_path = "vendor/private_gems/bar-1.0"
absolute_path = bundled_app(relative_path)
@@ -634,31 +643,31 @@ RSpec.describe "bundle clean" do
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo"
gem "bar", "1.0", :path => "#{relative_path}"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
bundle :clean
end
it "doesn't remove gems in dry-run mode with path set" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
G
@@ -670,25 +679,25 @@ RSpec.describe "bundle clean" do
expect(out).not_to include("Removing foo (1.0)")
expect(out).to include("Would have removed foo (1.0)")
- should_have_gems "thin-1.0", "rack-1.0.0", "foo-1.0"
+ should_have_gems "thin-1.0", "myrack-1.0.0", "foo-1.0"
- expect(vendored_gems("bin/rackup")).to exist
+ expect(vendored_gems("bin/myrackup")).to exist
end
it "doesn't remove gems in dry-run mode with no path set" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
G
@@ -700,26 +709,26 @@ RSpec.describe "bundle clean" do
expect(out).not_to include("Removing foo (1.0)")
expect(out).to include("Would have removed foo (1.0)")
- should_have_gems "thin-1.0", "rack-1.0.0", "foo-1.0"
+ should_have_gems "thin-1.0", "myrack-1.0.0", "foo-1.0"
- expect(vendored_gems("bin/rackup")).to exist
+ expect(vendored_gems("bin/myrackup")).to exist
end
it "doesn't store dry run as a config setting" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
- bundle "config set dry_run false"
+ bundle_config "dry_run false"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
G
@@ -731,50 +740,50 @@ RSpec.describe "bundle clean" do
expect(out).to include("Removing foo (1.0)")
expect(out).not_to include("Would have removed foo (1.0)")
- should_have_gems "thin-1.0", "rack-1.0.0"
+ should_have_gems "thin-1.0", "myrack-1.0.0"
should_not_have_gems "foo-1.0"
- expect(vendored_gems("bin/rackup")).to exist
+ expect(vendored_gems("bin/myrackup")).to exist
end
it "performs an automatic bundle install" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
gem "weakling"
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
bundle :clean
expect(out).to include("Installing weakling 0.0.3")
- should_have_gems "thin-1.0", "rack-1.0.0", "weakling-0.0.3"
+ should_have_gems "thin-1.0", "myrack-1.0.0", "weakling-0.0.3"
should_not_have_gems "foo-1.0"
end
- it "doesn't remove extensions artifacts from bundled git gems after clean", :ruby_repo do
+ it "doesn't remove extensions artifacts from bundled git gems after clean" do
build_git "very_simple_git_binary", &:add_c_extension
revision = revision_for(lib_path("very_simple_git_binary-1.0"))
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}", :ref => "#{revision}"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
expect(vendored_gems("bundler/gems/extensions")).to exist
expect(vendored_gems("bundler/gems/very_simple_git_binary-1.0-#{revision[0..11]}")).to exist
@@ -786,16 +795,16 @@ RSpec.describe "bundle clean" do
expect(vendored_gems("bundler/gems/very_simple_git_binary-1.0-#{revision[0..11]}")).to exist
end
- it "removes extension directories", :ruby_repo do
+ it "removes extension directories" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
gem "very_simple_binary"
gem "simple_binary"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
very_simple_binary_extensions_dir =
@@ -808,7 +817,7 @@ RSpec.describe "bundle clean" do
expect(simple_binary_extensions_dir).to exist
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
gem "simple_binary"
@@ -822,20 +831,20 @@ RSpec.describe "bundle clean" do
expect(simple_binary_extensions_dir).to exist
end
- it "removes git extension directories", :ruby_repo do
+ it "removes git extension directories" do
build_git "very_simple_git_binary", &:add_c_extension
revision = revision_for(lib_path("very_simple_git_binary-1.0"))
short_revision = revision[0..11]
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}", :ref => "#{revision}"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
very_simple_binary_extensions_dir =
@@ -844,6 +853,7 @@ RSpec.describe "bundle clean" do
expect(very_simple_binary_extensions_dir).to exist
gemfile <<-G
+ source "https://gem.repo1"
gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}", :ref => "#{revision}"
G
@@ -853,6 +863,7 @@ RSpec.describe "bundle clean" do
expect(very_simple_binary_extensions_dir).to exist
gemfile <<-G
+ source "https://gem.repo1"
G
bundle "install"
@@ -869,7 +880,7 @@ RSpec.describe "bundle clean" do
short_revision = revision[0..11]
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
group :development do
gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}", :ref => "#{revision}"
@@ -877,9 +888,9 @@ RSpec.describe "bundle clean" do
G
bundle :lock
- bundle "config set without development"
- bundle "config set path vendor/bundle"
- bundle "install"
+ bundle_config "without development"
+ bundle_config "path vendor/bundle"
+ bundle "install", verbose: true
bundle :clean
very_simple_binary_extensions_dir =
@@ -887,4 +898,39 @@ RSpec.describe "bundle clean" do
expect(very_simple_binary_extensions_dir).to be_nil
end
+
+ it "does not remove the bundler version currently running" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "myrack"
+ G
+
+ bundle_config "path vendor/bundle"
+ bundle "install"
+
+ version = Bundler.gem_version.to_s
+ # Simulate that the locked bundler version is installed in the bundle path
+ # by creating the gem directory and gemspec (as would happen after bundle install with that version)
+ Pathname(vendored_gems("cache/bundler-#{version}.gem")).tap do |path|
+ FileUtils.touch(path)
+ end
+ FileUtils.touch(vendored_gems("gems/bundler-#{version}"))
+ Pathname(vendored_gems("specifications/bundler-#{version}.gemspec")).tap do |path|
+ path.write(<<~GEMSPEC)
+ Gem::Specification.new do |s|
+ s.name = "bundler"
+ s.version = "#{version}"
+ s.authors = ["bundler team"]
+ s.summary = "The best way to manage your application's dependencies"
+ end
+ GEMSPEC
+ end
+
+ should_have_gems "bundler-#{version}"
+
+ bundle :clean
+
+ should_have_gems "bundler-#{version}"
+ end
end
diff --git a/spec/bundler/commands/config_spec.rb b/spec/bundler/commands/config_spec.rb
index 70e3feff00..e8ab32ca5d 100644
--- a/spec/bundler/commands/config_spec.rb
+++ b/spec/bundler/commands/config_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe ".bundle/config" do
context "with env overwrite" do
it "prints config with env" do
- bundle "config list --parseable", :env => { "BUNDLE_FOO" => "bar3" }
+ bundle "config list --parseable", env: { "BUNDLE_FOO" => "bar3" }
expect(out).to include("foo=bar3")
end
end
@@ -38,35 +38,55 @@ RSpec.describe ".bundle/config" do
describe "location with a gemfile" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
G
end
+ it "is local by default" do
+ bundle "config set foo bar"
+ expect(bundled_app(".bundle/config")).to exist
+ expect(home(".bundle/config")).not_to exist
+ end
+
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
expect(tmp("foo/bar/config")).to exist
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "can provide a relative path with the environment variable" do
FileUtils.mkdir_p bundled_app("omg")
ENV["BUNDLE_APP_CONFIG"] = "../foo"
- bundle "config --local path vendor/bundle"
- bundle "install", :dir => bundled_app("omg")
+ bundle "config set --local path vendor/bundle"
+ bundle "install", dir: bundled_app("omg")
expect(bundled_app(".bundle")).not_to exist
expect(bundled_app("../foo/config")).to exist
- expect(the_bundle).to include_gems "rack 1.0.0", :dir => bundled_app("omg")
+ expect(the_bundle).to include_gems "myrack 1.0.0", dir: bundled_app("omg")
end
end
describe "location without a gemfile" do
+ it "is global by default" do
+ bundle "config set foo bar"
+ expect(bundled_app(".bundle/config")).not_to exist
+ expect(home(".bundle/config")).to exist
+ end
+
+ it "does not list global settings as local" do
+ bundle "config set --global foo bar"
+ bundle "config list", dir: home
+
+ expect(out).to include("for the current user")
+ expect(out).not_to include("for your local app")
+ end
+
it "works with an absolute path" do
ENV["BUNDLE_APP_CONFIG"] = tmp("foo/bar").to_s
bundle "config set --local path vendor/bundle"
@@ -76,11 +96,35 @@ 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
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
G
end
@@ -107,17 +151,15 @@ RSpec.describe ".bundle/config" do
end
it "has lower precedence than env" do
- begin
- ENV["BUNDLE_FOO"] = "env"
+ ENV["BUNDLE_FOO"] = "env"
- bundle "config set --global foo global"
- expect(out).to match(/You have a bundler environment variable for foo set to "env"/)
+ bundle "config set --global foo global"
+ expect(out).to match(/You have a bundler environment variable for foo set to "env"/)
- run "puts Bundler.settings[:foo]"
- expect(out).to eq("env")
- ensure
- ENV.delete("BUNDLE_FOO")
- end
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("env")
+ ensure
+ ENV.delete("BUNDLE_FOO")
end
it "can be deleted" do
@@ -173,8 +215,8 @@ RSpec.describe ".bundle/config" do
describe "local" do
before(:each) do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
G
end
@@ -185,15 +227,13 @@ RSpec.describe ".bundle/config" do
end
it "has higher precedence than env" do
- begin
- ENV["BUNDLE_FOO"] = "env"
- bundle "config set --local foo local"
-
- run "puts Bundler.settings[:foo]"
- expect(out).to eq("local")
- ensure
- ENV.delete("BUNDLE_FOO")
- end
+ ENV["BUNDLE_FOO"] = "env"
+ bundle "config set --local foo local"
+
+ run "puts Bundler.settings[:foo]"
+ expect(out).to eq("local")
+ ensure
+ ENV.delete("BUNDLE_FOO")
end
it "can be deleted" do
@@ -231,8 +271,8 @@ RSpec.describe ".bundle/config" do
describe "env" do
before(:each) do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
G
end
@@ -273,9 +313,10 @@ RSpec.describe ".bundle/config" do
describe "parseable option" do
it "prints an empty string" do
- bundle "config get foo --parseable"
+ bundle "config get foo --parseable", raise_on_error: false
expect(out).to eq ""
+ expect(last_command).to be_failure
end
it "only prints the value of the config" do
@@ -304,8 +345,8 @@ RSpec.describe ".bundle/config" do
describe "gem mirrors" do
before(:each) do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
G
end
@@ -318,10 +359,16 @@ end
E
expect(out).to eq("http://gems.example.org/ => http://gem-mirror.example.org/")
end
+
+ it "allows configuring fallback timeout for each mirror, and does not duplicate configs", rubygems: ">= 3.5.12" do
+ bundle "config set --global mirror.https://rubygems.org.fallback_timeout 1"
+ bundle "config set --global mirror.https://rubygems.org.fallback_timeout 2"
+ expect(File.read(home(".bundle/config"))).to include("BUNDLE_MIRROR").once
+ end
end
describe "quoting" do
- before(:each) { gemfile "# no gems" }
+ before(:each) { gemfile "source 'https://gem.repo1'" }
let(:long_string) do
"--with-xml2-include=/usr/pkg/include/libxml2 --with-xml2-lib=/usr/pkg/lib " \
"--with-xslt-dir=/usr/pkg"
@@ -335,7 +382,7 @@ E
it "doesn't return quotes around values" do
bundle "config set foo '1'"
- run "puts Bundler.settings.send(:global_config_file).read"
+ run "puts Bundler.settings.send(:local_config_file).read"
expect(out).to include('"1"')
run "puts Bundler.settings[:foo]"
expect(out).to eq("1")
@@ -371,8 +418,8 @@ E
describe "very long lines" do
before(:each) do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
G
end
@@ -399,20 +446,65 @@ E
end
end
+ describe "commented out settings with urls" do
+ before do
+ bundle "config set #mirror.https://rails-assets.org http://localhost:9292"
+ end
+
+ it "does not make bundler crash and ignores the configuration" do
+ bundle "config list --parseable"
+
+ expect(out).to be_empty
+ expect(err).to be_empty
+
+ ruby(<<~RUBY)
+ require "bundler"
+ print Bundler.settings.mirror_for("https://rails-assets.org")
+ RUBY
+ expect(out).to eq("https://rails-assets.org/")
+ expect(err).to be_empty
+
+ bundle "config set mirror.all http://localhost:9293"
+ ruby(<<~RUBY)
+ require "bundler"
+ print Bundler.settings.mirror_for("https://rails-assets.org")
+ RUBY
+ expect(out).to eq("http://localhost:9293/")
+ expect(err).to be_empty
+ end
+ end
+
describe "subcommands" do
it "list" do
- bundle "config list"
- expect(out).to eq "Settings are listed in order of priority. The top value will be used.\nspec_run\nSet via BUNDLE_SPEC_RUN: \"true\""
+ bundle "config list", env: { "BUNDLE_FOO" => "bar" }
+ expect(out).to eq "Settings are listed in order of priority. The top value will be used.\nfoo\nSet via BUNDLE_FOO: \"bar\""
+
+ bundle "config list", env: { "BUNDLE_FOO" => "bar" }, parseable: true
+ expect(out).to eq "foo=bar"
+ 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]\""
+
+ bundle "config list", parseable: true, env: { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" }
+ expect(out).to eq "gems.myserver.com=user:password"
+ 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\""
- bundle "config list", :parseable => true
- expect(out).to eq "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"
end
it "get" do
ENV["BUNDLE_BAR"] = "bar_val"
- bundle "config get foo"
+ bundle "config get foo", raise_on_error: false
expect(out).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`"
+ expect(last_command).to be_failure
ENV["BUNDLE_FOO"] = "foo_val"
@@ -439,7 +531,7 @@ E
bundle "config set --local foo 4.1"
expect(out).to eq "You are replacing the current local value of foo, which is currently \"4\""
- bundle "config set --global --local foo 5", :raise_on_error => false
+ bundle "config set --global --local foo 5", raise_on_error: false
expect(last_command).to be_failure
expect(err).to eq "The options global and local were specified. Please only use one of the switches at a time."
end
@@ -457,7 +549,8 @@ E
bundle "config unset foo"
expect(out).to eq ""
- expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`"
+ expect(bundle("config get foo", raise_on_error: false)).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`"
+ expect(last_command).to be_failure
bundle "config set --local foo 1"
bundle "config set --global foo 2"
@@ -467,7 +560,8 @@ E
expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nSet for the current user (#{home(".bundle/config")}): \"2\""
bundle "config unset foo --global"
expect(out).to eq ""
- expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`"
+ expect(bundle("config get foo", raise_on_error: false)).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`"
+ expect(last_command).to be_failure
bundle "config set --local foo 1"
bundle "config set --global foo 2"
@@ -477,9 +571,10 @@ E
expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nSet for your local app (#{bundled_app(".bundle/config")}): \"1\""
bundle "config unset foo --local"
expect(out).to eq ""
- expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`"
+ expect(bundle("config get foo", raise_on_error: false)).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`"
+ expect(last_command).to be_failure
- bundle "config unset foo --local --global", :raise_on_error => false
+ bundle "config unset foo --local --global", raise_on_error: false
expect(last_command).to be_failure
expect(err).to eq "The options global and local were specified. Please only use one of the switches at a time."
end
@@ -487,11 +582,41 @@ E
end
RSpec.describe "setting gemfile via config" do
+ context "when a default Gemfile exists" do
+ before do
+ gemfile <<-G
+ source "https://gem.repo1"
+ G
+
+ gemfile bundled_app("foo/bar_gemfile"), <<-G
+ source "https://gem.repo1"
+ G
+ end
+
+ it "reports the local gemfile setting without promoting it to the environment" do
+ bundle "config set gemfile foo/bar_gemfile"
+
+ bundle "config list"
+ expect(out).to include("Set for your local app (#{bundled_app(".bundle/config")}): \"foo/bar_gemfile\"")
+ expect(out).not_to include("Set via BUNDLE_GEMFILE")
+ end
+
+ it "unsets the local gemfile setting from the app config" do
+ bundle "config set gemfile foo/bar_gemfile"
+
+ bundle "config unset gemfile"
+ bundle "config get gemfile", raise_on_error: false
+
+ expect(out).to include("You have not configured a value for `gemfile`")
+ expect(File.read(bundled_app(".bundle/config"))).not_to include("BUNDLE_GEMFILE")
+ end
+ end
+
context "when only the non-default Gemfile exists" do
it "persists the gemfile location to .bundle/config" do
gemfile bundled_app("NotGemfile"), <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
bundle "config set --local gemfile #{bundled_app("NotGemfile")}"
@@ -502,3 +627,20 @@ RSpec.describe "setting gemfile via config" do
end
end
end
+
+RSpec.describe "setting lockfile via config" do
+ it "persists the lockfile location to .bundle/config" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+
+ bundle "config set --local gemfile #{bundled_app("NotGemfile")}"
+ bundle "config set --local lockfile #{bundled_app("ReallyNotGemfile.lock")}"
+ expect(File.exist?(bundled_app(".bundle/config"))).to eq(true)
+
+ bundle "config list"
+ expect(out).to include("NotGemfile")
+ expect(out).to include("ReallyNotGemfile.lock")
+ end
+end
diff --git a/spec/bundler/commands/console_spec.rb b/spec/bundler/commands/console_spec.rb
index 3092184f45..a44f607546 100644
--- a/spec/bundler/commands/console_spec.rb
+++ b/spec/bundler/commands/console_spec.rb
@@ -1,106 +1,214 @@
# frozen_string_literal: true
-RSpec.describe "bundle console", :bundler => "< 3", :readline => true do
+RSpec.describe "bundle console", readline: true do
before :each do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "activesupport", :group => :test
- gem "rack_middleware", :group => :development
- G
- end
+ build_repo2 do
+ # A minimal fake pry console
+ build_gem "pry" do |s|
+ s.write "lib/pry.rb", <<-RUBY
+ class Pry
+ class << self
+ def toplevel_binding
+ unless defined?(@toplevel_binding) && @toplevel_binding
+ TOPLEVEL_BINDING.eval %{
+ def self.__pry__; binding; end
+ Pry.instance_variable_set(:@toplevel_binding, __pry__)
+ class << self; undef __pry__; end
+ }
+ end
+ @toplevel_binding.eval('private')
+ @toplevel_binding
+ end
+
+ def __pry__
+ while line = gets
+ begin
+ puts eval(line, toplevel_binding).inspect.sub(/^"(.*)"$/, '=> \\1')
+ rescue Exception => e
+ puts "\#{e.class}: \#{e.message}"
+ puts e.backtrace.first
+ end
+ end
+ end
+ alias start __pry__
+ end
+ end
+ RUBY
+ end
- it "starts IRB with the default group loaded" do
- bundle "console" do |input, _, _|
- input.puts("puts RACK")
- input.puts("exit")
+ build_dummy_irb
end
- expect(out).to include("0.9.1")
end
- it "uses IRB as default console" do
- bundle "console" do |input, _, _|
- input.puts("__method__")
- input.puts("exit")
+ context "when the library requires a non-existent file" do
+ before do
+ build_lib "loadfuuu", "1.0.0" do |s|
+ s.write "lib/loadfuuu.rb", "require_relative 'loadfuuu/bar'"
+ s.write "lib/loadfuuu/bar.rb", "require 'not-in-bundle'"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ path "#{lib_path}" do
+ gem "loadfuuu", require: true
+ end
+ G
end
- expect(out).to include(":irb_binding")
- end
- it "starts another REPL if configured as such" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "pry"
- G
- bundle "config set console pry"
+ it "does not show the bug report template" do
+ bundle("console", raise_on_error: false) do |input, _, _|
+ input.puts("exit")
+ end
- bundle "console" do |input, _, _|
- input.puts("__method__")
- input.puts("exit")
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
end
- expect(out).to include(":__pry__")
end
- it "falls back to IRB if the other REPL isn't available" do
- bundle "config set console pry"
- # make sure pry isn't there
+ context "when the library references a non-existent constant" do
+ before do
+ build_lib "loadfuuu", "1.0.0" do |s|
+ s.write "lib/loadfuuu.rb", "Some::NonExistent::Constant"
+ end
- bundle "console" do |input, _, _|
- input.puts("__method__")
- input.puts("exit")
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ path "#{lib_path}" do
+ gem "loadfuuu", require: true
+ end
+ G
end
- expect(out).to include(":irb_binding")
- end
- it "doesn't load any other groups" do
- bundle "console" do |input, _, _|
- input.puts("puts ACTIVESUPPORT")
- input.puts("exit")
+ it "does not show the bug report template" do
+ bundle("console", raise_on_error: false) do |input, _, _|
+ input.puts("exit")
+ end
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
end
- expect(out).to include("NameError")
end
- describe "when given a group" do
- it "loads the given group" do
- bundle "console test" do |input, _, _|
- input.puts("puts ACTIVESUPPORT")
+ context "when the library does not have any errors" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ gem "myrack"
+ gem "activesupport", :group => :test
+ gem "myrack_middleware", :group => :development
+ G
+ end
+
+ it "starts IRB with the default group loaded" do
+ bundle "console" do |input, _, _|
+ input.puts("puts MYRACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "uses IRB as default console" do
+ skip "Does not work in a ruby-core context if irb is in the default $LOAD_PATH because it enables the real IRB, not our dummy one" if ruby_core? && Gem.ruby_version < Gem::Version.new("3.5.0.a")
+
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
input.puts("exit")
end
- expect(out).to include("2.3.5")
+ expect(out).to include("__irb__")
end
- it "loads the default group" do
- bundle "console test" do |input, _, _|
- input.puts("puts RACK")
+ it "starts another REPL if configured as such" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ gem "pry"
+ G
+ bundle_config "console pry"
+
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
input.puts("exit")
end
- expect(out).to include("0.9.1")
+ expect(out).to include(":__pry__")
end
- it "doesn't load other groups" do
- bundle "console test" do |input, _, _|
- input.puts("puts RACK_MIDDLEWARE")
+ it "falls back to IRB if the other REPL isn't available" do
+ skip "Does not work in a ruby-core context if irb is in the default $LOAD_PATH because it enables the real IRB, not our dummy one" if ruby_core? && Gem.ruby_version < Gem::Version.new("3.5.0.a")
+
+ bundle_config "console pry"
+ # make sure pry isn't there
+
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
+ input.puts("exit")
+ end
+ expect(out).to include("__irb__")
+ end
+
+ it "does not try IRB twice if no console is configured and IRB is not available" do
+ create_file("irb.rb", "raise LoadError, 'irb is not available'")
+
+ bundle("console", env: { "RUBYOPT" => "-I#{bundled_app} #{ENV["RUBYOPT"]}" }, raise_on_error: false) do |input, _, _|
+ input.puts("puts ACTIVESUPPORT")
+ input.puts("exit")
+ end
+ expect(err).not_to include("falling back to irb")
+ expect(err).to include("irb is not available")
+ end
+
+ it "doesn't load any other groups" do
+ bundle "console" do |input, _, _|
+ input.puts("puts ACTIVESUPPORT")
input.puts("exit")
end
expect(out).to include("NameError")
end
- end
- it "performs an automatic bundle install" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "activesupport", :group => :test
- gem "rack_middleware", :group => :development
- gem "foo"
- G
-
- bundle "config set auto_install 1"
- bundle :console do |input, _, _|
- input.puts("puts 'hello'")
- input.puts("exit")
+ describe "when given a group" do
+ it "loads the given group" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts ACTIVESUPPORT")
+ input.puts("exit")
+ end
+ expect(out).to include("2.3.5")
+ end
+
+ it "loads the default group" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts MYRACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "doesn't load other groups" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts MYRACK_MIDDLEWARE")
+ input.puts("exit")
+ end
+ expect(out).to include("NameError")
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ gem "myrack"
+ gem "activesupport", :group => :test
+ gem "myrack_middleware", :group => :development
+ gem "foo"
+ G
+
+ bundle_config "auto_install 1"
+ bundle :console do |input, _, _|
+ input.puts("puts 'hello'")
+ input.puts("exit")
+ end
+ expect(out).to include("Installing foo 1.0")
+ expect(out).to include("hello")
+ expect(the_bundle).to include_gems "foo 1.0"
end
- expect(out).to include("Installing foo 1.0")
- expect(out).to include("hello")
- expect(the_bundle).to include_gems "foo 1.0"
end
end
diff --git a/spec/bundler/commands/doctor_spec.rb b/spec/bundler/commands/doctor_spec.rb
index d8e9674a24..d350b4b3d1 100644
--- a/spec/bundler/commands/doctor_spec.rb
+++ b/spec/bundler/commands/doctor_spec.rb
@@ -4,17 +4,18 @@ require "find"
require "stringio"
require "bundler/cli"
require "bundler/cli/doctor"
+require "bundler/cli/doctor/diagnose"
RSpec.describe "bundle doctor" do
before(:each) do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
@stdout = StringIO.new
- [:error, :warn].each do |method|
+ [:error, :warn, :info].each do |method|
allow(Bundler.ui).to receive(method).and_wrap_original do |m, message|
m.call message
@stdout.puts message
@@ -32,85 +33,153 @@ 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(:writable?).and_call_original
+ allow(File).to receive(:readable?).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 }
allow(File).to receive(:readable?).with(unwritable_file) { true }
+
+ # The following lines are for `Gem::PathSupport#initialize`.
+ allow(File).to receive(:exist?).with(Gem.default_dir)
+ allow(File).to receive(:writable?).with(Gem.default_dir)
+ allow(File).to receive(:writable?).with(File.expand_path("..", Gem.default_dir))
end
- it "exits with no message if the installed gem has no C extensions" do
- expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
- expect(@stdout.string).to be_empty
+ it "exits with a success message if the installed gem has no C extensions" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
+ expect { doctor.run }.not_to raise_error
+ expect(@stdout.string).to include("No issues")
end
- it "exits with no message if the installed gem's C extension dylib breakage is fine" do
- doctor = Bundler::CLI::Doctor.new({})
- expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
+ it "exits with a success message if the installed gem's C extension dylib breakage is fine" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/myrack/myrack.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
+ allow(doctor).to receive(:lookup_with_fiddle).with("/usr/lib/libSystem.dylib").and_return(false)
+ expect { doctor.run }.not_to raise_error
+ expect(@stdout.string).to include("No issues")
+ end
+
+ it "parses otool output correctly" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ expect(doctor).to receive(:`).with("/usr/bin/otool -L fake").and_return("/home/gem/ruby/3.4.3/gems/blake3-rb-1.5.4.4/lib/digest/blake3/blake3_ext.bundle:\n\t (compatibility version 0.0.0, current version 0.0.0)\n\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)")
+ expect(doctor.dylibs_darwin("fake")).to eq(["/usr/lib/libSystem.B.dylib"])
end
it "exits with a message if one of the linked libraries is missing" do
- doctor = Bundler::CLI::Doctor.new({})
- expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/myrack/myrack.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
+ allow(doctor).to receive(:lookup_with_fiddle).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(true)
+ expect { doctor.run }.to raise_error(Bundler::ProductionError, <<~E.strip), @stdout.string
The following gems are missing OS dependencies:
* bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
- * rack: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
+ * myrack: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
E
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
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
+ expect { doctor.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(:writable?).and_call_original
+ allow(File).to receive(:readable?).and_call_original
+ allow(File).to receive(:exist?).with(@unwritable_file) { true }
allow(File).to receive(:stat).with(@unwritable_file) { @stat }
end
- it "exits with an error if home contains files that are not readable/writable" do
+ it "exits with an error if home contains files that are not readable" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
allow(@stat).to receive(:uid) { Process.uid }
allow(File).to receive(:writable?).with(@unwritable_file) { false }
allow(File).to receive(:readable?).with(@unwritable_file) { false }
- expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
- "Files exist in the Bundler home that are not readable/writable by the current user. These files are:\n - #{@unwritable_file}"
+ "Files exist in the Bundler home that are not readable by the current user. These files are:\n - #{@unwritable_file}"
)
expect(@stdout.string).not_to include("No issues")
end
+ it "exits without an error if home contains files that are not writable" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
+ allow(@stat).to receive(:uid) { Process.uid }
+ allow(File).to receive(:writable?).with(@unwritable_file) { false }
+ allow(File).to receive(:readable?).with(@unwritable_file) { true }
+ expect { doctor.run }.not_to raise_error
+ expect(@stdout.string).not_to include(
+ "Files exist in the Bundler home that are not readable by the current user. These files are:\n - #{@unwritable_file}"
+ )
+ expect(@stdout.string).to include("No issues")
+ end
+
context "when home contains files that are not owned by the current process", :permissions do
before(:each) do
allow(@stat).to receive(:uid) { 0o0000 }
end
it "exits with an error if home contains files that are not readable/writable and are not owned by the current user" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
allow(File).to receive(:writable?).with(@unwritable_file) { false }
allow(File).to receive(:readable?).with(@unwritable_file) { false }
- expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
- "Files exist in the Bundler home that are owned by another user, and are not readable/writable. These files are:\n - #{@unwritable_file}"
+ "Files exist in the Bundler home that are owned by another user, and are not readable. These files are:\n - #{@unwritable_file}"
)
expect(@stdout.string).not_to include("No issues")
end
it "exits with a warning if home contains files that are read/write but not owned by current user" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
allow(File).to receive(:writable?).with(@unwritable_file) { true }
allow(File).to receive(:readable?).with(@unwritable_file) { true }
- expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
- "Files exist in the Bundler home that are owned by another user, but are still readable/writable. These files are:\n - #{@unwritable_file}"
+ "Files exist in the Bundler home that are owned by another user, but are still readable. These files are:\n - #{@unwritable_file}"
)
expect(@stdout.string).not_to include("No issues")
end
end
end
+
+ context "when home contains filenames with special characters" do
+ it "escape filename before command execute" do
+ doctor = Bundler::CLI::Doctor::Diagnose.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 fbce4b7bc1..aa35685be8 100644
--- a/spec/bundler/commands/exec_spec.rb
+++ b/spec/bundler/commands/exec_spec.rb
@@ -1,99 +1,126 @@
# frozen_string_literal: true
RSpec.describe "bundle exec" do
- let(:system_gems_to_install) { %w[rack-1.0.0 rack-0.9.1] }
- before :each do
- system_gems(system_gems_to_install, :path => default_bundle_path)
- end
-
it "works with --gemfile flag" do
- create_file "CustomGemfile", <<-G
- gem "rack", "1.0.0"
+ system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path)
+
+ gemfile "CustomGemfile", <<-G
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
G
- bundle "exec --gemfile CustomGemfile rackup"
+ bundle "exec --gemfile CustomGemfile myrackup"
expect(out).to eq("1.0.0")
end
it "activates the correct gem" do
+ system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path)
+
gemfile <<-G
- gem "rack", "0.9.1"
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
G
- bundle "exec rackup"
+ bundle "exec myrackup"
expect(out).to eq("0.9.1")
end
+ it "works and prints no warnings when HOME is not writable" do
+ system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path)
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
+ G
+
+ bundle "exec myrackup", 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
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "exec rackup"
+ bundle "exec myrackup"
expect(out).to eq("1.0.0")
end
it "works when running from a random directory" do
install_gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "exec 'cd #{tmp("gems")} && rackup'"
+ bundle "exec 'cd #{tmp("gems")} && myrackup'"
expect(out).to eq("1.0.0")
end
it "works when exec'ing something else" do
- install_gemfile 'gem "rack"'
+ install_gemfile "source \"https://gem.repo1\"; gem \"myrack\""
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 \"https://gem.repo1\"; gem \"myrack\""
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 \"https://gem.repo1\"; gem \"myrack\""
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 \"https://gem.repo1\"; gem \"myrack\""
bundle "exec sh -c '#{gem_cmd} --version'"
expect(out).to eq(Gem::VERSION)
end
+ it "works when exec'ing back to bundler to run a remote resolve" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
+ G
+
+ bundle "exec bundle lock", env: { "BUNDLER_VERSION" => Bundler::VERSION }
+
+ expect(out).to include("Writing lockfile")
+ end
+
it "respects custom process title when loading through ruby" do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
script_that_changes_its_own_title_and_checks_if_picked_up_by_ps_unix_utility = <<~'RUBY'
Process.setproctitle("1-2-3-4-5-6-7")
puts `ps -ocommand= -p#{$$}`
RUBY
- create_file "Gemfile"
+ gemfile "Gemfile", "source \"https://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 \"https://gem.repo1\"; gem \"myrack\""
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 \"https://gem.repo1\"; gem \"myrack\""
bundle "exec echo --verbose"
expect(out).to eq("--verbose")
end
it "handles --keep-file-descriptors" do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
require "tempfile"
@@ -111,24 +138,24 @@ RSpec.describe "bundle exec" do
end
G
- install_gemfile ""
- sys_exec "#{Gem.ruby} #{command.path}"
+ install_gemfile "source \"https://gem.repo1\""
+ in_bundled_app "#{Gem.ruby} #{command.path}"
expect(out).to be_empty
expect(err).to be_empty
end
it "accepts --keep-file-descriptors" do
- install_gemfile ""
+ install_gemfile "source \"https://gem.repo1\""
bundle "exec --keep-file-descriptors echo foobar"
expect(err).to be_empty
end
it "can run a command named --verbose" do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
- install_gemfile 'gem "rack"'
+ install_gemfile "source \"https://gem.repo1\"; gem \"myrack\""
File.open(bundled_app("--verbose"), "w") do |f|
f.puts "#!/bin/sh"
f.puts "echo foobar"
@@ -142,191 +169,185 @@ RSpec.describe "bundle exec" do
it "handles different versions in different bundles" do
build_repo2 do
- build_gem "rack_two", "1.0.0" do |s|
- s.executables = "rackup"
+ build_gem "myrack_two", "1.0.0" do |s|
+ s.executables = "myrackup"
end
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "0.9.1"
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
G
- install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2
- source "#{file_uri_for(gem_repo2)}"
- gem "rack_two", "1.0.0"
+ install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2
+ source "https://gem.repo2"
+ gem "myrack_two", "1.0.0"
G
- bundle "exec rackup"
+ bundle "exec myrackup"
expect(out).to eq("0.9.1")
- bundle "exec rackup", :dir => bundled_app2
+ bundle "exec myrackup", dir: bundled_app2
expect(out).to eq("1.0.0")
end
context "with default gems" do
- let(:system_gems_to_install) { [] }
-
- let(:default_irb_version) { ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", :raise_on_error => false }
+ # TODO: Switch to ERB::VERSION once Ruby 3.4 support is dropped, so all
+ # supported rubies include an `erb` gem version where `ERB::VERSION` is
+ # public
+ let(:default_erb_version) { ruby "require 'erb/version'; puts ERB.const_get(:VERSION)" }
context "when not specified in Gemfile" do
before do
- skip "irb isn't a default gem" if default_irb_version.empty?
-
- install_gemfile ""
+ install_gemfile "source \"https://gem.repo1\""
end
it "uses version provided by ruby" do
- bundle "exec irb --version"
+ bundle "exec erb --version"
- expect(out).to include(default_irb_version)
+ expect(stdboth).to eq(default_erb_version)
end
end
context "when specified in Gemfile directly" do
- let(:specified_irb_version) { "0.9.6" }
+ let(:specified_erb_version) { "2.0.0" }
before do
- skip "irb isn't a default gem" if default_irb_version.empty?
-
build_repo2 do
- build_gem "irb", specified_irb_version do |s|
- s.executables = "irb"
+ build_gem "erb", specified_erb_version do |s|
+ s.executables = "erb"
end
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "irb", "#{specified_irb_version}"
+ source "https://gem.repo2"
+ gem "erb", "#{specified_erb_version}"
G
end
it "uses version specified" do
- bundle "exec irb --version"
+ bundle "exec erb --version"
- expect(out).to eq(specified_irb_version)
- expect(err).to be_empty
+ expect(stdboth).to eq(specified_erb_version)
end
end
context "when specified in Gemfile indirectly" do
- let(:indirect_irb_version) { "0.9.6" }
+ let(:indirect_erb_version) { "2.0.0" }
before do
- skip "irb isn't a default gem" if default_irb_version.empty?
-
build_repo2 do
- build_gem "irb", indirect_irb_version do |s|
- s.executables = "irb"
+ build_gem "erb", indirect_erb_version do |s|
+ s.executables = "erb"
end
- build_gem "gem_depending_on_old_irb" do |s|
- s.add_dependency "irb", indirect_irb_version
+ build_gem "gem_depending_on_old_erb" do |s|
+ s.add_dependency "erb", indirect_erb_version
end
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "gem_depending_on_old_irb"
+ source "https://gem.repo2"
+ gem "gem_depending_on_old_erb"
G
-
- bundle "exec irb --version"
end
it "uses resolved version" do
- expect(out).to eq(indirect_irb_version)
- expect(err).to be_empty
+ bundle "exec erb --version"
+
+ expect(stdboth).to eq(indirect_erb_version)
end
end
end
it "warns about executable conflicts" do
build_repo2 do
- build_gem "rack_two", "1.0.0" do |s|
- s.executables = "rackup"
+ build_gem "myrack_two", "1.0.0" do |s|
+ s.executables = "myrackup"
end
end
- bundle "config set path.system true"
+ bundle_config_global "path.system true"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "0.9.1"
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
G
- install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2
- source "#{file_uri_for(gem_repo2)}"
- gem "rack_two", "1.0.0"
+ install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2
+ source "https://gem.repo2"
+ gem "myrack_two", "1.0.0"
G
- bundle "exec rackup"
+ bundle "exec myrackup"
expect(last_command.stderr).to eq(
- "Bundler is using a binstub that was created for a different gem (rack).\n" \
- "You should run `bundle binstub rack_two` to work around a system/bundle conflict."
+ "Bundler is using a binstub that was created for a different gem (myrack).\n" \
+ "You should run `bundle binstub myrack_two` to work around a system/bundle conflict."
)
end
it "handles gems installed with --without" do
- bundle "config --local without middleware"
+ bundle_config "without middleware"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack" # rack 0.9.1 and 1.0 exist
+ source "https://gem.repo1"
+ gem "myrack" # myrack 0.9.1 and 1.0 exist
group :middleware do
- gem "rack_middleware" # rack_middleware depends on rack 0.9.1
+ gem "myrack_middleware" # myrack_middleware depends on myrack 0.9.1
end
G
- bundle "exec rackup"
+ bundle "exec myrackup"
expect(out).to eq("0.9.1")
- expect(the_bundle).not_to include_gems "rack_middleware 1.0"
+ expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
end
it "does not duplicate already exec'ed RUBYOPT" do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
-
+ create_file("echoopt", "#!/usr/bin/env ruby\nprint ENV['RUBYOPT']")
install_gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
bundler_setup_opt = "-r#{lib_dir}/bundler/setup"
rubyopt = opt_add(bundler_setup_opt, ENV["RUBYOPT"])
- bundle "exec 'echo $RUBYOPT'"
+ bundle "exec echoopt"
expect(out.split(" ").count(bundler_setup_opt)).to eq(1)
- bundle "exec 'echo $RUBYOPT'", :env => { "RUBYOPT" => rubyopt }
+ bundle "exec echoopt", env: { "RUBYOPT" => rubyopt }
expect(out.split(" ").count(bundler_setup_opt)).to eq(1)
end
it "does not duplicate already exec'ed RUBYLIB" do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
-
+ create_file("echolib", "#!/usr/bin/env ruby\nprint ENV['RUBYLIB']")
install_gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
rubylib = ENV["RUBYLIB"]
rubylib = rubylib.to_s.split(File::PATH_SEPARATOR).unshift lib_dir.to_s
rubylib = rubylib.uniq.join(File::PATH_SEPARATOR)
- bundle "exec 'echo $RUBYLIB'"
+ bundle "exec echolib"
expect(out).to include(rubylib)
- bundle "exec 'echo $RUBYLIB'", :env => { "RUBYLIB" => rubylib }
+ bundle "exec echolib", env: { "RUBYLIB" => rubylib }
expect(out).to include(rubylib)
end
it "errors nicely when the argument doesn't exist" do
install_gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "exec foobarbaz", :raise_on_error => false
+ bundle "exec foobarbaz", raise_on_error: false
expect(exitstatus).to eq(127)
expect(err).to include("bundler: command not found: foobarbaz")
expect(err).to include("Install missing gem executables with `bundle install`")
@@ -334,35 +355,39 @@ RSpec.describe "bundle exec" do
it "errors nicely when the argument is not executable" do
install_gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "exec touch foo"
- bundle "exec ./foo", :raise_on_error => false
+ bundled_app("foo").write("")
+ bundle "exec ./foo", raise_on_error: false
expect(exitstatus).to eq(126)
expect(err).to include("bundler: not executable: ./foo")
end
it "errors nicely when no arguments are passed" do
install_gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "exec", :raise_on_error => false
+ bundle "exec", raise_on_error: false
expect(exitstatus).to eq(128)
expect(err).to include("bundler: exec needs a command to run")
end
it "raises a helpful error when exec'ing to something outside of the bundle" do
- bundle "config set clean false" # want to keep the rackup binstub
+ system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path)
+
+ bundle_config "clean false" # want to keep the myrackup binstub
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "with_license"
+ source "https://gem.repo1"
+ gem "foo"
G
[true, false].each do |l|
- bundle "config set disable_exec_load #{l}"
- bundle "exec rackup", :raise_on_error => false
- expect(err).to include "can't find executable rackup for gem rack. rack is not currently included in the bundle, perhaps you meant to add it to your Gemfile?"
+ bundle_config "disable_exec_load #{l}"
+ bundle "exec myrackup", raise_on_error: false
+ expect(err).to include "can't find executable myrackup for gem myrack. myrack is not currently included in the bundle, perhaps you meant to add it to your Gemfile?"
end
end
@@ -373,17 +398,15 @@ RSpec.describe "bundle exec" do
each_prefix.call("exec") do |exec|
describe "when #{exec} is used" do
before(:each) do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
-
install_gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
create_file("print_args", <<-'RUBY')
#!/usr/bin/env ruby
puts "args: #{ARGV.inspect}"
RUBY
- bundled_app("print_args").chmod(0o755)
end
it "shows executable's man page when --help is after the executable" do
@@ -404,6 +427,7 @@ RSpec.describe "bundle exec" do
it "shows executable's man page when the executable has a -" do
FileUtils.mv(bundled_app("print_args"), bundled_app("docker-template"))
+ FileUtils.mv(bundled_app("print_args.bat"), bundled_app("docker-template.bat")) if Gem.win_platform?
bundle "#{exec} docker-template build discourse --help"
expect(out).to eq('args: ["build", "discourse", "--help"]')
end
@@ -420,37 +444,37 @@ RSpec.describe "bundle exec" do
it "shows bundle-exec's man page when --help is between exec and the executable" do
with_fake_man do
- bundle "#{exec} --help cat"
+ bundle "#{exec} --help echo"
end
- expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ expect(out).to include(%(["#{man_dir}/bundle-exec.1"]))
end
it "shows bundle-exec's man page when --help is before exec" do
with_fake_man do
bundle "--help #{exec}"
end
- expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ expect(out).to include(%(["#{man_dir}/bundle-exec.1"]))
end
it "shows bundle-exec's man page when -h is before exec" do
with_fake_man do
bundle "-h #{exec}"
end
- expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ expect(out).to include(%(["#{man_dir}/bundle-exec.1"]))
end
it "shows bundle-exec's man page when --help is after exec" do
with_fake_man do
bundle "#{exec} --help"
end
- expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ expect(out).to include(%(["#{man_dir}/bundle-exec.1"]))
end
it "shows bundle-exec's man page when -h is after exec" do
with_fake_man do
bundle "#{exec} -h"
end
- expect(out).to include(%(["#{root}/man/bundle-exec.1"]))
+ expect(out).to include(%(["#{man_dir}/bundle-exec.1"]))
end
end
end
@@ -460,29 +484,31 @@ RSpec.describe "bundle exec" do
describe "run from a random directory" do
before(:each) do
install_gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
it "works when unlocked" do
- bundle "exec 'cd #{tmp("gems")} && rackup'"
+ bundle "exec 'cd #{tmp("gems")} && myrackup'"
expect(out).to eq("1.0.0")
end
it "works when locked" do
expect(the_bundle).to be_locked
- bundle "exec 'cd #{tmp("gems")} && rackup'"
+ bundle "exec 'cd #{tmp("gems")} && myrackup'"
expect(out).to eq("1.0.0")
end
end
describe "from gems bundled via :path" do
before(:each) do
- build_lib "fizz", :path => home("fizz") do |s|
+ build_lib "fizz", path: home("fizz") do |s|
s.executables = "fizz"
end
install_gemfile <<-G
+ source "https://gem.repo1"
gem "fizz", :path => "#{File.expand_path(home("fizz"))}"
G
end
@@ -507,6 +533,7 @@ RSpec.describe "bundle exec" do
end
install_gemfile <<-G
+ source "https://gem.repo1"
gem "fizz_git", :git => "#{lib_path("fizz_git-1.0")}"
G
end
@@ -525,11 +552,12 @@ RSpec.describe "bundle exec" do
describe "from gems bundled via :git with no gemspec" do
before(:each) do
- build_git "fizz_no_gemspec", :gemspec => false do |s|
+ build_git "fizz_no_gemspec", gemspec: false do |s|
s.executables = "fizz_no_gemspec"
end
install_gemfile <<-G
+ source "https://gem.repo1"
gem "fizz_no_gemspec", "1.0", :git => "#{lib_path("fizz_no_gemspec-1.0")}"
G
end
@@ -549,16 +577,59 @@ RSpec.describe "bundle exec" do
it "performs an automatic bundle install" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "0.9.1"
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
gem "foo"
G
- bundle "config set auto_install 1"
- bundle "exec rackup"
+ bundle_config "auto_install 1"
+ bundle "exec myrackup", artifice: "compact_index"
expect(out).to include("Installing foo 1.0")
end
+ it "performs an automatic bundle install with git gems" do
+ build_git "foo" do |s|
+ s.executables = "foo"
+ end
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle_config "auto_install 1"
+ bundle "exec foo", artifice: "compact_index"
+ expect(out).to include("Fetching myrack 0.9.1")
+ expect(out).to include("Fetching #{lib_path("foo-1.0")}")
+ expect(out.lines).to end_with("1.0")
+ end
+
+ it "loads the correct optparse when `auto_install` is set, and optparse is a dependency" do
+ 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 "auto_install 1"
+ bundle_config "path vendor/bundle"
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "fastlane"
+ G
+
+ bundle "exec fastlane", artifice: "compact_index"
+ 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"
@@ -571,26 +642,28 @@ RSpec.describe "bundle exec" do
s.version = '1.0'
s.summary = 'TODO: Add summary'
s.authors = 'Me'
+ s.rubygems_version = nil
end
G
end
gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
- bundle "exec irb", :raise_on_error => false
+ bundle "exec erb", raise_on_error: false
expect(err).to match("The gemspec at #{lib_path("foo-1.0").join("foo.gemspec")} is not valid")
- expect(err).to match('"TODO" is not a summary')
+ expect(err).to match(/missing value for attribute rubygems_version|rubygems_version must not be nil/)
end
end
describe "with gems bundled for deployment" do
it "works when calling bundler from another script" do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
-
gemfile <<-G
+ source "https://gem.repo1"
+
module Monkey
def bin_path(a,b,c)
raise Gem::GemNotFoundException.new('Fail')
@@ -598,73 +671,124 @@ RSpec.describe "bundle exec" do
end
Bundler.rubygems.extend(Monkey)
G
- bundle "config set path.system true"
+ bundle_config "path.system true"
bundle "install"
- bundle "exec ruby -e '`bundle -v`; puts $?.success?'", :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ bundle "exec ruby -e '`bundle -v`; puts $?.success?'", env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(out).to match("true")
end
end
+ describe "bundle exec gem uninstall" do
+ before do
+ build_repo4 do
+ build_gem "foo"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "foo"
+ G
+ end
+
+ it "works" do
+ bundle "exec #{gem_cmd} uninstall foo"
+ expect(out).to eq("Successfully uninstalled foo-1.0")
+ end
+ end
+
+ describe "running gem commands in presence of rubygems plugins" do
+ before do
+ build_repo4 do
+ build_gem "foo" do |s|
+ s.write "lib/rubygems_plugin.rb", "puts 'FAIL'"
+ end
+ end
+
+ system_gems "foo-1.0", path: default_bundle_path, gem_repo: gem_repo4
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ G
+ end
+
+ it "does not load plugins outside of the bundle" do
+ bundle "exec #{gem_cmd} -v"
+ expect(out).not_to include("FAIL")
+ end
+ end
+
context "`load`ing a ruby file instead of `exec`ing" do
let(:path) { bundled_app("ruby_executable") }
let(:shebang) { "#!/usr/bin/env ruby" }
- let(:executable) { <<-RUBY.gsub(/^ */, "").strip }
+ let(:executable) { <<~RUBY.strip }
#{shebang}
- require "rack"
+ require "myrack"
puts "EXEC: \#{caller.grep(/load/).empty? ? 'exec' : 'load'}"
puts "ARGS: \#{$0} \#{ARGV.join(' ')}"
- puts "RACK: \#{RACK}"
- process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip
+ puts "MYRACK: \#{MYRACK}"
+ if Gem.win_platform?
+ process_title = "ruby"
+ else
+ process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip
+ end
puts "PROCESS: \#{process_title}"
RUBY
before do
- bundled_app(path).open("w") {|f| f << executable }
- bundled_app(path).chmod(0o755)
+ create_file(bundled_app(path), executable)
install_gemfile <<-G
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
let(:exec) { "EXEC: load" }
let(:args) { "ARGS: #{path} arg1 arg2" }
- let(:rack) { "RACK: 1.0.0" }
+ let(:myrack) { "MYRACK: 1.0.0" }
let(:process) do
- title = "PROCESS: #{path}"
- title += " arg1 arg2"
- title
+ if Gem.win_platform?
+ "PROCESS: ruby"
+ else
+ "PROCESS: #{path} arg1 arg2"
+ end
end
let(:exit_code) { 0 }
- let(:expected) { [exec, args, rack, process].join("\n") }
+ let(:expected) { [exec, args, myrack, process].join("\n") }
let(:expected_err) { "" }
- 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/bundler/issues/6898" if Gem.win_platform?
+ subject { bundle "exec #{path} arg1 arg2", raise_on_error: false }
- subject
- expect(exitstatus).to eq(exit_code)
- expect(err).to eq(expected_err)
- expect(out).to eq(expected)
- end
+ it "runs" do
+ 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
+ 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
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
end
@@ -677,11 +801,17 @@ RSpec.describe "bundle exec" do
end
let(:expected_err) { "" }
let(:exit_code) do
- # signal mask 128 + plus signal 15 -> TERM
- # this is specified by C99
- 128 + 15
+ exit_status_for_signal(Signal.list["TERM"])
+ end
+
+ it "runs" do
+ skip "https://github.com/ruby/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
- it_behaves_like "it runs"
end
context "the executable is empty" do
@@ -690,86 +820,146 @@ 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
+ # it's empty, so `create_file` won't add executable permission and bat scripts on Windows
+ bundled_app(path).chmod(0o755)
+ path.sub_ext(".bat").write <<~SCRIPT if Gem.win_platform?
+ @ECHO OFF
+ @"ruby.exe" "%~dpn0" %*
+ SCRIPT
+
+ 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
let(:executable) { super() << "\nraise 'ERROR'" }
let(:exit_code) { 1 }
let(:expected_err) do
- "bundler: failed to load command: #{path} (#{path})" \
- "\nRuntimeError: ERROR\n #{path}:10:in `<top (required)>'"
+ /\Abundler: failed to load command: #{Regexp.quote(path.to_s)} \(#{Regexp.quote(path.to_s)}\)\n#{Regexp.quote(path.to_s)}:[0-9]+:in [`']<top \(required\)>': ERROR \(RuntimeError\)/
+ end
+
+ it "runs like a normally executed executable" do
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to match(expected_err)
+ expect(out).to eq(expected)
end
- it_behaves_like "it runs"
end
context "the executable raises an error without a backtrace" do
let(:executable) { super() << "\nclass Err < Exception\ndef backtrace; end;\nend\nraise Err" }
let(:exit_code) { 1 }
- let(:expected_err) { "bundler: failed to load command: #{path} (#{path})\nErr: Err" }
+ 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
+ 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
+ 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
+ context "when Bundler.setup fails" do
before do
+ system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path)
+
gemfile <<-G
- gem 'rack', '2'
+ source "https://gem.repo1"
+ gem 'myrack', '2'
G
ENV["BUNDLER_FORCE_TTY"] = "true"
end
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
+ let(:expected_err) { <<~EOS.strip }
+ Could not find gem 'myrack (= 2)' in locally installed gems.
+
+ The source contains the following gems matching 'myrack':
+ * myrack-0.9.1
+ * myrack-1.0.0
+ Run `bundle install` to install missing gems.
EOS
- it_behaves_like "it runs"
+ it "runs" do
+ 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
+ context "when Bundler.setup fails and Gemfile is not the default" do
before do
- gemfile <<-G
- gem 'rack', '2'
+ gemfile "CustomGemfile", <<-G
+ source "https://gem.repo1"
+ gem 'myrack', '2'
G
ENV["BUNDLER_FORCE_TTY"] = "true"
+ ENV["BUNDLE_GEMFILE"] = "CustomGemfile"
+ ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = nil
end
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 'rack' at: 1.0.0\e[0m
-\e[33mRun `bundle install` to install missing gems.\e[0m
- EOS
- it_behaves_like "it runs"
+ it "prints proper suggestion" do
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to include("Run `bundle install --gemfile CustomGemfile` to install missing gems.")
+ 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
+ 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
let(:exec) { "EXEC: exec" }
- let(:process) { "PROCESS: ruby #{path} arg1 arg2" }
+ let(:process) do
+ if Gem.win_platform?
+ "PROCESS: ruby"
+ else
+ "PROCESS: ruby #{path} arg1 arg2"
+ end
+ end
before do
- bundle "config set disable_exec_load true"
+ bundle_config "disable_exec_load true"
end
- it_behaves_like "it runs"
+ it "runs" do
+ 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
@@ -779,45 +969,72 @@ The source contains 'rack' at: 1.0.0\e[0m
puts "__FILE__: #{__FILE__.inspect}"
RUBY
- let(:expected) { super() + <<-EOS.chomp }
+ context "when the path is absolute" do
+ let(:expected) { super() + <<~EOS.chomp }
-$0: #{path.to_s.inspect}
-__FILE__: #{path.to_s.inspect}
- EOS
+ $0: #{path.to_s.inspect}
+ __FILE__: #{path.to_s.inspect}
+ EOS
- it_behaves_like "it runs"
+ it "runs" do
+ 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" do
let(:path) { super().relative_path_from(bundled_app) }
+ let(:expected) { super() + <<~EOS.chomp }
+
+ $0: #{path.to_s.inspect}
+ __FILE__: #{path.to_s.inspect}
+ EOS
- it_behaves_like "it runs"
+ it "runs" do
+ 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
let(:path) { Pathname.new("./#{super().relative_path_from(bundled_app)}") }
+ let(:expected) { super() + <<~EOS.chomp }
+
+ $0: #{path.to_s.inspect}
+ __FILE__: #{File.expand_path(path, bundled_app).inspect}
+ EOS
- pending "relative paths with ./ have absolute __FILE__"
+ it "runs" do
+ subject
+ expect(exitstatus).to eq(exit_code)
+ expect(err).to eq(expected_err)
+ expect(out).to eq(expected)
+ end
end
end
context "signal handling" do
let(:test_signals) do
open3_reserved_signals = %w[CHLD CLD PIPE]
- reserved_signals = %w[SEGV BUS ILL FPE VTALRM KILL STOP EXIT]
+ reserved_signals = %w[SEGV BUS ILL FPE ABRT IOT VTALRM KILL STOP EXIT]
bundler_signals = %w[INT]
Signal.list.keys - (bundler_signals + reserved_signals + open3_reserved_signals)
end
context "signals being trapped by bundler" do
- let(:executable) { strip_whitespace <<-RUBY }
+ let(:executable) { <<~RUBY }
#{shebang}
begin
Thread.new do
puts 'Started' # For process sync
STDOUT.flush
sleep 1 # ignore quality_spec
- raise "Didn't receive INT at all"
+ raise RuntimeError, "Didn't receive expected INT"
end.join
rescue Interrupt
puts "foo"
@@ -825,7 +1042,7 @@ __FILE__: #{path.to_s.inspect}
RUBY
it "receives the signal" do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
bundle("exec #{path}") do |_, o, thr|
o.gets # Consumes 'Started' and ensures that thread has started
@@ -837,7 +1054,7 @@ __FILE__: #{path.to_s.inspect}
end
context "signals not being trapped by bunder" do
- let(:executable) { strip_whitespace <<-RUBY }
+ let(:executable) { <<~RUBY }
#{shebang}
signals = #{test_signals.inspect}
@@ -848,7 +1065,7 @@ __FILE__: #{path.to_s.inspect}
RUBY
it "makes sure no unexpected signals are restored to DEFAULT" do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
test_signals.each do |n|
Signal.trap(n, "IGNORE")
@@ -865,47 +1082,148 @@ __FILE__: #{path.to_s.inspect}
context "nested bundle exec" do
context "when bundle in a local path" do
before do
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
+
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "config set path vendor/bundler"
+ bundle_config "path vendor/bundler"
bundle :install
end
it "correctly shells out" do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
-
file = bundled_app("file_that_bundle_execs.rb")
create_file(file, <<-RUBY)
#!#{Gem.ruby}
puts `bundle exec echo foo`
RUBY
file.chmod(0o777)
- bundle "exec #{file}", :env => { "PATH" => path }
+ bundle "exec #{file}", env: { "PATH" => path }
expect(out).to eq("foo")
end
end
+ context "when Kernel.require uses extra monkeypatches" do
+ before do
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
+
+ install_gemfile "source \"https://gem.repo1\""
+ end
+
+ it "does not undo the monkeypatches" do
+ karafka = bundled_app("bin/karafka")
+ create_file(karafka, <<~RUBY)
+ #!#{Gem.ruby}
+
+ module Kernel
+ module_function
+
+ alias_method :require_before_extra_monkeypatches, :require
+
+ def require(path)
+ puts "requiring \#{path} used the monkeypatch"
+
+ require_before_extra_monkeypatches(path)
+ end
+ end
+
+ Bundler.setup(:default)
+
+ require "foo"
+ RUBY
+ karafka.chmod(0o777)
+
+ foreman = bundled_app("bin/foreman")
+ create_file(foreman, <<~RUBY)
+ #!#{Gem.ruby}
+
+ puts `bundle exec bin/karafka`
+ RUBY
+ foreman.chmod(0o777)
+
+ bundle "exec #{foreman}"
+ expect(out).to eq("requiring foo used the monkeypatch")
+ end
+ end
+
+ context "when gemfile and path are configured", :ruby_repo do
+ before do
+ build_repo2 do
+ build_gem "rails", "6.1.0" do |s|
+ s.executables = "rails"
+ end
+ end
+
+ bundle_config "path vendor/bundle"
+ bundle_config "gemfile gemfiles/myrack_6_1.gemfile"
+
+ gemfile(bundled_app("gemfiles/myrack_6_1.gemfile"), <<~RUBY)
+ source "https://gem.repo2"
+
+ gem "rails", "6.1.0"
+ RUBY
+
+ # A Gemfile needs to be in the root to trick bundler's root resolution
+ gemfile "source 'https://gem.repo1'"
+
+ bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ end
+
+ it "can still find gems after a nested subprocess" do
+ script = bundled_app("bin/myscript")
+
+ create_file(script, <<~RUBY)
+ #!#{Gem.ruby}
+
+ puts `bundle exec rails`
+ RUBY
+
+ script.chmod(0o777)
+
+ bundle "exec #{script}"
+
+ expect(err).to be_empty
+ expect(out).to eq("6.1.0")
+ end
+
+ it "can still find gems after a nested subprocess when using bundler (with a final r) executable" do
+ script = bundled_app("bin/myscript")
+
+ create_file(script, <<~RUBY)
+ #!#{Gem.ruby}
+
+ puts `bundler exec rails`
+ RUBY
+
+ script.chmod(0o777)
+
+ bundle "exec #{script}"
+
+ expect(err).to be_empty
+ expect(out).to eq("6.1.0")
+ end
+ end
+
context "with a system gem that shadows a default gem" do
let(:openssl_version) { "99.9.9" }
- let(:expected) { ruby "gem 'openssl', '< 999999'; require 'openssl'; puts OpenSSL::VERSION", :artifice => nil, :raise_on_error => false }
it "only leaves the default gem in the stdlib available" do
- skip "https://github.com/rubygems/bundler/issues/6898" if Gem.win_platform?
- skip "openssl isn't a default gem" if expected.empty?
+ default_openssl_version = ruby "require 'openssl'; puts OpenSSL::VERSION"
+
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
- install_gemfile "" # must happen before installing the broken system gem
+ install_gemfile "source \"https://gem.repo1\"" # must happen before installing the broken system gem
build_repo4 do
build_gem "openssl", openssl_version do |s|
s.write("lib/openssl.rb", <<-RUBY)
- raise "custom openssl should not be loaded, it's not in the gemfile!"
+ raise ArgumentError, "custom openssl should not be loaded"
RUBY
end
end
- system_gems("openssl-#{openssl_version}", :gem_repo => gem_repo4)
+ system_gems("openssl-#{openssl_version}", gem_repo: gem_repo4)
file = bundled_app("require_openssl.rb")
create_file(file, <<-RUBY)
@@ -918,15 +1236,15 @@ __FILE__: #{path.to_s.inspect}
env = { "PATH" => path }
aggregate_failures do
- expect(bundle("exec #{file}", :artifice => nil, :env => env)).to eq(expected)
- expect(bundle("exec bundle exec #{file}", :artifice => nil, :env => env)).to eq(expected)
- expect(bundle("exec ruby #{file}", :artifice => nil, :env => env)).to eq(expected)
- expect(run(file.read, :artifice => nil, :env => env)).to eq(expected)
+ expect(bundle("exec #{file}", env: env)).to eq(default_openssl_version)
+ expect(bundle("exec bundle exec #{file}", env: env)).to eq(default_openssl_version)
+ expect(bundle("exec ruby #{file}", env: env)).to eq(default_openssl_version)
+ expect(run(file.read, artifice: nil, env: env)).to eq(default_openssl_version)
end
skip "ruby_core has openssl and rubygems in the same folder, and this test needs rubygems require but default openssl not in a directly added entry in $LOAD_PATH" if ruby_core?
# sanity check that we get the newer, custom version without bundler
- sys_exec "#{Gem.ruby} #{file}", :env => env, :raise_on_error => false
+ sys_exec "#{Gem.ruby} #{file}", env: env, raise_on_error: false
expect(err).to include("custom openssl should not be loaded")
end
end
@@ -934,8 +1252,9 @@ __FILE__: #{path.to_s.inspect}
context "with a git gem that includes extensions", :ruby_repo do
before do
build_git "simple_git_binary", &:add_c_extension
- bundle "config set --local path .bundle"
+ bundle_config "path .bundle"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "simple_git_binary", :git => '#{lib_path("simple_git_binary-1.0")}'
G
end
@@ -945,7 +1264,7 @@ __FILE__: #{path.to_s.inspect}
end
it "allows calling bundle install after removing gem.build_complete" do
- FileUtils.rm_rf Dir[bundled_app(".bundle/**/gem.build_complete")]
+ FileUtils.rm_r Dir[bundled_app(".bundle/**/gem.build_complete")]
bundle "exec #{Gem.ruby} -S bundle install"
end
end
diff --git a/spec/bundler/commands/fund_spec.rb b/spec/bundler/commands/fund_spec.rb
new file mode 100644
index 0000000000..5883b8a63a
--- /dev/null
+++ b/spec/bundler/commands/fund_spec.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle fund" do
+ before do
+ build_repo2 do
+ build_gem "has_funding_and_other_metadata" do |s|
+ s.metadata = {
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ }
+ end
+
+ build_gem "has_funding", "1.2.3" do |s|
+ s.metadata = {
+ "funding_uri" => "https://example.com/has_funding/funding",
+ }
+ end
+
+ build_gem "gem_with_dependent_funding", "1.0" do |s|
+ s.add_dependency "has_funding"
+ end
+ end
+ end
+
+ it "prints fund information for all gems in the bundle" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'has_funding_and_other_metadata'
+ gem 'has_funding'
+ gem 'myrack-obama'
+ G
+
+ bundle "fund"
+
+ expect(out).to include("* has_funding_and_other_metadata (1.0)\n Funding: https://example.com/has_funding_and_other_metadata/funding")
+ expect(out).to include("* has_funding (1.2.3)\n Funding: https://example.com/has_funding/funding")
+ expect(out).to_not include("myrack-obama")
+ end
+
+ it "does not consider fund information for gem dependencies" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'gem_with_dependent_funding'
+ G
+
+ bundle "fund"
+
+ expect(out).to_not include("* has_funding (1.2.3)\n Funding: https://example.com/has_funding/funding")
+ expect(out).to_not include("gem_with_dependent_funding")
+ end
+
+ it "does not consider fund information for uninstalled optional dependencies" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ group :whatever, optional: true do
+ gem 'has_funding_and_other_metadata'
+ end
+ gem 'has_funding'
+ gem 'myrack-obama'
+ G
+
+ bundle "fund"
+
+ expect(out).to include("* has_funding (1.2.3)\n Funding: https://example.com/has_funding/funding")
+ expect(out).to_not include("has_funding_and_other_metadata")
+ expect(out).to_not include("myrack-obama")
+ end
+
+ it "considers fund information for installed optional dependencies" do
+ bundle_config "with whatever"
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ group :whatever, optional: true do
+ gem 'has_funding_and_other_metadata'
+ end
+ gem 'has_funding'
+ gem 'myrack-obama'
+ G
+
+ bundle "fund"
+
+ expect(out).to include("* has_funding_and_other_metadata (1.0)\n Funding: https://example.com/has_funding_and_other_metadata/funding")
+ expect(out).to include("* has_funding (1.2.3)\n Funding: https://example.com/has_funding/funding")
+ expect(out).to_not include("myrack-obama")
+ end
+
+ it "prints message if none of the gems have fund information" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'myrack-obama'
+ G
+
+ bundle "fund"
+
+ expect(out).to include("None of the installed gems you directly depend on are looking for funding.")
+ end
+
+ describe "with --group option" do
+ it "prints fund message for only specified group gems" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'has_funding_and_other_metadata', :group => :development
+ gem 'has_funding'
+ G
+
+ bundle "fund --group development"
+ expect(out).to include("* has_funding_and_other_metadata (1.0)\n Funding: https://example.com/has_funding_and_other_metadata/funding")
+ expect(out).to_not include("* has_funding (1.2.3)\n Funding: https://example.com/has_funding/funding")
+ end
+ end
+end
diff --git a/spec/bundler/commands/help_spec.rb b/spec/bundler/commands/help_spec.rb
index 788c1b8d29..f9ad9fff14 100644
--- a/spec/bundler/commands/help_spec.rb
+++ b/spec/bundler/commands/help_spec.rb
@@ -1,30 +1,32 @@
# frozen_string_literal: true
RSpec.describe "bundle help" do
- it "uses mann when available" do
+ it "uses man when available" do
with_fake_man do
bundle "help gemfile"
end
- expect(out).to eq(%(["#{root}/man/gemfile.5"]))
+ expect(out).to eq(%(["#{man_dir}/gemfile.5"]))
end
- it "prefixes bundle commands with bundle- when finding the groff files" do
+ it "prefixes bundle commands with bundle- when finding the man files" do
with_fake_man do
bundle "help install"
end
- expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ expect(out).to eq(%(["#{man_dir}/bundle-install.1"]))
end
- it "simply outputs the txt file when there is no man on the path" do
- with_path_as("") do
- bundle "help install"
+ it "prexifes bundle commands with bundle- and resolves aliases when finding the man files" do
+ with_fake_man do
+ bundle "help package"
end
- expect(out).to match(/BUNDLE-INSTALL/)
+ expect(out).to eq(%(["#{man_dir}/bundle-cache.1"]))
end
- it "still outputs the old help for commands that do not have man pages yet" do
- bundle "help version"
- expect(out).to include("Prints the bundler's version information")
+ it "simply outputs the human readable file when there is no man on the path" do
+ with_path_as("") do
+ bundle "help install"
+ end
+ expect(out).to match(/bundle-install/)
end
it "looks for a binary and executes it with --help option if it's named bundler-<task>" do
@@ -45,33 +47,33 @@ RSpec.describe "bundle help" do
with_fake_man do
bundle "install --help"
end
- expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ expect(out).to eq(%(["#{man_dir}/bundle-install.1"]))
end
it "is called when the --help flag is used before the command" do
with_fake_man do
bundle "--help install"
end
- expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ expect(out).to eq(%(["#{man_dir}/bundle-install.1"]))
end
it "is called when the -h flag is used before the command" do
with_fake_man do
bundle "-h install"
end
- expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ expect(out).to eq(%(["#{man_dir}/bundle-install.1"]))
end
it "is called when the -h flag is used after the command" do
with_fake_man do
bundle "install -h"
end
- expect(out).to eq(%(["#{root}/man/bundle-install.1"]))
+ expect(out).to eq(%(["#{man_dir}/bundle-install.1"]))
end
it "has helpful output when using --help flag for a non-existent command" do
with_fake_man do
- bundle "instill -h", :raise_on_error => false
+ bundle "instill -h", raise_on_error: false
end
expect(err).to include('Could not find command "instill".')
end
@@ -80,11 +82,11 @@ RSpec.describe "bundle help" do
with_fake_man do
bundle "--help"
end
- expect(out).to eq(%(["#{root}/man/bundle.1"]))
+ expect(out).to eq(%(["#{man_dir}/bundle.1"]))
with_fake_man do
bundle "-h"
end
- expect(out).to eq(%(["#{root}/man/bundle.1"]))
+ expect(out).to eq(%(["#{man_dir}/bundle.1"]))
end
end
diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb
index 9286e6824a..a26b1696fb 100644
--- a/spec/bundler/commands/info_spec.rb
+++ b/spec/bundler/commands/info_spec.rb
@@ -3,10 +3,25 @@
RSpec.describe "bundle info" do
context "with a standard Gemfile" do
before do
+ build_repo2 do
+ build_gem "has_metadata" do |s|
+ s.metadata = {
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ }
+ end
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "rails"
gem "has_metadata"
+ gem "thin"
G
end
@@ -36,24 +51,45 @@ 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 is missing, even if using a custom path without bundler there" do
+ bundle_config "path vendor/bundle"
+ bundle "install"
+ bundle "info bundler"
+ expect(out).to include("\tPath: #{root}")
+ expect(err).not_to match(/The gem bundler is missing/i)
+ end
+
it "complains if gem not in bundle" do
- bundle "info missing", :raise_on_error => false
+ bundle "info missing", raise_on_error: false
expect(err).to eq("Could not find gem 'missing'.")
end
- it "warns if path no longer exists on disk" do
- FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2"))
+ it "warns if path does not exist on disk, but specification is there" do
+ FileUtils.rm_r(default_bundle_path("gems", "rails-2.3.2"))
bundle "info rails --path"
- expect(err).to match(/has been deleted/i)
- expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
+ expect(err).to include("The gem rails is missing.")
+ expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
+
+ bundle "info rail --path"
+ expect(err).to include("The gem rails is missing.")
+ expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
+
+ bundle "info rails"
+ expect(err).to include("The gem rails is missing.")
+ expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
end
- context "given a default gem shippped in ruby", :ruby_repo do
+ context "given a default gem shipped in ruby", :ruby_repo do
it "prints information about the default gem" do
- bundle "info rdoc"
- expect(out).to include("* rdoc")
+ bundle "info json"
+ expect(out).to include("* json")
expect(out).to include("Default Gem: yes")
end
end
@@ -88,6 +124,30 @@ RSpec.describe "bundle info" do
expect(out).to_not include("Homepage:")
end
end
+
+ context "when gem has a reverse dependency on any version" do
+ it "prints the details" do
+ bundle "info myrack"
+
+ expect(out).to include("Reverse Dependencies: \n\t\tthin (1.0) depends on myrack (>= 0)")
+ end
+ end
+
+ context "when gem has a reverse dependency on a specific version" do
+ it "prints the details" do
+ bundle "info actionpack"
+
+ expect(out).to include("Reverse Dependencies: \n\t\trails (2.3.2) depends on actionpack (= 2.3.2)")
+ end
+ end
+
+ context "when gem has no reverse dependencies" do
+ it "excludes the reverse dependencies field from the output" do
+ bundle "info rails"
+
+ expect(out).not_to include("Reverse Dependencies:")
+ end
+ end
end
context "with a git repo in the Gemfile" do
@@ -97,21 +157,23 @@ RSpec.describe "bundle info" do
it "prints out git info" do
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
expect(the_bundle).to include_gems "foo 1.0"
bundle "info foo"
- expect(out).to include("foo (1.0 #{@git.ref_for("master", 6)}")
+ expect(out).to include("foo (1.0 #{@git.ref_for("main", 6)}")
end
- it "prints out branch names other than master" do
- update_git "foo", :branch => "omg" do |s|
+ it "prints out branch names other than main" do
+ update_git "foo", branch: "omg" do |s|
s.write "lib/foo.rb", "FOO = '1.0.omg'"
end
@revision = revision_for(lib_path("foo-1.0"))[0...6]
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "omg"
G
expect(the_bundle).to include_gems "foo 1.0.omg"
@@ -123,6 +185,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 "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{sha}"
G
@@ -131,8 +194,9 @@ RSpec.describe "bundle info" do
end
it "handles when a version is a '-' prerelease" do
- @git = build_git("foo", "1.0.0-beta.1", :path => lib_path("foo"))
+ @git = build_git("foo", "1.0.0-beta.1", path: lib_path("foo"))
install_gemfile <<-G
+ source "https://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"
@@ -145,27 +209,41 @@ RSpec.describe "bundle info" do
context "with a valid regexp for gem name" do
it "presents alternatives", :readline do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rack-obama"
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "myrack-obama"
G
bundle "info rac"
- expect(out).to eq "1 : rack\n2 : rack-obama\n0 : - exit -\n>"
+ expect(out).to match(/\A1 : myrack\n2 : myrack-obama\n0 : - exit -(\n>|\z)/)
end
end
context "with an invalid regexp for gem name" do
it "does not find the gem" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
invalid_regexp = "[]"
- bundle "info #{invalid_regexp}", :raise_on_error => false
+ bundle "info #{invalid_regexp}", raise_on_error: false
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 "https://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/init_spec.rb b/spec/bundler/commands/init_spec.rb
index 683a453c7d..989d6fa812 100644
--- a/spec/bundler/commands/init_spec.rb
+++ b/spec/bundler/commands/init_spec.rb
@@ -7,19 +7,42 @@ RSpec.describe "bundle init" do
expect(bundled_app_gemfile).to be_file
end
+ context "with a template with permission flags not matching current process umask" do
+ let(:template_file) do
+ gemfile = Bundler.preferred_gemfile_name
+ templates_dir.join(gemfile)
+ end
+
+ let(:target_dir) { bundled_app("init_permissions_test") }
+
+ around do |example|
+ old_chmod = File.stat(template_file).mode
+ FileUtils.chmod(old_chmod | 0o111, template_file) # chmod +x
+ example.run
+ FileUtils.chmod(old_chmod, template_file)
+ end
+
+ it "honours the current process umask when generating from a template" do
+ FileUtils.mkdir(target_dir)
+ bundle :init, dir: target_dir
+ generated_mode = File.stat(File.join(target_dir, "Gemfile")).mode & 0o111
+ expect(generated_mode).to be_zero
+ end
+ end
+
context "when a Gemfile already exists" do
before do
- create_file "Gemfile", <<-G
+ gemfile <<-G
gem "rails"
G
end
it "does not change existing Gemfiles" do
- expect { bundle :init, :raise_on_error => false }.not_to change { File.read(bundled_app_gemfile) }
+ expect { bundle :init, raise_on_error: false }.not_to change { File.read(bundled_app_gemfile) }
end
it "notifies the user that an existing Gemfile already exists" do
- bundle :init, :raise_on_error => false
+ bundle :init, raise_on_error: false
expect(err).to include("Gemfile already exists")
end
end
@@ -32,7 +55,7 @@ RSpec.describe "bundle init" do
FileUtils.mkdir bundled_app(subdir)
- bundle :init, :dir => bundled_app(subdir)
+ bundle :init, dir: bundled_app(subdir)
expect(out).to include("Writing new Gemfile")
expect(bundled_app("#{subdir}/Gemfile")).to be_file
@@ -42,13 +65,13 @@ RSpec.describe "bundle init" do
context "when the dir is not writable by the current user" do
let(:subdir) { "child_dir" }
- it "notifies the user that it can not write to it" do
+ it "notifies the user that it cannot write to it" do
FileUtils.mkdir bundled_app(subdir)
# chmod a-w it
mode = File.stat(bundled_app(subdir)).mode ^ 0o222
FileUtils.chmod mode, bundled_app(subdir)
- bundle :init, :dir => bundled_app(subdir), :raise_on_error => false
+ bundle :init, dir: bundled_app(subdir), raise_on_error: false
expect(err).to include("directory is not writable")
expect(Dir[bundled_app("#{subdir}/*")]).to be_empty
@@ -56,24 +79,24 @@ RSpec.describe "bundle init" do
end
context "given --gemspec option" do
- let(:spec_file) { tmp.join("test.gemspec") }
+ let(:spec_file) { tmp("test.gemspec") }
it "should generate from an existing gemspec" do
File.open(spec_file, "w") do |file|
file << <<-S
Gem::Specification.new do |s|
s.name = 'test'
- s.add_dependency 'rack', '= 1.0.1'
+ s.add_dependency 'myrack', '= 1.0.1'
s.add_development_dependency 'rspec', '1.2'
end
S
end
- bundle :init, :gemspec => spec_file
+ bundle :init, gemspec: spec_file
gemfile = bundled_app_gemfile.read
expect(gemfile).to match(%r{source 'https://rubygems.org'})
- expect(gemfile.scan(/gem "rack", "= 1.0.1"/).size).to eq(1)
+ expect(gemfile.scan(/gem "myrack", "= 1.0.1"/).size).to eq(1)
expect(gemfile.scan(/gem "rspec", "= 1.2"/).size).to eq(1)
expect(gemfile.scan(/group :development/).size).to eq(1)
end
@@ -89,14 +112,14 @@ RSpec.describe "bundle init" do
S
end
- bundle :init, :gemspec => spec_file, :raise_on_error => false
+ bundle :init, gemspec: spec_file, raise_on_error: false
expect(err).to include("There was an error while loading `test.gemspec`")
end
end
end
context "when init_gems_rb setting is enabled" do
- before { bundle "config set init_gems_rb true" }
+ before { bundle_config "init_gems_rb true" }
it "generates a gems.rb" do
bundle :init
@@ -106,17 +129,17 @@ RSpec.describe "bundle init" do
context "when gems.rb already exists" do
before do
- create_file("gems.rb", <<-G)
+ gemfile("gems.rb", <<-G)
gem "rails"
G
end
it "does not change existing Gemfiles" do
- expect { bundle :init, :raise_on_error => false }.not_to change { File.read(bundled_app("gems.rb")) }
+ expect { bundle :init, raise_on_error: false }.not_to change { File.read(bundled_app("gems.rb")) }
end
it "notifies the user that an existing gems.rb already exists" do
- bundle :init, :raise_on_error => false
+ bundle :init, raise_on_error: false
expect(err).to include("gems.rb already exists")
end
end
@@ -129,7 +152,7 @@ RSpec.describe "bundle init" do
FileUtils.mkdir bundled_app(subdir)
- bundle :init, :dir => bundled_app(subdir)
+ bundle :init, dir: bundled_app(subdir)
expect(out).to include("Writing new gems.rb")
expect(bundled_app("#{subdir}/gems.rb")).to be_file
@@ -137,14 +160,14 @@ RSpec.describe "bundle init" do
end
context "given --gemspec option" do
- let(:spec_file) { tmp.join("test.gemspec") }
+ let(:spec_file) { tmp("test.gemspec") }
before do
File.open(spec_file, "w") do |file|
file << <<-S
Gem::Specification.new do |s|
s.name = 'test'
- s.add_dependency 'rack', '= 1.0.1'
+ s.add_dependency 'myrack', '= 1.0.1'
s.add_development_dependency 'rspec', '1.2'
end
S
@@ -152,20 +175,33 @@ RSpec.describe "bundle init" do
end
it "should generate from an existing gemspec" do
- bundle :init, :gemspec => spec_file
+ bundle :init, gemspec: spec_file
gemfile = bundled_app("gems.rb").read
expect(gemfile).to match(%r{source 'https://rubygems.org'})
- expect(gemfile.scan(/gem "rack", "= 1.0.1"/).size).to eq(1)
+ expect(gemfile.scan(/gem "myrack", "= 1.0.1"/).size).to eq(1)
expect(gemfile.scan(/gem "rspec", "= 1.2"/).size).to eq(1)
expect(gemfile.scan(/group :development/).size).to eq(1)
end
it "prints message to user" do
- bundle :init, :gemspec => spec_file
+ bundle :init, gemspec: spec_file
expect(out).to include("Writing new gems.rb")
end
end
end
+
+ describe "using the --gemfile" do
+ it "should use the --gemfile value to name the gemfile" do
+ custom_gemfile_name = "NiceGemfileName"
+
+ bundle :init, gemfile: custom_gemfile_name
+
+ expect(out).to include("Writing new #{custom_gemfile_name}")
+ used_template = File.read("#{source_root}/lib/bundler/templates/Gemfile")
+ generated_gemfile = bundled_app(custom_gemfile_name).read
+ expect(generated_gemfile).to eq(used_template)
+ end
+ end
end
diff --git a/spec/bundler/commands/inject_spec.rb b/spec/bundler/commands/inject_spec.rb
deleted file mode 100644
index 2d97bf6ff0..0000000000
--- a/spec/bundler/commands/inject_spec.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "bundle inject", :bundler => "< 3" do
- before :each do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
- end
-
- context "without a lockfile" do
- it "locks with the injected gems" do
- expect(bundled_app_lock).not_to exist
- bundle "inject 'rack-obama' '> 0'"
- expect(bundled_app_lock.read).to match(/rack-obama/)
- end
- end
-
- context "with a lockfile" do
- before do
- bundle "install"
- end
-
- it "adds the injected gems to the Gemfile" do
- expect(bundled_app_gemfile.read).not_to match(/rack-obama/)
- bundle "inject 'rack-obama' '> 0'"
- expect(bundled_app_gemfile.read).to match(/rack-obama/)
- end
-
- it "locks with the injected gems" do
- expect(bundled_app_lock.read).not_to match(/rack-obama/)
- bundle "inject 'rack-obama' '> 0'"
- expect(bundled_app_lock.read).to match(/rack-obama/)
- end
- end
-
- context "with injected gems already in the Gemfile" do
- it "doesn't add existing gems" do
- bundle "inject 'rack' '> 0'", :raise_on_error => false
- expect(err).to match(/cannot specify the same gem twice/i)
- end
- end
-
- context "incorrect arguments" do
- it "fails when more than 2 arguments are passed" do
- bundle "inject gem_name 1 v", :raise_on_error => false
- expect(err).to eq(<<-E.strip)
-ERROR: "bundle inject" was called with arguments ["gem_name", "1", "v"]
-Usage: "bundle inject GEM VERSION"
- E
- end
- end
-
- context "with source option" do
- it "add gem with source option in gemfile" do
- bundle "inject 'foo' '>0' --source #{file_uri_for(gem_repo1)}"
- gemfile = bundled_app_gemfile.read
- str = "gem \"foo\", \"> 0\", :source => \"#{file_uri_for(gem_repo1)}\""
- expect(gemfile).to include str
- end
- end
-
- context "with group option" do
- it "add gem with group option in gemfile" do
- bundle "inject 'rack-obama' '>0' --group=development"
- gemfile = bundled_app_gemfile.read
- str = "gem \"rack-obama\", \"> 0\", :group => :development"
- expect(gemfile).to include str
- end
-
- it "add gem with multiple groups in gemfile" do
- bundle "inject 'rack-obama' '>0' --group=development,test"
- gemfile = bundled_app_gemfile.read
- str = "gem \"rack-obama\", \"> 0\", :groups => [:development, :test]"
- expect(gemfile).to include str
- end
- end
-
- context "when frozen" do
- before do
- bundle "install"
- if Bundler.feature_flag.bundler_3_mode?
- bundle "config set --local deployment true"
- else
- bundle "config set --local frozen true"
- end
- end
-
- it "injects anyway" do
- bundle "inject 'rack-obama' '> 0'"
- expect(bundled_app_gemfile.read).to match(/rack-obama/)
- end
-
- it "locks with the injected gems" do
- expect(bundled_app_lock.read).not_to match(/rack-obama/)
- bundle "inject 'rack-obama' '> 0'"
- expect(bundled_app_lock.read).to match(/rack-obama/)
- end
-
- it "restores frozen afterwards" do
- bundle "inject 'rack-obama' '> 0'"
- config = YAML.load(bundled_app(".bundle/config").read)
- expect(config["BUNDLE_DEPLOYMENT"] || config["BUNDLE_FROZEN"]).to eq("true")
- end
-
- it "doesn't allow Gemfile changes" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack-obama"
- G
- bundle "inject 'rack' '> 0'", :raise_on_error => false
- expect(err).to match(/trying to install in deployment mode after changing/)
-
- expect(bundled_app_lock.read).not_to match(/rack-obama/)
- end
- end
-end
diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb
index 98290bbc9e..3b24434dc7 100644
--- a/spec/bundler/commands/install_spec.rb
+++ b/spec/bundler/commands/install_spec.rb
@@ -4,7 +4,7 @@ RSpec.describe "bundle install with gem sources" do
describe "the simple case" do
it "prints output and returns if no dependencies are specified" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
G
bundle :install
@@ -12,7 +12,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "does not make a lockfile if the install fails" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
raise StandardError, "FAIL"
G
@@ -22,37 +22,91 @@ RSpec.describe "bundle install with gem sources" do
it "creates a Gemfile.lock" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
expect(bundled_app_lock).to exist
end
- it "does not create ./.bundle by default", :bundler => "< 3" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ it "creates lockfile based on the lockfile method in Gemfile" do
+ install_gemfile <<-G
+ lockfile "OmgFile.lock"
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+
+ bundle "install"
+
+ expect(bundled_app("OmgFile.lock")).to exist
+ end
+
+ it "creates lockfile using BUNDLE_LOCKFILE instead of lockfile method" do
+ ENV["BUNDLE_LOCKFILE"] = "ReallyOmgFile.lock"
+ install_gemfile <<-G
+ lockfile "OmgFile.lock"
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+
+ expect(bundled_app("ReallyOmgFile.lock")).to exist
+ expect(bundled_app("OmgFile.lock")).not_to exist
+ ensure
+ ENV.delete("BUNDLE_LOCKFILE")
+ end
+
+ it "creates lockfile based on --lockfile option is given" do
+ gemfile bundled_app("OmgFile"), <<-G
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+
+ bundle "install --gemfile OmgFile --lockfile ReallyOmgFile.lock"
+
+ expect(bundled_app("ReallyOmgFile.lock")).to exist
+ end
+
+ it "does not make a lockfile if lockfile false is used in Gemfile" do
+ install_gemfile <<-G
+ lockfile false
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+
+ expect(bundled_app_lock).not_to exist
+ end
+
+ it "does not create ./.bundle by default" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle :install # can't use install_gemfile since it sets retry
expect(bundled_app(".bundle")).not_to exist
end
+ it "will create a ./.bundle by default", bundler: "5" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ expect(bundled_app(".bundle")).to exist
+ end
+
it "does not create ./.bundle by default when installing to system gems" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ install_gemfile <<-G, env: { "BUNDLE_PATH__SYSTEM" => "true" }
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle :install, :env => { "BUNDLE_PATH__SYSTEM" => "true" } # can't use install_gemfile since it sets retry
expect(bundled_app(".bundle")).not_to exist
end
- it "creates lock files based on the Gemfile name" do
+ it "creates lockfiles based on the Gemfile name" do
gemfile bundled_app("OmgFile"), <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
G
bundle "install --gemfile OmgFile"
@@ -60,15 +114,38 @@ RSpec.describe "bundle install with gem sources" do
expect(bundled_app("OmgFile.lock")).to exist
end
+ it "doesn't create a lockfile if --no-lock option is given" do
+ gemfile bundled_app("OmgFile"), <<-G
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+
+ bundle "install --gemfile OmgFile --no-lock"
+
+ expect(bundled_app("OmgFile.lock")).not_to exist
+ end
+
+ it "doesn't create a lockfile if --no-lock and --lockfile options are given" do
+ gemfile bundled_app("OmgFile"), <<-G
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+
+ bundle "install --gemfile OmgFile --no-lock --lockfile ReallyOmgFile.lock"
+
+ expect(bundled_app("OmgFile.lock")).not_to exist
+ expect(bundled_app("ReallyOmgFile.lock")).not_to exist
+ end
+
it "doesn't delete the lockfile if one already exists" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
lockfile = File.read(bundled_app_lock)
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
raise StandardError, "FAIL"
G
@@ -77,8 +154,8 @@ RSpec.describe "bundle install with gem sources" do
it "does not touch the lockfile if nothing changed" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
expect { run "1" }.not_to change { File.mtime(bundled_app_lock) }
@@ -86,45 +163,77 @@ RSpec.describe "bundle install with gem sources" do
it "fetches gems" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
- expect(default_bundle_path("gems/rack-1.0.0")).to exist
- expect(the_bundle).to include_gems("rack 1.0.0")
+ expect(default_bundle_path("gems/myrack-1.0.0")).to exist
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
+ it "auto-heals missing gems" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+
+ FileUtils.rm_r(default_bundle_path("gems/myrack-1.0.0"))
+
+ bundle "install --verbose"
+
+ expect(out).to include("Installing myrack 1.0.0")
+ expect(default_bundle_path("gems/myrack-1.0.0")).to exist
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
+ it "does not state that it's constantly reinstalling empty gems" do
+ build_repo4 do
+ build_gem "empty", "1.0.0", no_default: true
+ end
+
+ install_gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "empty"
+ G
+ gem_dir = default_bundle_path("gems/empty-1.0.0")
+ expect(gem_dir).to be_empty
+
+ bundle "install --verbose"
+ expect(out).not_to include("Installing empty")
end
it "fetches gems when multiple versions are specified" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack', "> 0.9", "< 1.0"
+ source "https://gem.repo1"
+ gem 'myrack', "> 0.9", "< 1.0"
G
- expect(default_bundle_path("gems/rack-0.9.1")).to exist
- expect(the_bundle).to include_gems("rack 0.9.1")
+ expect(default_bundle_path("gems/myrack-0.9.1")).to exist
+ expect(the_bundle).to include_gems("myrack 0.9.1")
end
it "fetches gems when multiple versions are specified take 2" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack', "< 1.0", "> 0.9"
+ source "https://gem.repo1"
+ gem 'myrack', "< 1.0", "> 0.9"
G
- expect(default_bundle_path("gems/rack-0.9.1")).to exist
- expect(the_bundle).to include_gems("rack 0.9.1")
+ expect(default_bundle_path("gems/myrack-0.9.1")).to exist
+ expect(the_bundle).to include_gems("myrack 0.9.1")
end
it "raises an appropriate error when gems are specified using symbols" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem :rack
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem :myrack
G
expect(exitstatus).to eq(4)
end
it "pulls in dependencies" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
@@ -133,16 +242,22 @@ RSpec.describe "bundle install with gem sources" do
it "does the right version" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "0.9.1"
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
G
- expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).to include_gems "myrack 0.9.1"
end
it "does not install the development dependency" do
+ build_repo2 do
+ build_gem "with_development_dependency" do |s|
+ s.add_development_dependency "activesupport", "= 2.3.5"
+ end
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "with_development_dependency"
G
@@ -152,7 +267,7 @@ RSpec.describe "bundle install with gem sources" do
it "resolves correctly" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activemerchant"
gem "rails"
G
@@ -162,12 +277,12 @@ RSpec.describe "bundle install with gem sources" do
it "activates gem correctly according to the resolved gems" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport", "2.3.5"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activemerchant"
gem "rails"
G
@@ -176,7 +291,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "does not reinstall any gem that is already available locally" do
- system_gems "activesupport-2.3.2", :path => default_bundle_path
+ system_gems "activesupport-2.3.2", path: default_bundle_path
build_repo2 do
build_gem "activesupport", "2.3.2" do |s|
@@ -185,7 +300,7 @@ RSpec.describe "bundle install with gem sources" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "activerecord", "2.3.2"
G
@@ -193,222 +308,474 @@ RSpec.describe "bundle install with gem sources" do
end
it "works when the gemfile specifies gems that only exist in the system" do
- build_gem "foo", :to_bundle => true
+ build_gem "foo", to_bundle: true
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
gem "foo"
G
- expect(the_bundle).to include_gems "rack 1.0.0", "foo 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0", "foo 1.0.0"
end
it "prioritizes local gems over remote gems" do
- build_gem "rack", "1.0.0", :to_bundle => true do |s|
- s.add_dependency "activesupport", "2.3.5"
- end
+ build_gem "myrack", "9.0.0", to_bundle: true
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems "myrack 9.0.0"
+ end
+
+ it "loads env plugins" do
+ plugin_msg = "hello from an env plugin!"
+ create_file "plugins/rubygems_plugin.rb", "puts '#{plugin_msg}'"
+ install_gemfile <<-G, env: { "RUBYLIB" => rubylib.unshift(bundled_app("plugins").to_s).join(File::PATH_SEPARATOR) }
+ source "https://gem.repo1"
+ gem "myrack"
G
- expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ expect(stdboth).to include(plugin_msg)
end
describe "with a gem that installs multiple platforms" do
it "installs gems for the local platform as first choice" do
- skip "version is 1.0, not 1.0.0" if Gem.win_platform?
+ simulate_platform "x86-darwin-100" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "platform_specific"
- G
-
- run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
- expect(out).to eq("1.0.0 #{Bundler.local_platform}")
+ expect(the_bundle).to include_gems("platform_specific 1.0 x86-darwin-100")
+ end
end
it "falls back on plain ruby" do
- simulate_platform "foo-bar-baz"
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "platform_specific"
- G
+ simulate_platform "foo-bar-baz" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
- run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
- expect(out).to eq("1.0.0 RUBY")
+ expect(the_bundle).to include_gems("platform_specific 1.0 ruby")
+ end
end
it "installs gems for java" do
- simulate_platform "java"
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "platform_specific"
- G
+ simulate_platform "java" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
- run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
- expect(out).to eq("1.0.0 JAVA")
+ expect(the_bundle).to include_gems("platform_specific 1.0 java")
+ end
end
it "installs gems for windows" do
- simulate_platform mswin
+ simulate_platform "x86-mswin32" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "platform_specific"
- G
+ expect(the_bundle).to include_gems("platform_specific 1.0 x86-mswin32")
+ end
+ end
- run "require 'platform_specific' ; puts PLATFORM_SPECIFIC"
- expect(out).to eq("1.0.0 MSWIN")
+ it "installs gems for aarch64-mingw-ucrt" do
+ simulate_platform "aarch64-mingw-ucrt" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
+ end
+
+ expect(out).to include("Installing platform_specific 1.0 (aarch64-mingw-ucrt)")
end
end
- describe "doing bundle install foo" do
- before do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
+ it "gives useful errors if no global sources are set, and gems not installed locally, with and without a lockfile" do
+ install_gemfile <<-G, raise_on_error: false
+ gem "myrack"
+ G
+
+ expect(err).to eq("Could not find gem 'myrack' in locally installed gems.")
+
+ lockfile <<~L
+ GEM
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install", raise_on_error: false
+
+ expect(err).to include(
+ "Because your Gemfile specifies no global remote source, your bundle is locked to " \
+ "myrack (1.0.0) from locally installed gems. However, myrack (1.0.0) is not installed. " \
+ "You'll need to either add a global remote source to your Gemfile or make sure myrack (1.0.0) " \
+ "is available locally before rerunning Bundler."
+ )
+ end
+
+ it "creates a Gemfile.lock on a blank Gemfile" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ G
+
+ expect(File.exist?(bundled_app_lock)).to eq(true)
+ end
+
+ it "throws a warning if a gem is added twice in Gemfile without version requirements" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ gem "myrack"
+ G
+
+ expect(err).to include("Your Gemfile lists the gem myrack (>= 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 "throws a warning if a gem is added twice in Gemfile with same versions" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack", "1.0"
+ gem "myrack", "1.0"
+ G
+
+ expect(err).to include("Your Gemfile lists the gem myrack (= 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
+
+ it "throws a warning if a gem is added twice under different platforms and does not crash when using the generated lockfile" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack", :platform => :jruby
+ gem "myrack"
+ G
+
+ bundle "install"
+
+ expect(err).to include("Your Gemfile lists the gem myrack (>= 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 "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
- it "works" do
- bundle "config --local path vendor"
- bundle "install"
- expect(the_bundle).to include_gems "rack 1.0"
+ build_repo2 do
+ build_gem "my-private-gem"
end
- it "allows running bundle install --system without deleting foo", :bundler => "< 3" do
- bundle "install --path vendor"
- bundle "install --system"
- FileUtils.rm_rf(bundled_app("vendor"))
- expect(the_bundle).to include_gems "rack 1.0"
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ 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 "does not warn if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with compatible requirements" do
+ build_lib "my-gem", path: bundled_app do |s|
+ s.add_development_dependency "rubocop", "~> 1.36.0"
end
- it "allows running bundle install --system after deleting foo", :bundler => "< 3" do
- bundle "install --path vendor"
- FileUtils.rm_rf(bundled_app("vendor"))
- bundle "install --system"
- expect(the_bundle).to include_gems "rack 1.0"
+ build_repo4 do
+ build_gem "rubocop", "1.36.0"
+ build_gem "rubocop", "1.37.1"
end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gemspec
+
+ gem "rubocop", group: :development
+ G
+
+ bundle :install
+
+ expect(err).to be_empty
+
+ expect(the_bundle).to include_gems("rubocop 1.36.0")
end
- it "finds gems in multiple sources", :bundler => "< 3" do
- build_repo2
- update_repo2
+ it "raises an error if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with incompatible requirements" do
+ build_lib "my-gem", path: bundled_app do |s|
+ s.add_development_dependency "rubocop", "~> 1.36.0"
+ end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- source "#{file_uri_for(gem_repo2)}"
+ build_repo4 do
+ build_gem "rubocop", "1.36.0"
+ build_gem "rubocop", "1.37.1"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
- gem "activesupport", "1.2.3"
- gem "rack", "1.2"
+ gemspec
+
+ gem "rubocop", "~> 1.37.0", group: :development
G
- expect(the_bundle).to include_gems "rack 1.2", "activesupport 1.2.3"
+ bundle :install, raise_on_error: false
+
+ expect(err).to include("The rubocop dependency has conflicting requirements in Gemfile (~> 1.37.0) and gemspec (~> 1.36.0)")
end
- it "gives a useful error if no sources are set" do
- install_gemfile <<-G, :raise_on_error => false
- gem "rack"
+ it "includes the gem without warning if two gemspecs add it with the same requirement" do
+ gem1 = tmp("my-gem-1")
+ gem2 = tmp("my-gem-2")
+
+ build_lib "my-gem", path: gem1 do |s|
+ s.add_development_dependency "rubocop", "~> 1.36.0"
+ end
+
+ build_lib "my-gem-2", path: gem2 do |s|
+ s.add_development_dependency "rubocop", "~> 1.36.0"
+ end
+
+ build_repo4 do
+ build_gem "rubocop", "1.36.0"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gemspec path: "#{gem1}"
+ gemspec path: "#{gem2}"
G
- expect(err).to include("Your Gemfile has no gem server sources")
+ bundle :install
+
+ expect(err).to be_empty
+ expect(the_bundle).to include_gems("rubocop 1.36.0")
end
- it "creates a Gemfile.lock on a blank Gemfile" do
- install_gemfile <<-G
+ it "includes the gem without warning if two gemspecs add it with compatible requirements" do
+ gem1 = tmp("my-gem-1")
+ gem2 = tmp("my-gem-2")
+
+ build_lib "my-gem", path: gem1 do |s|
+ s.add_development_dependency "rubocop", "~> 1.0"
+ end
+
+ build_lib "my-gem-2", path: gem2 do |s|
+ s.add_development_dependency "rubocop", "~> 1.36.0"
+ end
+
+ build_repo4 do
+ build_gem "rubocop", "1.36.0"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gemspec path: "#{gem1}"
+ gemspec path: "#{gem2}"
G
- expect(File.exist?(bundled_app_lock)).to eq(true)
+ bundle :install
+
+ expect(err).to be_empty
+ expect(the_bundle).to include_gems("rubocop 1.36.0")
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 "errors out if two gemspecs add it with incompatible requirements" do
+ gem1 = tmp("my-gem-1")
+ gem2 = tmp("my-gem-2")
- 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.")
+ build_lib "my-gem", path: gem1 do |s|
+ s.add_development_dependency "rubocop", "~> 2.0"
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
+ build_lib "my-gem-2", path: gem2 do |s|
+ s.add_development_dependency "rubocop", "~> 1.36.0"
+ end
+
+ build_repo4 do
+ build_gem "rubocop", "1.36.0"
+ 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.")
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gemspec path: "#{gem1}"
+ gemspec path: "#{gem2}"
+ G
+
+ bundle :install, raise_on_error: false
+
+ expect(err).to include("Two gemspec development dependencies have conflicting requirements on the same gem: rubocop (~> 1.36.0) and rubocop (~> 2.0). Bundler cannot continue.")
+ end
+
+ it "errors out if a gem is specified in a gemspec and in the Gemfile" do
+ gem = tmp("my-gem-1")
+
+ build_lib "rubocop", path: gem do |s|
+ s.add_development_dependency "rubocop", "~> 1.0"
+ end
+
+ build_repo4 do
+ build_gem "rubocop"
end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "rubocop", :path => "#{gem}"
+ gemspec path: "#{gem}"
+ G
+
+ bundle :install, raise_on_error: false
+
+ expect(err).to include("There was an error parsing `Gemfile`: You cannot specify the same gem twice coming from different sources.")
+ expect(err).to include("You specified that rubocop (>= 0) should come from source at `#{gem}` and gemspec at `#{gem}`")
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 warn if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with same requirements, and different sources" do
+ build_lib "my-gem", path: bundled_app do |s|
+ s.add_development_dependency "activesupport"
+ 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_repo4 do
+ build_gem "activesupport"
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
+ build_git "activesupport", "1.0", path: lib_path("activesupport")
+
+ install_gemfile <<~G
+ source "https://gem.repo4"
+
+ gemspec
+
+ gem "activesupport", :git => "#{lib_path("activesupport")}"
+ G
+
+ expect(err).to be_empty
+ expect(the_bundle).to include_gems "activesupport 1.0", source: "git@#{lib_path("activesupport")}"
+
+ # if the Gemfile dependency is specified first
+ install_gemfile <<~G
+ source "https://gem.repo4"
- 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).")
+ gem "activesupport", :git => "#{lib_path("activesupport")}"
+
+ gemspec
+ G
+
+ expect(err).to be_empty
+ expect(the_bundle).to include_gems "activesupport 1.0", source: "git@#{lib_path("activesupport")}"
+ end
+
+ it "considers both dependencies for resolution if a gem is added once in Gemfile and also inside a local gemspec as a runtime dependency, with different requirements" do
+ build_lib "my-gem", path: bundled_app do |s|
+ s.add_dependency "rubocop", "~> 1.36.0"
end
+
+ build_repo4 do
+ build_gem "rubocop", "1.36.0"
+ build_gem "rubocop", "1.37.1"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gemspec
+
+ gem "rubocop"
+ G
+
+ bundle :install
+
+ expect(err).to be_empty
+ expect(the_bundle).to include_gems("rubocop 1.36.0")
end
- it "gracefully handles error when rubygems server is unavailable" do
- skip "networking issue" if Gem.win_platform?
+ 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 "https://gem.repo2"
+ gem "myrack"
+ gem "myrack", "1.0"
+ G
+
+ expect(err).to include("You cannot specify the same gem twice with different version requirements")
+ expect(err).to include("You specified: myrack (>= 0) and myrack (= 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 "https://gem.repo2"
+ gem "myrack", "1.0"
+ gem "myrack", "1.1"
+ G
+
+ expect(err).to include("You cannot specify the same gem twice with different version requirements")
+ expect(err).to include("You specified: myrack (= 1.0) and myrack (= 1.1).")
+ end
- install_gemfile <<-G, :artifice => nil, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
+ it "gracefully handles error when rubygems server is unavailable" do
+ install_gemfile <<-G, artifice: nil, raise_on_error: false
+ source "https://gem.repo1"
source "http://0.0.0.0:9384" do
gem 'foo'
end
G
- expect(err).to include("Could not fetch specs from http://0.0.0.0:9384/")
+ expect(err).to eq("Could not reach host 0.0.0.0:9384. Check your network connection and try again.")
expect(err).not_to include("file://")
end
it "fails gracefully when downloading an invalid specification from the full index" do
- build_repo2 do
- build_gem "ajp-rails", "0.0.0", :gemspec => false, :skip_validation => true do |s|
- bad_deps = [["ruby-ajp", ">= 0.2.0"], ["rails", ">= 0.14"]]
+ build_repo2(build_compact_index: false) do
+ build_gem "ajp-rails", "0.0.0", gemspec: false, skip_validation: true do |s|
+ invalid_deps = [["ruby-ajp", ">= 0.2.0"], ["rails", ">= 0.14"]]
s.
instance_variable_get(:@spec).
- instance_variable_set(:@dependencies, bad_deps)
-
- raise "failed to set bad deps" unless s.dependencies == bad_deps
+ instance_variable_set(:@dependencies, invalid_deps)
end
+
build_gem "ruby-ajp", "1.0.0"
end
- install_gemfile <<-G, :full_index => true, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
+ install_gemfile <<-G, full_index: true, raise_on_error: false
+ source "https://gem.repo2"
gem "ajp-rails", "0.0.0"
G
- expect(last_command.stdboth).not_to match(/Error Report/i)
+ expect(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
@@ -416,7 +783,7 @@ RSpec.describe "bundle install with gem sources" do
FileUtils.touch(bundled_app(".bundle/config"))
install_gemfile(<<-G)
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem 'foo'
G
@@ -427,7 +794,7 @@ RSpec.describe "bundle install with gem sources" do
FileUtils.touch("#{Bundler.rubygems.user_home}/.bundle/config")
install_gemfile(<<-G)
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem 'foo'
G
@@ -435,136 +802,705 @@ RSpec.describe "bundle install with gem sources" do
end
describe "Ruby version in Gemfile.lock" do
- include Bundler::GemHelpers
-
context "and using an unsupported Ruby version" do
it "prints an error" do
- install_gemfile <<-G, :raise_on_error => false
- ::RUBY_VERSION = '2.0.1'
- ruby '~> 2.2'
+ install_gemfile <<-G, raise_on_error: false
+ ruby '~> 1.2'
+ source "https://gem.repo1"
G
- expect(err).to include("Your Ruby version is 2.0.1, but your Gemfile specified ~> 2.2")
+ expect(err).to include("Your Ruby version is #{Gem.ruby_version}, but your Gemfile specified ~> 1.2")
end
end
context "and using a supported Ruby version" do
before do
install_gemfile <<-G
- ::RUBY_VERSION = '2.1.3'
- ::RUBY_PATCHLEVEL = 100
- ruby '~> 2.1.0'
+ ruby '~> #{Gem.ruby_version}'
+ source "https://gem.repo1"
G
end
it "writes current Ruby version to Gemfile.lock" do
- lockfile_should_be <<-L
+ checksums = checksums_section_when_enabled
+ expect(lockfile).to eq <<~L
GEM
+ remote: https://gem.repo1/
specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
-
+ #{checksums}
RUBY VERSION
- ruby 2.1.3p100
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
- it "updates Gemfile.lock with updated incompatible ruby version" do
+ it "updates Gemfile.lock with updated yet still compatible ruby version" do
install_gemfile <<-G
- ::RUBY_VERSION = '2.2.3'
- ::RUBY_PATCHLEVEL = 100
- ruby '~> 2.2.0'
+ ruby '~> #{current_ruby_minor}'
+ source "https://gem.repo1"
G
- lockfile_should_be <<-L
+ checksums = checksums_section_when_enabled
+
+ expect(lockfile).to eq <<~L
GEM
+ remote: https://gem.repo1/
specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
-
+ #{checksums}
RUBY VERSION
- ruby 2.2.3p100
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
+
+ it "does not crash when unlocking" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby '>= 2.1.0'
+ G
+
+ bundle "update"
+
+ expect(err).not_to include("Could not find gem 'Ruby")
+ end
end
end
describe "when Bundler root contains regex chars" do
- it "doesn't blow up" do
+ it "doesn't blow up when using the `gem` DSL" do
root_dir = tmp("foo[]bar")
FileUtils.mkdir_p(root_dir)
build_lib "foo"
gemfile = <<-G
+ source "https://gem.repo1"
gem 'foo', :path => "#{lib_path("foo-1.0")}"
G
File.open("#{root_dir}/Gemfile", "w") do |file|
file.puts gemfile
end
- bundle :install, :dir => root_dir
+ bundle :install, dir: root_dir
+ end
+
+ it "doesn't blow up when using the `gemspec` DSL" do
+ root_dir = tmp("foo[]bar")
+
+ FileUtils.mkdir_p(root_dir)
+
+ build_lib "foo", path: root_dir
+ gemfile = <<-G
+ source "https://gem.repo1"
+ gemspec
+ G
+ File.open("#{root_dir}/Gemfile", "w") do |file|
+ file.puts gemfile
+ end
+
+ bundle :install, dir: root_dir
end
end
describe "when requesting a quiet install via --quiet" do
- it "should be quiet" do
- bundle "config set force_ruby_platform true"
+ it "should be quiet if there are no warnings" do
+ bundle_config "force_ruby_platform true"
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+
+ 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 "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
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'non-existing-gem'
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, 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
- describe "when bundle path does not have write access", :permissions do
+ describe "when bundle path does not have cd permission", :permissions do
+ let(:bundle_path) { bundled_app("vendor") }
+
before do
- FileUtils.mkdir_p(bundled_app("vendor"))
+ FileUtils.mkdir_p(bundle_path)
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
end
it "should display a proper message to explain the problem" do
- FileUtils.chmod(0o500, bundled_app("vendor"))
+ FileUtils.chmod(0o500, bundle_path)
+
+ bundle_config "path vendor"
+ bundle :install, raise_on_error: false
+ expect(err).to include(bundle_path.to_s)
+ expect(err).to include("grant executable permissions")
+ end
+ end
+
+ describe "when bundle gems path does not have cd permission", :permissions do
+ let(:gems_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems") }
+
+ before do
+ FileUtils.mkdir_p(gems_path)
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ FileUtils.chmod("-x", gems_path)
+ bundle_config "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("myrack-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 there's an empty install folder (like with default gems) without cd permissions", :permissions do
+ let(:full_gem_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems/myrack-1.0.0") }
+
+ before do
+ FileUtils.mkdir_p(full_gem_path)
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ FileUtils.chmod("-x", full_gem_path)
+ bundle_config "path vendor"
+
+ begin
+ bundle :install, raise_on_error: false
+ ensure
+ FileUtils.chmod("+x", full_gem_path)
+ end
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+
+ expect(err).to include(
+ "There was an error while trying to write to `#{full_gem_path}`. " \
+ "It is likely that you need to grant write permissions for that path."
+ )
+ end
+ end
+
+ describe "when bundle bin dir does not have cd permission", :permissions do
+ let(:bin_dir) { bundled_app("vendor/#{Bundler.ruby_scope}/bin") }
+
+ before do
+ FileUtils.mkdir_p(bin_dir)
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ FileUtils.chmod("-x", bin_dir)
+ bundle_config "path vendor"
+
+ begin
+ bundle :install, raise_on_error: false
+ ensure
+ FileUtils.chmod("+x", bin_dir)
+ end
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+
+ expect(err).to include(
+ "There was an error while trying to write to `#{bin_dir}`. " \
+ "It is likely that you need to grant write permissions for that path."
+ )
+ end
+ end
+
+ describe "when bundle bin dir does not have write access", :permissions do
+ let(:bin_dir) { bundled_app("vendor/#{Bundler.ruby_scope}/bin") }
+
+ before do
+ FileUtils.mkdir_p(bin_dir)
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ FileUtils.chmod("-w", bin_dir)
+ bundle_config "path vendor"
+
+ begin
+ bundle :install, raise_on_error: false
+ ensure
+ FileUtils.chmod("+w", bin_dir)
+ end
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+
+ expect(err).to include(
+ "There was an error while trying to write to `#{bin_dir}`. " \
+ "It is likely that you need to grant write permissions for that path."
+ )
+ end
+ end
+
+ describe "when bundle extensions path does not have write access", :permissions do
+ let(:extensions_path) { bundled_app("vendor/#{Bundler.ruby_scope}/extensions/#{Gem::Platform.local}/#{Gem.extension_api_version}") }
+
+ before do
+ FileUtils.mkdir_p(extensions_path)
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'simple_binary'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ FileUtils.chmod("-x", extensions_path)
+ bundle_config "path vendor"
+
+ begin
+ bundle :install, raise_on_error: false
+ ensure
+ FileUtils.chmod("+x", extensions_path)
+ end
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+
+ expect(err).to include(
+ "There was an error while trying to create `#{extensions_path.join("simple_binary-1.0")}`. " \
+ "It is likely that you need to grant executable permissions for all parent directories and write permissions for `#{extensions_path}`."
+ )
+ end
+ end
+
+ describe "when the path of a specific gem does not have cd permission", :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 "https://gem.repo4"
+ gem 'foo'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ bundle_config "path vendor"
+ bundle :install
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+
+ FileUtils.chmod("-x", foo_path)
+
+ begin
+ bundle "install --force", raise_on_error: false
+ ensure
+ FileUtils.chmod("+x", foo_path)
+ end
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+ expect(err).to include("Could not delete previous installation of `#{foo_path}`.")
+ expect(err).to include("The underlying error was Errno::EACCES")
+ end
+ end
+
+ describe "when gem home does not have the writable bit set, yet it's still writable", :permissions do
+ let(:gem_home) { bundled_app("vendor/#{Bundler.ruby_scope}") }
+
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0.0" do |s|
+ s.write "CHANGELOG.md", "foo"
+ end
+ end
- bundle "config --local path vendor"
- bundle :install, :raise_on_error => false
- expect(err).to include(bundled_app("vendor").to_s)
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem 'foo'
+ G
+ end
+
+ it "should still work" do
+ bundle_config "path vendor"
+ bundle :install
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+
+ FileUtils.chmod("-w", gem_home)
+
+ begin
+ bundle "install --force"
+ ensure
+ FileUtils.chmod("+w", gem_home)
+ end
+
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+ end
+ end
+
+ describe "when gems path is world writable (no sticky bit set)", :permissions do
+ let(:gems_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems") }
+
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0.0" do |s|
+ s.write "CHANGELOG.md", "foo"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem 'foo'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ bundle_config "path vendor"
+ bundle :install
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+
+ FileUtils.chmod(0o777, gems_path)
+
+ bundle "install --force", raise_on_error: false
+
+ expect(err).to include("Bundler cannot reinstall foo-1.0.0 because there's a previous installation of it at #{gems_path}/foo-1.0.0 that is unsafe to remove")
+ end
+ end
+
+ describe "when gems path is world writable (no sticky bit set), but previous install is just an empty dir (like it happens with default gems)", :permissions do
+ let(:gems_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems") }
+ let(:full_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 "https://gem.repo4"
+ gem 'foo'
+ G
+ end
+
+ it "does not try to remove the directory and thus don't abort with an error about unsafe directory removal" do
+ bundle_config "path vendor"
+
+ FileUtils.mkdir_p(gems_path)
+ FileUtils.chmod(0o777, gems_path)
+ Dir.mkdir(full_path)
+
+ bundle "install"
+ end
+ end
+
+ describe "when bundle cache path does not have write access", :permissions do
+ let(:cache_path) { bundled_app("vendor/#{Bundler.ruby_scope}/cache") }
+
+ before do
+ FileUtils.mkdir_p(cache_path)
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+ end
+
+ it "should display a proper message to explain the problem" do
+ FileUtils.chmod(0o500, cache_path)
+
+ bundle_config "path vendor"
+ bundle :install, raise_on_error: false
+ expect(err).to include(cache_path.to_s)
expect(err).to include("grant write permissions")
end
end
+ describe "when gemspecs are unreadable", :permissions do
+ let(:gemspec_path) { vendored_gems("specifications/myrack-1.0.0.gemspec") }
+
+ before do
+ gemfile <<~G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+ bundle_config "path vendor/bundle"
+ bundle :install
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+
+ FileUtils.chmod("-r", gemspec_path)
+ end
+
+ it "shows a good error" do
+ bundle :install, raise_on_error: false
+ expect(err).to include(gemspec_path.to_s)
+ expect(err).to include("grant read permissions")
+ end
+ end
+
+ describe "when using umask 002 and setgid bit", :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 "https://gem.repo4"
+ gem 'foo'
+ G
+
+ FileUtils.mkdir_p(gems_path)
+ FileUtils.chmod("g+s", gems_path)
+ end
+
+ it "should create the gem directory with proper permissions" do
+ with_umask(0o002) do
+ bundle_config "path vendor"
+ bundle :install
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+ # Linux's SysV-derived mkdir(2) propagates the set-group-ID bit
+ # from the parent directory to newly created subdirectories. BSD
+ # (including macOS) inherits the parent's group via mkdir(2) but
+ # does not copy the set-group-ID bit itself, so the expected
+ # mode differs by platform.
+ expected = RUBY_PLATFORM.include?("darwin") ? 0o0775 : 0o2775
+ expect(File.stat(foo_path).mode & 0o7777).to eq(expected)
+ end
+ end
+ end
+
+ describe "parallel make" do
+ before do
+ unless Gem::Installer.private_method_defined?(:build_jobs)
+ skip "This example is runnable when RubyGems::Installer implements `build_jobs`"
+ end
+
+ @old_makeflags = ENV["MAKEFLAGS"]
+ @gemspec = nil
+
+ extconf_code = <<~CODE
+ require "mkmf"
+ create_makefile("foo")
+ CODE
+
+ build_repo4 do
+ build_gem "mypsych", "4.0.6" do |s|
+ @gemspec = s
+ extension = "ext/mypsych/extconf.rb"
+ s.extensions = extension
+
+ s.write(extension, extconf_code)
+ end
+ end
+ end
+
+ after do
+ if @old_makeflags
+ ENV["MAKEFLAGS"] = @old_makeflags
+ else
+ ENV.delete("MAKEFLAGS")
+ end
+ end
+
+ it "doesn't pass down -j to make when MAKEFLAGS is set" do
+ ENV["MAKEFLAGS"] = "-j1"
+
+ install_gemfile(<<~G, env: { "BUNDLE_JOBS" => "8" })
+ source "https://gem.repo4"
+ gem "mypsych"
+ G
+
+ gem_make_out = File.read(File.join(@gemspec.extension_dir, "gem_make.out"))
+
+ expect(gem_make_out).not_to include("make -j8")
+ end
+
+ it "pass down the BUNDLE_JOBS to RubyGems when running the compilation of an extension" do
+ ENV.delete("MAKEFLAGS")
+
+ install_gemfile(<<~G, env: { "BUNDLE_JOBS" => "8" })
+ source "https://gem.repo4"
+ gem "mypsych"
+ G
+
+ gem_make_out = File.read(File.join(@gemspec.extension_dir, "gem_make.out"))
+
+ expect(gem_make_out).to include("make -j8")
+ end
+
+ it "uses nprocessors by default" do
+ ENV.delete("MAKEFLAGS")
+
+ install_gemfile(<<~G)
+ source "https://gem.repo4"
+ gem "mypsych"
+ G
+
+ gem_make_out = File.read(File.join(@gemspec.extension_dir, "gem_make.out"))
+
+ expect(gem_make_out).to include("make -j#{Etc.nprocessors + 1}")
+ end
+ end
+
+ describe "when a native extension requires a transitive dependency at build time" do
+ before do
+ build_repo4 do
+ build_gem "alpha", "1.0.0" do |s|
+ extension = "ext/alpha/extconf.rb"
+ s.extensions = extension
+ s.write(extension, <<~CODE)
+ require "mkmf"
+ sleep 1
+ create_makefile("alpha")
+ CODE
+ s.write "lib/alpha.rb", "ALPHA = '1.0.0'"
+ end
+
+ build_gem "beta", "1.0.0" do |s|
+ s.add_dependency "alpha"
+ s.write "lib/beta.rb", "require 'alpha'\nBETA = '1.0.0'"
+ end
+
+ build_gem "gamma", "1.0.0" do |s|
+ s.add_dependency "beta"
+ extension = "ext/gamma/extconf.rb"
+ s.extensions = extension
+ s.write(extension, <<~EXTCONF)
+ require "beta"
+ require "mkmf"
+ create_makefile("gamma")
+ EXTCONF
+ end
+ end
+ end
+
+ it "installs successfully" do
+ install_gemfile <<~G
+ source "https://gem.repo4"
+ gem "gamma"
+ G
+
+ expect(the_bundle).to include_gems "alpha 1.0.0", "beta 1.0.0", "gamma 1.0.0"
+ end
+ end
+
+ describe "when configured path is UTF-8 and a file inside a gem package too" do
+ let(:app_path) do
+ path = tmp("♥")
+ FileUtils.mkdir_p(path)
+ path
+ end
+
+ let(:path) do
+ root.join("vendor/bundle")
+ end
+
+ before do
+ build_repo4 do
+ build_gem "mygem" do |s|
+ s.write "spec/fixtures/_posts/2016-04-01-错误.html"
+ end
+ end
+ end
+
+ it "works" do
+ bundle "config set path #{app_path}/vendor/bundle", dir: app_path
+
+ install_gemfile app_path.join("Gemfile"),<<~G, dir: app_path
+ source "https://gem.repo4"
+ gem "mygem", "1.0"
+ G
+ end
+ end
+
context "after installing with --standalone" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "config --local path bundle"
- bundle "install", :standalone => true
+ bundle_config "path bundle"
+ bundle "install", standalone: true
end
it "includes the standalone path" do
- bundle "binstubs rack", :standalone => true
- standalone_line = File.read(bundled_app("bin/rackup")).each_line.find {|line| line.include? "$:.unshift" }.strip
- expect(standalone_line).to eq %($:.unshift File.expand_path "../../bundle", path.realpath)
+ bundle "binstubs myrack", standalone: true
+ standalone_line = File.read(bundled_app("bin/myrackup")).each_line.find {|line| line.include? "$:.unshift" }.strip
+ expect(standalone_line).to eq %($:.unshift File.expand_path "../bundle", __dir__)
end
end
@@ -577,10 +1513,603 @@ RSpec.describe "bundle install with gem sources" do
end
it "should display a helpful message explaining how to fix it" do
- bundle :install, :env => { "BUNDLE_RUBYGEMS__ORG" => "user:pass{word" }, :raise_on_error => false
+ bundle :install, env: { "BUNDLE_RUBYGEMS__ORG" => "user:pass{word" }, raise_on_error: false
expect(exitstatus).to eq(17)
expect(err).to eq("Please CGI escape your usernames and passwords before " \
"setting them for authentication.")
end
end
+
+ context "when current platform not included in the lockfile" do
+ around do |example|
+ build_repo4 do
+ build_gem "libv8", "8.4.255.0" do |s|
+ s.platform = "x86_64-darwin-19"
+ end
+
+ build_gem "libv8", "8.4.255.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "libv8"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ libv8 (8.4.255.0-x86_64-darwin-19)
+
+ PLATFORMS
+ x86_64-darwin-19
+
+ DEPENDENCIES
+ libv8
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform("x86_64-linux", &example)
+ end
+
+ it "adds the current platform to the lockfile" do
+ bundle "install --verbose"
+
+ expect(out).to include("re-resolving dependencies because your lockfile is missing the current platform")
+ expect(out).not_to include("you are adding a new platform to your lockfile")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ libv8 (8.4.255.0-x86_64-darwin-19)
+ libv8 (8.4.255.0-x86_64-linux)
+
+ PLATFORMS
+ x86_64-darwin-19
+ x86_64-linux
+
+ DEPENDENCIES
+ libv8
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "fails loudly if frozen mode set" do
+ bundle_config "deployment true"
+ bundle "install", raise_on_error: false
+
+ expect(err).to eq(
+ "Your bundle only supports platforms [\"x86_64-darwin-19\"] but your local platform is x86_64-linux. " \
+ "Add the current platform to the lockfile with\n`bundle lock --add-platform x86_64-linux` and try again."
+ )
+ end
+ end
+
+ context "with missing platform specific gems in lockfile" do
+ before do
+ build_repo4 do
+ build_gem "racca", "1.5.2"
+
+ build_gem "nokogiri", "1.12.4" do |s|
+ s.platform = "x86_64-darwin"
+ s.add_dependency "racca", "~> 1.4"
+ end
+
+ build_gem "nokogiri", "1.12.4" do |s|
+ s.platform = "x86_64-linux"
+ s.add_dependency "racca", "~> 1.4"
+ end
+
+ build_gem "crass", "1.0.6"
+
+ build_gem "loofah", "2.12.0" do |s|
+ s.add_dependency "crass", "~> 1.0.2"
+ s.add_dependency "nokogiri", ">= 1.5.9"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ ruby "#{Gem.ruby_version}"
+
+ gem "loofah", "~> 2.12.0"
+ G
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo4, "crass", "1.0.6"
+ c.checksum gem_repo4, "loofah", "2.12.0"
+ c.checksum gem_repo4, "nokogiri", "1.12.4", "x86_64-darwin"
+ c.checksum gem_repo4, "racca", "1.5.2"
+ end
+
+ 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)
+ racca (~> 1.4)
+ racca (1.5.2)
+
+ PLATFORMS
+ x86_64-darwin-20
+ x86_64-linux
+
+ DEPENDENCIES
+ loofah (~> 2.12.0)
+ #{checksums}
+ RUBY VERSION
+ #{Bundler::RubyVersion.system}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile" do
+ bundle_config "path vendor/bundle"
+
+ simulate_platform "x86_64-linux" do
+ bundle "install", artifice: "compact_index"
+ end
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "crass", "1.0.6"
+ c.checksum gem_repo4, "loofah", "2.12.0"
+ c.checksum gem_repo4, "nokogiri", "1.12.4", "x86_64-darwin"
+ c.checksum gem_repo4, "racca", "1.5.2"
+ c.checksum gem_repo4, "nokogiri", "1.12.4", "x86_64-linux"
+ 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)
+ racca (~> 1.4)
+ nokogiri (1.12.4-x86_64-linux)
+ racca (~> 1.4)
+ racca (1.5.2)
+
+ PLATFORMS
+ x86_64-darwin-20
+ x86_64-linux
+
+ DEPENDENCIES
+ loofah (~> 2.12.0)
+ #{checksums}
+ RUBY VERSION
+ #{Bundler::RubyVersion.system}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when lockfile has incorrect dependencies" do
+ before do
+ build_repo2
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack_middleware"
+ G
+
+ system_gems "myrack_middleware-1.0", path: default_bundle_path
+
+ # we want to raise when the 1.0 line should be followed by " myrack (= 0.9.1)" but isn't
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack_middleware (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a clear error message when frozen" do
+ bundle_config "frozen true"
+ bundle "install", raise_on_error: false
+
+ expect(exitstatus).to eq(41)
+ expect(err).to include("Bundler found incorrect dependencies in the lockfile for myrack_middleware-1.0")
+ expect(err).to include("myrack: gemspec specifies = 0.9.1, not in lockfile")
+ end
+
+ it "updates the lockfile when not frozen" do
+ missing_dep = "myrack (0.9.1)"
+ expect(lockfile).not_to include(missing_dep)
+
+ bundle_config "frozen false"
+ bundle :install
+
+ expect(lockfile).to include(missing_dep)
+ expect(out).to include("now installed")
+ end
+ end
+
+ context "with --local flag" do
+ before do
+ system_gems "myrack-1.0.0", path: default_bundle_path
+ end
+
+ it "respects installed gems without fetching any remote sources" do
+ install_gemfile <<-G, local: true
+ source "https://gem.repo1"
+
+ source "https://not-existing-source" do
+ gem "myrack"
+ end
+ G
+
+ expect(last_command).to be_success
+ end
+ end
+
+ context "with only option" do
+ before do
+ bundle_config "only a:b"
+ end
+
+ it "installs only gems of the specified groups" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails"
+ gem "myrack", group: :a
+ gem "rake", group: :b
+ gem "yard", group: :c
+ G
+
+ expect(out).to include("Installing myrack")
+ expect(out).to include("Installing rake")
+ expect(out).not_to include("Installing yard")
+ end
+ end
+
+ context "with --prefer-local flag" do
+ context "and gems available locally" do
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0.1"
+ build_gem "foo", "1.0.0"
+ build_gem "bar", "1.0.0"
+
+ build_gem "a", "1.0.0" do |s|
+ s.add_dependency "foo", "~> 1.0.0"
+ end
+
+ build_gem "b", "1.0.0" do |s|
+ s.add_dependency "foo", "~> 1.0.1"
+ end
+ end
+
+ system_gems "foo-1.0.0", path: default_bundle_path, gem_repo: gem_repo4
+ end
+
+ it "fetches remote sources when not available locally" do
+ install_gemfile <<-G, "prefer-local": true, verbose: true
+ source "https://gem.repo4"
+
+ gem "foo"
+ gem "bar"
+ G
+
+ expect(out).to include("Using foo 1.0.0").and include("Fetching bar 1.0.0").and include("Installing bar 1.0.0")
+ expect(last_command).to be_success
+ end
+
+ it "fetches remote sources when local version does not match requirements" do
+ install_gemfile <<-G, "prefer-local": true, verbose: true
+ source "https://gem.repo4"
+
+ gem "foo", "1.0.1"
+ gem "bar"
+ G
+
+ expect(out).to include("Fetching foo 1.0.1").and include("Installing foo 1.0.1").and include("Fetching bar 1.0.0").and include("Installing bar 1.0.0")
+ expect(last_command).to be_success
+ end
+
+ it "uses the locally available version for sub-dependencies when possible" do
+ install_gemfile <<-G, "prefer-local": true, verbose: true
+ source "https://gem.repo4"
+
+ gem "a"
+ G
+
+ expect(out).to include("Using foo 1.0.0").and include("Fetching a 1.0.0").and include("Installing a 1.0.0")
+ expect(last_command).to be_success
+ end
+
+ it "fetches remote sources for sub-dependencies when the locally available version does not satisfy the requirement" do
+ install_gemfile <<-G, "prefer-local": true, verbose: true
+ source "https://gem.repo4"
+
+ gem "b"
+ G
+
+ expect(out).to include("Fetching foo 1.0.1").and include("Installing foo 1.0.1").and include("Fetching b 1.0.0").and include("Installing b 1.0.0")
+ expect(last_command).to be_success
+ end
+ end
+
+ context "and no gems available locally" do
+ before do
+ build_repo4 do
+ build_gem "myreline", "0.3.8"
+ build_gem "debug", "0.2.1"
+
+ build_gem "debug", "1.10.0" do |s|
+ s.add_dependency "myreline"
+ end
+ end
+ end
+
+ it "resolves to the latest version if no gems are available locally" do
+ install_gemfile <<~G, "prefer-local": true, verbose: true
+ source "https://gem.repo4"
+
+ gem "debug"
+ G
+
+ expect(out).to include("Fetching debug 1.10.0").and include("Installing debug 1.10.0").and include("Fetching myreline 0.3.8").and include("Installing myreline 0.3.8")
+ expect(last_command).to be_success
+ end
+ end
+ end
+
+ context "with a symlinked configured as bundle path and a gem with symlinks" do
+ before do
+ symlinked_bundled_app = tmp("bundled_app-symlink")
+ File.symlink(bundled_app, symlinked_bundled_app)
+ bundle_config "path #{File.join(symlinked_bundled_app, ".vendor")}"
+
+ binman_path = tmp("binman")
+ FileUtils.mkdir_p binman_path
+
+ readme_path = File.join(binman_path, "README.markdown")
+ FileUtils.touch(readme_path)
+
+ man_path = File.join(binman_path, "man", "man0")
+ FileUtils.mkdir_p man_path
+
+ File.symlink("../../README.markdown", File.join(man_path, "README.markdown"))
+
+ build_repo4 do
+ build_gem "binman", path: gem_repo4("gems"), lib_path: binman_path, no_default: true do |s|
+ s.files = ["README.markdown", "man/man0/README.markdown"]
+ end
+ end
+ end
+
+ it "installs fine" do
+ install_gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "binman"
+ G
+ end
+ end
+
+ context "when a gem has equivalent versions with inconsistent dependencies" do
+ before do
+ build_repo4 do
+ build_gem "autobuild", "1.10.rc2" do |s|
+ s.add_dependency "utilrb", ">= 1.6.0"
+ end
+
+ build_gem "autobuild", "1.10.0.rc2" do |s|
+ s.add_dependency "utilrb", ">= 2.0"
+ end
+ end
+ end
+
+ it "does not crash unexpectedly" do
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "autobuild", "1.10.rc2"
+ G
+
+ bundle "install --jobs 1", raise_on_error: false
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+ expect(err).to include("Could not find compatible versions")
+ end
+ end
+
+ context "when a lockfile has unmet dependencies, and the Gemfile has no resolution" do
+ before do
+ build_repo4 do
+ build_gem "aaa", "0.2.0" do |s|
+ s.add_dependency "zzz", "< 0.2.0"
+ end
+
+ build_gem "zzz", "0.2.0"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "aaa"
+ gem "zzz"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ aaa (0.2.0)
+ zzz (< 0.2.0)
+ zzz (0.2.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ aaa!
+ zzz!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "does not install, but raises a resolution error" do
+ bundle "install", raise_on_error: false
+ expect(err).to include("Could not find compatible versions")
+ end
+ end
+
+ context "when --jobs option given" do
+ before do
+ install_gemfile "source 'https://gem.repo1'", jobs: 1
+ end
+
+ it "does not save the flag to config" do
+ expect(bundled_app(".bundle/config")).not_to exist
+ end
+ end
+
+ context "when bundler installation is corrupt" do
+ before do
+ system_gems "bundler-9.99.8"
+
+ replace_version_file("9.99.9", dir: system_gem_path("gems/bundler-9.99.8"))
+ end
+
+ it "shows a proper error" do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ 9.99.8
+ L
+
+ install_gemfile "source \"https://gem.repo1\"", env: { "BUNDLER_VERSION" => "9.99.8" }, raise_on_error: false
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+ expect(err).to include("The running version of Bundler (9.99.9) does not match the version of the specification installed for it (9.99.8)")
+ end
+ end
+
+ it "only installs executable files in bin" do
+ bundle_config "path vendor/bundle"
+
+ install_gemfile <<~G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ expected_executables = [vendored_gems("bin/myrackup").to_s]
+ expected_executables << vendored_gems("bin/myrackup.bat").to_s if Gem.win_platform?
+ expect(Dir.glob(vendored_gems("bin/*"))).to eq(expected_executables)
+ end
+
+ it "prevents removing binstubs when BUNDLE_CLEAN is set" do
+ build_repo4 do
+ build_gem "kamal", "4.0.6" do |s|
+ s.executables = ["kamal"]
+ end
+ end
+
+ gemfile = <<~G
+ source "https://gem.repo4"
+ gem "kamal"
+ G
+
+ install_gemfile(gemfile, env: { "BUNDLE_CLEAN" => "true", "BUNDLE_PATH" => "vendor/bundle" })
+
+ expected_executables = [vendored_gems("bin/kamal").to_s]
+ expected_executables << vendored_gems("bin/kamal.bat").to_s if Gem.win_platform?
+ expect(Dir.glob(vendored_gems("bin/*"))).to eq(expected_executables)
+ end
+
+ it "preserves lockfile versions conservatively" do
+ build_repo4 do
+ build_gem "mypsych", "4.0.6" do |s|
+ s.add_dependency "mystringio"
+ end
+
+ build_gem "mypsych", "5.1.2" do |s|
+ s.add_dependency "mystringio"
+ end
+
+ build_gem "mystringio", "3.1.0"
+ build_gem "mystringio", "3.1.1"
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ mypsych (4.0.6)
+ mystringio
+ mystringio (3.1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ mypsych (~> 4.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile <<~G
+ source "https://gem.repo4"
+ gem "mypsych", "~> 5.0"
+ G
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ mypsych (5.1.2)
+ mystringio
+ mystringio (3.1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ mypsych (~> 5.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
end
diff --git a/spec/bundler/commands/issue_spec.rb b/spec/bundler/commands/issue_spec.rb
index 143f6333ce..346cdedc42 100644
--- a/spec/bundler/commands/issue_spec.rb
+++ b/spec/bundler/commands/issue_spec.rb
@@ -3,7 +3,7 @@
RSpec.describe "bundle issue" do
it "exits with a message" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
diff --git a/spec/bundler/commands/licenses_spec.rb b/spec/bundler/commands/licenses_spec.rb
index d4fa02d0a7..ebfad5ed4a 100644
--- a/spec/bundler/commands/licenses_spec.rb
+++ b/spec/bundler/commands/licenses_spec.rb
@@ -2,8 +2,14 @@
RSpec.describe "bundle licenses" do
before :each do
+ build_repo2 do
+ build_gem "with_license" do |s|
+ s.license = "MIT"
+ end
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "rails"
gem "with_license"
G
@@ -18,13 +24,13 @@ RSpec.describe "bundle licenses" do
it "performs an automatic bundle install" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "rails"
gem "with_license"
gem "foo"
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
bundle :licenses
expect(out).to include("Installing foo 1.0")
end
diff --git a/spec/bundler/commands/list_spec.rb b/spec/bundler/commands/list_spec.rb
index ed3edad163..c890646a81 100644
--- a/spec/bundler/commands/list_spec.rb
+++ b/spec/bundler/commands/list_spec.rb
@@ -1,9 +1,31 @@
# frozen_string_literal: true
+require "json"
+
RSpec.describe "bundle list" do
+ def find_gem_name(json:, name:)
+ parse_json(json)["gems"].detect {|h| h["name"] == name }
+ end
+
+ def parse_json(json)
+ JSON.parse(json)
+ end
+
+ context "in verbose mode" do
+ it "logs the actual flags passed to the command" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ G
+
+ bundle "list --verbose"
+
+ expect(out).to include("Running `bundle list --verbose`")
+ end
+ end
+
context "with name-only and paths option" do
it "raises an error" do
- bundle "list --name-only --paths", :raise_on_error => false
+ bundle "list --name-only --paths", raise_on_error: false
expect(err).to eq "The `--name-only` and `--paths` options cannot be used together"
end
@@ -11,18 +33,32 @@ RSpec.describe "bundle list" do
context "with without-group and only-group option" do
it "raises an error" do
- bundle "list --without-group dev --only-group test", :raise_on_error => false
+ bundle "list --without-group dev --only-group test", raise_on_error: false
expect(err).to eq "The `--only-group` and `--without-group` options cannot be used together"
end
end
+ context "with invalid format option" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ G
+ end
+
+ it "raises an error" do
+ bundle "list --format=nope", raise_on_error: false
+
+ expect(err).to eq "Unknown option`--format=nope`. Supported formats: `json`"
+ end
+ end
+
describe "with without-group option" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
gem "rspec", :group => [:test]
gem "rails", :group => [:production]
G
@@ -32,15 +68,26 @@ RSpec.describe "bundle list" do
it "prints the gems not in the specified group" do
bundle "list --without-group test"
- expect(out).to include(" * rack (1.0.0)")
+ expect(out).to include(" * myrack (1.0.0)")
expect(out).to include(" * rails (2.3.2)")
expect(out).not_to include(" * rspec (1.2.7)")
end
+
+ it "prints the gems not in the specified group with json" do
+ bundle "list --without-group test --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["version"]).to eq("1.0.0")
+ gem = find_gem_name(json: out, name: "rails")
+ expect(gem["version"]).to eq("2.3.2")
+ gem = find_gem_name(json: out, name: "rspec")
+ expect(gem).to be_nil
+ end
end
context "when group is not found" do
it "raises an error" do
- bundle "list --without-group random", :raise_on_error => false
+ bundle "list --without-group random", raise_on_error: false
expect(err).to eq "`random` group could not be found."
end
@@ -50,19 +97,30 @@ RSpec.describe "bundle list" do
it "prints the gems not in the specified groups" do
bundle "list --without-group test production"
- expect(out).to include(" * rack (1.0.0)")
+ expect(out).to include(" * myrack (1.0.0)")
expect(out).not_to include(" * rails (2.3.2)")
expect(out).not_to include(" * rspec (1.2.7)")
end
+
+ it "prints the gems not in the specified groups with json" do
+ bundle "list --without-group test production --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["version"]).to eq("1.0.0")
+ gem = find_gem_name(json: out, name: "rails")
+ expect(gem).to be_nil
+ gem = find_gem_name(json: out, name: "rspec")
+ expect(gem).to be_nil
+ end
end
end
describe "with only-group option" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
gem "rspec", :group => [:test]
gem "rails", :group => [:production]
G
@@ -72,14 +130,23 @@ RSpec.describe "bundle list" do
it "prints the gems in the specified group" do
bundle "list --only-group default"
- expect(out).to include(" * rack (1.0.0)")
+ expect(out).to include(" * myrack (1.0.0)")
expect(out).not_to include(" * rspec (1.2.7)")
end
+
+ it "prints the gems in the specified group with json" do
+ bundle "list --only-group default --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["version"]).to eq("1.0.0")
+ gem = find_gem_name(json: out, name: "rspec")
+ expect(gem).to be_nil
+ end
end
context "when group is not found" do
it "raises an error" do
- bundle "list --only-group random", :raise_on_error => false
+ bundle "list --only-group random", raise_on_error: false
expect(err).to eq "`random` group could not be found."
end
@@ -89,19 +156,30 @@ RSpec.describe "bundle list" do
it "prints the gems in the specified groups" do
bundle "list --only-group default production"
- expect(out).to include(" * rack (1.0.0)")
+ expect(out).to include(" * myrack (1.0.0)")
expect(out).to include(" * rails (2.3.2)")
expect(out).not_to include(" * rspec (1.2.7)")
end
+
+ it "prints the gems in the specified groups with json" do
+ bundle "list --only-group default production --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["version"]).to eq("1.0.0")
+ gem = find_gem_name(json: out, name: "rails")
+ expect(gem["version"]).to eq("2.3.2")
+ gem = find_gem_name(json: out, name: "rspec")
+ expect(gem).to be_nil
+ end
end
end
context "with name-only option" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
gem "rspec", :group => [:test]
G
end
@@ -109,45 +187,79 @@ RSpec.describe "bundle list" do
it "prints only the name of the gems in the bundle" do
bundle "list --name-only"
- expect(out).to include("rack")
+ expect(out).to include("myrack")
expect(out).to include("rspec")
end
+
+ it "prints only the name of the gems in the bundle with json" do
+ bundle "list --name-only --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem.keys).to eq(["name"])
+ gem = find_gem_name(json: out, name: "rspec")
+ expect(gem.keys).to eq(["name"])
+ end
end
context "with paths option" do
before do
build_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
build_gem "bar"
end
- build_git "git_test", "1.0.0", :path => lib_path("git_test")
+ build_git "git_test", "1.0.0", path: lib_path("git_test")
- build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s|
+ build_lib("gemspec_test", path: tmp("gemspec_test")) do |s|
s.add_dependency "bar", "=1.0.0"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
+ source "https://gem.repo2"
+ gem "myrack"
gem "rails"
gem "git_test", :git => "#{lib_path("git_test")}"
- gemspec :path => "#{tmp.join("gemspec_test")}"
+ gemspec :path => "#{tmp("gemspec_test")}"
G
end
it "prints the path of each gem in the bundle" do
bundle "list --paths"
expect(out).to match(%r{.*\/rails\-2\.3\.2})
- expect(out).to match(%r{.*\/rack\-1\.2})
+ expect(out).to match(%r{.*\/myrack\-1\.2})
expect(out).to match(%r{.*\/git_test\-\w})
expect(out).to match(%r{.*\/gemspec_test})
end
+
+ it "prints the path of each gem in the bundle with json" do
+ bundle "list --paths --format=json"
+
+ gem = find_gem_name(json: out, name: "rails")
+ expect(gem["path"]).to match(%r{.*\/rails\-2\.3\.2})
+ expect(gem["git_version"]).to be_nil
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["path"]).to match(%r{.*\/myrack\-1\.2})
+ expect(gem["git_version"]).to be_nil
+
+ gem = find_gem_name(json: out, name: "git_test")
+ expect(gem["path"]).to match(%r{.*\/git_test\-\w})
+ expect(gem["git_version"]).to be_truthy
+ expect(gem["git_version"].strip).to eq(gem["git_version"])
+
+ gem = find_gem_name(json: out, name: "gemspec_test")
+ expect(gem["path"]).to match(%r{.*\/gemspec_test})
+ expect(gem["git_version"]).to be_nil
+ end
end
context "when no gems are in the gemfile" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
G
end
@@ -155,30 +267,42 @@ RSpec.describe "bundle list" do
bundle "list"
expect(out).to include("No gems in the Gemfile")
end
+
+ it "prints empty json" do
+ bundle "list --format=json"
+ expect(parse_json(out)["gems"]).to eq([])
+ end
end
context "without options" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
gem "rspec", :group => [:test]
G
end
it "lists gems installed in the bundle" do
bundle "list"
- expect(out).to include(" * rack (1.0.0)")
+ expect(out).to include(" * myrack (1.0.0)")
+ end
+
+ it "lists gems installed in the bundle with json" do
+ bundle "list --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["version"]).to eq("1.0.0")
end
end
context "when using the ls alias" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
gem "rspec", :group => [:test]
G
end
diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb
index c97358c62d..8ab3cc7e8d 100644
--- a/spec/bundler/commands/lock_spec.rb
+++ b/spec/bundler/commands/lock_spec.rb
@@ -1,27 +1,22 @@
# frozen_string_literal: true
RSpec.describe "bundle lock" do
- def strip_lockfile(lockfile)
- strip_whitespace(lockfile).sub(/\n\Z/, "")
- end
-
- def read_lockfile(file = "Gemfile.lock")
- strip_lockfile bundled_app(file).read
- end
-
- let(:repo) { gem_repo1 }
-
- before :each do
- gemfile <<-G
- source "#{file_uri_for(repo)}"
- gem "rails"
- gem "with_license"
- gem "foo"
- G
+ let(:expected_lockfile) do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "actionmailer", "2.3.2"
+ c.checksum gem_repo4, "actionpack", "2.3.2"
+ c.checksum gem_repo4, "activerecord", "2.3.2"
+ c.checksum gem_repo4, "activeresource", "2.3.2"
+ c.checksum gem_repo4, "activesupport", "2.3.2"
+ c.checksum gem_repo4, "foo", "1.0"
+ c.checksum gem_repo4, "rails", "2.3.2"
+ c.checksum gem_repo4, "rake", rake_version
+ c.checksum gem_repo4, "weakling", "0.0.3"
+ end
- @lockfile = strip_lockfile(<<-L)
+ <<~L
GEM
- remote: #{file_uri_for(repo)}/
+ remote: https://gem.repo4/
specs:
actionmailer (2.3.2)
activesupport (= 2.3.2)
@@ -38,9 +33,9 @@ RSpec.describe "bundle lock" do
actionpack (= 2.3.2)
activerecord (= 2.3.2)
activeresource (= 2.3.2)
- rake (= 13.0.1)
- rake (13.0.1)
- with_license (1.0)
+ rake (= #{rake_version})
+ rake (#{rake_version})
+ weakling (0.0.3)
PLATFORMS
#{lockfile_platforms}
@@ -48,55 +43,247 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
foo
rails
- with_license
+ weakling
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ let(:outdated_lockfile) do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "actionmailer", "2.3.1"
+ c.checksum gem_repo4, "actionpack", "2.3.1"
+ c.checksum gem_repo4, "activerecord", "2.3.1"
+ c.checksum gem_repo4, "activeresource", "2.3.1"
+ c.checksum gem_repo4, "activesupport", "2.3.1"
+ c.checksum gem_repo4, "foo", "1.0"
+ c.checksum gem_repo4, "rails", "2.3.1"
+ c.checksum gem_repo4, "rake", rake_version
+ c.checksum gem_repo4, "weakling", "0.0.3"
+ end
+
+ <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ actionmailer (2.3.1)
+ activesupport (= 2.3.1)
+ actionpack (2.3.1)
+ activesupport (= 2.3.1)
+ activerecord (2.3.1)
+ activesupport (= 2.3.1)
+ activeresource (2.3.1)
+ activesupport (= 2.3.1)
+ activesupport (2.3.1)
+ foo (1.0)
+ rails (2.3.1)
+ actionmailer (= 2.3.1)
+ actionpack (= 2.3.1)
+ activerecord (= 2.3.1)
+ activeresource (= 2.3.1)
+ rake (= #{rake_version})
+ rake (#{rake_version})
+ weakling (0.0.3)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ rails
+ weakling
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
+ let(:gemfile_with_rails_weakling_and_foo_from_repo4) do
+ build_repo4 do
+ build_gem "rake", "10.0.1"
+ build_gem "rake", rake_version
+
+ %w[2.3.1 2.3.2].each do |version|
+ build_gem "rails", version do |s|
+ s.executables = "rails"
+ s.add_dependency "rake", version == "2.3.1" ? "10.0.1" : rake_version
+ s.add_dependency "actionpack", version
+ s.add_dependency "activerecord", version
+ s.add_dependency "actionmailer", version
+ s.add_dependency "activeresource", version
+ end
+ build_gem "actionpack", version do |s|
+ s.add_dependency "activesupport", version
+ end
+ build_gem "activerecord", version do |s|
+ s.add_dependency "activesupport", version
+ end
+ build_gem "actionmailer", version do |s|
+ s.add_dependency "activesupport", version
+ end
+ build_gem "activeresource", version do |s|
+ s.add_dependency "activesupport", version
+ end
+ build_gem "activesupport", version
+ end
+
+ build_gem "weakling", "0.0.3"
+
+ build_gem "foo"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "rails"
+ gem "weakling"
+ gem "foo"
+ G
+ end
+
it "prints a lockfile when there is no existing lockfile with --print" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --print"
- expect(out).to eq(@lockfile)
+ expect(out).to eq(expected_lockfile.chomp)
end
it "prints a lockfile when there is an existing lockfile with --print" do
- lockfile @lockfile
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile expected_lockfile
bundle "lock --print"
- expect(out).to eq(@lockfile)
+ expect(out).to eq(expected_lockfile.chomp)
+ end
+
+ it "prints a lockfile when there is an existing checksums lockfile with --print" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile expected_lockfile
+
+ bundle "lock --print"
+
+ expect(out).to eq(expected_lockfile.chomp)
end
it "writes a lockfile when there is no existing lockfile" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock"
- expect(read_lockfile).to eq(@lockfile)
+ expect(read_lockfile).to eq(expected_lockfile)
+ end
+
+ it "prints a lockfile without fetching new checksums if the existing lockfile had no checksums" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile expected_lockfile
+
+ bundle "lock --print"
+
+ expect(out).to eq(expected_lockfile.chomp)
+ end
+
+ it "touches the lockfile when there is an existing lockfile that does not need changes" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile expected_lockfile
+
+ expect do
+ bundle "lock"
+ end.to change { bundled_app_lock.mtime }
+ end
+
+ it "does not touch lockfile with --print" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile expected_lockfile
+
+ expect do
+ bundle "lock --print"
+ end.not_to change { bundled_app_lock.mtime }
end
it "writes a lockfile when there is an outdated lockfile using --update" do
- lockfile @lockfile.gsub("2.3.2", "2.3.1")
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile outdated_lockfile
bundle "lock --update"
- expect(read_lockfile).to eq(@lockfile)
+ expect(read_lockfile).to eq(expected_lockfile)
+ end
+
+ it "prints an updated lockfile when there is an outdated lockfile using --print --update" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile outdated_lockfile
+
+ bundle "lock --print --update"
+
+ expect(out).to eq(expected_lockfile.rstrip)
+ end
+
+ it "emits info messages to stderr when updating an outdated lockfile using --print --update" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile outdated_lockfile
+
+ bundle "lock --print --update"
+
+ expect(err).to eq(<<~STDERR.rstrip)
+ Fetching gem metadata from https://gem.repo4/...
+ Resolving dependencies...
+ STDERR
+ end
+
+ it "writes a lockfile when there is an outdated lockfile and bundle is frozen" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile outdated_lockfile
+
+ bundle "lock --update", env: { "BUNDLE_FROZEN" => "true" }
+
+ expect(read_lockfile).to eq(expected_lockfile)
end
it "does not fetch remote specs when using the --local option" do
- bundle "lock --update --local", :raise_on_error => false
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ bundle "lock --update --local", raise_on_error: false
+
+ expect(err).to match(/locally installed gems/)
+ end
+
+ it "does not fetch remote checksums with --local" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile expected_lockfile
+
+ bundle "lock --print --local"
- expect(err).to match(/sources listed in your Gemfile|installed locally/)
+ expect(out).to eq(expected_lockfile.chomp)
end
it "works with --gemfile flag" do
- create_file "CustomGemfile", <<-G
- source "#{file_uri_for(repo)}"
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ gemfile "CustomGemfile", <<-G
+ source "https://gem.repo4"
gem "foo"
G
- lockfile = strip_lockfile(<<-L)
+ bundle "lock --gemfile CustomGemfile"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "foo", "1.0"
+ end
+
+ lockfile = <<~L
GEM
- remote: #{file_uri_for(repo)}/
+ remote: https://gem.repo4/
specs:
foo (1.0)
@@ -105,61 +292,395 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
foo
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
- bundle "lock --gemfile CustomGemfile"
-
expect(out).to match(/Writing lockfile to.+CustomGemfile\.lock/)
expect(read_lockfile("CustomGemfile.lock")).to eq(lockfile)
expect { read_lockfile }.to raise_error(Errno::ENOENT)
end
it "writes to a custom location using --lockfile" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --lockfile=lock"
expect(out).to match(/Writing lockfile to.+lock/)
- expect(read_lockfile("lock")).to eq(@lockfile)
+ expect(read_lockfile("lock")).to eq(expected_lockfile)
expect { read_lockfile }.to raise_error(Errno::ENOENT)
end
+ it "updates a specific gem and write to a custom location" do
+ build_repo4 do
+ build_gem "foo", %w[1.0.2 1.0.3]
+ build_gem "warning", %w[1.4.0 1.5.0]
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "foo"
+ gem "warning"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4
+ specs:
+ foo (1.0.2)
+ warning (1.4.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ uri
+ warning
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update foo --lockfile=lock"
+
+ lockfile_content = read_lockfile("lock")
+ expect(lockfile_content).to include("foo (1.0.3)")
+ expect(lockfile_content).to include("warning (1.4.0)")
+ end
+
it "writes to custom location using --lockfile when a default lockfile is present" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "install"
bundle "lock --lockfile=lock"
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "actionmailer", "2.3.2"
+ c.checksum gem_repo4, "actionpack", "2.3.2"
+ c.checksum gem_repo4, "activerecord", "2.3.2"
+ c.checksum gem_repo4, "activeresource", "2.3.2"
+ c.checksum gem_repo4, "activesupport", "2.3.2"
+ c.checksum gem_repo4, "foo", "1.0"
+ c.checksum gem_repo4, "rails", "2.3.2"
+ c.checksum gem_repo4, "rake", rake_version
+ c.checksum gem_repo4, "weakling", "0.0.3"
+ end
+
+ lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ foo (1.0)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= #{rake_version})
+ rake (#{rake_version})
+ weakling (0.0.3)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ rails
+ weakling
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
expect(out).to match(/Writing lockfile to.+lock/)
- expect(read_lockfile("lock")).to eq(@lockfile)
+ expect(read_lockfile("lock")).to eq(lockfile)
end
it "update specific gems using --update" do
- lockfile @lockfile.gsub("2.3.2", "2.3.1").gsub("13.0.1", "10.0.1")
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "actionmailer", "2.3.1"
+ c.checksum gem_repo4, "actionpack", "2.3.1"
+ c.checksum gem_repo4, "activerecord", "2.3.1"
+ c.checksum gem_repo4, "activeresource", "2.3.1"
+ c.checksum gem_repo4, "activesupport", "2.3.1"
+ c.checksum gem_repo4, "foo", "1.0"
+ c.checksum gem_repo4, "rails", "2.3.1"
+ c.checksum gem_repo4, "rake", "10.0.1"
+ c.checksum gem_repo4, "weakling", "0.0.3"
+ end
+
+ lockfile_with_outdated_rails_and_rake = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ actionmailer (2.3.1)
+ activesupport (= 2.3.1)
+ actionpack (2.3.1)
+ activesupport (= 2.3.1)
+ activerecord (2.3.1)
+ activesupport (= 2.3.1)
+ activeresource (2.3.1)
+ activesupport (= 2.3.1)
+ activesupport (2.3.1)
+ foo (1.0)
+ rails (2.3.1)
+ actionmailer (= 2.3.1)
+ actionpack (= 2.3.1)
+ activerecord (= 2.3.1)
+ activeresource (= 2.3.1)
+ rake (= 10.0.1)
+ rake (10.0.1)
+ weakling (0.0.3)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ rails
+ weakling
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile lockfile_with_outdated_rails_and_rake
bundle "lock --update rails rake"
- expect(read_lockfile).to eq(@lockfile)
+ expect(read_lockfile).to eq(expected_lockfile)
+ end
+
+ it "updates specific gems using --update, even if that requires unlocking other top level gems" do
+ build_repo4 do
+ build_gem "prism", "0.15.1"
+ build_gem "prism", "0.24.0"
+
+ build_gem "ruby-lsp", "0.12.0" do |s|
+ s.add_dependency "prism", "< 0.24.0"
+ end
+
+ build_gem "ruby-lsp", "0.16.1" do |s|
+ s.add_dependency "prism", ">= 0.24.0"
+ end
+
+ build_gem "tapioca", "0.11.10" do |s|
+ s.add_dependency "prism", "< 0.24.0"
+ end
+
+ build_gem "tapioca", "0.13.1" do |s|
+ s.add_dependency "prism", ">= 0.24.0"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "tapioca"
+ gem "ruby-lsp"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4
+ specs:
+ prism (0.15.1)
+ ruby-lsp (0.12.0)
+ prism (< 0.24.0)
+ tapioca (0.11.10)
+ prism (< 0.24.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ruby-lsp
+ tapioca
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update tapioca --verbose"
+
+ expect(lockfile).to include("tapioca (0.13.1)")
+ end
+
+ it "updates specific gems using --update, even if that requires unlocking other top level gems, but only as few as possible" do
+ build_repo4 do
+ build_gem "prism", "0.15.1"
+ build_gem "prism", "0.24.0"
+
+ build_gem "ruby-lsp", "0.12.0" do |s|
+ s.add_dependency "prism", "< 0.24.0"
+ end
+
+ build_gem "ruby-lsp", "0.16.1" do |s|
+ s.add_dependency "prism", ">= 0.24.0"
+ end
+
+ build_gem "tapioca", "0.11.10" do |s|
+ s.add_dependency "prism", "< 0.24.0"
+ end
+
+ build_gem "tapioca", "0.13.1" do |s|
+ s.add_dependency "prism", ">= 0.24.0"
+ end
+
+ build_gem "other-prism-dependent", "1.0.0" do |s|
+ s.add_dependency "prism", ">= 0.15.1"
+ end
+
+ build_gem "other-prism-dependent", "1.1.0" do |s|
+ s.add_dependency "prism", ">= 0.15.1"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "tapioca"
+ gem "ruby-lsp"
+ gem "other-prism-dependent"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4
+ specs:
+ other-prism-dependent (1.0.0)
+ prism (>= 0.15.1)
+ prism (0.15.1)
+ ruby-lsp (0.12.0)
+ prism (< 0.24.0)
+ tapioca (0.11.10)
+ prism (< 0.24.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ruby-lsp
+ tapioca
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update tapioca"
+
+ expect(lockfile).to include("tapioca (0.13.1)")
+ expect(lockfile).to include("other-prism-dependent (1.0.0)")
+ end
+
+ it "preserves unknown checksum algorithms" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile expected_lockfile.gsub(/(sha256=[a-f0-9]+)$/, "constant=true,\\1,xyz=123")
+
+ previous_lockfile = read_lockfile
+
+ bundle "lock"
+
+ expect(read_lockfile).to eq(previous_lockfile)
+ end
+
+ it "does not unlock git sources when only uri shape changes" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ build_git("foo")
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ # Change uri format to end with "/" and reinstall
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}/"
+ G
+
+ expect(out).to include("using resolution from the lockfile")
+ expect(out).not_to include("re-resolving dependencies because the list of sources changed")
+ end
+
+ it "updates specific gems using --update using the locked revision of unrelated git gems for resolving" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ ref = build_git("foo").ref_for("HEAD")
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "rake"
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "deadbeef"
+ G
+
+ lockfile <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{ref}
+ branch: deadbeef
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ rake (10.0.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+ rake
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update rake --verbose"
+ expect(out).to match(/Writing lockfile to.+lock/)
+ expect(lockfile).to include("rake (#{rake_version})")
end
it "errors when updating a missing specific gems using --update" do
- lockfile @lockfile
+ gemfile_with_rails_weakling_and_foo_from_repo4
- bundle "lock --update blahblah", :raise_on_error => false
+ lockfile expected_lockfile
+
+ bundle "lock --update blahblah", raise_on_error: false
expect(err).to eq("Could not find gem 'blahblah'.")
- expect(read_lockfile).to eq(@lockfile)
+ expect(read_lockfile).to eq(expected_lockfile)
end
it "can lock without downloading gems" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "thin"
- gem "rack_middleware", :group => "test"
+ gem "myrack_middleware", :group => "test"
G
- bundle "config set without test"
- bundle "config set path .bundle"
- bundle "lock"
- expect(bundled_app(".bundle")).not_to exist
+ bundle_config "without test"
+ bundle_config "path vendor/bundle"
+ bundle "lock", verbose: true
+ expect(bundled_app("vendor/bundle")).not_to exist
end
# see update_spec for more coverage on same options. logic is shared so it's not necessary
@@ -176,13 +697,16 @@ RSpec.describe "bundle lock" do
build_gem "foo", %w[1.5.1] do |s|
s.add_dependency "bar", "~> 3.0"
end
- build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "foo", %w[2.0.0.pre] do |s|
+ s.add_dependency "bar"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 2.1.2.pre 3.0.0 3.1.0.pre 4.0.0.pre]
build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0]
end
# establish a lockfile set to 1.4.3
install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem 'foo', '1.4.3'
gem 'bar', '2.0.3'
gem 'qux', '1.0.0'
@@ -191,7 +715,7 @@ RSpec.describe "bundle lock" do
# remove 1.4.3 requirement and bar altogether
# to setup update specs below
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem 'foo'
gem 'qux'
G
@@ -202,52 +726,320 @@ RSpec.describe "bundle lock" do
it "single gem updates dependent gem to minor" do
bundle "lock --update foo --patch"
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.1 qux-1.0.0].sort)
+ expect(the_bundle.locked_specs).to eq(%w[foo-1.4.5 bar-2.1.1 qux-1.0.0].sort)
end
it "minor preferred with strict" do
bundle "lock --update --minor --strict"
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort)
+ expect(the_bundle.locked_specs).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort)
+ end
+
+ it "shows proper error when Gemfile changes forbid patch upgrades, and --patch --strict is given" do
+ # force next minor via Gemfile
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem 'foo', '1.5.0'
+ gem 'qux'
+ G
+
+ bundle "lock --update foo --patch --strict", raise_on_error: false
+
+ expect(err).to include(
+ "foo is locked to 1.4.3, while Gemfile is requesting foo (= 1.5.0). " \
+ "--strict --patch was specified, but there are no patch level upgrades from 1.4.3 satisfying foo (= 1.5.0), so version solving has failed"
+ )
+ end
+
+ context "pre" do
+ it "defaults to major" do
+ bundle "lock --update --pre"
+
+ expect(the_bundle.locked_specs).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
+ end
+
+ it "patch preferred" do
+ bundle "lock --update --patch --pre"
+
+ expect(the_bundle.locked_specs).to eq(%w[foo-1.4.5 bar-2.1.2.pre qux-1.0.1].sort)
+ end
+
+ it "minor preferred" do
+ bundle "lock --update --minor --pre"
+
+ expect(the_bundle.locked_specs).to eq(%w[foo-1.5.1 bar-3.1.0.pre qux-1.1.0].sort)
+ end
+
+ it "major preferred" do
+ bundle "lock --update --major --pre"
+
+ expect(the_bundle.locked_specs).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
+ end
+ end
+ end
+
+ context "conservative updates when minor update adds a new dependency" do
+ before do
+ build_repo4 do
+ build_gem "sequel", "5.71.0"
+ build_gem "sequel", "5.72.0" do |s|
+ s.add_dependency "bigdecimal", ">= 0"
+ end
+ build_gem "bigdecimal", %w[1.4.4 99.1.4]
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem 'sequel'
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sequel (5.71.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ sequel
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ end
+
+ it "adds the latest version of the new dependency" do
+ bundle "lock --minor --update sequel"
+
+ expect(the_bundle.locked_specs).to eq(%w[sequel-5.72.0 bigdecimal-99.1.4].sort)
+ end
+ end
+
+ it "updates the bundler version in the lockfile to the latest bundler version" do
+ build_repo4 do
+ build_gem "bundler", "55"
+ end
+
+ system_gems "bundler-55", gem_repo: gem_repo4
+
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "https://gem.repo4"
+ G
+ lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
+
+ bundle "lock --update --bundler --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(lockfile).to end_with("BUNDLED WITH\n 55\n")
+
+ build_repo4 do
+ build_gem "bundler", "99"
end
+
+ bundle "lock --update --bundler --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ expect(lockfile).to end_with("BUNDLED WITH\n 99\n")
+ end
+
+ it "supports adding new platforms when there's no previous lockfile" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ bundle "lock --add-platform java x86-mingw32 --verbose"
+ expect(out).to include("Resolving dependencies because there's no lockfile")
+
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
+ end
+
+ it "supports adding new platforms when a previous lockfile exists" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ bundle "lock"
+ bundle "lock --add-platform java x86-mingw32 --verbose"
+ expect(out).to include("Found changes from the lockfile, re-resolving dependencies because you are adding a new platform to your lockfile")
+
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
end
- it "supports adding new platforms" do
+ it "supports adding new platforms, when most specific locked platform is not the current platform, and current resolve is not compatible with the target platform" do
+ simulate_platform "arm64-darwin-23" do
+ build_repo4 do
+ build_gem "foo" do |s|
+ s.platform = "arm64-darwin"
+ end
+
+ build_gem "foo" do |s|
+ s.platform = "java"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "foo"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ foo (1.0-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --add-platform java"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ foo (1.0-arm64-darwin)
+ foo (1.0-java)
+
+ PLATFORMS
+ arm64-darwin
+ java
+
+ DEPENDENCIES
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "supports adding new platforms with force_ruby_platform = true" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-x86-64_linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ platform_specific
+ L
+
+ bundle_config "force_ruby_platform true"
bundle "lock --add-platform java x86-mingw32"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(local_platforms.unshift(java, mingw).uniq)
+ expect(the_bundle.locked_platforms).to contain_exactly(Gem::Platform::RUBY, "x86_64-linux", "java", "x86-mingw32")
end
it "supports adding the `ruby` platform" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --add-platform ruby"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(local_platforms.unshift("ruby").uniq)
+ expect(the_bundle.locked_platforms).to match_array(default_platform_list("ruby"))
end
- it "warns when adding an unknown platform" do
- bundle "lock --add-platform foobarbaz"
- expect(err).to include("The platform `foobarbaz` is unknown to RubyGems and adding it will likely lead to resolution errors")
+ it "fails when adding an unknown platform" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ bundle "lock --add-platform foobarbaz", raise_on_error: false
+ expect(err).to include("The platform `foobarbaz` is unknown to RubyGems and can't be added to the lockfile")
+ expect(last_command).to be_failure
end
it "allows removing platforms" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --add-platform java x86-mingw32"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(local_platforms.unshift(java, mingw).uniq)
+ expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
bundle "lock --remove-platform java"
- lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(local_platforms.unshift(mingw).uniq)
+ expect(the_bundle.locked_platforms).to match_array(default_platform_list("x86-mingw32"))
+ end
+
+ it "also cleans up redundant platform gems when removing platforms" do
+ build_repo4 do
+ build_gem "nokogiri", "1.12.0"
+ build_gem "nokogiri", "1.12.0" do |s|
+ s.platform = "x86_64-darwin"
+ end
+ end
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.12.0"
+ c.checksum gem_repo4, "nokogiri", "1.12.0", "x86_64-darwin"
+ end
+
+ simulate_platform "x86_64-darwin-22" do
+ install_gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.12.0)
+ nokogiri (1.12.0-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ checksums.delete("nokogiri", Gem::Platform::RUBY)
+
+ simulate_platform "x86_64-darwin-22" do
+ bundle "lock --remove-platform ruby"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.12.0-x86_64-darwin)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
it "errors when removing all platforms" do
- bundle "lock --remove-platform #{local_platforms.join(" ")}", :raise_on_error => false
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ bundle "lock --remove-platform #{local_platform}", raise_on_error: false
expect(err).to include("Removing all platforms from the bundle is not allowed")
end
@@ -256,7 +1048,7 @@ RSpec.describe "bundle lock" do
build_repo4 do
build_gem "ffi", "1.9.14"
build_gem "ffi", "1.9.14" do |s|
- s.platform = mingw
+ s.platform = "x86-mingw32"
end
build_gem "gssapi", "0.1"
@@ -282,17 +1074,24 @@ RSpec.describe "bundle lock" do
end
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem "mixlib-shellout"
gem "gssapi"
G
- simulate_platform(mingw) { bundle :lock }
+ simulate_platform("x86-mingw32") { bundle :lock }
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "ffi", "1.9.14", "x86-mingw32"
+ c.checksum gem_repo4, "gssapi", "1.2.0"
+ c.checksum gem_repo4, "mixlib-shellout", "2.2.6", "universal-mingw32"
+ c.checksum gem_repo4, "win32-process", "0.8.3"
+ end
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo4)}/
+ remote: https://gem.repo4/
specs:
ffi (1.9.14-x86-mingw32)
gssapi (1.2.0)
@@ -308,16 +1107,20 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
gssapi
mixlib-shellout
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
- simulate_platform(rb) { bundle :lock }
+ bundle_config "force_ruby_platform true"
+ bundle :lock
+
+ checksums.checksum gem_repo4, "ffi", "1.9.14"
+ checksums.checksum gem_repo4, "mixlib-shellout", "2.2.6"
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo4)}/
+ remote: https://gem.repo4/
specs:
ffi (1.9.14)
ffi (1.9.14-x86-mingw32)
@@ -336,33 +1139,1739 @@ RSpec.describe "bundle lock" do
DEPENDENCIES
gssapi
mixlib-shellout
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "doesn't crash when an update candidate doesn't have any matching platform" do
+ build_repo4 do
+ build_gem "libv8", "8.4.255.0"
+ build_gem "libv8", "8.4.255.0" do |s|
+ s.platform = "x86_64-darwin-19"
+ end
+
+ build_gem "libv8", "15.0.71.48.1beta2" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "libv8"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ libv8 (8.4.255.0)
+ libv8 (8.4.255.0-x86_64-darwin-19)
+
+ PLATFORMS
+ ruby
+ x86_64-darwin-19
+
+ DEPENDENCIES
+ libv8
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
+
+ simulate_platform("x86_64-darwin-19") { bundle "lock --update" }
+
+ expect(out).to match(/Writing lockfile to.+Gemfile\.lock/)
end
- context "when an update is available" do
- let(:repo) { gem_repo2 }
+ it "adds all more specific candidates when they all have the same dependencies" do
+ build_repo4 do
+ build_gem "libv8", "8.4.255.0" do |s|
+ s.platform = "x86_64-darwin-19"
+ end
+
+ build_gem "libv8", "8.4.255.0" do |s|
+ s.platform = "x86_64-darwin-20"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "libv8"
+ G
+
+ simulate_platform("x86_64-darwin-19") { bundle "lock" }
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "libv8", "8.4.255.0", "x86_64-darwin-19"
+ c.checksum gem_repo4, "libv8", "8.4.255.0", "x86_64-darwin-20"
+ end
+
+ expect(lockfile).to eq <<~G
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ libv8 (8.4.255.0-x86_64-darwin-19)
+ libv8 (8.4.255.0-x86_64-darwin-20)
+
+ PLATFORMS
+ x86_64-darwin-19
+ x86_64-darwin-20
+
+ DEPENDENCIES
+ libv8
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "respects the previous lockfile if it had a matching less specific platform already locked, and installs the best variant for each platform" do
+ build_repo4 do
+ build_gem "libv8", "8.4.255.0" do |s|
+ s.platform = "x86_64-darwin-19"
+ end
+
+ build_gem "libv8", "8.4.255.0" do |s|
+ s.platform = "x86_64-darwin-20"
+ end
+ end
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "libv8", "8.4.255.0", "x86_64-darwin-19"
+ c.checksum gem_repo4, "libv8", "8.4.255.0", "x86_64-darwin-20"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "libv8"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ libv8 (8.4.255.0-x86_64-darwin-19)
+ libv8 (8.4.255.0-x86_64-darwin-20)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ libv8
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ previous_lockfile = lockfile
+
+ %w[x86_64-darwin-19 x86_64-darwin-20].each do |platform|
+ simulate_platform(platform) do
+ bundle "lock"
+ expect(lockfile).to eq(previous_lockfile)
+
+ bundle "install"
+ expect(the_bundle).to include_gem("libv8 8.4.255.0 #{platform}")
+ end
+ end
+ end
+
+ it "does not conflict on ruby requirements when adding new platforms" do
+ build_repo4 do
+ build_gem "raygun-apm", "1.0.78" do |s|
+ s.platform = "x86_64-linux"
+ s.required_ruby_version = "< #{next_ruby_minor}.dev"
+ end
+
+ build_gem "raygun-apm", "1.0.78" do |s|
+ s.platform = "universal-darwin"
+ s.required_ruby_version = "< #{next_ruby_minor}.dev"
+ end
+
+ build_gem "raygun-apm", "1.0.78" do |s|
+ s.platform = "x64-mingw-ucrt"
+ s.required_ruby_version = "< #{next_ruby_minor}.dev"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "raygun-apm"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ 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"
+ end
+
+ it "adds platform specific gems as necessary, even when adding the current platform" do
+ build_repo4 do
+ build_gem "nokogiri", "1.16.0"
+
+ build_gem "nokogiri", "1.16.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.16.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --add-platform x86_64-linux"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.16.0)
+ nokogiri (1.16.0-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "refuses to add platforms incompatible with the lockfile" do
+ build_repo4 do
+ build_gem "sorbet-static", "0.5.11989" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet-static"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet-static (0.5.11989-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ sorbet-static
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --add-platform ruby", raise_on_error: false
+ end
+
+ nice_error = <<~E.strip
+ Could not find gems matching 'sorbet-static' valid for all resolution platforms (x86_64-linux, ruby) in rubygems repository https://gem.repo4/ or installed locally.
+
+ The source contains the following gems matching 'sorbet-static':
+ * sorbet-static-0.5.11989-x86_64-linux
+ E
+ expect(err).to include(nice_error)
+ end
+
+ it "respects lower bound ruby requirements" do
+ build_repo4 do
+ build_gem "our_private_gem", "0.1.0" do |s|
+ s.required_ruby_version = ">= #{Gem.ruby_version}"
+ end
+ end
+ gemfile <<-G
+ source "https://localgemserver.test"
+
+ gem "our_private_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://localgemserver.test/
+ specs:
+ our_private_gem (0.1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ our_private_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ end
+
+ context "when an update is available" do
before do
- lockfile(@lockfile)
- build_repo2 do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ build_repo4 do
build_gem "foo", "2.0"
end
+
+ lockfile(expected_lockfile)
end
it "does not implicitly update" do
bundle "lock"
- expect(read_lockfile).to eq(@lockfile)
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "actionmailer", "2.3.2"
+ c.checksum gem_repo4, "actionpack", "2.3.2"
+ c.checksum gem_repo4, "activerecord", "2.3.2"
+ c.checksum gem_repo4, "activeresource", "2.3.2"
+ c.checksum gem_repo4, "activesupport", "2.3.2"
+ c.checksum gem_repo4, "foo", "1.0"
+ c.checksum gem_repo4, "rails", "2.3.2"
+ c.checksum gem_repo4, "rake", rake_version
+ c.checksum gem_repo4, "weakling", "0.0.3"
+ end
+
+ expected_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ foo (1.0)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= #{rake_version})
+ rake (#{rake_version})
+ weakling (0.0.3)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ rails
+ weakling
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ expect(read_lockfile).to eq(expected_lockfile)
end
it "accounts for changes in the gemfile" do
gemfile gemfile.gsub('"foo"', '"foo", "2.0"')
bundle "lock"
- expect(read_lockfile).to eq(@lockfile.sub("foo (1.0)", "foo (2.0)").sub(/foo$/, "foo (= 2.0)"))
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "actionmailer", "2.3.2"
+ c.checksum gem_repo4, "actionpack", "2.3.2"
+ c.checksum gem_repo4, "activerecord", "2.3.2"
+ c.checksum gem_repo4, "activeresource", "2.3.2"
+ c.checksum gem_repo4, "activesupport", "2.3.2"
+ c.checksum gem_repo4, "foo", "2.0"
+ c.checksum gem_repo4, "rails", "2.3.2"
+ c.checksum gem_repo4, "rake", rake_version
+ c.checksum gem_repo4, "weakling", "0.0.3"
+ end
+
+ expected_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ actionmailer (2.3.2)
+ activesupport (= 2.3.2)
+ actionpack (2.3.2)
+ activesupport (= 2.3.2)
+ activerecord (2.3.2)
+ activesupport (= 2.3.2)
+ activeresource (2.3.2)
+ activesupport (= 2.3.2)
+ activesupport (2.3.2)
+ foo (2.0)
+ rails (2.3.2)
+ actionmailer (= 2.3.2)
+ actionpack (= 2.3.2)
+ activerecord (= 2.3.2)
+ activeresource (= 2.3.2)
+ rake (= #{rake_version})
+ rake (#{rake_version})
+ weakling (0.0.3)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo (= 2.0)
+ rails
+ weakling
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ expect(read_lockfile).to eq(expected_lockfile)
+ end
+ end
+
+ context "when a system gem has incorrect dependencies, different from the lockfile" do
+ before do
+ build_repo4 do
+ build_gem "debug", "1.6.3" do |s|
+ s.add_dependency "irb", ">= 1.3.6"
+ end
+
+ build_gem "irb", "1.5.0"
+ end
+
+ system_gems "irb-1.5.0", gem_repo: gem_repo4
+ system_gems "debug-1.6.3", gem_repo: gem_repo4
+
+ # simulate gemspec with wrong empty dependencies
+ debug_gemspec_path = system_gem_path("specifications/debug-1.6.3.gemspec")
+ debug_gemspec = Gem::Specification.load(debug_gemspec_path.to_s)
+ debug_gemspec.dependencies.clear
+ File.write(debug_gemspec_path, debug_gemspec.to_ruby)
+ end
+
+ it "respects the existing lockfile, even when reresolving" do
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "debug"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "debug", "1.6.3"
+ c.checksum gem_repo4, "irb", "1.5.0"
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ debug (1.6.3)
+ irb (>= 1.3.6)
+ irb (1.5.0)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ debug
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "arm64-darwin-22" do
+ bundle "lock"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ debug (1.6.3)
+ irb (>= 1.3.6)
+ irb (1.5.0)
+
+ PLATFORMS
+ arm64-darwin-22
+ x86_64-linux
+
+ DEPENDENCIES
+ debug
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when a system gem has incorrect dependencies, different from remote gems" do
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0.0" do |s|
+ s.add_dependency "bar"
+ end
+
+ build_gem "bar", "1.0.0"
+ end
+
+ system_gems "foo-1.0.0", gem_repo: gem_repo4, path: default_bundle_path
+
+ # simulate gemspec with wrong empty dependencies
+ foo_gemspec_path = default_bundle_path("specifications/foo-1.0.0.gemspec")
+ foo_gemspec = Gem::Specification.load(foo_gemspec_path.to_s)
+ foo_gemspec.dependencies.clear
+ File.write(foo_gemspec_path, foo_gemspec.to_ruby)
+ end
+
+ it "generates a lockfile using remote dependencies, and prints a warning" do
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "foo"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "foo", "1.0.0"
+ c.checksum gem_repo4, "bar", "1.0.0"
+ end
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --verbose"
+ end
+
+ expect(err).to eq("Local specification for foo-1.0.0 has different dependencies than the remote gem, ignoring it")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ bar (1.0.0)
+ foo (1.0.0)
+ bar
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ foo
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "properly shows resolution errors including OR requirements" do
+ build_repo4 do
+ build_gem "activeadmin", "2.13.1" do |s|
+ s.add_dependency "railties", ">= 6.1", "< 7.1"
+ end
+ build_gem "actionpack", "6.1.4"
+ build_gem "actionpack", "7.0.3.1"
+ build_gem "actionpack", "7.0.4"
+ build_gem "railties", "6.1.4" do |s|
+ s.add_dependency "actionpack", "6.1.4"
+ end
+ build_gem "rails", "7.0.3.1" do |s|
+ s.add_dependency "railties", "7.0.3.1"
+ end
+ build_gem "rails", "7.0.4" do |s|
+ s.add_dependency "railties", "7.0.4"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "rails", ">= 7.0.3.1"
+ gem "activeadmin", "2.13.1"
+ G
+
+ bundle "lock", raise_on_error: false
+
+ expect(err).to eq <<~ERR.strip
+ Could not find compatible versions
+
+ Because rails >= 7.0.4 depends on railties = 7.0.4
+ and rails < 7.0.4 depends on railties = 7.0.3.1,
+ railties = 7.0.3.1 OR = 7.0.4 is required.
+ So, because railties = 7.0.3.1 OR = 7.0.4 could not be found in rubygems repository https://gem.repo4/ or installed locally,
+ version solving has failed.
+ ERR
+ end
+
+ it "is able to display some explanation on crazy irresolvable cases" do
+ build_repo4 do
+ build_gem "activeadmin", "2.13.1" do |s|
+ s.add_dependency "ransack", "= 3.1.0"
+ end
+
+ # Activemodel is missing as a dependency in lockfile
+ build_gem "ransack", "3.1.0" do |s|
+ s.add_dependency "activemodel", ">= 6.0.4"
+ s.add_dependency "activesupport", ">= 6.0.4"
+ end
+
+ %w[6.0.4 7.0.2.3 7.0.3.1 7.0.4].each do |version|
+ build_gem "activesupport", version
+
+ # Activemodel is only available on 6.0.4
+ if version == "6.0.4"
+ build_gem "activemodel", version do |s|
+ s.add_dependency "activesupport", version
+ end
+ end
+
+ build_gem "rails", version do |s|
+ # Depednencies of Rails 7.0.2.3 are in reverse order
+ if version == "7.0.2.3"
+ s.add_dependency "activesupport", version
+ s.add_dependency "activemodel", version
+ else
+ s.add_dependency "activemodel", version
+ s.add_dependency "activesupport", version
+ end
+ end
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "rails", ">= 7.0.2.3"
+ gem "activeadmin", "= 2.13.1"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ activeadmin (2.13.1)
+ ransack (= 3.1.0)
+ ransack (3.1.0)
+ activemodel (>= 6.0.4)
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ activeadmin (= 2.13.1)
+ ransack (= 3.1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ expected_error = <<~ERR.strip
+ Could not find compatible versions
+
+ Because rails >= 7.0.4 depends on activemodel = 7.0.4
+ and rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1,
+ rails >= 7.0.3.1 requires activemodel = 7.0.3.1 OR = 7.0.4.
+ (1) So, because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3
+ and every version of activemodel depends on activesupport = 6.0.4,
+ rails >= 7.0.2.3 requires activesupport = 6.0.4.
+
+ Because rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3
+ and rails >= 7.0.3.1, < 7.0.4 depends on activesupport = 7.0.3.1,
+ rails >= 7.0.2.3, < 7.0.4 requires activesupport = 7.0.2.3 OR = 7.0.3.1.
+ And because rails >= 7.0.4 depends on activesupport = 7.0.4,
+ rails >= 7.0.2.3 requires activesupport = 7.0.2.3 OR = 7.0.3.1 OR = 7.0.4.
+ And because rails >= 7.0.2.3 requires activesupport = 6.0.4 (1),
+ rails >= 7.0.2.3 cannot be used.
+ So, because Gemfile depends on rails >= 7.0.2.3,
+ version solving has failed.
+ ERR
+
+ bundle "lock", raise_on_error: false
+ expect(err).to eq(expected_error)
+
+ lockfile lockfile.gsub(/PLATFORMS\n #{local_platform}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
+
+ bundle "lock", raise_on_error: false
+ expect(err).to eq(expected_error)
+ end
+
+ it "does not accidentally resolves to prereleases" do
+ build_repo4 do
+ build_gem "autoproj", "2.0.3" do |s|
+ s.add_dependency "autobuild", ">= 1.10.0.a"
+ s.add_dependency "tty-prompt"
+ end
+
+ build_gem "tty-prompt", "0.6.0"
+ build_gem "tty-prompt", "0.7.0"
+
+ build_gem "autobuild", "1.10.0.b3"
+ build_gem "autobuild", "1.10.1" do |s|
+ s.add_dependency "tty-prompt", "~> 0.6.0"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "autoproj", ">= 2.0.0"
+ G
+
+ bundle "lock"
+ expect(lockfile).to_not include("autobuild (1.10.0.b3)")
+ expect(lockfile).to include("autobuild (1.10.1)")
+ end
+
+ # Newer rails depends on Bundler, while ancient Rails does not. Bundler tries
+ # a first resolution pass that does not consider pre-releases. However, when
+ # using a pre-release Bundler (like the .dev version), that results in that
+ # pre-release being ignored and resolving to a version that does not depend on
+ # Bundler at all. We should avoid that and still consider .dev Bundler.
+ #
+ it "does not ignore prereleases with there's only one candidate" do
+ build_repo4 do
+ build_gem "rails", "7.4.0.2" do |s|
+ s.add_dependency "bundler", ">= 1.15.0"
+ end
+
+ build_gem "rails", "2.3.18"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "rails"
+ G
+
+ bundle "lock"
+ expect(lockfile).to_not include("rails (2.3.18)")
+ expect(lockfile).to include("rails (7.4.0.2)")
+ end
+
+ it "deals with platform specific incompatibilities" do
+ build_repo4 do
+ build_gem "activerecord", "6.0.6"
+ build_gem "activerecord-jdbc-adapter", "60.4" do |s|
+ s.platform = "java"
+ s.add_dependency "activerecord", "~> 6.0.0"
+ end
+ build_gem "activerecord-jdbc-adapter", "61.0" do |s|
+ s.platform = "java"
+ s.add_dependency "activerecord", "~> 6.1.0"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "activerecord", "6.0.6"
+ gem "activerecord-jdbc-adapter", "61.0"
+ G
+
+ simulate_platform "universal-java-19" do
+ bundle "lock", raise_on_error: false
+ end
+
+ expect(err).to include("Could not find compatible versions")
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+ end
+
+ it "adds checksums to an existing lockfile, when re-resolving is necessary" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.2"
+ build_gem "nokogiri", "1.14.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ # lockfile has a typo (nogokiri) in the dependencies section, so Bundler
+ # sees dependencies have changed, and re-resolves
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nogokiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --add-checksums"
+ end
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.2"
+ c.checksum gem_repo4, "nokogiri", "1.14.2", "x86_64-linux"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "adds checksums to an existing lockfile, when no re-resolve is necessary" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.2"
+ build_gem "nokogiri", "1.14.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --add-checksums"
+ end
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.2"
+ c.checksum gem_repo4, "nokogiri", "1.14.2", "x86_64-linux"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "adds checksums when source is not specified" do
+ system_gems(%w[myrack-1.0.0], path: default_bundle_path)
+
+ gemfile <<-G
+ gem "myrack"
+ G
+
+ lockfile <<~L
+ GEM
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ myrack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --add-checksums"
+ end
+
+ # myrack is coming from gem_repo1
+ # but it's simulated to install in the system gems path
+ checksums = checksums_section do |c|
+ c.checksum gem_repo1, "myrack", "1.0.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "adds checksums to an existing lockfile, when gems are already installed" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.2"
+ build_gem "nokogiri", "1.14.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "install"
+
+ bundle "lock --add-checksums"
+ end
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.2"
+ c.checksum gem_repo4, "nokogiri", "1.14.2", "x86_64-linux"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "generates checksums by default" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.2"
+ build_gem "nokogiri", "1.14.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ simulate_platform "x86_64-linux" do
+ install_gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+ end
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.2"
+ c.checksum gem_repo4, "nokogiri", "1.14.2", "x86_64-linux"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "disables checksums if configured to do so" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.2"
+ build_gem "nokogiri", "1.14.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ bundle_config "lockfile_checksums false"
+
+ simulate_platform "x86_64-linux" do
+ install_gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "add checksums for gems installed on disk" do
+ build_repo4 do
+ build_gem "warning", "18.0.0"
+ end
+
+ bundle_config "lockfile_checksums false"
+
+ simulate_platform "x86_64-linux" do
+ install_gemfile(<<-G, artifice: "endpoint")
+ source "https://gem.repo4"
+
+ gem "warning"
+ G
+
+ bundle "config --delete lockfile_checksums"
+ bundle("lock --add-checksums", artifice: "endpoint")
+ end
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo4, "warning", "18.0.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ warning (18.0.0)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ warning
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "doesn't add checksum for gems not installed on disk" do
+ lockfile(<<~L)
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ warning (18.0.0)
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ warning
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile(<<~G)
+ source "https://gem.repo4"
+
+ gem "warning"
+ G
+
+ build_repo4 do
+ build_gem "warning", "18.0.0"
+ end
+
+ FileUtils.rm_rf("#{gem_repo4}/gems")
+
+ bundle("lock --add-checksums", artifice: "endpoint")
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "warning", "18.0.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ warning (18.0.0)
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ warning
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ context "when re-resolving to include prereleases" do
+ before do
+ build_repo4 do
+ build_gem "tzinfo-data", "1.2022.7"
+ build_gem "rails", "7.1.0.alpha" do |s|
+ s.add_dependency "activesupport"
+ end
+ build_gem "activesupport", "7.1.0.alpha"
+ end
+ end
+
+ it "does not end up including gems scoped to other platforms in the lockfile" do
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "rails"
+ gem "tzinfo-data", platform: :windows
+ G
+
+ simulate_platform "x86_64-darwin-22" do
+ bundle "lock"
+ end
+
+ expect(lockfile).not_to include("tzinfo-data (1.2022.7)")
+ end
+ end
+
+ context "when resolving platform specific gems as indirect dependencies on truffleruby", :truffleruby_only do
+ before do
+ build_lib "foo", path: bundled_app do |s|
+ s.add_dependency "nokogiri"
+ end
+
+ build_repo4 do
+ build_gem "nokogiri", "1.14.2"
+ build_gem "nokogiri", "1.14.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gemspec
+ G
+ end
+
+ it "locks both ruby and platform specific specs" do
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo4, "nokogiri", "1.14.2"
+ c.checksum gem_repo4, "nokogiri", "1.14.2", "x86_64-linux"
+ end
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock"
+ end
+
+ expect(lockfile).to eq <<~L
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+ nokogiri
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ context "and a lockfile with platform specific gems only already exists" do
+ before do
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo4, "nokogiri", "1.14.2", "x86_64-linux"
+ end
+
+ lockfile <<~L
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+ nokogiri
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "keeps platform specific gems" do
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo4, "nokogiri", "1.14.2"
+ c.checksum gem_repo4, "nokogiri", "1.14.2", "x86_64-linux"
+ end
+
+ simulate_platform "x86_64-linux" do
+ bundle "install"
+ end
+
+ expect(lockfile).to eq <<~L
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+ nokogiri
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+ end
+
+ context "when adding a new gem that requires unlocking other transitive deps" do
+ before do
+ build_repo4 do
+ build_gem "govuk_app_config", "0.1.0"
+
+ build_gem "govuk_app_config", "4.13.0" do |s|
+ s.add_dependency "railties", ">= 5.0"
+ end
+
+ %w[7.0.4.1 7.0.4.3].each do |v|
+ build_gem "railties", v do |s|
+ s.add_dependency "actionpack", v
+ s.add_dependency "activesupport", v
+ end
+
+ build_gem "activesupport", v
+ build_gem "actionpack", v
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "govuk_app_config"
+ gem "activesupport", "7.0.4.3"
+ G
+
+ # Simulate out of sync lockfile because top level dependency on
+ # activesuport has just been added to the Gemfile, and locked to a higher
+ # version
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ actionpack (7.0.4.1)
+ activesupport (7.0.4.1)
+ govuk_app_config (4.13.0)
+ railties (>= 5.0)
+ railties (7.0.4.1)
+ actionpack (= 7.0.4.1)
+ activesupport (= 7.0.4.1)
+
+ PLATFORMS
+ arm64-darwin-22
+
+ DEPENDENCIES
+ govuk_app_config
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "does not downgrade top level dependencies" do
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "actionpack", "7.0.4.3"
+ c.no_checksum "activesupport", "7.0.4.3"
+ c.no_checksum "govuk_app_config", "4.13.0"
+ c.no_checksum "railties", "7.0.4.3"
+ end
+
+ simulate_platform "arm64-darwin-22" do
+ bundle "lock"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ actionpack (7.0.4.3)
+ activesupport (7.0.4.3)
+ govuk_app_config (4.13.0)
+ railties (>= 5.0)
+ railties (7.0.4.3)
+ actionpack (= 7.0.4.3)
+ activesupport (= 7.0.4.3)
+
+ PLATFORMS
+ arm64-darwin-22
+
+ DEPENDENCIES
+ activesupport (= 7.0.4.3)
+ govuk_app_config
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when lockfile has incorrectly indented platforms" do
+ before do
+ build_repo4 do
+ build_gem "ffi", "1.1.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem "ffi", "1.1.0" do |s|
+ s.platform = "arm64-darwin"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "ffi"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ ffi (1.1.0-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin
+
+ DEPENDENCIES
+ ffi
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "does not remove any gems" do
+ simulate_platform "x86_64-linux" do
+ bundle "lock --update"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ ffi (1.1.0-arm64-darwin)
+ ffi (1.1.0-x86_64-linux)
+
+ PLATFORMS
+ arm64-darwin
+ x86_64-linux
+
+ DEPENDENCIES
+ ffi
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ describe "--normalize-platforms on linux" do
+ let(:normalized_lockfile) do
+ <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0)
+ irb (1.0.0-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ before do
+ build_repo4 do
+ build_gem "irb", "1.0.0"
+
+ build_gem "irb", "1.0.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "irb"
+ G
+ end
+
+ context "when already normalized" do
+ before do
+ lockfile normalized_lockfile
+ end
+
+ it "is a noop" do
+ simulate_platform "x86_64-linux" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+
+ context "when not already normalized" do
+ before do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "normalizes the list of platforms and native gems in the lockfile" do
+ simulate_platform "x86_64-linux" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+ end
+
+ describe "--normalize-platforms on darwin" do
+ let(:normalized_lockfile) do
+ <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0)
+ irb (1.0.0-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin
+ ruby
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ before do
+ build_repo4 do
+ build_gem "irb", "1.0.0"
+
+ build_gem "irb", "1.0.0" do |s|
+ s.platform = "arm64-darwin"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "irb"
+ G
+ end
+
+ context "when already normalized" do
+ before do
+ lockfile normalized_lockfile
+ end
+
+ it "is a noop" do
+ simulate_platform "arm64-darwin-23" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+
+ context "when having only ruby" do
+ before do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "normalizes the list of platforms and native gems in the lockfile" do
+ simulate_platform "arm64-darwin-23" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+
+ context "when having only the current platform with version" do
+ before do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin-23
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "normalizes the list of platforms by removing version" do
+ simulate_platform "arm64-darwin-23" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+
+ context "when having other platforms with version" do
+ before do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin-22
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "normalizes the list of platforms by removing version" do
+ simulate_platform "arm64-darwin-23" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+ end
+
+ describe "--normalize-platforms with gems without generic variant" do
+ let(:original_lockfile) do
+ <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet-static (1.0-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ sorbet-static
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ before do
+ build_repo4 do
+ build_gem "sorbet-static" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet-static"
+ G
+
+ lockfile original_lockfile
+ end
+
+ it "removes invalid platforms" do
+ simulate_platform "x86_64-linux" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(original_lockfile.gsub(/^ ruby\n/m, ""))
end
end
end
diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb
index 7dca5f5188..65fbad05aa 100644
--- a/spec/bundler/commands/newgem_spec.rb
+++ b/spec/bundler/commands/newgem_spec.rb
@@ -6,97 +6,85 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/README.md")).to exist
expect(bundled_app("#{gem_name}/Gemfile")).to exist
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}/lib/#{gem_name}.rb")).to exist
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}/version.rb")).to exist
+
+ expect(ignore_paths).to include("bin/")
+ expect(ignore_paths).to include("Gemfile")
end
def bundle_exec_rubocop
prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec"))
- rubocop_version = RUBY_VERSION > "2.4" ? "0.85.1" : "0.80.1"
- gems = ["minitest", "rake", "rake-compiler", "rspec", "rubocop -v #{rubocop_version}", "test-unit"]
- 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 "exec rubocop --config .rubocop.yml", :dir => bundled_app(gem_name)
+ bundle "config set path #{rubocop_gem_path}", dir: bundled_app(gem_name)
+ bundle "exec rubocop --debug --config .rubocop.yml", 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" }
-
- 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
+ def bundle_exec_standardrb
+ prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec"))
+ bundle "config set path #{standard_gem_path}", dir: bundled_app(gem_name)
+ bundle "exec standardrb --debug", dir: bundled_app(gem_name)
end
- after do
- FileUtils.rm(ENV["GIT_CONFIG"]) if File.exist?(ENV["GIT_CONFIG"])
- ENV["GIT_CONFIG"] = @git_config_location
+ def ignore_paths
+ generated = bundled_app("#{gem_name}/#{gem_name}.gemspec").read
+ matched = generated.match(/^\s+f\.start_with\?\(\*%w\[(?<ignored>.*)\]\)$/)
+ matched[:ignored]&.split(" ")
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
+ def installed_go?
+ sys_exec("go version", raise_on_error: true)
+ true
+ rescue StandardError
+ false
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
+ let(:generated_gemspec) { Bundler.load_gemspec_uncached(bundled_app(gem_name).join("#{gem_name}.gemspec")) }
- 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
+ let(:gem_name) { "mygem" }
+
+ before do
+ git("config --global user.name 'Bundler User'")
+ git("config --global user.email user@example.com")
+ git("config --global github.user bundleuser")
+
+ bundle_config_global "gem.mit false"
+ bundle_config_global "gem.test false"
+ bundle_config_global "gem.coc false"
+ bundle_config_global "gem.linter false"
+ bundle_config_global "gem.ci false"
+ bundle_config_global "gem.changelog false"
+ bundle_config_global "gem.bundle false"
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" 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" 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" 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" do
+ skip "path with spaces needs special handling on Windows" if Gem.win_platform?
+
+ bundle "gem #{gem_name}", dir: bundled_app("path with spaces")
+ expect(bundled_app("path with spaces/#{gem_name}/.git")).to exist
end
end
end
@@ -123,19 +111,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
+ 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
@@ -148,22 +141,60 @@ 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
+
+ shared_examples_for "--changelog flag" do
+ before do
+ bundle "gem #{gem_name} --changelog"
+ end
+ it "generates a gem skeleton with a CHANGELOG" do
+ gem_skeleton_assertions
+ expect(bundled_app("#{gem_name}/CHANGELOG.md")).to exist
+ end
+ end
+
+ shared_examples_for "--no-changelog flag" do
+ before do
+ bundle "gem #{gem_name} --no-changelog"
+ end
+ it "generates a gem skeleton without a CHANGELOG" do
+ gem_skeleton_assertions
+ expect(bundled_app("#{gem_name}/CHANGELOG.md")).to_not exist
+ end
+ end
+
+ shared_examples_for "--bundle flag" do
+ before do
+ bundle "gem #{gem_name} --bundle"
+ end
+ it "generates a gem skeleton with bundle install" do
+ gem_skeleton_assertions
+ expect(out).to include("Running bundle install in the new gem directory.")
end
end
- shared_examples_for "--rubocop flag" do
+ shared_examples_for "--no-bundle flag" do
before do
- bundle "gem #{gem_name} --rubocop"
+ bundle "gem #{gem_name} --no-bundle"
+ end
+ it "generates a gem skeleton without bundle install" do
+ gem_skeleton_assertions
+ expect(out).to_not include("Running bundle install in the new gem directory.")
+ end
+ end
+
+ shared_examples_for "--linter=rubocop flag" do
+ before do
+ bundle "gem #{gem_name} --linter=rubocop"
end
it "generates a gem skeleton with rubocop" do
gem_skeleton_assertions
- expect(bundled_app("test-gem/Rakefile")).to read_as(
+ expect(bundled_app("#{gem_name}/Rakefile")).to read_as(
include("# frozen_string_literal: true").
and(include('require "rubocop/rake_task"').
and(include("RuboCop::RakeTask.new").
@@ -177,25 +208,62 @@ RSpec.describe "bundle gem" do
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
+ expect(rubocop_dep).not_to be_specific
+ expect(rubocop_dep.requirement).to eq(Gem::Requirement.new([">= 0"]))
end
it "generates a default .rubocop.yml" do
expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
end
+
+ it "includes .rubocop.yml into ignore list" do
+ expect(ignore_paths).to include(".rubocop.yml")
+ end
+ end
+
+ 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("#{gem_name}/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_specific
+ expect(standard_dep.requirement).to eq(Gem::Requirement.new([">= 0"]))
+ end
+
+ it "generates a default .standard.yml" do
+ expect(bundled_app("#{gem_name}/.standard.yml")).to exist
+ end
+
+ it "includes .standard.yml into ignore list" do
+ expect(ignore_paths).to include(".standard.yml")
+ end
end
- shared_examples_for "--no-rubocop flag" do
+ shared_examples_for "--no-linter flag" do
define_negated_matcher :exclude, :include
before do
- bundle "gem #{gem_name} --no-rubocop"
+ bundle "gem #{gem_name} --no-linter"
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"))
+ expect(bundled_app("#{gem_name}/Rakefile")).to read_as(exclude("rubocop"))
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec")).to read_as(exclude("rubocop"))
end
it "does not include rubocop in generated Gemfile" do
@@ -207,50 +275,109 @@ 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 "does not add .rubocop.yml into ignore list" do
+ expect(ignore_paths).not_to include(".rubocop.yml")
+ end
+
+ it "doesn't generate a default .standard.yml" do
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ end
+
+ it "does not add .standard.yml into ignore list" do
+ expect(ignore_paths).not_to include(".standard.yml")
+ end
end
- it "has no rubocop offenses when using --rubocop flag", :readline do
+ it "has no rubocop offenses when using --linter=rubocop flag" 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=c and --linter=rubocop flag" 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=c --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=c, --test=minitest, and --linter=rubocop flag" 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=c --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=c, --test=rspec, and --linter=rubocop flag" 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=c --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=c, --test=test-unit, and --linter=rubocop flag" 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=c --test=test-unit --linter=rubocop"
bundle_exec_rubocop
- expect(err).to be_empty
+ expect(last_command).to be_success
+ end
+
+ it "has no standard offenses when using --linter=standard flag" 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(last_command).to be_success
+ end
+
+ it "has no rubocop offenses when using --ext=rust and --linter=rubocop flag" 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=rust --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
+ it "has no rubocop offenses when using --ext=rust, --test=minitest, and --linter=rubocop flag" 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=rust --test=minitest --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
+ it "has no rubocop offenses when using --ext=rust, --test=rspec, and --linter=rubocop flag" 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=rust --test=rspec --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
+ end
+
+ it "has no rubocop offenses when using --ext=rust, --test=test-unit, and --linter=rubocop flag" 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=rust --test=test-unit --linter=rubocop"
+ bundle_exec_rubocop
+ expect(last_command).to be_success
end
shared_examples_for "CI config is absent" do
it "does not create any CI files" do
expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
end
@@ -259,14 +386,20 @@ RSpec.describe "bundle gem" do
shared_examples_for "test framework is absent" do
it "does not create any test framework files" do
expect(bundled_app("#{gem_name}/.rspec")).to_not exist
- expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb")).to_not exist
+ expect(bundled_app("#{gem_name}/spec/#{gem_name}_spec.rb")).to_not exist
expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to_not exist
- expect(bundled_app("#{gem_name}/test/#{require_path}.rb")).to_not exist
+ expect(bundled_app("#{gem_name}/test/#{gem_name}.rb")).to_not exist
expect(bundled_app("#{gem_name}/test/test_helper.rb")).to_not exist
end
+
+ it "does not add any test framework files into ignore list" do
+ expect(ignore_paths).not_to include("test/")
+ expect(ignore_paths).not_to include(".rspec")
+ expect(ignore_paths).not_to include("spec/")
+ end
end
- context "README.md", :readline do
+ context "README.md" do
context "git config github.user present" do
before do
bundle "gem #{gem_name}"
@@ -280,7 +413,7 @@ RSpec.describe "bundle gem" do
context "git config github.user is absent" do
before do
- sys_exec("git config --unset github.user")
+ git("config --global --unset github.user")
bundle "gem #{gem_name}"
end
@@ -289,20 +422,33 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/README.md").read).not_to include("github.com/bundleuser")
end
end
+
+ describe "test task name on readme" do
+ shared_examples_for "test task name on readme" do |framework, task_name|
+ before do
+ bundle "gem #{gem_name} --test=#{framework}"
+ end
+
+ it "renders with correct name" do
+ expect(bundled_app("#{gem_name}/README.md").read).to include("Then, run `rake #{task_name}` to run the tests.")
+ end
+ end
+
+ it_behaves_like "test task name on readme", "test-unit", "test"
+ it_behaves_like "test task name on readme", "minitest", "test"
+ it_behaves_like "test task name on readme", "rspec", "spec"
+ end
end
- it "creates a new git repository", :readline do
+ it "creates a new git repository" do
bundle "gem #{gem_name}"
expect(bundled_app("#{gem_name}/.git")).to exist
end
- context "when git is not available", :readline do
+ context "when git is not available" do
# This spec cannot have `git` available in the test env
before do
- load_paths = [lib_dir, spec_dir]
- load_path_str = "-I#{load_paths.join(File::PATH_SEPARATOR)}"
-
- sys_exec "#{Gem.ruby} #{load_path_str} #{bindir.join("bundle")} gem #{gem_name}", :env => { "PATH" => "" }
+ bundle "gem #{gem_name}", env: { "PATH" => "" }
end
it "creates the gem without the need for git" do
@@ -316,26 +462,32 @@ RSpec.describe "bundle gem" do
it "doesn't create a .gitignore file" do
expect(bundled_app("#{gem_name}/.gitignore")).to_not exist
end
+
+ it "does not add .gitignore into ignore list" do
+ expect(ignore_paths).not_to include(".gitignore")
+ end
end
- it "generates a valid gemspec", :readline, :ruby_repo do
+ it "generates a valid gemspec" do
bundle "gem newgem --bin"
prepare_gemspec(bundled_app("newgem", "newgem.gemspec"))
- gems = ["rake-13.0.1"]
- path = Bundler.feature_flag.default_install_uses_path? ? local_gem_path(:base => bundled_app("newgem")) : system_gem_path
- system_gems gems, :path => path
- bundle "exec rake build", :dir => bundled_app("newgem")
+ build_repo2 do
+ build_dummy_irb "9.9.9"
+ end
+ gems = ["rake-#{rake_version}", "irb-9.9.9"]
+ system_gems gems, path: system_gem_path, gem_repo: gem_repo2
+ bundle "exec rake build", dir: bundled_app("newgem")
- expect(last_command.stdboth).not_to include("ERROR")
+ expect(stdboth).not_to include("ERROR")
end
- context "gem naming with relative paths", :readline do
+ context "gem naming with relative paths" do
it "resolves ." do
create_temporary_dir("tmp")
- bundle "gem .", :dir => bundled_app("tmp")
+ bundle "gem .", dir: bundled_app("tmp")
expect(bundled_app("tmp/lib/tmp.rb")).to exist
end
@@ -343,7 +495,7 @@ RSpec.describe "bundle gem" do
it "resolves .." do
create_temporary_dir("temp/empty_dir")
- bundle "gem ..", :dir => bundled_app("temp/empty_dir")
+ bundle "gem ..", dir: bundled_app("temp/empty_dir")
expect(bundled_app("temp/lib/temp.rb")).to exist
end
@@ -351,7 +503,7 @@ RSpec.describe "bundle gem" do
it "resolves relative directory" do
create_temporary_dir("tmp/empty/tmp")
- bundle "gem ../../empty", :dir => bundled_app("tmp/empty/tmp")
+ bundle "gem ../../empty", dir: bundled_app("tmp/empty/tmp")
expect(bundled_app("tmp/empty/lib/empty.rb")).to exist
end
@@ -361,552 +513,1170 @@ RSpec.describe "bundle gem" do
end
end
- shared_examples_for "generating a gem" do
+ 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
- bundle "gem #{gem_name}"
+ gem_skeleton_assertions
+ end
- expect(bundled_app("#{gem_name}/#{gem_name}.gemspec")).to exist
- expect(bundled_app("#{gem_name}/Gemfile")).to exist
- 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}/.gitignore")).to exist
+ 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
- expect(bundled_app("#{gem_name}/bin/setup")).to exist
- expect(bundled_app("#{gem_name}/bin/console")).to exist
+ shared_examples_for "github_username configuration" do
+ context "with github_username setting set to some value" do
+ before do
+ bundle_config_global "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
+ bundle_config_global "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
+
+ it "generates a gem skeleton" do
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec")).to exist
+ expect(bundled_app("#{gem_name}/Gemfile")).to exist
+ expect(bundled_app("#{gem_name}/Rakefile")).to exist
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}.rb")).to exist
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}/version.rb")).to exist
+ expect(bundled_app("#{gem_name}/sig/#{gem_name}.rbs")).to exist
+ expect(bundled_app("#{gem_name}/.gitignore")).to exist
+
+ expect(bundled_app("#{gem_name}/bin/setup")).to exist
+ expect(bundled_app("#{gem_name}/bin/console")).to exist
+
+ unless Gem.win_platform?
expect(bundled_app("#{gem_name}/bin/setup")).to be_executable
expect(bundled_app("#{gem_name}/bin/console")).to be_executable
end
- it "starts with version 0.1.0" do
+ expect(bundled_app("#{gem_name}/bin/setup").read).to start_with("#!")
+ expect(bundled_app("#{gem_name}/bin/console").read).to start_with("#!")
+ end
+
+ it "includes bin/ into ignore list" do
+ bundle "gem #{gem_name}"
+
+ expect(ignore_paths).to include("bin/")
+ end
+
+ it "includes Gemfile into ignore list" do
+ bundle "gem #{gem_name}"
+
+ expect(ignore_paths).to include("Gemfile")
+ end
+
+ it "includes .gitignore into ignore list" do
+ bundle "gem #{gem_name}"
+
+ expect(ignore_paths).to include(".gitignore")
+ end
+
+ it "starts with version 0.1.0" do
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}/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/#{gem_name}.rbs").read).to match(/VERSION: String/)
+ end
+
+ context "git config user.{name,email} is set" do
+ before do
bundle "gem #{gem_name}"
+ end
- expect(bundled_app("#{gem_name}/lib/#{require_path}/version.rb").read).to match(/VERSION = "0.1.0"/)
+ it "sets gemspec author to git user.name if available" do
+ expect(generated_gemspec.authors.first).to eq("Bundler User")
end
- context "git config user.{name,email} is set" do
- before do
- bundle "gem #{gem_name}"
+ 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
+ git("config --global --unset user.name")
+ git("config --global --unset user.email")
+ bundle "gem #{gem_name}"
+ end
+
+ 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(/example\.com/)
+ end
+
+ it "includes a commented-out rubygems_mfa_required metadata hint" do
+ bundle "gem #{gem_name}"
+
+ gemspec_contents = bundled_app("#{gem_name}/#{gem_name}.gemspec").read
+
+ expect(gemspec_contents).to include('# spec.metadata["rubygems_mfa_required"] = "true"')
+ expect(gemspec_contents).to include("https://guides.rubygems.org/mfa-requirement-opt-in/")
+ end
+
+ it "sets a minimum ruby version" do
+ bundle "gem #{gem_name}"
+
+ expect(generated_gemspec.required_ruby_version.to_s).to start_with(">=")
+ end
+
+ it "does not include the gemspec file in files" do
+ bundle "gem #{gem_name}"
+
+ bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
+
+ expect(bundler_gemspec.files).not_to include("#{gem_name}.gemspec")
+ end
+
+ it "does not include the Gemfile file in files" do
+ bundle "gem #{gem_name}"
+
+ bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
+
+ expect(bundler_gemspec.files).not_to include("Gemfile")
+ end
+
+ it "runs rake without problems" do
+ bundle "gem #{gem_name}"
+
+ system_gems ["rake-#{rake_version}"]
+
+ rakefile = <<~RAKEFILE
+ task :default do
+ puts 'SUCCESS'
end
+ RAKEFILE
+ File.open(bundled_app("#{gem_name}/Rakefile"), "w") do |file|
+ file.puts rakefile
+ end
+
+ sys_exec("rake", dir: bundled_app(gem_name))
+ expect(out).to include("SUCCESS")
+ end
- it_should_behave_like "git config is present"
+ context "--exe parameter set" do
+ before do
+ bundle "gem #{gem_name} --exe"
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)
- bundle "gem #{gem_name}"
+ it "builds exe skeleton" do
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
+ unless Gem.win_platform?
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to be_executable
end
+ end
+ end
- it_should_behave_like "git config is absent"
+ context "--bin parameter set" do
+ before do
+ bundle "gem #{gem_name} --bin"
end
- it "sets gemspec metadata['allowed_push_host']" do
+ it "builds exe skeleton" do
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
+ end
+ end
+
+ context "no --test parameter" do
+ before do
bundle "gem #{gem_name}"
+ end
+
+ it_behaves_like "test framework is absent"
+ end
+
+ context "--test parameter set to rspec" do
+ before do
+ bundle "gem #{gem_name} --test=rspec"
+ end
- expect(generated_gemspec.metadata["allowed_push_host"]).
- to match(/mygemserver\.com/)
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/.rspec")).to exist
+ expect(bundled_app("#{gem_name}/spec/#{gem_name}_spec.rb")).to exist
+ expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
end
- it "sets a minimum ruby version" do
+ it "includes .rspec and spec/ into ignore list" do
+ expect(ignore_paths).to include(".rspec")
+ expect(ignore_paths).to include("spec/")
+ end
+
+ it "depends on a non-specific version of rspec 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
+ rspec_dep = builder.dependencies.find {|d| d.name == "rspec" }
+ expect(rspec_dep).not_to be_specific
+ expect(rspec_dep.requirement).to eq(Gem::Requirement.new([">= 0"]))
+ end
+ end
+
+ context "init_gems_rb setting to true" do
+ before do
+ bundle_config "init_gems_rb true"
bundle "gem #{gem_name}"
+ end
- bundler_gemspec = Bundler::GemHelper.new(gemspec_dir).gemspec
+ it "generates gems.rb instead of Gemfile" do
+ expect(bundled_app("#{gem_name}/gems.rb")).to exist
+ expect(bundled_app("#{gem_name}/Gemfile")).to_not exist
+ end
- expect(bundler_gemspec.required_ruby_version).to eq(generated_gemspec.required_ruby_version)
+ it "includes gems.rb and gems.locked into ignore list" do
+ expect(ignore_paths).to include("gems.rb")
+ expect(ignore_paths).to include("gems.locked")
+ expect(ignore_paths).not_to include("Gemfile")
end
+ end
- it "requires the version file" do
+ context "init_gems_rb setting to false" do
+ before do
+ bundle_config "init_gems_rb false"
bundle "gem #{gem_name}"
+ end
- expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(%r{require "#{require_path}/version"})
+ it "generates Gemfile instead of gems.rb" do
+ expect(bundled_app("#{gem_name}/gems.rb")).to_not exist
+ expect(bundled_app("#{gem_name}/Gemfile")).to exist
end
- it "creates a base error class" do
+ it "includes Gemfile into ignore list" do
+ expect(ignore_paths).to include("Gemfile")
+ expect(ignore_paths).not_to include("gems.rb")
+ expect(ignore_paths).not_to include("gems.locked")
+ end
+ end
+
+ context "gem.test setting set to rspec" do
+ before do
+ bundle_config "gem.test rspec"
bundle "gem #{gem_name}"
+ end
- expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(/class Error < StandardError; end$/)
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/.rspec")).to exist
+ expect(bundled_app("#{gem_name}/spec/#{gem_name}_spec.rb")).to exist
+ expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
+ end
+
+ it "includes .rspec and spec/ into ignore list" do
+ expect(ignore_paths).to include(".rspec")
+ expect(ignore_paths).to include("spec/")
+ end
+ end
+
+ context "gem.test setting set to rspec and --test is set to minitest" do
+ before do
+ bundle_config "gem.test rspec"
+ bundle "gem #{gem_name} --test=minitest"
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/test/test_#{gem_name}.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
+ end
+
+ it "includes test/ into ignore list" do
+ expect(ignore_paths).to include("test/")
+ end
+ end
+
+ context "--test parameter set to minitest" do
+ before do
+ bundle "gem #{gem_name} --test=minitest"
+ end
+
+ it "depends on a non-specific version of minitest" 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
+ minitest_dep = builder.dependencies.find {|d| d.name == "minitest" }
+ expect(minitest_dep).not_to be_specific
+ expect(minitest_dep.requirement).to eq(Gem::Requirement.new([">= 0"]))
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/test/test_#{gem_name}.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
+ end
+
+ it "includes test/ into ignore list" do
+ expect(ignore_paths).to include("test/")
+ end
+
+ it "creates a default rake task to run the test suite" do
+ rakefile = <<~RAKEFILE
+ # frozen_string_literal: true
+
+ require "bundler/gem_tasks"
+ require "minitest/test_task"
+
+ Minitest::TestTask.create
+
+ task default: :test
+ RAKEFILE
+
+ expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
+ end
- it "runs rake without problems" do
+ context "gem.test setting set to minitest" do
+ before do
+ bundle_config "gem.test minitest"
bundle "gem #{gem_name}"
+ end
+
+ it "creates a default rake task to run the test suite" do
+ rakefile = <<~RAKEFILE
+ # frozen_string_literal: true
+
+ require "bundler/gem_tasks"
+ require "minitest/test_task"
+
+ Minitest::TestTask.create
+
+ task default: :test
+ RAKEFILE
+
+ expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
+ end
+ end
+
+ context "--test parameter set to test-unit" do
+ before do
+ bundle "gem #{gem_name} --test=test-unit"
+ end
+
+ it "depends on a non-specific version of test-unit" 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
+ test_unit_dep = builder.dependencies.find {|d| d.name == "test-unit" }
+ expect(test_unit_dep).not_to be_specific
+ expect(test_unit_dep.requirement).to eq(Gem::Requirement.new([">= 0"]))
+ end
+
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/test/#{gem_name}_test.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
+ end
+
+ it "includes test/ into ignore list" do
+ expect(ignore_paths).to include("test/")
+ end
+
+ it "creates a default rake task to run the test suite" do
+ rakefile = <<~RAKEFILE
+ # frozen_string_literal: true
- system_gems ["rake-13.0.1"]
+ require "bundler/gem_tasks"
+ require "rake/testtask"
- rakefile = strip_whitespace <<-RAKEFILE
- task :default do
- puts 'SUCCESS'
+ Rake::TestTask.new(:test) do |t|
+ t.libs << "test"
+ t.libs << "lib"
+ t.test_files = FileList["test/**/*_test.rb"]
end
+
+ task default: :test
RAKEFILE
- File.open(bundled_app("#{gem_name}/Rakefile"), "w") do |file|
- file.puts rakefile
- end
- sys_exec(rake, :dir => bundled_app(gem_name))
- expect(out).to include("SUCCESS")
+ expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
+ end
- context "--exe parameter set" do
- before do
- bundle "gem #{gem_name} --exe"
- end
+ context "--test parameter set to an invalid value" do
+ before do
+ bundle "gem #{gem_name} --test=foo", raise_on_error: false
+ end
- it "builds exe skeleton" do
- expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
- end
+ it "fails loudly" do
+ expect(last_command).to be_failure
+ expect(err).to match(/Expected '--test' to be one of .*; got foo/)
+ end
+ end
- it "requires the main file" do
- expect(bundled_app("#{gem_name}/exe/#{gem_name}").read).to match(/require "#{require_path}"/)
- end
+ context "gem.test set to rspec and --test with no arguments" do
+ before do
+ bundle_config "gem.test rspec"
+ bundle "gem #{gem_name} --test"
end
- context "--bin parameter set" do
- before do
- bundle "gem #{gem_name} --bin"
- end
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/.rspec")).to exist
+ expect(bundled_app("#{gem_name}/spec/#{gem_name}_spec.rb")).to exist
+ expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
+ end
- it "builds exe skeleton" do
- expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
- end
+ it "includes .rspec and spec/ into ignore list" do
+ expect(ignore_paths).to include(".rspec")
+ expect(ignore_paths).to include("spec/")
+ end
- it "requires the main file" do
- expect(bundled_app("#{gem_name}/exe/#{gem_name}").read).to match(/require "#{require_path}"/)
- end
+ it "hints that --test is already configured" do
+ expect(out).to match("rspec is already configured, ignoring --test flag.")
end
+ end
- context "no --test parameter" do
- before do
- bundle "gem #{gem_name}"
+ context "gem.test setting set to false and --test with no arguments", :readline do
+ before do
+ bundle_config "gem.test false"
+ bundle "gem #{gem_name} --test" do |input, _, _|
+ input.puts
end
+ end
- it_behaves_like "test framework is absent"
+ it "asks to generate test files" do
+ expect(out).to match("Do you want to generate tests with your gem?")
end
- context "--test parameter set to rspec" do
- before do
- bundle "gem #{gem_name} --test=rspec"
- 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
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/.rspec")).to exist
- expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb")).to exist
- expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
- end
+ it_behaves_like "test framework is absent"
+ end
- it "depends on a specific version of rspec 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
- rspec_dep = builder.dependencies.find {|d| d.name == "rspec" }
- expect(rspec_dep).to be_specific
+ context "gem.test setting not set and --test with no arguments", :readline do
+ before do
+ bundle_config_global "BUNDLE_GEM__TEST" => nil
+ bundle "gem #{gem_name} --test" do |input, _, _|
+ input.puts
end
+ end
- it "requires the main file" do
- expect(bundled_app("#{gem_name}/spec/spec_helper.rb").read).to include(%(require "#{require_path}"))
- end
+ it "asks to generate test files" do
+ expect(out).to match("Do you want to generate tests with your gem?")
+ end
- it "creates a default test which fails" do
- expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb").read).to include("expect(false).to eq(true)")
- 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.test`."
+ expect(out).to match(hint)
end
- context "gem.test setting set to rspec" do
- before do
- bundle "config set gem.test rspec"
- bundle "gem #{gem_name}"
- end
+ it_behaves_like "test framework is absent"
+ end
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/.rspec")).to exist
- expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb")).to exist
- expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
- end
+ context "gem.test setting set to a test framework and --no-test" do
+ before do
+ bundle_config "gem.test rspec"
+ bundle "gem #{gem_name} --no-test"
end
- context "gem.test setting set to rspec and --test is set to minitest" do
- before do
- bundle "config set gem.test rspec"
- bundle "gem #{gem_name} --test=minitest"
- end
+ it_behaves_like "test framework is absent"
+ end
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb")).to exist
- expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
- end
+ context "--ci with no argument" do
+ before do
+ bundle "gem #{gem_name}"
end
- context "--test parameter set to minitest" do
- before do
- bundle "gem #{gem_name} --test=minitest"
- end
+ it "does not generate any CI config" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
+ end
- it "depends on a specific version of minitest" 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
- minitest_dep = builder.dependencies.find {|d| d.name == "minitest" }
- expect(minitest_dep).to be_specific
- end
+ it "does not add any CI config files into ignore list" do
+ expect(ignore_paths).not_to include(".github/")
+ expect(ignore_paths).not_to include(".gitlab-ci.yml")
+ expect(ignore_paths).not_to include(".circleci/")
+ end
+ end
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb")).to exist
- expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
- end
+ context "--ci set to github" do
+ before do
+ bundle "gem #{gem_name} --ci=github"
+ end
- it "requires the main file" do
- expect(bundled_app("#{gem_name}/test/test_helper.rb").read).to include(%(require "#{require_path}"))
- end
+ it "generates a GitHub Actions config file" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
+ end
- it "requires 'test_helper'" do
- expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb").read).to include(%(require "test_helper"))
- end
+ it "includes .github/ into ignore list" do
+ expect(ignore_paths).to include(".github/")
+ end
+ end
- it "creates a default test which fails" do
- expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb").read).to include("assert false")
- end
+ context "--ci set to gitlab" do
+ before do
+ bundle "gem #{gem_name} --ci=gitlab"
end
- context "gem.test setting set to minitest" do
- before do
- bundle "config set gem.test minitest"
- bundle "gem #{gem_name}"
- end
+ it "generates a GitLab CI config file" do
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
+ end
- it "creates a default rake task to run the test suite" do
- rakefile = strip_whitespace <<-RAKEFILE
- # frozen_string_literal: true
+ it "includes .gitlab-ci.yml into ignore list" do
+ expect(ignore_paths).to include(".gitlab-ci.yml")
+ end
+ end
- require "bundler/gem_tasks"
- require "rake/testtask"
+ context "--ci set to circle" do
+ before do
+ bundle "gem #{gem_name} --ci=circle"
+ end
- Rake::TestTask.new(:test) do |t|
- t.libs << "test"
- t.libs << "lib"
- t.test_files = FileList["test/**/*_test.rb"]
- end
+ it "generates a CircleCI config file" do
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
+ end
- task default: :test
- RAKEFILE
+ it "includes .circleci/ into ignore list" do
+ expect(ignore_paths).to include(".circleci/")
+ end
+ end
- expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
- end
+ context "--ci set to an invalid value" do
+ before do
+ bundle "gem #{gem_name} --ci=foo", raise_on_error: false
end
- context "--test parameter set to test-unit" do
- before do
- bundle "gem #{gem_name} --test=test-unit"
- end
+ it "fails loudly" do
+ expect(last_command).to be_failure
+ expect(err).to match(/Expected '--ci' to be one of .*; got foo/)
+ end
+ end
- it "depends on a specific version of test-unit" 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
- test_unit_dep = builder.dependencies.find {|d| d.name == "test-unit" }
- expect(test_unit_dep).to be_specific
- end
+ context "gem.ci setting set to none" do
+ it "doesn't generate any CI config" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
+ end
+ end
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb")).to exist
- expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
- end
+ context "gem.ci setting set to github" do
+ it "generates a GitHub Actions config file" do
+ bundle_config "gem.ci github"
+ bundle "gem #{gem_name}"
- it "requires the main file" do
- expect(bundled_app("#{gem_name}/test/test_helper.rb").read).to include(%(require "#{require_path}"))
- end
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
+ end
+ end
- it "requires 'test_helper'" do
- expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb").read).to include(%(require "test_helper"))
- end
+ context "gem.ci setting set to gitlab" do
+ it "generates a GitLab CI config file" do
+ bundle_config "gem.ci gitlab"
+ bundle "gem #{gem_name}"
- it "creates a default test which fails" do
- expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb").read).to include("assert_equal(\"expected\", \"actual\")")
- end
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
end
+ end
- context "gem.test setting set to test-unit" do
- before do
- bundle "config set gem.test test-unit"
- bundle "gem #{gem_name}"
- end
+ context "gem.ci setting set to circle" do
+ it "generates a CircleCI config file" do
+ bundle_config "gem.ci circle"
+ bundle "gem #{gem_name}"
- it "creates a default rake task to run the test suite" do
- rakefile = strip_whitespace <<-RAKEFILE
- # frozen_string_literal: true
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
+ end
+ end
- require "bundler/gem_tasks"
- require "rake/testtask"
+ context "gem.ci set to github and --ci with no arguments" do
+ before do
+ bundle_config "gem.ci github"
+ bundle "gem #{gem_name} --ci"
+ end
- Rake::TestTask.new(:test) do |t|
- t.libs << "test"
- t.libs << "lib"
- t.test_files = FileList["test/**/*_test.rb"]
- end
+ it "generates a GitHub Actions config file" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
+ end
- task default: :test
- RAKEFILE
+ it "hints that --ci is already configured" do
+ expect(out).to match("github is already configured, ignoring --ci flag.")
+ end
+ end
- expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
+ context "gem.ci setting set to false and --ci with no arguments", :readline do
+ before do
+ bundle_config "gem.ci false"
+ bundle "gem #{gem_name} --ci" do |input, _, _|
+ input.puts "github"
end
end
- context "gem.test set to rspec and --test with no arguments", :hint_text do
- before do
- bundle "config set gem.test rspec"
- bundle "gem #{gem_name} --test"
- end
+ it "asks to setup CI" do
+ expect(out).to match("Do you want to set up continuous integration for your gem?")
+ end
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/.rspec")).to exist
- expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb")).to exist
- expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
- 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
- it "hints that --test is already configured" do
- expect(out).to match("rspec is already configured, ignoring --test flag.")
+ context "gem.ci setting not set and --ci with no arguments", :readline do
+ before do
+ bundle_config_global "BUNDLE_GEM__CI" => nil
+ bundle "gem #{gem_name} --ci" do |input, _, _|
+ input.puts "github"
end
end
- context "gem.test setting set to false and --test with no arguments", :hint_text do
- before do
- bundle "config set gem.test false"
- bundle "gem #{gem_name} --test"
- end
+ it "asks to setup CI" do
+ expect(out).to match("Do you want to set up continuous integration for your gem?")
+ end
- it "asks to generate test files" do
- expect(out).to match("Do you want to generate tests with 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.ci`."
+ expect(out).to match(hint)
+ end
+ 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
+ context "gem.ci setting set to a CI service and --no-ci" do
+ before do
+ bundle_config "gem.ci github"
+ bundle "gem #{gem_name} --no-ci"
+ end
- it_behaves_like "test framework is absent"
+ it "does not generate any CI config" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
end
+ end
- context "gem.test setting not set and --test with no arguments", :hint_text do
- before do
- bundle "gem #{gem_name} --test"
- end
+ context "--linter with no argument" do
+ before do
+ bundle "gem #{gem_name}"
+ end
- it "asks to generate test files" do
- expect(out).to match("Do you want to generate tests with your gem?")
- end
+ it "does not generate any linter config" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ 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.test`."
- expect(out).to match(hint)
- end
+ it "does not add any linter config files into ignore list" do
+ expect(ignore_paths).not_to include(".rubocop.yml")
+ expect(ignore_paths).not_to include(".standard.yml")
+ end
+ end
- it_behaves_like "test framework is absent"
+ context "--linter set to rubocop" do
+ before do
+ bundle "gem #{gem_name} --linter=rubocop"
end
- context "--ci with no argument" do
- it "does not generate any CI config" do
- bundle "gem #{gem_name}"
+ it "generates a RuboCop config" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ end
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
- end
+ it "includes .rubocop.yml into ignore list" do
+ expect(ignore_paths).to include(".rubocop.yml")
+ expect(ignore_paths).not_to include(".standard.yml")
end
+ end
- context "--ci set to github" do
- it "generates a GitHub Actions config file" do
- bundle "gem #{gem_name} --ci=github"
+ context "--linter set to standard" do
+ before do
+ bundle "gem #{gem_name} --linter=standard"
+ end
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
- end
+ it "generates a Standard config" do
+ expect(bundled_app("#{gem_name}/.standard.yml")).to exist
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
end
- context "--ci set to gitlab" do
- it "generates a GitLab CI config file" do
- bundle "gem #{gem_name} --ci=gitlab"
+ it "includes .standard.yml into ignore list" do
+ expect(ignore_paths).to include(".standard.yml")
+ expect(ignore_paths).not_to include(".rubocop.yml")
+ end
+ end
- expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
- end
+ context "--linter set to an invalid value" do
+ before do
+ bundle "gem #{gem_name} --linter=foo", raise_on_error: false
end
- context "--ci set to circle" do
- it "generates a CircleCI config file" do
- bundle "gem #{gem_name} --ci=circle"
+ it "fails loudly" do
+ expect(last_command).to be_failure
+ expect(err).to match(/Expected '--linter' to be one of .*; got foo/)
+ end
+ end
- expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
- end
+ context "gem.linter setting set to none" do
+ before do
+ bundle "gem #{gem_name}"
end
- context "--ci set to travis" do
- it "generates a Travis CI config file" do
- bundle "gem #{gem_name} --ci=travis"
+ it "doesn't generate any linter config" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ end
- expect(bundled_app("#{gem_name}/.travis.yml")).to exist
- end
+ it "does not add any linter config files into ignore list" do
+ expect(ignore_paths).not_to include(".rubocop.yml")
+ expect(ignore_paths).not_to include(".standard.yml")
end
+ end
- context "gem.ci setting set to none" do
- it "doesn't generate any CI config" do
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
- end
+ context "gem.linter setting set to rubocop" do
+ before do
+ bundle_config "gem.linter rubocop"
+ bundle "gem #{gem_name}"
end
- context "gem.ci setting set to github" do
- it "generates a GitHub Actions config file" do
- bundle "config set gem.ci github"
- bundle "gem #{gem_name}"
+ it "generates a RuboCop config file" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ end
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
- end
+ it "includes .rubocop.yml into ignore list" do
+ expect(ignore_paths).to include(".rubocop.yml")
end
+ end
- context "gem.ci setting set to travis" do
- it "generates a Travis CI config file" do
- bundle "config set gem.ci travis"
- bundle "gem #{gem_name}"
+ context "gem.linter setting set to standard" do
+ before do
+ bundle_config "gem.linter standard"
+ bundle "gem #{gem_name}"
+ end
- expect(bundled_app("#{gem_name}/.travis.yml")).to exist
- end
+ it "generates a Standard config file" do
+ expect(bundled_app("#{gem_name}/.standard.yml")).to exist
end
- context "gem.ci setting set to gitlab" do
- it "generates a GitLab CI config file" do
- bundle "config set gem.ci gitlab"
- bundle "gem #{gem_name}"
+ it "includes .standard.yml into ignore list" do
+ expect(ignore_paths).to include(".standard.yml")
+ end
+ end
- expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
+ context "gem.linter set to rubocop and --linter with no arguments" do
+ before do
+ bundle_config "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 "includes .rubocop.yml into ignore list" do
+ expect(ignore_paths).to include(".rubocop.yml")
+ 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", :readline do
+ before do
+ bundle_config "gem.linter false"
+ bundle "gem #{gem_name} --linter" do |input, _, _|
+ input.puts "rubocop"
end
end
- context "gem.ci setting set to circle" do
- it "generates a CircleCI config file" do
- bundle "config set gem.ci circle"
- bundle "gem #{gem_name}"
+ 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
- expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
+ context "gem.linter setting not set and --linter with no arguments", :readline do
+ before do
+ bundle_config_global "BUNDLE_GEM__LINTER" => nil
+ bundle "gem #{gem_name} --linter" do |input, _, _|
+ input.puts "rubocop"
end
end
- context "gem.ci set to github and --ci with no arguments", :hint_text do
+ 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 "gem.linter setting set to a linter and --no-linter" do
+ before do
+ bundle_config "gem.linter rubocop"
+ bundle "gem #{gem_name} --no-linter"
+ end
+
+ it "does not generate any linter config" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ end
+
+ it "does not add any linter config files into ignore list" do
+ expect(ignore_paths).not_to include(".rubocop.yml")
+ expect(ignore_paths).not_to include(".standard.yml")
+ 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"
+ gemspec_path = File.join(bundled_app, gem_name, "#{gem_name}.gemspec")
+ expect(output).to include("echo \"#{gemspec_path}\"")
+ end
+ end
+
+ shared_examples_for "paths that depend on gem name" do
+ it "generates entrypoint, version file and signatures file at the proper path, with the proper content" do
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/lib/#{require_path}.rb")).to exist
+ expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(%r{require_relative "#{require_relative_path}/version"})
+ expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(/class Error < StandardError; end$/)
+
+ expect(bundled_app("#{gem_name}/lib/#{require_path}/version.rb")).to exist
+ expect(bundled_app("#{gem_name}/sig/#{require_path}.rbs")).to exist
+ end
+
+ context "--exe parameter set" do
before do
- bundle "config set gem.ci github"
- bundle "gem #{gem_name} --ci"
+ bundle "gem #{gem_name} --exe"
end
- it "generates a GitHub Actions config file" do
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
+ it "builds an exe file that requires the proper entrypoint" do
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}").read).to match(/require "#{require_path}"/)
+ end
+ end
+
+ context "--bin parameter set" do
+ before do
+ bundle "gem #{gem_name} --bin"
end
- it "hints that --ci is already configured" do
- expect(out).to match("github is already configured, ignoring --ci flag.")
+ it "builds an exe file that requires the proper entrypoint" do
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}").read).to match(/require "#{require_path}"/)
end
end
- context "gem.ci setting set to false and --ci with no arguments", :hint_text do
+ context "--test parameter set to rspec" do
before do
- bundle "config set gem.ci false"
- bundle "gem #{gem_name} --ci"
+ bundle "gem #{gem_name} --test=rspec"
end
- it "asks to setup CI" do
- expect(out).to match("Do you want to set up continuous integration for your gem?")
+ it "builds a spec helper that requires the proper entrypoint, and a default test in the proper path which fails" do
+ expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
+ expect(bundled_app("#{gem_name}/spec/spec_helper.rb").read).to include(%(require "#{require_path}"))
+ expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb")).to exist
+ expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb").read).to include("expect(false).to eq(true)")
+ end
+ end
+
+ context "--test parameter set to minitest" do
+ before do
+ bundle "gem #{gem_name} --test=minitest"
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.")
+ it "builds a test helper that requires the proper entrypoint, and default test file in the proper path that defines the proper test class name, requires helper, and fails" do
+ expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/test_helper.rb").read).to include(%(require "#{require_path}"))
+
+ expect(bundled_app("#{gem_name}/#{minitest_test_file_path}")).to exist
+ expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include(minitest_test_class_name)
+ expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include(%(require "test_helper"))
+ expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include("assert false")
end
end
- context "gem.ci setting not set and --ci with no arguments", :hint_text do
+ context "--test parameter set to test-unit" do
before do
- bundle "gem #{gem_name} --ci"
+ bundle "gem #{gem_name} --test=test-unit"
end
- it "asks to setup CI" do
- expect(out).to match("Do you want to set up continuous integration for your gem?")
+ it "builds a test helper that requires the proper entrypoint, and default test file in the proper path which requires helper and fails" do
+ expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/test_helper.rb").read).to include(%(require "#{require_path}"))
+ expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb").read).to include(%(require "test_helper"))
+ expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb").read).to include("assert_equal(\"expected\", \"actual\")")
end
+ end
+ 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.ci`."
- expect(out).to match(hint)
- end
+ context "with mit option in bundle config settings set to true" do
+ before do
+ bundle_config_global "gem.mit true"
end
+ it_behaves_like "--mit flag"
+ it_behaves_like "--no-mit flag"
+ end
- context "--edit option" do
- it "opens the generated gemspec in the user's text editor" do
- output = bundle "gem #{gem_name} --edit=echo"
- gemspec_path = File.join(bundled_app, gem_name, "#{gem_name}.gemspec")
- expect(output).to include("echo \"#{gemspec_path}\"")
- end
+ context "with mit option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.mit false"
end
+ it_behaves_like "--mit flag"
+ it_behaves_like "--no-mit flag"
end
- context "testing --mit and --coc options against bundle config settings", :readline do
- let(:gem_name) { "test-gem" }
+ context "with coc option in bundle config settings set to true" do
+ before do
+ bundle_config_global "gem.coc true"
+ end
+ it_behaves_like "--coc flag"
+ it_behaves_like "--no-coc flag"
+ end
- let(:require_path) { "test/gem" }
+ context "with coc option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.coc false"
+ end
+ it_behaves_like "--coc flag"
+ it_behaves_like "--no-coc flag"
+ end
+
+ context "with rubocop option in bundle config settings set to true" do
+ before do
+ bundle_config_global "gem.rubocop true"
+ end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--no-linter flag"
+ end
+
+ context "with rubocop option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.rubocop false"
+ end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--no-linter flag"
+ end
+
+ context "with linter option in bundle config settings set to rubocop" do
+ before do
+ bundle_config_global "gem.linter rubocop"
+ end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--no-linter flag"
+ end
+
+ context "with linter option in bundle config settings set to standard" do
+ before do
+ bundle_config_global "gem.linter standard"
+ end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--no-linter flag"
+ end
+
+ context "with linter option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.linter false"
+ end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--no-linter flag"
+ end
+
+ context "with changelog option in bundle config settings set to true" do
+ before do
+ bundle_config_global "gem.changelog true"
+ end
+ it_behaves_like "--changelog flag"
+ it_behaves_like "--no-changelog flag"
+ end
+
+ context "with changelog option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.changelog false"
+ end
+ it_behaves_like "--changelog flag"
+ it_behaves_like "--no-changelog flag"
+ end
- context "with mit option in bundle config settings set to true" do
+ context "with bundle option in bundle config settings set to true" do
+ before do
+ bundle_config_global "gem.bundle true"
+ end
+ it_behaves_like "--bundle flag"
+ it_behaves_like "--no-bundle flag"
+
+ it "runs bundle install" do
+ bundle "gem #{gem_name}"
+ expect(out).to include("Running bundle install in the new gem directory.")
+ end
+ end
+
+ context "with bundle option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.bundle false"
+ end
+ it_behaves_like "--bundle flag"
+ it_behaves_like "--no-bundle flag"
+
+ it "does not run bundle install" do
+ bundle "gem #{gem_name}"
+ expect(out).to_not include("Running bundle install in the new gem directory.")
+ end
+ end
+
+ context "without git config github.user set" do
+ before do
+ 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__MIT" => "true"
+ bundle_config_global "gem.github_username different_username"
end
- it_behaves_like "--mit flag"
- it_behaves_like "--no-mit flag"
+ it_behaves_like "--github-username option", "gh_user"
end
- context "with mit option in bundle config settings set to false" do
+ it_behaves_like "github_username configuration"
+
+ context "with github-username option in bundle config settings set to false" do
before do
- global_config "BUNDLE_GEM__MIT" => "false"
+ bundle_config_global "gem.github_username false"
end
- it_behaves_like "--mit flag"
- it_behaves_like "--no-mit flag"
+ it_behaves_like "--github-username option", "gh_user"
end
- context "with coc option in bundle config settings set to true" do
- before do
- global_config "BUNDLE_GEM__COC" => "true"
+ context "when changelog is enabled" do
+ it "sets gemspec changelog_uri, homepage, homepage_uri, source_code_uri to TODOs" do
+ bundle "gem #{gem_name} --changelog"
+
+ expect(generated_gemspec.metadata["changelog_uri"]).
+ to eq("TODO: Put your gem's CHANGELOG.md URL here.")
+ expect(generated_gemspec.homepage).to eq("TODO: Put your gem's website or public repo URL here.")
+ expect(generated_gemspec.metadata["homepage_uri"]).to eq("TODO: Put your gem's website or public repo URL here.")
+ expect(generated_gemspec.metadata["source_code_uri"]).to eq("TODO: Put your gem's public repo URL here.")
end
- it_behaves_like "--coc flag"
- it_behaves_like "--no-coc flag"
end
- context "with coc option in bundle config settings set to false" do
- before do
- global_config "BUNDLE_GEM__COC" => "false"
+ context "when changelog is not enabled" do
+ it "sets gemspec homepage, homepage_uri, source_code_uri to TODOs and changelog_uri to nil" do
+ bundle "gem #{gem_name}"
+
+ expect(generated_gemspec.metadata["changelog_uri"]).to be_nil
+ expect(generated_gemspec.homepage).to eq("TODO: Put your gem's website or public repo URL here.")
+ expect(generated_gemspec.metadata["homepage_uri"]).to eq("TODO: Put your gem's website or public repo URL here.")
+ expect(generated_gemspec.metadata["source_code_uri"]).to eq("TODO: Put your gem's public repo URL here.")
end
- it_behaves_like "--coc flag"
- it_behaves_like "--no-coc flag"
end
+ end
- context "with rubocop option in bundle config settings set to true" do
+ context "with git config github.user set" do
+ context "with github-username option in bundle config settings set to some value" do
before do
- global_config "BUNDLE_GEM__RUBOCOP" => "true"
+ bundle_config_global "gem.github_username different_username"
end
- it_behaves_like "--rubocop flag"
- it_behaves_like "--no-rubocop flag"
+ it_behaves_like "--github-username option", "gh_user"
end
- context "with rubocop option in bundle config settings set to false" do
+ it_behaves_like "github_username configuration"
+
+ context "with github-username option in bundle config settings set to false" do
before do
- global_config "BUNDLE_GEM__RUBOCOP" => "false"
+ bundle_config_global "gem.github_username false"
+ end
+ it_behaves_like "--github-username option", "gh_user"
+ end
+
+ context "when changelog is enabled" do
+ it "sets gemspec changelog_uri, homepage, homepage_uri, source_code_uri based on git username" do
+ bundle "gem #{gem_name} --changelog"
+
+ expect(generated_gemspec.metadata["changelog_uri"]).
+ to eq("https://github.com/bundleuser/#{gem_name}/blob/main/CHANGELOG.md")
+ expect(generated_gemspec.homepage).to eq("https://github.com/bundleuser/#{gem_name}")
+ expect(generated_gemspec.metadata["homepage_uri"]).to eq("https://github.com/bundleuser/#{gem_name}")
+ expect(generated_gemspec.metadata["source_code_uri"]).to eq("https://github.com/bundleuser/#{gem_name}")
+ end
+ end
+
+ context "when changelog is not enabled" do
+ it "sets gemspec source_code_uri, homepage, homepage_uri but not changelog_uri" do
+ bundle "gem #{gem_name}"
+
+ expect(generated_gemspec.metadata["changelog_uri"]).to be_nil
+ expect(generated_gemspec.homepage).to eq("https://github.com/bundleuser/#{gem_name}")
+ expect(generated_gemspec.metadata["homepage_uri"]).to eq("https://github.com/bundleuser/#{gem_name}")
+ expect(generated_gemspec.metadata["source_code_uri"]).to eq("https://github.com/bundleuser/#{gem_name}")
end
- it_behaves_like "--rubocop flag"
- it_behaves_like "--no-rubocop flag"
end
end
- context "gem naming with underscore", :readline do
+ context "standard gem naming" do
+ let(:require_path) { gem_name }
+
+ let(:require_relative_path) { gem_name }
+
+ let(:minitest_test_file_path) { "test/test_#{gem_name}.rb" }
+
+ let(:minitest_test_class_name) { "class TestMygem < Minitest::Test" }
+
+ include_examples "paths that depend on gem name"
+ end
+
+ context "gem naming with underscore" do
let(:gem_name) { "test_gem" }
let(:require_path) { "test_gem" }
+ let(:require_relative_path) { "test_gem" }
+
+ let(:minitest_test_file_path) { "test/test_test_gem.rb" }
+
+ let(:minitest_test_class_name) { "class TestTestGem < Minitest::Test" }
+
let(:flags) { nil }
it "does not nest constants" do
@@ -915,10 +1685,10 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(/module TestGem/)
end
- include_examples "generating a gem"
+ include_examples "paths that depend on gem name"
- context "--ext parameter set" do
- let(:flags) { "--ext" }
+ context "--ext parameter set with C" do
+ let(:flags) { "--ext=c" }
before do
bundle ["gem", gem_name, flags].compact.join(" ")
@@ -930,12 +1700,22 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
end
- it "includes rake-compiler" do
+ it "generates native extension loading code" do
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}.rb").read).to include(<<~RUBY)
+ require_relative "test_gem/version"
+ require "#{gem_name}/#{gem_name}"
+ RUBY
+ end
+
+ it "includes rake-compiler, but no Rust related changes" do
expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
+
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to_not include('spec.add_dependency "rb_sys"')
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to_not include('spec.required_rubygems_version = ">= ')
end
it "depends on compile task for build" do
- rakefile = strip_whitespace <<-RAKEFILE
+ rakefile = <<~RAKEFILE
# frozen_string_literal: true
require "bundler/gem_tasks"
@@ -943,7 +1723,9 @@ RSpec.describe "bundle gem" do
task build: :compile
- Rake::ExtensionTask.new("#{gem_name}") do |ext|
+ GEMSPEC = Gem::Specification.load("#{gem_name}.gemspec")
+
+ Rake::ExtensionTask.new("#{gem_name}", GEMSPEC) do |ext|
ext.lib_dir = "lib/#{gem_name}"
end
@@ -953,46 +1735,274 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
end
+
+ context "--ext parameter set with rust" do
+ let(:flags) { "--ext=rust" }
+
+ before do
+ bundle ["gem", gem_name, flags].compact.join(" ")
+ end
+
+ it "is not deprecated" do
+ expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated."
+ end
+
+ it "builds ext skeleton" do
+ expect(bundled_app("#{gem_name}/Cargo.toml")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/Cargo.toml")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/src/lib.rs")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/build.rs")).to exist
+ end
+
+ it "includes rake-compiler and rb_sys gems constraint" do
+ expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.add_dependency "rb_sys"')
+ end
+
+ it "depends on compile task for build" do
+ rakefile = <<~RAKEFILE
+ # frozen_string_literal: true
+
+ require "bundler/gem_tasks"
+ require "rb_sys/extensiontask"
+
+ task build: :compile
+
+ GEMSPEC = Gem::Specification.load("#{gem_name}.gemspec")
+
+ RbSys::ExtensionTask.new("#{gem_name}", GEMSPEC) do |ext|
+ ext.lib_dir = "lib/#{gem_name}"
+ end
+
+ task default: :compile
+ RAKEFILE
+
+ expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
+ end
+
+ it "configures the crate such that `cargo test` works", :ruby_repo, :mri_only do
+ env = setup_rust_env
+ gem_path = bundled_app(gem_name)
+ result = sys_exec("cargo test", env: env, dir: gem_path, timeout: 300)
+
+ expect(result).to include("1 passed")
+ end
+
+ def setup_rust_env
+ skip "rust toolchain of mingw is broken" if RUBY_PLATFORM.match?("mingw")
+
+ env = {
+ "CARGO_HOME" => ENV.fetch("CARGO_HOME", File.join(ENV["HOME"], ".cargo")),
+ "RUSTUP_HOME" => ENV.fetch("RUSTUP_HOME", File.join(ENV["HOME"], ".rustup")),
+ "RUSTUP_TOOLCHAIN" => ENV.fetch("RUSTUP_TOOLCHAIN", "stable"),
+ }
+
+ system(env, "cargo", "-V", out: IO::NULL, err: [:child, :out])
+ skip "cargo not present" unless $?.success?
+ # Hermetic Cargo setup
+ RbConfig::CONFIG.each {|k, v| env["RBCONFIG_#{k}"] = v }
+ env
+ end
+ end
+
+ context "--ext parameter set with go" do
+ let(:flags) { "--ext=go" }
+
+ before do
+ bundle ["gem", gem_name, flags].compact.join(" ")
+ end
+
+ after do
+ sys_exec("go clean -modcache", raise_on_error: true) if installed_go?
+ end
+
+ it "is not deprecated" do
+ expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated."
+ end
+
+ it "builds ext skeleton" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.go")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.h")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/go.mod")).to exist
+ end
+
+ it "includes extconf.rb in gem_name.gemspec" do
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(%(spec.extensions = ["ext/#{gem_name}/extconf.rb"]))
+ end
+
+ it "includes go_gem in gem_name.gemspec" do
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.add_dependency "go_gem", ">= 0.2"')
+ end
+
+ it "includes go_gem extension in extconf.rb" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb").read).to include(<<~RUBY)
+ require "mkmf"
+ require "go_gem/mkmf"
+ RUBY
+
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb").read).to include(%(create_go_makefile("#{gem_name}/#{gem_name}")))
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb").read).not_to include("create_makefile")
+ end
+
+ it "includes go_gem extension in gem_name.c" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c").read).to eq(<<~C)
+ #include "#{gem_name}.h"
+ #include "_cgo_export.h"
+ C
+ end
+
+ it "includes skeleton code in gem_name.go" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.go").read).to include(<<~GO)
+ /*
+ #include "#{gem_name}.h"
+
+ VALUE rb_#{gem_name}_sum(VALUE self, VALUE a, VALUE b);
+ */
+ import "C"
+ GO
+
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.go").read).to include(<<~GO)
+ //export rb_#{gem_name}_sum
+ func rb_#{gem_name}_sum(_ C.VALUE, a C.VALUE, b C.VALUE) C.VALUE {
+ GO
+
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.go").read).to include(<<~GO)
+ //export Init_#{gem_name}
+ func Init_#{gem_name}() {
+ GO
+ end
+
+ it "includes valid module name in go.mod" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/go.mod").read).to include("module github.com/bundleuser/#{gem_name}")
+ end
+
+ it "includes go_gem extension in Rakefile" do
+ expect(bundled_app("#{gem_name}/Rakefile").read).to include(<<~RUBY)
+ require "go_gem/rake_task"
+
+ GoGem::RakeTask.new("#{gem_name}")
+ RUBY
+ end
+
+ context "with --no-ci" do
+ let(:flags) { "--ext=go --no-ci" }
+
+ it_behaves_like "CI config is absent"
+ end
+
+ context "--ci set to github" do
+ let(:flags) { "--ext=go --ci=github" }
+
+ it "generates .github/workflows/main.yml" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml").read).to include("go-version-file: ext/#{gem_name}/go.mod")
+ end
+ end
+
+ context "--ci set to circle" do
+ let(:flags) { "--ext=go --ci=circle" }
+
+ it "generates a .circleci/config.yml" do
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
+
+ expect(bundled_app("#{gem_name}/.circleci/config.yml").read).to include(<<-YAML.strip)
+ environment:
+ GO_VERSION:
+ YAML
+
+ expect(bundled_app("#{gem_name}/.circleci/config.yml").read).to include(<<-YAML)
+ - run:
+ name: Install Go
+ command: |
+ wget https://go.dev/dl/go$GO_VERSION.linux-amd64.tar.gz -O /tmp/go.tar.gz
+ tar -C /usr/local -xzf /tmp/go.tar.gz
+ echo 'export PATH=/usr/local/go/bin:"$PATH"' >> "$BASH_ENV"
+ YAML
+ end
+ end
+
+ context "--ci set to gitlab" do
+ let(:flags) { "--ext=go --ci=gitlab" }
+
+ it "generates a .gitlab-ci.yml" do
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
+
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml").read).to include(<<-YAML)
+ - wget https://go.dev/dl/go$GO_VERSION.linux-amd64.tar.gz -O /tmp/go.tar.gz
+ - tar -C /usr/local -xzf /tmp/go.tar.gz
+ - export PATH=/usr/local/go/bin:$PATH
+ YAML
+
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml").read).to include(<<-YAML.strip)
+ variables:
+ GO_VERSION:
+ YAML
+ end
+ end
+
+ context "without github.user" do
+ before do
+ # FIXME: GitHub Actions Windows Runner hang up here for some reason...
+ skip "Workaround for hung up" if Gem.win_platform?
+
+ git("config --global --unset github.user")
+ bundle ["gem", gem_name, flags].compact.join(" ")
+ end
+
+ it "includes valid module name in go.mod" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/go.mod").read).to include("module github.com/username/#{gem_name}")
+ end
+ end
+ end
end
- context "gem naming with dashed", :readline do
+ context "gem naming with dashed" do
let(:gem_name) { "test-gem" }
let(:require_path) { "test/gem" }
+ let(:require_relative_path) { "gem" }
+
+ let(:minitest_test_file_path) { "test/test/test_gem.rb" }
+
+ let(:minitest_test_class_name) { "class Test::TestGem < Minitest::Test" }
+
it "nests constants so they work" do
bundle "gem #{gem_name}"
expect(bundled_app("#{gem_name}/lib/#{require_path}/version.rb").read).to match(/module Test\n module Gem/)
expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(/module Test\n module Gem/)
end
- include_examples "generating a gem"
+ include_examples "paths that depend on gem name"
end
describe "uncommon gem names" do
- it "can deal with two dashes", :readline do
+ it "can deal with two dashes" do
bundle "gem a--a"
expect(bundled_app("a--a/a--a.gemspec")).to exist
end
it "fails gracefully with a ." do
- bundle "gem foo.gemspec", :raise_on_error => false
+ bundle "gem foo.gemspec", raise_on_error: false
expect(err).to end_with("Invalid gem name foo.gemspec -- `Foo.gemspec` is an invalid constant name")
end
it "fails gracefully with a ^" do
- bundle "gem ^", :raise_on_error => false
+ bundle "gem ^", raise_on_error: false
expect(err).to end_with("Invalid gem name ^ -- `^` is an invalid constant name")
end
it "fails gracefully with a space" do
- bundle "gem 'foo bar'", :raise_on_error => false
+ bundle "gem 'foo bar'", raise_on_error: false
expect(err).to end_with("Invalid gem name foo bar -- `Foo bar` is an invalid constant name")
end
it "fails gracefully when multiple names are passed" do
- bundle "gem foo bar baz", :raise_on_error => false
+ bundle "gem foo bar baz", raise_on_error: false
expect(err).to eq(<<-E.strip)
ERROR: "bundle gem" was called with arguments ["foo", "bar", "baz"]
Usage: "bundle gem NAME [OPTIONS]"
@@ -1000,9 +2010,9 @@ Usage: "bundle gem NAME [OPTIONS]"
end
end
- describe "#ensure_safe_gem_name", :readline do
+ describe "#ensure_safe_gem_name" do
before do
- bundle "gem #{subject}", :raise_on_error => false
+ bundle "gem #{subject}", raise_on_error: false
end
context "with an existing const name" do
@@ -1015,6 +2025,19 @@ Usage: "bundle gem NAME [OPTIONS]"
it { expect(err).to include("Invalid gem name #{subject}") }
end
+ context "starting with a number" do
+ subject { "1gem" }
+ it { expect(err).to include("Invalid gem name #{subject}") }
+ end
+
+ context "including capital letter" do
+ subject { "CAPITAL" }
+ it "should warn but not error" do
+ expect(err).to include("Gem names with capital letters are not recommended")
+ expect(bundled_app("#{subject}/#{subject}.gemspec")).to exist
+ end
+ end
+
context "starting with an existing const name" do
subject { "gem-somenewconstantname" }
it { expect(err).not_to include("Invalid gem name #{subject}") }
@@ -1028,14 +2051,14 @@ Usage: "bundle gem NAME [OPTIONS]"
context "on first run", :readline do
it "asks about test framework" do
- global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__COC" => "false"
+ bundle_config_global "BUNDLE_GEM__TEST" => nil
bundle "gem foobar" do |input, _, _|
input.puts "rspec"
end
expect(bundled_app("foobar/spec/spec_helper.rb")).to exist
- rakefile = strip_whitespace <<-RAKEFILE
+ rakefile = <<~RAKEFILE
# frozen_string_literal: true
require "bundler/gem_tasks"
@@ -1051,7 +2074,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"
+ bundle_config_global "BUNDLE_GEM__CI" => nil
bundle "gem foobar" do |input, _, _|
input.puts "github"
@@ -1060,8 +2083,8 @@ Usage: "bundle gem NAME [OPTIONS]"
expect(bundled_app("foobar/.github/workflows/main.yml")).to exist
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"
+ it "asks about MIT license just once" do
+ bundle_config_global "BUNDLE_GEM__MIT" => nil
bundle "config list"
@@ -1070,29 +2093,42 @@ Usage: "bundle gem NAME [OPTIONS]"
end
expect(bundled_app("foobar/LICENSE.txt")).to exist
+ expect(out).to include("Using a MIT license means").once
end
- it "asks about CoC" do
- global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__RUBOCOP" => "false"
+ it "asks about CoC just once" do
+ bundle_config_global "BUNDLE_GEM__COC" => nil
bundle "gem foobar" do |input, _, _|
input.puts "yes"
end
expect(bundled_app("foobar/CODE_OF_CONDUCT.md")).to exist
+ expect(out).to include("Codes of conduct can increase contributions to your project").once
+ end
+
+ it "asks about CHANGELOG just once" do
+ bundle_config_global "BUNDLE_GEM__CHANGELOG" => nil
+
+ bundle "gem foobar" do |input, _, _|
+ input.puts "yes"
+ end
+
+ expect(bundled_app("foobar/CHANGELOG.md")).to exist
+ expect(out).to include("A changelog is a file which contains").once
end
end
- context "on conflicts with a previously created file", :readline do
+ context "on conflicts with a previously created file" do
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")
+ bundle "gem conflict-foobar", raise_on_error: false
+ 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
- context "on conflicts with a previously created directory", :readline do
+ context "on conflicts with a previously created directory" do
it "should succeed" do
FileUtils.mkdir_p(bundled_app("conflict-foobar/Gemfile"))
bundle "gem conflict-foobar"
diff --git a/spec/bundler/commands/open_spec.rb b/spec/bundler/commands/open_spec.rb
index d18e620783..664dc58919 100644
--- a/spec/bundler/commands/open_spec.rb
+++ b/spec/bundler/commands/open_spec.rb
@@ -4,90 +4,148 @@ RSpec.describe "bundle open" do
context "when opening a regular gem" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
end
it "opens the gem with BUNDLER_EDITOR as highest priority" do
- bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
expect(out).to include("bundler_editor #{default_bundle_path("gems", "rails-2.3.2")}")
end
it "opens the gem with VISUAL as 2nd highest priority" do
- bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "" }
+ bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "" }
expect(out).to include("visual #{default_bundle_path("gems", "rails-2.3.2")}")
end
it "opens the gem with EDITOR as 3rd highest priority" do
- bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to include("editor #{default_bundle_path("gems", "rails-2.3.2")}")
end
it "complains if no EDITOR is set" do
- bundle "open rails", :env => { "EDITOR" => "", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle "open rails", env: { "EDITOR" => "", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to eq("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR")
end
it "complains if gem not in bundle" do
- bundle "open missing", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ bundle "open missing", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
expect(err).to match(/could not find gem 'missing'/i)
end
it "does not blow up if the gem to open does not have a Gemfile" do
git = build_git "foo"
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem 'foo', :git => "#{lib_path("foo-1.0")}"
G
- bundle "open foo", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
- expect(out).to match("editor #{default_bundle_path.join("bundler/gems/foo-1.0-#{ref}")}")
+ bundle "open foo", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("bundler", "gems", "foo-1.0-#{ref}")}")
end
it "suggests alternatives for similar-sounding gems" do
- bundle "open Rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
- expect(err).to match(/did you mean rails\?/i)
+ bundle "open Rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
+ expect(err).to match(/did you mean 'rails'\?/i)
end
it "opens the gem with short words" do
- bundle "open rec", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open rec", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2")}")
end
+ it "opens subpath of the gem" do
+ bundle "open activerecord --path lib/activerecord", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/activerecord")
+ end
+
+ it "opens subpath file of the gem" do
+ bundle "open activerecord --path lib/version.rb", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/version.rb")
+ end
+
+ it "opens deep subpath of the gem" do
+ bundle "open activerecord --path lib/active_record", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/active_record")
+ end
+
+ it "requires value for --path arg" do
+ bundle "open activerecord --path", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
+ expect(err).to eq "Cannot specify `--path` option without a value"
+ end
+
+ it "suggests alternatives for similar-sounding gems when using subpath" do
+ bundle "open Rails --path README.md", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
+ expect(err).to match(/did you mean 'rails'\?/i)
+ end
+
+ it "suggests alternatives for similar-sounding gems when using deep subpath" do
+ bundle "open Rails --path some/path/here", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
+ expect(err).to match(/did you mean 'rails'\?/i)
+ end
+
+ it "opens subpath of the short worded gem" do
+ bundle "open rec --path CHANGELOG.md", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/CHANGELOG.md")
+ end
+
+ it "opens deep subpath of the short worded gem" do
+ bundle "open rec --path lib/activerecord", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/activerecord")
+ end
+
+ it "opens subpath of the selected matching gem", :readline do
+ env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open active --path CHANGELOG.md", env: env do |input, _, _|
+ input.puts "2"
+ end
+
+ expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2").join("CHANGELOG.md")}")
+ end
+
+ it "opens deep subpath of the selected matching gem", :readline do
+ env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open active --path lib/activerecord/version.rb", env: env do |input, _, _|
+ input.puts "2"
+ end
+
+ expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2").join("lib", "activerecord", "version.rb")}")
+ end
+
it "select the gem from many match gems", :readline do
env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
- bundle "open active", :env => env do |input, _, _|
+ bundle "open active", env: env do |input, _, _|
input.puts "2"
end
- expect(out).to match(/bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}\z/)
+ expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2")}")
end
it "allows selecting exit from many match gems", :readline do
env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
- bundle "open active", :env => env do |input, _, _|
+ bundle "open active", env: env do |input, _, _|
input.puts "0"
end
end
it "performs an automatic bundle install" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
gem "foo"
G
- bundle "config set auto_install 1"
- bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
+ bundle_config "auto_install 1"
+ bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to include("Installing foo 1.0")
end
it "opens the editor with a clean env" do
- bundle "open", :env => { "EDITOR" => "sh -c 'env'", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false
+ bundle "open", env: { "EDITOR" => "sh -c 'env'", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
expect(out).not_to include("BUNDLE_GEMFILE=")
end
end
@@ -105,12 +163,12 @@ RSpec.describe "bundle open" do
skip "No default gems available on this test run" if default_gems.empty?
install_gemfile <<-G
- gem "json"
+ source "https://gem.repo1"
G
end
it "throws proper error when trying to open default gem" do
- bundle "open json", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
+ bundle "open json", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" }
expect(out).to include("Unable to open json because it's a default gem, so the directory it would normally be installed to does not exist.")
end
end
diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb
index 2a07f1c784..28ed51d61e 100644
--- a/spec/bundler/commands/outdated_spec.rb
+++ b/spec/bundler/commands/outdated_spec.rb
@@ -1,36 +1,36 @@
# 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 "https://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"
build_gem "weakling", "0.2"
- update_git "foo", :path => lib_path("foo")
- update_git "zebra", :path => lib_path("zebra")
+ update_git "foo", path: lib_path("foo")
+ update_git "zebra", path: lib_path("zebra")
end
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 default
foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default
weakling 0.0.3 0.2 ~> 0.0.1 default
@@ -46,14 +46,14 @@ RSpec.describe "bundle outdated" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "AAA", "1.0.0"
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
AAA 1.0.0 2.0.0 = 1.0.0 default
TABLE
@@ -63,10 +63,10 @@ RSpec.describe "bundle outdated" do
it "returns non zero exit status if outdated gems present" do
update_repo2 do
build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
+ update_git "foo", path: lib_path("foo")
end
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expect(exitstatus).to_not be_zero
end
@@ -77,7 +77,7 @@ RSpec.describe "bundle outdated" do
it "adds gem group to dependency output when repo is updated" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "terranova", '8'
@@ -89,10 +89,10 @@ RSpec.describe "bundle outdated" do
update_repo2 { build_gem "activesupport", "3.0" }
update_repo2 { build_gem "terranova", "9" }
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 development, test
terranova 8 9 = 8 default
TABLE
@@ -102,32 +102,49 @@ 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 "https://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"
+ bundle_config "clean false"
update_repo2 { build_gem "activesupport", "3.0" }
update_repo2 { build_gem "terranova", "9" }
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "terranova", '9'
gem 'activesupport', '2.3.5'
G
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "terranova", '8'
gem 'activesupport', '2.3.5'
G
- bundle "outdated --verbose", :raise_on_error => false
+ bundle "outdated --verbose", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups Path
+ Gem Current Latest Requested Groups Release Date Path
activesupport 2.3.5 3.0 = 2.3.5 default
- terranova 8 9 = 8 default #{default_bundle_path("specifications/terranova-9.gemspec")}
+ terranova 8 9 = 8 default #{default_bundle_path("specifications/terranova-9.gemspec")}
TABLE
expect(out).to end_with(expected_output)
@@ -136,8 +153,13 @@ RSpec.describe "bundle outdated" do
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)}"
+ source "https://gem.repo2"
gem "weakling", "~> 0.0.1"
gem "terranova", '8'
@@ -155,7 +177,7 @@ RSpec.describe "bundle outdated" do
build_gem "duradura", "8.0"
end
- bundle "outdated --group #{group}", :raise_on_error => false
+ bundle "outdated --group #{group}", raise_on_error: false
end
it "works when the bundle is up to date" do
@@ -163,11 +185,19 @@ RSpec.describe "bundle outdated" do
expect(out).to end_with("Bundle up to date!")
end
+ it "works when only out of date gems are not in given group" do
+ update_repo2 do
+ build_gem "terranova", "9"
+ end
+ bundle "outdated --group development"
+ expect(out).to end_with("Bundle up to date!")
+ end
+
it "returns a sorted list of outdated gems from one group => 'default'" do
test_group_option("default")
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
terranova 8 9 = 8 default
TABLE
@@ -178,7 +208,7 @@ RSpec.describe "bundle outdated" do
test_group_option("development")
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 development, test
duradura 7.0 8.0 = 7.0 development, test
TABLE
@@ -190,7 +220,7 @@ RSpec.describe "bundle outdated" do
test_group_option("test")
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 development, test
duradura 7.0 8.0 = 7.0 development, test
TABLE
@@ -201,7 +231,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|
@@ -210,7 +243,7 @@ RSpec.describe "bundle outdated" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "bar_dependant", '7.0'
G
@@ -221,10 +254,10 @@ RSpec.describe "bundle outdated" do
end
it "returns a sorted list of outdated gems" do
- bundle "outdated --groups", :raise_on_error => false
+ bundle "outdated --groups", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
bar 2.0.0 3.0.0
TABLE
@@ -234,8 +267,13 @@ 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)}"
+ source "https://gem.repo2"
gem "weakling", "~> 0.0.1"
gem "terranova", '8'
@@ -258,10 +296,10 @@ RSpec.describe "bundle outdated" do
build_gem "duradura", "8.0"
end
- bundle "outdated --groups", :raise_on_error => false
+ bundle "outdated --groups", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 development, test
duradura 7.0 8.0 = 7.0 development, test
terranova 8 9 = 8 default
@@ -272,22 +310,40 @@ 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 "https://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"
end
- bundle "config set clean false"
+ bundle_config "clean false"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "activesupport", "2.3.4"
G
- bundle "outdated --local", :raise_on_error => false
+ bundle "outdated --local", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.4 2.3.5 = 2.3.4 default
TABLE
@@ -295,7 +351,7 @@ RSpec.describe "bundle outdated" do
end
it "doesn't hit repo2" do
- FileUtils.rm_rf(gem_repo2)
+ FileUtils.rm_r(gem_repo2)
bundle "outdated --local"
expect(out).not_to match(/Fetching (gem|version|dependency) metadata from/)
@@ -305,59 +361,163 @@ 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 "https://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
+ it "outputs a sorted list of outdated gems with a more minimal format to stdout" do
minimal_output = "activesupport (newest 3.0, installed 2.3.5, requested = 2.3.5)\n" \
"weakling (newest 0.2, installed 0.0.3, requested ~> 0.0.1)"
subject
expect(out).to eq(minimal_output)
end
+
+ it "outputs progress to stderr" do
+ subject
+ expect(err).to include("Fetching gem metadata")
+ end
end
context "and no gems are outdated" do
- it "has empty output" do
+ before do
+ build_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport", "3.0"
+ G
+ end
+
+ it "does not output to stdout" do
subject
expect(out).to be_empty
end
+
+ it "outputs progress to stderr" do
+ subject
+ expect(err).to include("Fetching gem metadata")
+ end
end
end
describe "with --parseable option" do
- subject { bundle "outdated --parseable", :raise_on_error => false }
+ subject { bundle "outdated --parseable", raise_on_error: false }
it_behaves_like "a minimal output is desired"
end
describe "with aliased --porcelain option" do
- subject { bundle "outdated --porcelain", :raise_on_error => false }
+ subject { bundle "outdated --porcelain", raise_on_error: false }
it_behaves_like "a minimal output is desired"
end
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 "https://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")
+ update_git "foo", path: lib_path("foo")
end
- bundle "outdated foo", :raise_on_error => false
+ bundle "outdated foo", raise_on_error: false
expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default
TABLE
expect(out).to match(Regexp.new(expected_output))
end
+
+ it "does not require gems to be installed" do
+ build_repo4 do
+ build_gem "zeitwerk", "1.0.0"
+ build_gem "zeitwerk", "2.0.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "zeitwerk"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ zeitwerk (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ zeitwerk
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "outdated zeitwerk", raise_on_error: false
+
+ expected_output = <<~TABLE.tr(".", "\.").strip
+ Gem Current Latest Requested Groups Release Date
+ zeitwerk 1.0.0 2.0.0 >= 0 default
+ TABLE
+
+ expect(out).to match(Regexp.new(expected_output))
+ expect(err).to be_empty
+ end
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 "https://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
@@ -376,10 +536,10 @@ RSpec.describe "bundle outdated" do
build_gem "activesupport", "3.0.0.beta"
end
- bundle "outdated --pre", :raise_on_error => false
+ bundle "outdated --pre", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0.0.beta = 2.3.5 default
TABLE
@@ -395,14 +555,14 @@ RSpec.describe "bundle outdated" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "activesupport", "3.0.0.beta.1"
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 3.0.0.beta.1 3.0.0.beta.2 = 3.0.0.beta.1 default
TABLE
@@ -411,31 +571,74 @@ RSpec.describe "bundle outdated" do
end
end
- filter_strict_option = Bundler.feature_flag.bundler_2_mode? ? :"filter-strict" : :strict
- describe "with --#{filter_strict_option} option" do
+ describe "with --filter-strict 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 "https://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"
build_gem "weakling", "0.0.5"
end
- bundle :outdated, filter_strict_option => true, :raise_on_error => false
+ bundle :outdated, "filter-strict": true, raise_on_error: false
+
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups Release Date
+ weakling 0.0.3 0.0.5 ~> 0.0.1 default
+ TABLE
+
+ expect(out).to end_with(expected_output)
+ end
+
+ it "only reports gems that have a newer version that matches the specified dependency version requirements, using --strict alias" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ build_gem "weakling", "0.0.5"
+ end
+
+ bundle :outdated, strict: true, raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.0.3 0.0.5 ~> 0.0.1 default
TABLE
expect(out).to end_with(expected_output)
end
+ it "doesn't crash when some deps unused on the current platform" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport", platforms: [:ruby_22]
+ G
+
+ bundle :outdated, "filter-strict": true
+
+ expect(out).to end_with("Bundle up to date!")
+ end
+
it "only reports gem dependencies when they can actually be updated" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack_middleware", "1.0"
+ source "https://gem.repo2"
+ gem "myrack_middleware", "1.0"
G
- bundle :outdated, filter_strict_option => true
+ bundle :outdated, "filter-strict": true
expect(out).to end_with("Bundle up to date!")
end
@@ -443,7 +646,7 @@ RSpec.describe "bundle outdated" do
describe "and filter options" do
it "only reports gems that match requirement and patch filter level" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "activesupport", "~> 2.3"
gem "weakling", ">= 0.0.1"
G
@@ -453,10 +656,10 @@ RSpec.describe "bundle outdated" do
build_gem "weakling", "0.0.5"
end
- bundle :outdated, filter_strict_option => true, "filter-patch" => true, :raise_on_error => false
+ bundle :outdated, :"filter-strict" => true, "filter-patch" => true, :raise_on_error => false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.0.3 0.0.5 >= 0.0.1 default
TABLE
@@ -465,7 +668,7 @@ RSpec.describe "bundle outdated" do
it "only reports gems that match requirement and minor filter level" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "activesupport", "~> 2.3"
gem "weakling", ">= 0.0.1"
G
@@ -475,10 +678,10 @@ RSpec.describe "bundle outdated" do
build_gem "weakling", "0.1.5"
end
- bundle :outdated, filter_strict_option => true, "filter-minor" => true, :raise_on_error => false
+ bundle :outdated, :"filter-strict" => true, "filter-minor" => true, :raise_on_error => false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.0.3 0.1.5 >= 0.0.1 default
TABLE
@@ -487,7 +690,7 @@ RSpec.describe "bundle outdated" do
it "only reports gems that match requirement and major filter level" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "activesupport", "~> 2.3"
gem "weakling", ">= 0.0.1"
G
@@ -497,10 +700,10 @@ RSpec.describe "bundle outdated" do
build_gem "weakling", "1.1.5"
end
- bundle :outdated, filter_strict_option => true, "filter-major" => true, :raise_on_error => false
+ bundle :outdated, :"filter-strict" => true, "filter-major" => true, :raise_on_error => false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.0.3 1.1.5 >= 0.0.1 default
TABLE
@@ -510,43 +713,64 @@ 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 "https://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
+ bundle "outdated invalid_gem_name", raise_on_error: false
expect(err).to include("Could not find gem 'invalid_gem_name'.")
end
it "returns non-zero exit code" do
- bundle "outdated invalid_gem_name", :raise_on_error => false
+ bundle "outdated invalid_gem_name", raise_on_error: false
expect(exitstatus).to_not be_zero
end
end
it "performs an automatic bundle install" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "0.9.1"
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
gem "foo"
G
- bundle "config set auto_install 1"
- bundle :outdated, :raise_on_error => false
+ bundle_config "auto_install 1"
+ bundle :outdated, raise_on_error: false
expect(out).to include("Installing foo 1.0")
end
- context "after bundle install --deployment", :bundler => "< 3" do
+ context "in deployment mode" do
before do
- install_gemfile <<-G, :deployment => true, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
+ build_repo2
+
+ gemfile <<-G
+ source "https://gem.repo2"
- gem "rack"
+ gem "myrack"
gem "foo"
G
+ bundle :lock
+ bundle_config "deployment true"
end
it "outputs a helpful message about being in deployment mode" do
update_repo2 { build_gem "activesupport", "3.0" }
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expect(last_command).to be_failure
expect(err).to include("You are trying to check outdated gems in deployment mode.")
expect(err).to include("Run `bundle outdated` elsewhere.")
@@ -555,21 +779,26 @@ RSpec.describe "bundle outdated" do
end
end
- context "after bundle config set deployment true" 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)}"
+ source "https://gem.repo2"
- gem "rack"
+ gem "myrack"
gem "foo"
G
- bundle "config set deployment true"
+ bundle_config "deployment true"
end
it "outputs a helpful message about being in deployment mode" do
update_repo2 { build_gem "activesupport", "3.0" }
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expect(last_command).to be_failure
expect(err).to include("You are trying to check outdated gems in deployment mode.")
expect(err).to include("Run `bundle outdated` elsewhere.")
@@ -580,8 +809,10 @@ 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)}"
+ source "https://gem.repo2"
gem "laduradura", '= 5.15.2'
G
end
@@ -593,9 +824,13 @@ 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)}"
+ source "https://gem.repo2"
gem "laduradura", '= 5.15.2', :platforms => [:ruby, :jruby]
G
@@ -603,16 +838,16 @@ RSpec.describe "bundle outdated" do
expect(out).to end_with("Bundle up to date!")
end
- it "reports that updates are available if the JRuby platform is used", :jruby do
+ it "reports that updates are available if the JRuby platform is used", :jruby_only do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "laduradura", '= 5.15.2', :platforms => [:ruby, :jruby]
G
- bundle "outdated", :raise_on_error => false
+ bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
laduradura 5.15.2 5.15.3 = 5.15.2 default
TABLE
@@ -632,6 +867,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 "https://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"
@@ -643,21 +893,51 @@ RSpec.describe "bundle outdated" do
context "when on a new machine" do
before do
- simulate_new_machine
+ build_repo2 do
+ build_git "foo", path: lib_path("foo")
+ build_git "zebra", path: lib_path("zebra")
+ end
- update_git "foo", :path => lib_path("foo")
+ install_gemfile <<-G
+ source "https://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
+
+ pristine_system_gems
+
+ update_git "foo", path: lib_path("foo")
update_repo2 do
build_gem "activesupport", "3.3.5"
build_gem "weakling", "0.8.0"
end
end
- subject { bundle "outdated", :raise_on_error => false }
+ subject { bundle "outdated", raise_on_error: false }
it_behaves_like "version update is detected"
end
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 "https://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"
@@ -669,6 +949,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 "https://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"
@@ -687,6 +982,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 "https://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"
@@ -698,6 +1008,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 "https://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"
@@ -709,6 +1034,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 "https://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"
@@ -719,7 +1059,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-major option" do
- subject { bundle "outdated --filter-major", :raise_on_error => false }
+ subject { bundle "outdated --filter-major", raise_on_error: false }
it_behaves_like "major version updates are detected"
it_behaves_like "minor version is ignored"
@@ -727,7 +1067,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-minor option" do
- subject { bundle "outdated --filter-minor", :raise_on_error => false }
+ subject { bundle "outdated --filter-minor", raise_on_error: false }
it_behaves_like "minor version updates are detected"
it_behaves_like "major version is ignored"
@@ -735,7 +1075,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-patch option" do
- subject { bundle "outdated --filter-patch", :raise_on_error => false }
+ subject { bundle "outdated --filter-patch", raise_on_error: false }
it_behaves_like "patch version updates are detected"
it_behaves_like "major version is ignored"
@@ -743,7 +1083,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-minor --filter-patch options" do
- subject { bundle "outdated --filter-minor --filter-patch", :raise_on_error => false }
+ subject { bundle "outdated --filter-minor --filter-patch", raise_on_error: false }
it_behaves_like "minor version updates are detected"
it_behaves_like "patch version updates are detected"
@@ -751,7 +1091,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-major --filter-minor options" do
- subject { bundle "outdated --filter-major --filter-minor", :raise_on_error => false }
+ subject { bundle "outdated --filter-major --filter-minor", raise_on_error: false }
it_behaves_like "major version updates are detected"
it_behaves_like "minor version updates are detected"
@@ -759,7 +1099,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-major --filter-patch options" do
- subject { bundle "outdated --filter-major --filter-patch", :raise_on_error => false }
+ subject { bundle "outdated --filter-major --filter-patch", raise_on_error: false }
it_behaves_like "major version updates are detected"
it_behaves_like "patch version updates are detected"
@@ -767,7 +1107,7 @@ RSpec.describe "bundle outdated" do
end
describe "with --filter-major --filter-minor --filter-patch options" do
- subject { bundle "outdated --filter-major --filter-minor --filter-patch", :raise_on_error => false }
+ subject { bundle "outdated --filter-major --filter-minor --filter-patch", raise_on_error: false }
it_behaves_like "major version updates are detected"
it_behaves_like "minor version updates are detected"
@@ -775,116 +1115,125 @@ RSpec.describe "bundle outdated" do
end
context "conservative updates" do
- context "without update-strict" do
- before do
- build_repo4 do
- build_gem "patch", %w[1.0.0 1.0.1]
- build_gem "minor", %w[1.0.0 1.0.1 1.1.0]
- build_gem "major", %w[1.0.0 1.0.1 1.1.0 2.0.0]
- end
+ before do
+ build_repo4 do
+ build_gem "patch", %w[1.0.0 1.0.1]
+ build_gem "minor", %w[1.0.0 1.0.1 1.1.0]
+ build_gem "major", %w[1.0.0 1.0.1 1.1.0 2.0.0]
+ end
- # establish a lockfile set to 1.0.0
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem 'patch', '1.0.0'
- gem 'minor', '1.0.0'
- gem 'major', '1.0.0'
- G
+ # establish a lockfile set to 1.0.0
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem 'patch', '1.0.0'
+ gem 'minor', '1.0.0'
+ gem 'major', '1.0.0'
+ G
- # remove 1.4.3 requirement and bar altogether
- # to setup update specs below
- gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem 'patch'
- gem 'minor'
- gem 'major'
- G
- end
+ # remove all version requirements
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem 'patch'
+ gem 'minor'
+ gem 'major'
+ G
+ end
- it "shows nothing when patching and filtering to minor" do
- bundle "outdated --patch --filter-minor"
+ it "shows nothing when patching and filtering to minor" do
+ bundle "outdated --patch --filter-minor"
- expect(out).to end_with("No minor updates to display.")
- end
+ expect(out).to end_with("No minor updates to display.")
+ end
- it "shows all gems when patching and filtering to patch" do
- bundle "outdated --patch --filter-patch", :raise_on_error => false
+ it "shows all gems when patching and filtering to patch" do
+ bundle "outdated --patch --filter-patch", raise_on_error: false
- expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
- major 1.0.0 1.0.1 >= 0 default
- minor 1.0.0 1.0.1 >= 0 default
- patch 1.0.0 1.0.1 >= 0 default
- TABLE
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups Release Date
+ major 1.0.0 1.0.1 >= 0 default
+ minor 1.0.0 1.0.1 >= 0 default
+ patch 1.0.0 1.0.1 >= 0 default
+ TABLE
- expect(out).to end_with(expected_output)
- end
+ expect(out).to end_with(expected_output)
+ end
- it "shows minor and major when updating to minor and filtering to patch and minor" do
- bundle "outdated --minor --filter-minor", :raise_on_error => false
+ it "shows minor and major when updating to minor and filtering to patch and minor" do
+ bundle "outdated --minor --filter-minor", raise_on_error: false
- expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
- major 1.0.0 1.1.0 >= 0 default
- minor 1.0.0 1.1.0 >= 0 default
- TABLE
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups Release Date
+ major 1.0.0 1.1.0 >= 0 default
+ minor 1.0.0 1.1.0 >= 0 default
+ TABLE
- expect(out).to end_with(expected_output)
- end
+ expect(out).to end_with(expected_output)
+ end
- it "shows minor when updating to major and filtering to minor with parseable" do
- bundle "outdated --major --filter-minor --parseable", :raise_on_error => false
+ it "shows minor when updating to major and filtering to minor with parseable" do
+ bundle "outdated --major --filter-minor --parseable", raise_on_error: false
- expect(out).not_to include("patch (newest")
- expect(out).to include("minor (newest")
- expect(out).not_to include("major (newest")
- end
+ expect(out).not_to include("patch (newest")
+ expect(out).to include("minor (newest")
+ expect(out).not_to include("major (newest")
end
+ end
- context "with update-strict" do
- before do
- build_repo4 do
- build_gem "foo", %w[1.4.3 1.4.4] do |s|
- s.add_dependency "bar", "~> 2.0"
- end
- build_gem "foo", %w[1.4.5 1.5.0] do |s|
- s.add_dependency "bar", "~> 2.1"
- end
- build_gem "foo", %w[1.5.1] do |s|
- s.add_dependency "bar", "~> 3.0"
- end
- build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
- build_gem "qux", %w[1.0.0 1.1.0 2.0.0]
+ context "tricky conservative updates" do
+ before do
+ build_repo4 do
+ build_gem "foo", %w[1.4.3 1.4.4] do |s|
+ s.add_dependency "bar", "~> 2.0"
+ end
+ build_gem "foo", %w[1.4.5 1.5.0] do |s|
+ s.add_dependency "bar", "~> 2.1"
end
+ build_gem "foo", %w[1.5.1] do |s|
+ s.add_dependency "bar", "~> 3.0"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "qux", %w[1.0.0 1.1.0 2.0.0]
+ end
- # establish a lockfile set to 1.4.3
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem 'foo', '1.4.3'
- gem 'bar', '2.0.3'
- gem 'qux', '1.0.0'
- G
+ # establish a lockfile set to 1.4.3
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem 'foo', '1.4.3'
+ gem 'bar', '2.0.3'
+ gem 'qux', '1.0.0'
+ G
- # remove 1.4.3 requirement and bar altogether
- # to setup update specs below
- gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem 'foo'
- gem 'qux'
- G
- end
+ # remove 1.4.3 requirement and bar altogether
+ # to setup update specs below
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem 'foo'
+ gem 'qux'
+ G
+ end
- it "shows gems with update-strict updating to patch and filtering to patch" do
- bundle "outdated --patch --update-strict --filter-patch", :raise_on_error => false
+ it "shows gems updating to patch and filtering to patch" do
+ bundle "outdated --patch --filter-patch", raise_on_error: false, env: { "DEBUG_RESOLVER" => "1" }
- expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
- bar 2.0.3 2.0.5
- foo 1.4.3 1.4.4 >= 0 default
- TABLE
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups Release Date
+ bar 2.0.3 2.0.5
+ foo 1.4.3 1.4.4 >= 0 default
+ TABLE
- expect(out).to end_with(expected_output)
- end
+ expect(out).to end_with(expected_output)
+ end
+
+ it "shows gems updating to patch and filtering to patch, in debug mode" do
+ bundle "outdated --patch --filter-patch", raise_on_error: false, env: { "DEBUG" => "1" }
+
+ expected_output = <<~TABLE.strip
+ Gem Current Latest Requested Groups Release Date Path
+ bar 2.0.3 2.0.5
+ foo 1.4.3 1.4.4 >= 0 default
+ TABLE
+
+ expect(out).to end_with(expected_output)
end
end
@@ -898,24 +1247,123 @@ RSpec.describe "bundle outdated" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem 'weakling', '0.2'
gem 'bar', '2.1'
G
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem 'weakling'
G
- bundle "outdated --only-explicit", :raise_on_error => false
+ bundle "outdated --only-explicit", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.2 0.3 >= 0 default
TABLE
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: https://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 "https://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 Release Date
+ 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 "https://gem.repo4"
+
+ gem "mini_portile2"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://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 Release Date
+ 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/platform_spec.rb b/spec/bundler/commands/platform_spec.rb
new file mode 100644
index 0000000000..9d7354c54f
--- /dev/null
+++ b/spec/bundler/commands/platform_spec.rb
@@ -0,0 +1,1260 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle platform" do
+ context "without flags" do
+ it "returns all the output" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ #{ruby_version_correct}
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{Gem::Platform.local}
+
+Your app has gems that work on these platforms:
+* #{local_platform}
+
+Your Gemfile specifies a Ruby version requirement:
+* ruby #{Gem.ruby_version}
+
+Your current platform satisfies the Ruby version requirement.
+G
+ end
+
+ it "returns all the output including the patchlevel" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ #{ruby_version_correct_patchlevel}
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{Gem::Platform.local}
+
+Your app has gems that work on these platforms:
+* #{local_platform}
+
+Your Gemfile specifies a Ruby version requirement:
+* #{Bundler::RubyVersion.system.single_version_string}
+
+Your current platform satisfies the Ruby version requirement.
+G
+ end
+
+ it "doesn't print ruby version requirement if it isn't specified" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{Gem::Platform.local}
+
+Your app has gems that work on these platforms:
+* #{local_platform}
+
+Your Gemfile does not specify a Ruby version requirement.
+G
+ end
+
+ it "doesn't match the ruby version requirement" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ #{ruby_version_incorrect}
+
+ gem "foo"
+ G
+
+ bundle "platform"
+ expect(out).to eq(<<-G.chomp)
+Your platform is: #{Gem::Platform.local}
+
+Your app has gems that work on these platforms:
+* #{local_platform}
+
+Your Gemfile specifies a Ruby version requirement:
+* ruby #{not_local_ruby_version}
+
+Your Ruby version is #{Gem.ruby_version}, but your Gemfile specified #{not_local_ruby_version}
+G
+ end
+ end
+
+ context "--ruby" do
+ it "returns ruby version when explicit" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby "1.9.3", :engine => 'ruby', :engine_version => '1.9.3'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.9.3")
+ end
+
+ it "defaults to MRI" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby "1.9.3"
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.9.3")
+ end
+
+ it "handles jruby" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby "1.8.7", :engine => 'jruby', :engine_version => '1.6.5'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.8.7 (jruby 1.6.5)")
+ end
+
+ it "handles rbx" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby "1.8.7", :engine => 'rbx', :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 1.8.7 (rbx 1.2.4)")
+ end
+
+ it "handles truffleruby" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby "2.5.1", :engine => 'truffleruby', :engine_version => '1.0.0-rc6'
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("ruby 2.5.1 (truffleruby 1.0.0-rc6)")
+ end
+
+ it "raises an error if engine is used but engine version is not" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby "1.8.7", :engine => 'rbx'
+
+ gem "foo"
+ G
+
+ bundle "platform", raise_on_error: false
+
+ expect(exitstatus).not_to eq(0)
+ end
+
+ it "raises an error if engine_version is used but engine is not" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby "1.8.7", :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle "platform", raise_on_error: false
+
+ expect(exitstatus).not_to eq(0)
+ end
+
+ it "raises an error if engine version doesn't match ruby version for MRI" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby "1.8.7", :engine => 'ruby', :engine_version => '1.2.4'
+
+ gem "foo"
+ G
+
+ bundle "platform", raise_on_error: false
+
+ expect(exitstatus).not_to eq(0)
+ end
+
+ it "should print if no ruby version is specified" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "foo"
+ G
+
+ bundle "platform --ruby"
+
+ expect(out).to eq("No ruby version specified")
+ end
+
+ it "handles when there is a locked requirement" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby "< 1.8.7"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+
+ RUBY VERSION
+ ruby 1.0.0p127
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "platform --ruby"
+ expect(out).to eq("ruby 1.0.0")
+ end
+
+ it "handles when there is a lockfile with no requirement" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "platform --ruby"
+ expect(out).to eq("No ruby version specified")
+ end
+
+ it "handles when there is a requirement in the gemfile" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby ">= 1.8.7"
+ G
+
+ bundle "platform --ruby"
+ expect(out).to eq("ruby 1.8.7")
+ end
+
+ it "handles when there are multiple requirements in the gemfile" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby ">= 1.8.7", "< 2.0.0"
+ G
+
+ bundle "platform --ruby"
+ expect(out).to eq("ruby 1.8.7")
+ end
+ end
+
+ let(:ruby_version_correct) { "ruby \"#{Gem.ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{local_engine_version}\"" }
+ let(:ruby_version_correct_engineless) { "ruby \"#{Gem.ruby_version}\"" }
+ let(:ruby_version_correct_patchlevel) { "#{ruby_version_correct}, :patchlevel => '#{RUBY_PATCHLEVEL}'" }
+ let(:ruby_version_incorrect) { "ruby \"#{not_local_ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_ruby_version}\"" }
+ let(:engine_incorrect) { "ruby \"#{Gem.ruby_version}\", :engine => \"#{not_local_tag}\", :engine_version => \"#{Gem.ruby_version}\"" }
+ let(:engine_version_incorrect) { "ruby \"#{Gem.ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_engine_version}\"" }
+ let(:patchlevel_incorrect) { "#{ruby_version_correct}, :patchlevel => '#{not_local_patchlevel}'" }
+ let(:patchlevel_fixnum) { "#{ruby_version_correct}, :patchlevel => #{RUBY_PATCHLEVEL}1" }
+
+ def should_be_ruby_version_incorrect
+ expect(exitstatus).to eq(18)
+ expect(err).to be_include("Your Ruby version is #{Gem.ruby_version}, but your Gemfile specified #{not_local_ruby_version}")
+ end
+
+ def should_be_engine_incorrect
+ expect(exitstatus).to eq(18)
+ expect(err).to be_include("Your Ruby engine is #{local_ruby_engine}, but your Gemfile specified #{not_local_tag}")
+ end
+
+ def should_be_engine_version_incorrect
+ expect(exitstatus).to eq(18)
+ expect(err).to be_include("Your #{local_ruby_engine} version is #{local_engine_version}, but your Gemfile specified #{local_ruby_engine} #{not_local_engine_version}")
+ end
+
+ def should_ignore_patchlevel
+ expect(exitstatus).to eq(0)
+ expect(err).to eq("")
+ end
+
+ def should_be_patchlevel_fixnum
+ expect(exitstatus).to eq(18)
+ expect(err).to be_include("The Ruby patchlevel in your Gemfile must be a string")
+ end
+
+ context "bundle install" do
+ it "installs fine when the ruby version matches" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{ruby_version_correct}
+ G
+
+ expect(bundled_app_lock).to exist
+ end
+
+ it "installs fine with any engine", :jruby_only do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ expect(bundled_app_lock).to exist
+ end
+
+ it "installs fine when the patchlevel matches" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{ruby_version_correct_patchlevel}
+ G
+
+ expect(bundled_app_lock).to exist
+ end
+
+ it "doesn't install when the ruby version doesn't match" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{ruby_version_incorrect}
+ G
+
+ expect(bundled_app_lock).not_to exist
+ should_be_ruby_version_incorrect
+ end
+
+ it "doesn't install when engine doesn't match" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{engine_incorrect}
+ G
+
+ expect(bundled_app_lock).not_to exist
+ should_be_engine_incorrect
+ end
+
+ it "doesn't install when engine version doesn't match", :jruby_only do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{engine_version_incorrect}
+ G
+
+ expect(bundled_app_lock).not_to exist
+ should_be_engine_version_incorrect
+ end
+
+ it "does install even when patchlevel doesn't match" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{patchlevel_incorrect}
+ G
+
+ expect(bundled_app_lock).to exist
+ should_ignore_patchlevel
+ end
+ end
+
+ context "bundle check" do
+ it "checks fine when the ruby version matches" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{ruby_version_correct}
+ G
+
+ bundle :check
+ expect(out).to match(/\AResolving dependencies\.\.\.\.*\nThe Gemfile's dependencies are satisfied\z/)
+ end
+
+ it "checks fine with any engine", :jruby_only do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle :check
+ expect(out).to match(/\AResolving dependencies\.\.\.\.*\nThe Gemfile's dependencies are satisfied\z/)
+ end
+
+ it "fails when ruby version doesn't match" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle :check, raise_on_error: false
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when engine doesn't match" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{engine_incorrect}
+ G
+
+ bundle :check, raise_on_error: false
+ should_be_engine_incorrect
+ end
+
+ it "fails when engine version doesn't match", :jruby_only do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{engine_version_incorrect}
+ G
+
+ bundle :check, raise_on_error: false
+ should_be_engine_version_incorrect
+ end
+
+ it "checks fine even when patchlevel doesn't match" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle :check
+ should_ignore_patchlevel
+ end
+ end
+
+ context "bundle update" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+ G
+ end
+
+ it "updates successfully when the ruby version matches" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+
+ #{ruby_version_correct}
+ G
+ update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update", all: true
+ expect(the_bundle).to include_gems "myrack 1.2", "myrack-obama 1.0", "activesupport 3.0"
+ end
+
+ it "updates fine with any engine", :jruby_only do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+
+ #{ruby_version_correct_engineless}
+ G
+ update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle "update", all: true
+ expect(the_bundle).to include_gems "myrack 1.2", "myrack-obama 1.0", "activesupport 3.0"
+ end
+
+ it "fails when ruby version doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+
+ #{ruby_version_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, all: true, raise_on_error: false
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when ruby engine doesn't match", :jruby_only do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+
+ #{engine_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, all: true, raise_on_error: false
+ should_be_engine_incorrect
+ end
+
+ it "fails when ruby engine version doesn't match", :jruby_only do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+
+ #{engine_version_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, all: true, raise_on_error: false
+ should_be_engine_version_incorrect
+ end
+
+ it "updates fine even when patchlevel doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+
+ #{patchlevel_incorrect}
+ G
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ end
+
+ bundle :update, all: true
+ should_ignore_patchlevel
+ expect(the_bundle).to include_gems "activesupport 3.0"
+ end
+ end
+
+ context "bundle info" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails"
+ G
+ end
+
+ it "prints path if ruby version is correct" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails"
+
+ #{ruby_version_correct}
+ G
+
+ bundle "info rails --path"
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+
+ it "prints path if ruby version is correct for any engine", :jruby_only do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "info rails --path"
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+
+ it "fails if ruby version doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "show rails", raise_on_error: false
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails if engine doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails"
+
+ #{engine_incorrect}
+ G
+
+ bundle "show rails", raise_on_error: false
+ should_be_engine_incorrect
+ end
+
+ it "fails if engine version doesn't match", jruby_only: true do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails"
+
+ #{engine_version_incorrect}
+ G
+
+ bundle "show rails", raise_on_error: false
+ should_be_engine_version_incorrect
+ end
+
+ it "prints path even when patchlevel doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle "show rails"
+ should_ignore_patchlevel
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
+ end
+ end
+
+ context "bundle cache" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+
+ #{ruby_version_correct}
+ G
+
+ bundle :cache
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches for any engine", :jruby_only do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle :cache
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
+ end
+
+ it "fails if the ruby version doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle :cache, raise_on_error: false
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails if the engine doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+
+ #{engine_incorrect}
+ G
+
+ bundle :cache, raise_on_error: false
+ should_be_engine_incorrect
+ end
+
+ it "fails if the engine version doesn't match", :jruby_only do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+
+ #{engine_version_incorrect}
+ G
+
+ bundle :cache, raise_on_error: false
+ should_be_engine_version_incorrect
+ end
+
+ it "copies the .gem file to vendor/cache even when patchlevel doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle :cache
+ should_ignore_patchlevel
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
+ end
+ end
+
+ context "bundle pack" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+
+ #{ruby_version_correct}
+ G
+
+ bundle :cache
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
+ end
+
+ it "copies the .gem file to vendor/cache when ruby version matches any engine", :jruby_only do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle :cache
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
+ end
+
+ it "fails if the ruby version doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle :cache, raise_on_error: false
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails if the engine doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+
+ #{engine_incorrect}
+ G
+
+ bundle :cache, raise_on_error: false
+ should_be_engine_incorrect
+ end
+
+ it "fails if the engine version doesn't match", :jruby_only do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+
+ #{engine_version_incorrect}
+ G
+
+ bundle :cache, raise_on_error: false
+ should_be_engine_version_incorrect
+ end
+
+ it "copies the .gem file to vendor/cache even when patchlevel doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle :cache
+ should_ignore_patchlevel
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
+ end
+ end
+
+ context "bundle exec" do
+ before do
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ system_gems "myrack-1.0.0", "myrack-0.9.1", path: default_bundle_path
+ end
+
+ it "activates the correct gem when ruby version matches" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
+
+ #{ruby_version_correct}
+ G
+
+ bundle "exec myrackup"
+ expect(out).to include("0.9.1")
+ end
+
+ it "activates the correct gem when ruby version matches any engine", :jruby_only do
+ system_gems "myrack-1.0.0", "myrack-0.9.1", path: default_bundle_path
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "exec myrackup"
+ expect(out).to include("0.9.1")
+ end
+
+ it "fails when the ruby version doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "exec myrackup", raise_on_error: false
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when the engine doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
+
+ #{engine_incorrect}
+ G
+
+ bundle "exec myrackup", raise_on_error: false
+ should_be_engine_incorrect
+ end
+
+ it "fails when the engine version doesn't match", :jruby_only do
+ gemfile <<-G
+ gem "myrack", "0.9.1"
+
+ #{engine_version_incorrect}
+ G
+
+ bundle "exec myrackup", raise_on_error: false
+ should_be_engine_version_incorrect
+ end
+
+ it "activates the correct gem even when patchlevel doesn't match" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle "exec myrackup"
+ should_ignore_patchlevel
+ expect(out).to include("1.0.0")
+ end
+ end
+
+ context "bundle console" do
+ before do
+ build_repo2 do
+ build_dummy_irb
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ gem "myrack"
+ gem "activesupport", :group => :test
+ gem "myrack_middleware", :group => :development
+ G
+ end
+
+ it "starts IRB with the default group loaded when ruby version matches", :readline do
+ gemfile gemfile + "\n\n#{ruby_version_correct}\n"
+
+ bundle "console" do |input, _, _|
+ input.puts("puts MYRACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "starts IRB with the default group loaded when ruby version matches", :readline, :jruby_only do
+ gemfile gemfile + "\n\n#{ruby_version_correct_engineless}\n"
+
+ bundle "console" do |input, _, _|
+ input.puts("puts MYRACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "fails when ruby version doesn't match" do
+ gemfile gemfile + "\n\n#{ruby_version_incorrect}\n"
+
+ bundle "console", raise_on_error: false
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when engine doesn't match" do
+ gemfile gemfile + "\n\n#{engine_incorrect}\n"
+
+ bundle "console", raise_on_error: false
+ should_be_engine_incorrect
+ end
+
+ it "fails when engine version doesn't match", :jruby_only do
+ gemfile gemfile + "\n\n#{engine_version_incorrect}\n"
+
+ bundle "console", raise_on_error: false
+ should_be_engine_version_incorrect
+ end
+
+ it "starts IRB with the default group loaded even when patchlevel doesn't match", :readline do
+ gemfile gemfile + "\n\n#{patchlevel_incorrect}\n"
+
+ bundle "console" do |input, _, _|
+ input.puts("puts MYRACK")
+ input.puts("exit")
+ end
+ should_ignore_patchlevel
+ expect(out).to include("0.9.1")
+ end
+ end
+
+ context "Bundler.setup" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "yard"
+ gem "myrack", :group => :test
+ G
+
+ ENV["BUNDLER_FORCE_TTY"] = "true"
+ end
+
+ it "makes a Gemfile.lock if setup succeeds" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "yard"
+ gem "myrack"
+
+ #{ruby_version_correct}
+ G
+
+ FileUtils.rm(bundled_app_lock)
+
+ run "1"
+ expect(bundled_app_lock).to exist
+ end
+
+ it "makes a Gemfile.lock if setup succeeds for any engine", :jruby_only do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "yard"
+ gem "myrack"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ FileUtils.rm(bundled_app_lock)
+
+ run "1"
+ expect(bundled_app_lock).to exist
+ end
+
+ it "fails when ruby version doesn't match" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "yard"
+ gem "myrack"
+
+ #{ruby_version_incorrect}
+ G
+
+ FileUtils.rm(bundled_app_lock)
+
+ ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false
+
+ expect(bundled_app_lock).not_to exist
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when engine doesn't match" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "yard"
+ gem "myrack"
+
+ #{engine_incorrect}
+ G
+
+ FileUtils.rm(bundled_app_lock)
+
+ ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false
+
+ expect(bundled_app_lock).not_to exist
+ should_be_engine_incorrect
+ end
+
+ it "fails when engine version doesn't match", :jruby_only do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "yard"
+ gem "myrack"
+
+ #{engine_version_incorrect}
+ G
+
+ FileUtils.rm(bundled_app_lock)
+
+ ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false
+
+ expect(bundled_app_lock).not_to exist
+ should_be_engine_version_incorrect
+ end
+
+ it "makes a Gemfile.lock even when patchlevel doesn't match" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "yard"
+ gem "myrack"
+
+ #{patchlevel_incorrect}
+ G
+
+ FileUtils.rm(bundled_app_lock)
+
+ ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }
+
+ should_ignore_patchlevel
+ expect(bundled_app_lock).to exist
+ end
+ end
+
+ context "bundle outdated" do
+ before do
+ build_repo2 do
+ build_git "foo", path: lib_path("foo")
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+ end
+
+ it "returns list of outdated gems when the ruby version matches" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", path: lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{ruby_version_correct}
+ G
+
+ bundle "outdated", raise_on_error: false
+
+ expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
+ Gem Current Latest Requested Groups Release Date
+ activesupport 2.3.5 3.0 = 2.3.5 default
+ foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default
+ TABLE
+
+ expect(out).to match(Regexp.new(expected_output))
+ end
+
+ it "returns list of outdated gems when the ruby version matches for any engine", :jruby_only do
+ bundle :install
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", path: lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{ruby_version_correct_engineless}
+ G
+
+ bundle "outdated", raise_on_error: false
+
+ expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
+ Gem Current Latest Requested Groups Release Date
+ activesupport 2.3.5 3.0 = 2.3.5 default
+ foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default
+ TABLE
+
+ expect(out).to match(Regexp.new(expected_output))
+ end
+
+ it "fails when the ruby version doesn't match" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", path: lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{ruby_version_incorrect}
+ G
+
+ bundle "outdated", raise_on_error: false
+ should_be_ruby_version_incorrect
+ end
+
+ it "fails when the engine doesn't match" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", path: lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{engine_incorrect}
+ G
+
+ bundle "outdated", raise_on_error: false
+ should_be_engine_incorrect
+ end
+
+ it "fails when the engine version doesn't match", :jruby_only do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", path: lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{engine_version_incorrect}
+ G
+
+ bundle "outdated", raise_on_error: false
+ should_be_engine_version_incorrect
+ end
+
+ it "reports outdated gems even when patchlevel doesn't match" do
+ update_repo2 do
+ build_gem "activesupport", "3.0"
+ update_git "foo", path: lib_path("foo")
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport", "2.3.5"
+ gem "foo", :git => "#{lib_path("foo")}"
+
+ #{patchlevel_incorrect}
+ G
+
+ bundle "outdated", raise_on_error: false
+ expect(err).not_to include("patchlevel")
+ expect(out).to include("activesupport")
+ expect(out).to include("foo")
+ 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..088fc29fe1 100644
--- a/spec/bundler/commands/post_bundle_message_spec.rb
+++ b/spec/bundler/commands/post_bundle_message_spec.rb
@@ -3,13 +3,13 @@
RSpec.describe "post bundle message" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
gem "activesupport", "2.3.5", :group => [:emo, :test]
group :test do
gem "rspec"
end
- gem "rack-obama", :group => :obama
+ gem "myrack-obama", :group => :obama
G
end
@@ -18,197 +18,165 @@ RSpec.describe "post bundle message" do
let(:bundle_show_path_message) { "Bundled gems are installed into `#{bundle_path}`" }
let(:bundle_complete_message) { "Bundle complete!" }
let(:bundle_updated_message) { "Bundle updated!" }
- let(:installed_gems_stats) { "4 Gemfile dependencies, 5 gems now installed." }
- let(:bundle_show_message) { Bundler::VERSION.split(".").first.to_i < 3 ? bundle_show_system_message : bundle_show_path_message }
+ let(:installed_gems_stats) { "4 Gemfile dependencies, 4 gems now installed." }
+
+ describe "when installing to system gems" do
+ before do
+ bundle_config "path.system true"
+ end
- describe "for fresh bundle install" do
it "shows proper messages according to the configured groups" do
bundle :install
- expect(out).to include(bundle_show_message)
+ expect(out).to include(bundle_show_system_message)
expect(out).not_to include("Gems in the group")
expect(out).to include(bundle_complete_message)
expect(out).to include(installed_gems_stats)
- bundle "config --local without emo"
+ bundle_config "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(bundle_show_system_message)
+ 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 "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(bundle_show_system_message)
+ 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.")
+ expect(out).to include("4 Gemfile dependencies, 2 gems now installed.")
- bundle "config --local without emo obama test"
+ bundle_config "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(bundle_show_system_message)
+ 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.")
+ expect(out).to include("4 Gemfile dependencies, 1 gem now installed.")
end
- describe "with `path` configured" do
- let(:bundle_path) { "./vendor" }
-
- it "shows proper messages according to the configured groups" do
- bundle "config --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 :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(bundle_complete_message)
-
- bundle "config --local path vendor"
- bundle "config --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(bundle_complete_message)
-
- bundle "config --local path vendor"
- bundle "config --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")
+ describe "for second bundle install run" do
+ it "without any options" do
+ 2.times { bundle :install }
+ expect(out).to include(bundle_show_system_message)
+ expect(out).to_not include("Gems in the groups")
expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
end
end
+ end
- describe "with an absolute `path` inside the cwd configured" do
- let(:bundle_path) { bundled_app("cache") }
-
- it "shows proper messages according to the configured groups" do
- bundle "config --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")
- expect(out).to include(bundle_complete_message)
- end
- end
-
- describe "with `path` configured to an absolute path outside the cwd" 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 :install
- expect(out).to include("Bundled gems are installed into `#{tmp("not_bundled_app")}`")
- expect(out).to_not include("Gems in the group")
- expect(out).to include(bundle_complete_message)
- end
- end
+ describe "with `path` configured" do
+ let(:bundle_path) { "./vendor" }
- describe "with misspelled or non-existent gem name" do
- before do
- bundle "config set force_ruby_platform true"
- end
+ it "shows proper messages according to the configured groups" do
+ bundle_config "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)
- 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
+ bundle_config "path vendor"
+ bundle_config "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(bundle_complete_message)
- 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 <<-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
+ bundle_config "path vendor"
+ bundle_config "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(bundle_complete_message)
- it "should report a helpful error message with reference to cache if available" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
- bundle :cache
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
- 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").
- and include("or in gems cached in vendor/cache.")
- end
+ bundle_config "path vendor"
+ bundle_config "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(bundle_complete_message)
end
end
- describe "for second bundle install run", :bundler => "< 3" do
- it "without any options" do
- 2.times { bundle :install }
- expect(out).to include(bundle_show_message)
- expect(out).to_not include("Gems in the groups")
- expect(out).to include(bundle_complete_message)
- expect(out).to include(installed_gems_stats)
- end
+ describe "with an absolute `path` inside the cwd configured" do
+ let(:bundle_path) { bundled_app("cache") }
- it "with --without one group" do
- bundle "install --without emo"
+ it "shows proper messages according to the configured groups" do
+ bundle_config "path #{bundle_path}"
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("Bundled gems are installed into `./cache`")
+ expect(out).to_not include("Gems in the group")
expect(out).to include(bundle_complete_message)
- expect(out).to include(installed_gems_stats)
end
+ end
- it "with --without two groups" do
- bundle "install --without emo test"
+ describe "with `path` configured to an absolute path outside the cwd" do
+ let(:bundle_path) { tmp("not_bundled_app") }
+
+ it "shows proper messages according to the configured groups" do
+ bundle_config "path #{bundle_path}"
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("Bundled gems are installed into `#{tmp("not_bundled_app")}`")
+ expect(out).to_not include("Gems in the group")
expect(out).to include(bundle_complete_message)
end
+ end
+
+ describe "with misspelled or non-existent gem name" do
+ before do
+ bundle_config "force_ruby_platform true"
+ end
- it "with --without more groups" do
- 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(bundle_complete_message)
+ it "should report a helpful error message" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(err).to include <<~EOS.strip
+ Could not find gem 'not-a-gem' in rubygems repository https://gem.repo1/ or installed locally.
+ EOS
+ end
+
+ it "should report a helpful error message with reference to cache if available" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+ bundle :cache
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(err).to include("Could not find gem 'not-a-gem' in").
+ and include("or in gems cached in vendor/cache.")
end
end
describe "for bundle update" do
it "shows proper messages according to the configured groups" do
- bundle :update, :all => true
+ bundle :update, all: true
expect(out).not_to include("Gems in the groups")
expect(out).to include(bundle_updated_message)
- bundle "config --local without emo"
+ bundle_config "without emo"
bundle :install
- bundle :update, :all => true
- expect(out).to include("Gems in the group emo were not updated")
+ bundle :update, all: true
+ 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 "without emo test"
bundle :install
- bundle :update, :all => true
- expect(out).to include("Gems in the groups emo and test were not updated")
+ bundle :update, all: true
+ 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 "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")
+ bundle :update, all: true
+ 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 20958ef338..5f80b9e534 100644
--- a/spec/bundler/commands/pristine_spec.rb
+++ b/spec/bundler/commands/pristine_spec.rb
@@ -2,9 +2,9 @@
require "bundler/vendored_fileutils"
-RSpec.describe "bundle pristine", :ruby_repo do
+RSpec.describe "bundle pristine" do
before :each do
- build_lib "baz", :path => bundled_app do |s|
+ build_lib "baz", path: bundled_app do |s|
s.version = "1.0.0"
s.add_development_dependency "baz-dev", "=1.0.0"
end
@@ -13,16 +13,16 @@ RSpec.describe "bundle pristine", :ruby_repo do
build_gem "weakling"
build_gem "baz-dev", "1.0.0"
build_gem "very_simple_binary", &:add_c_extension
- build_git "foo", :path => lib_path("foo")
- build_git "git_with_ext", :path => lib_path("git_with_ext"), &:add_c_extension
- build_lib "bar", :path => lib_path("bar")
+ build_git "foo", path: lib_path("foo")
+ build_git "git_with_ext", path: lib_path("git_with_ext"), &:add_c_extension
+ build_lib "bar", path: lib_path("bar")
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "weakling"
gem "very_simple_binary"
- gem "foo", :git => "#{lib_path("foo")}", :branch => "master"
+ gem "foo", :git => "#{lib_path("foo")}", :branch => "main"
gem "git_with_ext", :git => "#{lib_path("git_with_ext")}"
gem "bar", :path => "#{lib_path("bar")}"
@@ -49,13 +49,7 @@ RSpec.describe "bundle pristine", :ruby_repo do
bundle "pristine"
bundle "-v"
- expected = if Bundler::VERSION < "3.0"
- "Bundler version"
- else
- Bundler::VERSION
- end
-
- expect(out).to start_with(expected)
+ expect(out).to end_with(Bundler::VERSION)
end
end
@@ -85,7 +79,7 @@ RSpec.describe "bundle pristine", :ruby_repo do
it "displays warning and ignores changes when a local config exists" do
spec = find_spec("foo")
- bundle "config set local.#{spec.name} #{lib_path(spec.name)}"
+ bundle_config "local.#{spec.name} #{lib_path(spec.name)}"
changes_txt = Pathname.new(spec.full_gem_path).join("lib/changes.txt")
FileUtils.touch(changes_txt)
@@ -93,7 +87,67 @@ RSpec.describe "bundle pristine", :ruby_repo do
bundle "pristine"
expect(changes_txt).to be_file
- expect(err).to include("Cannot pristine #{spec.name} (#{spec.version}#{spec.git_version}). Gem is locally overriden.")
+ expect(err).to include("Cannot pristine #{spec.name} (#{spec.version}#{spec.git_version}). Gem is locally overridden.")
+ end
+
+ it "doesn't run multiple git processes for the same repository" do
+ nested_gems = [
+ "actioncable",
+ "actionmailer",
+ "actionpack",
+ "actionview",
+ "activejob",
+ "activemodel",
+ "activerecord",
+ "activestorage",
+ "activesupport",
+ "railties",
+ ]
+
+ build_repo2 do
+ nested_gems.each do |gem|
+ build_lib gem, path: lib_path("rails/#{gem}")
+ end
+
+ build_git "rails", path: lib_path("rails") do |s|
+ nested_gems.each do |gem|
+ s.add_dependency gem
+ end
+ end
+ end
+
+ install_gemfile <<-G
+ source 'https://rubygems.org'
+
+ git "#{lib_path("rails")}" do
+ gem "rails"
+ gem "actioncable"
+ gem "actionmailer"
+ gem "actionpack"
+ gem "actionview"
+ gem "activejob"
+ gem "activemodel"
+ gem "activerecord"
+ gem "activestorage"
+ gem "activesupport"
+ gem "railties"
+ end
+ G
+
+ changed_files = []
+ diff = "#Pristine spec changes"
+
+ nested_gems.each do |gem|
+ spec = find_spec(gem)
+ changed_files << Pathname.new(spec.full_gem_path).join("lib/#{gem}.rb")
+ File.open(changed_files.last, "a") {|f| f.puts diff }
+ end
+
+ bundle "pristine"
+
+ changed_files.each do |changed_file|
+ expect(File.read(changed_file)).to_not include(diff)
+ end
end
end
@@ -164,7 +218,7 @@ RSpec.describe "bundle pristine", :ruby_repo do
end
it "raises when one of them is not in the lockfile" do
- bundle "pristine abcabcabc", :raise_on_error => false
+ bundle "pristine abcabcabc", raise_on_error: false
expect(err).to include("Could not find gem 'abcabcabc'.")
end
end
@@ -173,7 +227,7 @@ RSpec.describe "bundle pristine", :ruby_repo do
let(:very_simple_binary) { find_spec("very_simple_binary") }
let(:c_ext_dir) { Pathname.new(very_simple_binary.full_gem_path).join("ext") }
let(:build_opt) { "--with-ext-lib=#{c_ext_dir}" }
- before { bundle "config set build.very_simple_binary -- #{build_opt}" }
+ before { bundle_config "build.very_simple_binary -- #{build_opt}" }
# This just verifies that the generated Makefile from the c_ext gem makes
# use of the build_args from the bundle config
@@ -181,8 +235,8 @@ RSpec.describe "bundle pristine", :ruby_repo do
bundle "pristine"
makefile_contents = File.read(c_ext_dir.join("Makefile").to_s)
- expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/)
- expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/)
+ expect(makefile_contents).to match(/libpath =.*#{Regexp.escape(c_ext_dir.to_s)}/)
+ expect(makefile_contents).to match(/LIBPATH =.*-L#{Regexp.escape(c_ext_dir.to_s)}/)
end
end
@@ -190,7 +244,7 @@ RSpec.describe "bundle pristine", :ruby_repo do
let(:git_with_ext) { find_spec("git_with_ext") }
let(:c_ext_dir) { Pathname.new(git_with_ext.full_gem_path).join("ext") }
let(:build_opt) { "--with-ext-lib=#{c_ext_dir}" }
- before { bundle "config set build.git_with_ext -- #{build_opt}" }
+ before { bundle_config "build.git_with_ext -- #{build_opt}" }
# This just verifies that the generated Makefile from the c_ext gem makes
# use of the build_args from the bundle config
@@ -198,8 +252,18 @@ RSpec.describe "bundle pristine", :ruby_repo do
bundle "pristine"
makefile_contents = File.read(c_ext_dir.join("Makefile").to_s)
- expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/)
- expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/)
+ expect(makefile_contents).to match(/libpath =.*#{Regexp.escape(c_ext_dir.to_s)}/)
+ expect(makefile_contents).to match(/LIBPATH =.*-L#{Regexp.escape(c_ext_dir.to_s)}/)
+ 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
diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb
index bee4490a8d..8a2e6778ea 100644
--- a/spec/bundler/commands/remove_spec.rb
+++ b/spec/bundler/commands/remove_spec.rb
@@ -4,27 +4,42 @@ RSpec.describe "bundle remove" do
context "when no gems are specified" do
it "throws error" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
G
- bundle "remove", :raise_on_error => false
+ bundle "remove", raise_on_error: false
expect(err).to include("Please specify gems to remove.")
end
end
- context "when --install flag is specified" do
- it "removes gems from .bundle" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ context "after 'bundle install' is run" do
+ describe "running 'bundle remove GEM_NAME'" do
+ it "removes it from the lockfile" do
+ myrack_dep = <<~L
- gem "rack"
- G
+ DEPENDENCIES
+ myrack
+
+ L
+
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "myrack"
+ G
+
+ bundle "install"
+
+ expect(lockfile).to include(myrack_dep)
- bundle "remove rack --install"
+ bundle "remove myrack"
- expect(out).to include("rack was removed.")
- expect(the_bundle).to_not include_gems "rack"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
+ G
+ expect(lockfile).to_not include(myrack_dep)
+ end
end
end
@@ -32,36 +47,39 @@ RSpec.describe "bundle remove" do
context "when gem is present in gemfile" do
it "shows success for removed gem" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
G
- bundle "remove rack"
+ bundle "remove myrack"
- expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(out).to include("myrack was removed.")
+ expect(the_bundle).to_not include_gems "myrack"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
G
end
context "when gem is specified in multiple lines" do
it "shows success for removed gem" do
+ build_git "myrack"
+
gemfile <<-G
- source '#{file_uri_for(gem_repo1)}'
+ source 'https://gem.repo1'
gem 'git'
- gem 'rack',
- git: 'https://github.com/rack/rack',
- branch: 'master'
+ gem 'myrack',
+ git: "#{lib_path("myrack-1.0")}",
+ branch: 'main'
gem 'nokogiri'
G
- bundle "remove rack"
+ bundle "remove myrack"
- expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
- source '#{file_uri_for(gem_repo1)}'
+ expect(out).to include("myrack was removed.")
+ expect(gemfile).to eq <<~G
+ source 'https://gem.repo1'
gem 'git'
gem 'nokogiri'
@@ -73,32 +91,32 @@ RSpec.describe "bundle remove" do
context "when gem is not present in gemfile" do
it "shows warning for gem that could not be removed" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
G
- bundle "remove rack", :raise_on_error => false
+ bundle "remove myrack", raise_on_error: false
- expect(err).to include("`rack` is not specified in #{bundled_app_gemfile} so it could not be removed.")
+ expect(err).to include("`myrack` is not specified in #{bundled_app_gemfile} so it could not be removed.")
end
end
end
- describe "remove mutiple gems from gemfile" do
+ describe "remove multiple gems from gemfile" do
context "when all gems are present in gemfile" do
it "shows success fir all removed gems" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
gem "rails"
G
- bundle "remove rack rails"
+ bundle "remove myrack rails"
- expect(out).to include("rack was removed.")
+ expect(out).to include("myrack was removed.")
expect(out).to include("rails was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
G
end
end
@@ -106,18 +124,18 @@ RSpec.describe "bundle remove" do
context "when some gems are not present in the gemfile" do
it "shows warning for those not present and success for those that can be removed" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
gem "minitest"
gem "rspec"
G
- bundle "remove rails rack minitest", :raise_on_error => false
+ bundle "remove rails myrack 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
- source "#{file_uri_for(gem_repo1)}"
+ expect(err).to include("`myrack` is not specified in #{bundled_app_gemfile} so it could not be removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
gem "rails"
gem "minitest"
@@ -130,16 +148,16 @@ RSpec.describe "bundle remove" do
context "with inline groups" do
it "removes the specified gem" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", :group => [:dev]
+ gem "myrack", :group => [:dev]
G
- bundle "remove rack"
+ bundle "remove myrack"
- expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(out).to include("myrack was removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
G
end
end
@@ -148,7 +166,7 @@ RSpec.describe "bundle remove" do
context "when single group block with gem to be removed is present" do
it "removes the group block" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
group :test do
gem "rspec"
@@ -158,8 +176,8 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
G
end
end
@@ -167,19 +185,19 @@ RSpec.describe "bundle remove" do
context "when gem to be removed is outside block" do
it "does not modify group" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
group :test do
gem "coffee-script-source"
end
G
- bundle "remove rack"
+ bundle "remove myrack"
- expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(out).to include("myrack was removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
group :test do
gem "coffee-script-source"
@@ -191,7 +209,7 @@ RSpec.describe "bundle remove" do
context "when an empty block is also present" do
it "removes all empty blocks" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
group :test do
gem "rspec"
@@ -204,16 +222,16 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
G
end
end
- context "when the gem belongs to mutiple groups" do
+ context "when the gem belongs to multiple groups" do
it "removes the groups" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
group :test, :serioustest do
gem "rspec"
@@ -223,16 +241,16 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(gemfile).to eq <<~G
+ source "https://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)}"
+ source "https://gem.repo1"
group :one do
gem "rspec"
@@ -246,8 +264,8 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
G
end
end
@@ -257,7 +275,7 @@ RSpec.describe "bundle remove" do
context "when all the groups will be empty after removal" do
it "removes the empty nested blocks" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
group :test do
group :serioustest do
@@ -269,8 +287,8 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
G
end
end
@@ -278,10 +296,10 @@ RSpec.describe "bundle remove" do
context "when outer group will not be empty after removal" do
it "removes only empty blocks" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
group :test do
- gem "rack-test"
+ gem "myrack-test"
group :serioustest do
gem "rspec"
@@ -292,11 +310,11 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
group :test do
- gem "rack-test"
+ gem "myrack-test"
end
G
@@ -306,12 +324,12 @@ RSpec.describe "bundle remove" do
context "when inner group will not be empty after removal" do
it "removes only empty blocks" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
group :test do
group :serioustest do
gem "rspec"
- gem "rack-test"
+ gem "myrack-test"
end
end
G
@@ -319,12 +337,12 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
group :test do
group :serioustest do
- gem "rack-test"
+ gem "myrack-test"
end
end
G
@@ -333,41 +351,41 @@ 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)}"
- gem "rack"; gem "rails"
+ source "https://gem.repo1"
+ gem "myrack"; gem "rails"
G
- bundle "remove rails", :raise_on_error => false
+ 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
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"; gem "rails"
+ expect(err).to include("Gems could not be removed. myrack (>= 0) would also have been removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
+ gem "myrack"; gem "rails"
G
end
end
context "when some gems could not be removed" do
it "shows warning for gems not removed and success for those removed" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem"rack"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem"myrack"
gem"rspec"
gem "rails"
gem "minitest"
G
- bundle "remove rails rack rspec minitest"
+ bundle "remove rails myrack rspec minitest"
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
- source "#{file_uri_for(gem_repo1)}"
- gem"rack"
+ expect(out).to include("myrack, rspec could not be removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
+ gem"myrack"
gem"rspec"
G
end
@@ -376,18 +394,18 @@ RSpec.describe "bundle remove" do
context "with sources" do
before do
- build_repo gem_repo3 do
+ build_repo3 do
build_gem "rspec"
end
end
it "removes gems and empty source blocks" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
- source "#{file_uri_for(gem_repo3)}" do
+ source "https://gem.repo3" do
gem "rspec"
end
G
@@ -397,10 +415,10 @@ RSpec.describe "bundle remove" do
bundle "remove rspec"
expect(out).to include("rspec was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
G
end
end
@@ -408,40 +426,40 @@ RSpec.describe "bundle remove" do
describe "with eval_gemfile" do
context "when gems are present in both gemfiles" do
it "removes the gems" do
- create_file "Gemfile-other", <<-G
- gem "rack"
+ gemfile "Gemfile-other", <<-G
+ gem "myrack"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
eval_gemfile "Gemfile-other"
- gem "rack"
+ gem "myrack"
G
- bundle "remove rack"
+ bundle "remove myrack"
- expect(out).to include("rack was removed.")
+ expect(out).to include("myrack was removed.")
end
end
context "when gems are present in other gemfile" do
it "removes the gems" do
- create_file "Gemfile-other", <<-G
- gem "rack"
+ gemfile "Gemfile-other", <<-G
+ gem "myrack"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
eval_gemfile "Gemfile-other"
G
- bundle "remove rack"
+ bundle "remove myrack"
- expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"")
- expect(out).to include("rack was removed.")
+ expect(bundled_app("Gemfile-other").read).to_not include("gem \"myrack\"")
+ expect(out).to include("myrack was removed.")
end
end
@@ -453,61 +471,61 @@ RSpec.describe "bundle remove" do
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
eval_gemfile "Gemfile-other"
G
- bundle "remove rack", :raise_on_error => false
+ bundle "remove myrack", raise_on_error: false
- expect(err).to include("`rack` is not specified in #{bundled_app_gemfile} so it could not be removed.")
+ expect(err).to include("`myrack` is not specified in #{bundled_app_gemfile} so it could not be removed.")
end
end
context "when the gem is present in parent file but not in gemfile specified by eval_gemfile" do
it "removes the gem" do
- create_file "Gemfile-other", <<-G
+ gemfile "Gemfile-other", <<-G
gem "rails"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
eval_gemfile "Gemfile-other"
- gem "rack"
+ gem "myrack"
G
- bundle "remove rack", :raise_on_error => false
+ bundle "remove myrack", raise_on_error: false
- 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
- source "#{file_uri_for(gem_repo1)}"
+ expect(out).to include("myrack was removed.")
+ expect(err).to include("`myrack` is not specified in #{bundled_app("Gemfile-other")} so it could not be removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
eval_gemfile "Gemfile-other"
G
end
end
- context "when gems can not be removed from other gemfile" do
+ context "when gems cannot be removed from other gemfile" do
it "shows error" do
- create_file "Gemfile-other", <<-G
- gem "rails"; gem "rack"
+ gemfile "Gemfile-other", <<-G
+ gem "rails"; gem "myrack"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
eval_gemfile "Gemfile-other"
- gem "rack"
+ gem "myrack"
G
- bundle "remove rack", :raise_on_error => false
+ bundle "remove myrack", raise_on_error: false
- expect(out).to include("rack was removed.")
+ expect(out).to include("myrack was removed.")
expect(err).to include("Gems could not be removed. rails (>= 0) would also have been removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
eval_gemfile "Gemfile-other"
G
@@ -516,47 +534,47 @@ RSpec.describe "bundle remove" do
context "when gems could not be removed from parent gemfile" do
it "shows error" do
- create_file "Gemfile-other", <<-G
- gem "rack"
+ gemfile "Gemfile-other", <<-G
+ gem "myrack"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
eval_gemfile "Gemfile-other"
- gem "rails"; gem "rack"
+ gem "rails"; gem "myrack"
G
- bundle "remove rack", :raise_on_error => false
+ bundle "remove myrack", raise_on_error: false
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
- source "#{file_uri_for(gem_repo1)}"
+ expect(bundled_app("Gemfile-other").read).to include("gem \"myrack\"")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
eval_gemfile "Gemfile-other"
- gem "rails"; gem "rack"
+ gem "rails"; gem "myrack"
G
end
end
context "when gem present in gemfiles but could not be removed from one from one of them" do
- it "removes gem which can be removed and shows warning for file from which it can not be removed" do
- create_file "Gemfile-other", <<-G
- gem "rack"
+ it "removes gem which can be removed and shows warning for file from which it cannot be removed" do
+ gemfile "Gemfile-other", <<-G
+ gem "myrack"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
eval_gemfile "Gemfile-other"
- gem"rack"
+ gem"myrack"
G
- bundle "remove rack"
+ bundle "remove myrack"
- expect(out).to include("rack was removed.")
- expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"")
+ expect(out).to include("myrack was removed.")
+ expect(bundled_app("Gemfile-other").read).to_not include("gem \"myrack\"")
end
end
end
@@ -564,18 +582,18 @@ RSpec.describe "bundle remove" do
context "with install_if" do
it "removes gems inside blocks and empty blocks" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
install_if(lambda { false }) do
- gem "rack"
+ gem "myrack"
end
G
- bundle "remove rack"
+ bundle "remove myrack"
- expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(out).to include("myrack was removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
G
end
end
@@ -583,32 +601,32 @@ RSpec.describe "bundle remove" do
context "with env" do
it "removes gems inside blocks and empty blocks" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
env "BUNDLER_TEST" do
- gem "rack"
+ gem "myrack"
end
G
- bundle "remove rack"
+ bundle "remove myrack"
- expect(out).to include("rack was removed.")
- gemfile_should_be <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(out).to include("myrack was removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
G
end
end
context "with gemspec" do
it "should not remove the gem" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp("foo")) do |s|
s.write("foo.gemspec", "")
- s.add_dependency "rack"
+ s.add_dependency "myrack"
end
install_gemfile(<<-G)
- source "#{file_uri_for(gem_repo1)}"
- gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ source "https://gem.repo1"
+ gemspec :path => '#{tmp("foo")}', :name => 'foo'
G
bundle "remove foo"
@@ -616,4 +634,103 @@ RSpec.describe "bundle remove" do
expect(out).to include("foo could not be removed.")
end
end
+
+ describe "with comments that mention gems" do
+ context "when comment is a separate line comment" do
+ it "does not remove the line comment" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ # gem "myrack" might be used in the future
+ gem "myrack"
+ G
+
+ bundle "remove myrack"
+
+ expect(out).to include("myrack was removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
+
+ # gem "myrack" might be used in the future
+ G
+ end
+ end
+
+ context "when gem specified for removal has an inline comment" do
+ it "removes the inline comment" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "myrack" # this can be removed
+ G
+
+ bundle "remove myrack"
+
+ expect(out).to include("myrack was removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
+ G
+ end
+ end
+
+ context "when gem specified for removal is mentioned in other gem's comment" do
+ it "does not remove other gem" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "puma" # implements interface provided by gem "myrack"
+
+ gem "myrack"
+ G
+
+ bundle "remove myrack"
+
+ expect(out).to_not include("puma was removed.")
+ expect(out).to include("myrack was removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
+ gem "puma" # implements interface provided by gem "myrack"
+ G
+ end
+ end
+
+ context "when gem specified for removal has a comment that mentions other gem" do
+ it "does not remove other gem" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "puma" # implements interface provided by gem "myrack"
+
+ gem "myrack"
+ G
+
+ bundle "remove puma"
+
+ expect(out).to include("puma was removed.")
+ expect(out).to_not include("myrack was removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
+
+ gem "myrack"
+ G
+ end
+ end
+ end
+
+ context "when gem definition has parentheses" do
+ it "removes the gem" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem("myrack")
+ gem("myrack", ">= 0")
+ gem("myrack", require: false)
+ G
+
+ bundle "remove myrack"
+
+ expect(out).to include("myrack was removed.")
+ expect(gemfile).to eq <<~G
+ source "https://gem.repo1"
+ G
+ end
+ end
end
diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb
index 30e9de9686..d0d55ffbb9 100644
--- a/spec/bundler/commands/show_spec.rb
+++ b/spec/bundler/commands/show_spec.rb
@@ -1,10 +1,12 @@
# frozen_string_literal: true
-RSpec.describe "bundle show", :bundler => "< 3" do
+RSpec.describe "bundle show" do
context "with a standard Gemfile" do
before :each do
+ build_repo2
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "rails"
G
end
@@ -35,12 +37,12 @@ RSpec.describe "bundle show", :bundler => "< 3" do
expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
end
- it "warns if path no longer exists on disk" do
- FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2"))
+ it "warns if specification is installed, but path does not exist on disk" do
+ FileUtils.rm_r(default_bundle_path("gems", "rails-2.3.2"))
bundle "show rails"
- expect(err).to match(/has been deleted/i)
+ expect(err).to match(/is missing/i)
expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
end
@@ -50,14 +52,14 @@ RSpec.describe "bundle show", :bundler => "< 3" do
end
it "complains if gem not in bundle" do
- bundle "show missing", :raise_on_error => false
+ bundle "show missing", raise_on_error: false
expect(err).to match(/could not find gem 'missing'/i)
end
it "prints path of all gems in bundle sorted by name" do
bundle "show --paths"
- expect(out).to include(default_bundle_path("gems", "rake-13.0.1").to_s)
+ expect(out).to include(default_bundle_path("gems", "rake-#{rake_version}").to_s)
expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
# Gem names are the last component of their path.
@@ -86,6 +88,24 @@ RSpec.describe "bundle show", :bundler => "< 3" do
\tStatus: Up to date
MSG
end
+
+ it "includes up to date status in summary of gems" do
+ update_repo2 do
+ build_gem "rails", "3.0.0"
+ end
+
+ bundle "show --verbose"
+
+ expect(out).to include <<~MSG
+ * rails (2.3.2)
+ \tSummary: This is just a fake gem for testing
+ \tHomepage: http://example.com
+ \tStatus: Outdated - 2.3.2 < 3.0.0
+ MSG
+
+ # check lockfile is not accidentally updated
+ expect(lockfile).to include("actionmailer (2.3.2)")
+ end
end
context "with a git repo in the Gemfile" do
@@ -100,11 +120,11 @@ RSpec.describe "bundle show", :bundler => "< 3" do
expect(the_bundle).to include_gems "foo 1.0"
bundle :show
- expect(out).to include("foo (1.0 #{@git.ref_for("master", 6)}")
+ expect(out).to include("foo (1.0 #{@git.ref_for("main", 6)}")
end
- it "prints out branch names other than master" do
- update_git "foo", :branch => "omg" do |s|
+ it "prints out branch names other than main" do
+ update_git "foo", branch: "omg" do |s|
s.write "lib/foo.rb", "FOO = '1.0.omg'"
end
@revision = revision_for(lib_path("foo-1.0"))[0...6]
@@ -129,7 +149,7 @@ RSpec.describe "bundle show", :bundler => "< 3" do
end
it "handles when a version is a '-' prerelease" do
- @git = build_git("foo", "1.0.0-beta.1", :path => lib_path("foo"))
+ @git = build_git("foo", "1.0.0-beta.1", path: lib_path("foo"))
install_gemfile <<-G
gem "foo", "1.0.0-beta.1", :git => "#{lib_path("foo")}"
G
@@ -142,24 +162,24 @@ RSpec.describe "bundle show", :bundler => "< 3" do
context "in a fresh gem in a blank git repo" do
before :each do
- build_git "foo", :path => lib_path("foo")
+ build_git "foo", path: lib_path("foo")
File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts "gemspec" }
- sys_exec "rm -rf .git && git init", :dir => lib_path("foo")
+ sys_exec "rm -rf .git && git init", dir: lib_path("foo")
end
it "does not output git errors" do
- bundle :show, :dir => lib_path("foo")
+ bundle :show, dir: lib_path("foo")
expect(err_without_deprecations).to be_empty
end
end
it "performs an automatic bundle install" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo"
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
bundle :show
expect(out).to include("Installing foo 1.0")
end
@@ -167,58 +187,31 @@ RSpec.describe "bundle show", :bundler => "< 3" do
context "with a valid regexp for gem name" do
it "presents alternatives", :readline do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rack-obama"
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "myrack-obama"
G
bundle "show rac"
- expect(out).to eq "1 : rack\n2 : rack-obama\n0 : - exit -\n>"
+ expect(out).to match(/\A1 : myrack\n2 : myrack-obama\n0 : - exit -(\n>|\z)/)
end
end
context "with an invalid regexp for gem name" do
it "does not find the gem" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
invalid_regexp = "[]"
- bundle "show #{invalid_regexp}", :raise_on_error => false
+ bundle "show #{invalid_regexp}", raise_on_error: false
expect(err).to include("Could not find gem '#{invalid_regexp}'.")
end
end
-
- context "--outdated option" do
- # Regression test for https://github.com/rubygems/bundler/issues/5375
- before do
- build_repo2
- end
-
- it "doesn't update gems to newer versions" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rails"
- G
-
- expect(the_bundle).to include_gem("rails 2.3.2")
-
- update_repo2 do
- build_gem "rails", "3.0.0" do |s|
- s.executables = "rails"
- end
- end
-
- bundle "show --outdated"
-
- bundle "install"
- expect(the_bundle).to include_gem("rails 2.3.2")
- end
- end
end
-RSpec.describe "bundle show", :bundler => "3" do
+RSpec.describe "bundle show", bundler: "5" do
pending "shows a friendly error about the command removal"
end
diff --git a/spec/bundler/commands/ssl_spec.rb b/spec/bundler/commands/ssl_spec.rb
new file mode 100644
index 0000000000..4220731b69
--- /dev/null
+++ b/spec/bundler/commands/ssl_spec.rb
@@ -0,0 +1,373 @@
+# frozen_string_literal: true
+
+require "bundler/cli"
+require "bundler/cli/doctor"
+require "bundler/cli/doctor/ssl"
+require_relative "../support/artifice/helpers/artifice"
+require "bundler/vendored_persistent.rb"
+
+RSpec.describe "bundle doctor ssl" do
+ before(:each) do
+ require_rack_test
+ require_relative "../support/artifice/helpers/endpoint"
+
+ @dummy_endpoint = Class.new(Endpoint) do
+ get "/" do
+ end
+ end
+
+ @previous_ui = Bundler.ui
+ Bundler.ui = Bundler::UI::Shell.new
+ Bundler.ui.level = "info"
+
+ @previous_client = Gem::Request::ConnectionPools.client
+ Artifice.activate_with(@dummy_endpoint)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+ end
+
+ after(:each) do
+ Bundler.ui = @previous_ui
+ Artifice.deactivate
+ Gem::Request::ConnectionPools.client = @previous_client
+ end
+
+ context "when a diagnostic fails" do
+ it "prints the diagnostic when openssl can't be loaded" do
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ allow(subject).to receive(:require).with("openssl").and_raise(LoadError)
+
+ expected_err = <<~MSG
+ Oh no! Your Ruby doesn't have OpenSSL, so it can't connect to rubygems.org.
+ You'll need to recompile or reinstall Ruby with OpenSSL support and try again.
+ MSG
+
+ expect { subject.run }.to output("").to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to certificate verification", :ruby_repo do
+ net_http = Class.new(Artifice::Net::HTTP) do
+ def connect
+ raise OpenSSL::SSL::SSLError, "certificate verify failed"
+ end
+ end
+
+ Artifice.replace_net_http(net_http)
+ Gem::Request::ConnectionPools.client = net_http
+ Gem::RemoteFetcher.fetcher.close_all
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (certificate verification)
+ RubyGems: failed (certificate verification)
+ Ruby net/http: failed
+
+ Unfortunately, this Ruby can't connect to rubygems.org.
+
+ Below affect only Ruby net/http connections:
+ SSL_CERT_FILE: exists #{OpenSSL::X509::DEFAULT_CERT_FILE}
+ SSL_CERT_DIR: exists #{OpenSSL::X509::DEFAULT_CERT_DIR}
+
+ Your Ruby can't connect to rubygems.org because you are missing the certificate files OpenSSL needs to verify you are connecting to the genuine rubygems.org servers.
+
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to a too old tls version" do
+ subject = Bundler::CLI::Doctor::SSL.new({})
+
+ net_http = Class.new(Artifice::Net::HTTP) do
+ def connect
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+
+ Artifice.replace_net_http(net_http)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+ Gem::RemoteFetcher.fetcher.close_all
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (SSL/TLS protocol version mismatch)
+ RubyGems: failed (SSL/TLS protocol version mismatch)
+ Ruby net/http: failed
+
+ Unfortunately, this Ruby can't connect to rubygems.org.
+
+ Your Ruby can't connect to rubygems.org because your version of OpenSSL is too old.
+ You'll need to upgrade your OpenSSL install and/or recompile Ruby to use a newer OpenSSL.
+
+ MSG
+
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to unsupported tls 1.3 version" do
+ net_http = Class.new(Artifice::Net::HTTP) do
+ def connect
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+
+ Artifice.replace_net_http(net_http)
+ Gem::Request::ConnectionPools.client = net_http
+ Gem::RemoteFetcher.fetcher.close_all
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (SSL/TLS protocol version mismatch)
+ RubyGems: failed (SSL/TLS protocol version mismatch)
+ Ruby net/http: failed
+
+ Unfortunately, this Ruby can't connect to rubygems.org.
+
+ Your Ruby can't connect to rubygems.org because TLS1_3 isn't supported yet.
+
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new("tls-version": "1.3")
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to a bundler and rubygems connection error" do
+ endpoint = Class.new(Endpoint) do
+ get "/" do
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+
+ Artifice.activate_with(endpoint)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ Ruby net/http: success
+
+ For some reason, your Ruby installation can connect to rubygems.org, but neither RubyGems nor Bundler can.
+ The most likely fix is to manually upgrade RubyGems by following the instructions at http://ruby.to/ssl-check-failed.
+ After you've done that, run `gem install bundler` to upgrade Bundler, and then run this script again to make sure everything worked. ❣
+
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (SSL/TLS protocol version mismatch)
+ RubyGems: failed (SSL/TLS protocol version mismatch)
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to a bundler connection error" do
+ endpoint = Class.new(Endpoint) do
+ get "/" do
+ if request.user_agent.include?("bundler")
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+ end
+
+ Artifice.activate_with(endpoint)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ RubyGems: success
+ Ruby net/http: success
+
+ Although your Ruby installation and RubyGems can both connect to rubygems.org, Bundler is having trouble.
+ The most likely way to fix this is to upgrade Bundler by running `gem install bundler`.
+ Run this script again after doing that to make sure everything is all set.
+ If you're still having trouble, check out the troubleshooting guide at http://ruby.to/ssl-check-failed.
+
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (SSL/TLS protocol version mismatch)
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to a RubyGems connection error" do
+ endpoint = Class.new(Endpoint) do
+ get "/" do
+ if request.user_agent.include?("Ruby, RubyGems")
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+ end
+
+ Artifice.activate_with(endpoint)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ Bundler: success
+ Ruby net/http: success
+
+ It looks like Ruby and Bundler can connect to rubygems.org, but RubyGems itself cannot.
+ You can likely solve this by manually downloading and installing a RubyGems update.
+ Visit http://ruby.to/ssl-check-failed for instructions on how to manually upgrade RubyGems.
+
+ MSG
+
+ expected_err = <<~MSG
+ RubyGems: failed (SSL/TLS protocol version mismatch)
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+ end
+
+ context "when no diagnostic fails" do
+ it "prints the SSL environment" do
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ Bundler: success
+ RubyGems: success
+ Ruby net/http: success
+
+ Hooray! This Ruby can connect to rubygems.org.
+ You are all set to use Bundler and RubyGems.
+
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output("").to_stderr
+ end
+
+ it "uses the tls_version verify mode and host when given as option" do
+ net_http = Class.new(Artifice::Net::HTTP) do
+ class << self
+ attr_accessor :verify_mode, :min_version, :max_version
+ end
+
+ def connect
+ self.class.verify_mode = verify_mode
+ self.class.min_version = min_version
+ self.class.max_version = max_version
+
+ super
+ end
+ end
+
+ net_http.endpoint = @dummy_endpoint
+ Artifice.replace_net_http(net_http)
+ Gem::Request::ConnectionPools.client = net_http
+ Gem::RemoteFetcher.fetcher.close_all
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://example.org:
+ Bundler: success
+ RubyGems: success
+ Ruby net/http: success
+
+ Hooray! This Ruby can connect to example.org.
+ You are all set to use Bundler and RubyGems.
+
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new("tls-version": "1.3", "verify-mode": :none, host: "example.org")
+ expect { subject.run }.to output(expected_out).to_stdout.and output("").to_stderr
+ expect(net_http.verify_mode).to eq(0)
+ expect(net_http.min_version.to_s).to eq("TLS1_3")
+ expect(net_http.max_version.to_s).to eq("TLS1_3")
+ end
+
+ it "warns when TLS1.2 is not supported" do
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ Bundler: success
+ RubyGems: success
+ Ruby net/http: success
+
+ Hooray! This Ruby can connect to rubygems.org.
+ You are all set to use Bundler and RubyGems.
+
+ MSG
+
+ expected_err = <<~MSG
+
+ WARNING: Although your Ruby can connect to rubygems.org today, your OpenSSL is very old!
+ WARNING: You will need to upgrade OpenSSL to use rubygems.org.
+
+ MSG
+
+ previous_version = OpenSSL::SSL::TLS1_2_VERSION
+ OpenSSL::SSL.send(:remove_const, :TLS1_2_VERSION)
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ ensure
+ OpenSSL::SSL.const_set(:TLS1_2_VERSION, previous_version)
+ end
+ end
+end
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
index 5ab932e5d7..03a3786d80 100644
--- a/spec/bundler/commands/update_spec.rb
+++ b/spec/bundler/commands/update_spec.rb
@@ -1,99 +1,148 @@
# 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 "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+ gem "platform_specific"
+ G
+ end
- describe "with no arguments", :bundler => "< 3" do
it "updates the entire bundle" do
update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
build_gem "activesupport", "3.0"
end
bundle "update"
expect(out).to include("Bundle updated!")
- expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
+ expect(the_bundle).to include_gems "myrack 1.2", "myrack-obama 1.0", "activesupport 3.0"
end
it "doesn't delete the Gemfile.lock file if something goes wrong" do
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "activesupport"
- gem "rack-obama"
+ gem "myrack-obama"
exit!
G
- bundle "update", :raise_on_error => false
+ bundle "update", raise_on_error: false
expect(bundled_app_lock).to exist
end
end
- describe "with --all", :bundler => "3" do
+ describe "with --verbose" do
+ before do
+ build_repo2
+
+ install_gemfile <<~G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+ end
+
+ it "logs the reason for re-resolving" do
+ bundle "update --verbose"
+ expect(out).not_to include("Found changes from the lockfile")
+ expect(out).to include("Re-resolving dependencies because bundler is unlocking")
+ end
+ end
+
+ describe "with --all" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "updates the entire bundle" do
update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
build_gem "activesupport", "3.0"
end
- bundle "update", :all => true
+ bundle "update", all: true
expect(out).to include("Bundle updated!")
- expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
+ expect(the_bundle).to include_gems "myrack 1.2", "myrack-obama 1.0", "activesupport 3.0"
end
it "doesn't delete the Gemfile.lock file if something goes wrong" do
+ install_gemfile "source 'https://gem.repo1'"
+
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "activesupport"
- gem "rack-obama"
+ gem "myrack-obama"
exit!
G
- bundle "update", :all => true, :raise_on_error => false
+ bundle "update", all: true, raise_on_error: false
expect(bundled_app_lock).to exist
end
end
describe "with --gemfile" do
- it "creates lock files based on the Gemfile name" do
+ it "creates lockfiles based on the Gemfile name" do
gemfile bundled_app("OmgFile"), <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
G
- bundle "update --gemfile OmgFile", :all => true
+ bundle "update --gemfile OmgFile", all: true
expect(bundled_app("OmgFile.lock")).to exist
end
end
context "when update_requires_all_flag is set" do
- before { bundle "config set update_requires_all_flag true" }
+ before { bundle_config "update_requires_all_flag true" }
it "errors when passed nothing" do
- install_gemfile ""
- bundle :update, :raise_on_error => false
+ install_gemfile "source 'https://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 ""
- bundle "update --all foo", :raise_on_error => false
+ install_gemfile "source 'https://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 'https://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 "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "hides UI messages" do
bundle "update --quiet"
expect(out).not_to include("Bundle updated!")
@@ -101,133 +150,604 @@ RSpec.describe "bundle update" do
end
describe "with a top level dependency" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "unlocks all child dependencies that are unrelated to other locked dependencies" do
update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
build_gem "activesupport", "3.0"
end
- bundle "update rack-obama"
- expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 2.3.5"
+ bundle "update myrack-obama"
+ expect(the_bundle).to include_gems "myrack 1.2", "myrack-obama 1.0", "activesupport 2.3.5"
end
end
describe "with an unknown dependency" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "should inform the user" do
- bundle "update halting-problem-solver", :raise_on_error => false
+ bundle "update halting-problem-solver", raise_on_error: false
expect(err).to include "Could not find gem 'halting-problem-solver'"
end
it "should suggest alternatives" do
- bundle "update platformspecific", :raise_on_error => false
- expect(err).to include "Did you mean platform_specific?"
+ bundle "update platformspecific", raise_on_error: false
+ expect(err).to include "Did you mean 'platform_specific'?"
end
end
describe "with a child dependency" do
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+ gem "platform_specific"
+ G
+ end
+
it "should update the child dependency" do
- update_repo2
- bundle "update rack"
- expect(the_bundle).to include_gems "rack 1.2"
+ update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+ end
+
+ bundle "update myrack"
+ expect(the_bundle).to include_gems "myrack 1.2"
end
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 "https://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")
+
+ build_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
- bundle "update", :all => true
+ build_gem "country_select", "1.2.0"
- expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
+ 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
+
+ build_gem "country_select", "5.1.0" do |s|
+ s.add_dependency "countries", "~> 3.0"
+ end
+
+ 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 "https://gem.repo4"
+
+ gem "country_select"
+ gem "countries"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo4, "countries", "3.1.0")
+ c.checksum(gem_repo4, "country_select", "5.1.0")
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ countries (3.1.0)
+ country_select (5.1.0)
+ countries (~> 3.0)
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ countries
+ country_select
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ previous_lockfile = lockfile
+
+ bundle "lock --update", env: { "DEBUG" => "1" }, verbose: true
+
+ expect(lockfile).to eq(previous_lockfile)
+ end
+
+ it "does not downgrade direct dependencies when run with --conservative" do
+ build_repo4 do
+ build_gem "oauth2", "2.0.6" do |s|
+ s.add_dependency "faraday", ">= 0.17.3", "< 3.0"
end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "a"
- gem "b"
- G
+ build_gem "oauth2", "1.4.10" do |s|
+ s.add_dependency "faraday", ">= 0.17.3", "< 3.0"
+ s.add_dependency "multi_json", "~> 1.3"
+ end
- expect(the_bundle).to include_gems("a 1.0", "b 2.0")
+ build_gem "faraday", "2.5.2"
- gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "a"
- gem "b", "1.0"
- G
+ build_gem "multi_json", "1.15.0"
+
+ build_gem "quickbooks-ruby", "1.0.19" do |s|
+ s.add_dependency "oauth2", "~> 1.4"
+ end
+
+ build_gem "quickbooks-ruby", "0.1.9" do |s|
+ s.add_dependency "oauth2"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "oauth2"
+ gem "quickbooks-ruby"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ faraday (2.5.2)
+ multi_json (1.15.0)
+ oauth2 (1.4.10)
+ faraday (>= 0.17.3, < 3.0)
+ multi_json (~> 1.3)
+ quickbooks-ruby (1.0.19)
+ oauth2 (~> 1.4)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ oauth2
+ quickbooks-ruby
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --conservative --verbose"
+
+ expect(out).not_to include("Installing quickbooks-ruby 0.1.9")
+ expect(out).to include("Installing quickbooks-ruby 1.0.19").and include("Installing oauth2 1.4.10")
+ end
+
+ it "does not downgrade direct dependencies when using gemspec sources" do
+ create_file("rails.gemspec", <<-G)
+ Gem::Specification.new do |gem|
+ gem.name = "rails"
+ gem.version = "7.1.0.alpha"
+ gem.author = "DHH"
+ gem.summary = "Full-stack web application framework."
+ end
+ G
+
+ build_repo4 do
+ build_gem "rake", "12.3.3"
+ build_gem "rake", "13.0.6"
+
+ build_gem "sneakers", "2.11.0" do |s|
+ s.add_dependency "rake"
+ end
+
+ build_gem "sneakers", "2.12.0" do |s|
+ s.add_dependency "rake", "~> 12.3"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gemspec
+
+ gem "rake"
+ gem "sneakers"
+ G
+
+ lockfile <<~L
+ PATH
+ remote: .
+ specs:
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ rake (13.0.6)
+ sneakers (2.11.0)
+ rake
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rake
+ sneakers
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --verbose"
+
+ expect(out).not_to include("Installing sneakers 2.12.0")
+ expect(out).not_to include("Installing rake 12.3.3")
+ expect(out).to include("Installing sneakers 2.11.0").and include("Installing rake 13.0.6")
+ end
+
+ it "downgrades indirect dependencies if required to fulfill an explicit upgrade request" do
+ build_repo4 do
+ build_gem "rbs", "3.6.1"
+ build_gem "rbs", "3.9.4"
+
+ build_gem "solargraph", "0.56.0" do |s|
+ s.add_dependency "rbs", "~> 3.3"
+ end
+
+ build_gem "solargraph", "0.56.2" do |s|
+ s.add_dependency "rbs", "~> 3.6.1"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem 'solargraph', '~> 0.56.0'
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ rbs (3.9.4)
+ solargraph (0.56.0)
+ rbs (~> 3.3)
+
+ PLATFORMS
+ #{lockfile_platforms}
- bundle "update b"
+ DEPENDENCIES
+ solargraph (~> 0.56.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update solargraph"
+
+ expect(lockfile).to include("solargraph (0.56.2)")
+ end
+
+ it "does not downgrade direct dependencies unnecessarily" do
+ build_repo4 do
+ build_gem "redis", "4.8.1"
+ build_gem "redis", "5.3.0"
+
+ build_gem "sidekiq", "6.5.5" do |s|
+ s.add_dependency "redis", ">= 4.5.0"
+ end
- expect(the_bundle).to include_gems("a 1.0", "b 1.0")
+ build_gem "sidekiq", "6.5.12" do |s|
+ s.add_dependency "redis", ">= 4.5.0", "< 5"
+ end
+
+ # one version of sidekiq above Gemfile's range is needed to make the
+ # resolver choose `redis` first and trying to upgrade it, reproducing
+ # the accidental sidekiq downgrade as a result
+ build_gem "sidekiq", "7.0.0 " do |s|
+ s.add_dependency "redis", ">= 4.2.0"
+ end
+
+ build_gem "sentry-sidekiq", "5.22.0" do |s|
+ s.add_dependency "sidekiq", ">= 3.0"
+ end
+
+ build_gem "sentry-sidekiq", "5.22.4" do |s|
+ s.add_dependency "sidekiq", ">= 3.0"
+ end
end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "redis"
+ gem "sidekiq", "~> 6.5"
+ gem "sentry-sidekiq"
+ G
+
+ original_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ redis (4.8.1)
+ sentry-sidekiq (5.22.0)
+ sidekiq (>= 3.0)
+ sidekiq (6.5.12)
+ redis (>= 4.5.0, < 5)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ redis
+ sentry-sidekiq
+ sidekiq (~> 6.5)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile original_lockfile
+
+ bundle "lock --update sentry-sidekiq"
+
+ expect(lockfile).to eq(original_lockfile.sub("sentry-sidekiq (5.22.0)", "sentry-sidekiq (5.22.4)"))
+ 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, verbose: true
+ source "https://gem.repo4"
+ gem "a"
+ G
+
+ expect(the_bundle).to include_gems("a 1.0", "b 1.0", "c 2.0")
+
+ build_repo4 do
+ build_gem "b", "2.0" do |s|
+ s.add_dependency "c", "< 2"
+ end
+ end
+
+ bundle "update", all: true, verbose: true
+ expect(the_bundle).to include_gems("a 1.0", "b 1.0", "c 2.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"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "a"
+ gem "b"
+ G
+
+ expect(the_bundle).to include_gems("a 1.0", "b 2.0")
+
+ gemfile <<-G
+ source "https://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 "https://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 "https://gem.repo4"
+ gem "activesupport", "~> 6.0.0"
+ G
+
+ original_lockfile = lockfile
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "activesupport", "6.0.4.1"
+ c.checksum gem_repo4, "tzinfo", "1.2.9"
+ end
+
+ expected_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ activesupport (6.0.4.1)
+ tzinfo (~> 1.1)
+ tzinfo (1.2.9)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ activesupport (~> 6.0.0)
+ #{checksums}
+ 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
- it "doesn't hit repo2" do
- FileUtils.rm_rf(gem_repo2)
+ before do
+ build_repo2
- bundle "update --local --all"
- expect(out).not_to include("Fetching source index")
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+ gem "platform_specific"
+ G
+ end
+
+ it "doesn't hit repo2" do
+ simulate_platform "x86-darwin-100" do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ activesupport (2.3.5)
+ platform_specific (1.0-x86-darwin-100)
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
+
+ PLATFORMS
+ x86-darwin-100
+
+ DEPENDENCIES
+ activesupport
+ platform_specific
+ myrack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ FileUtils.rm_r(gem_repo2)
+
+ bundle "update --local --all"
+ expect(out).not_to include("Fetching source index")
+ end
end
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)}"
+ source "https://gem.repo2"
gem "activesupport", :group => :development
- gem "rack"
+ gem "myrack"
G
update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
build_gem "activesupport", "3.0"
end
bundle "update --group development"
expect(the_bundle).to include_gems "activesupport 3.0"
- expect(the_bundle).not_to include_gems "rack 1.2"
+ expect(the_bundle).not_to include_gems "myrack 1.2"
end
context "when conservatively updating a group with non-group sub-deps" do
it "should update only specified group gems" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "activemerchant", :group => :development
gem "activesupport"
G
@@ -242,10 +762,10 @@ 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
- build_git "foo", :path => lib_path("activesupport")
+ before do
+ build_git "foo", path: lib_path("activesupport")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "activesupport", :group => :development
gem "foo", :git => "#{lib_path("activesupport")}"
G
@@ -253,7 +773,7 @@ RSpec.describe "bundle update" do
it "should not update the gems from that source" do
update_repo2 { build_gem "activesupport", "3.0" }
- update_git "foo", "2.0", :path => lib_path("activesupport")
+ update_git "foo", "2.0", path: lib_path("activesupport")
bundle "update --group development"
expect(the_bundle).to include_gems "activesupport 3.0"
@@ -264,82 +784,100 @@ RSpec.describe "bundle update" do
context "when bundler itself is a transitive dependency" do
it "executes without error" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport", :group => :development
- gem "rack"
+ gem "myrack"
G
update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
build_gem "activesupport", "3.0"
end
bundle "update --group development"
expect(the_bundle).to include_gems "activesupport 2.3.5"
expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}"
- expect(the_bundle).not_to include_gems "rack 1.2"
+ expect(the_bundle).not_to include_gems "myrack 1.2"
end
end
end
describe "in a frozen bundle" do
- it "should fail loudly", :bundler => "< 3" do
- bundle "install --deployment"
- bundle "update", :all => true, :raise_on_error => false
+ before do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ gem "myrack-obama"
+ gem "platform_specific"
+ G
+ end
+
+ it "should fail loudly" do
+ bundle_config "deployment true"
+ bundle "update", all: true, raise_on_error: false
expect(last_command).to be_failure
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m)
- expect(err).to match(/freeze \nby running `bundle config unset deployment`./m)
+ expect(err).to eq <<~ERROR.strip
+ Bundler is unlocking, but the lockfile can't be updated because frozen mode is set
+
+ If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`.
+ ERROR
end
- it "should suggest different command when frozen is set globally", :bundler => "< 3" do
- bundle "config set --global frozen 1"
- bundle "update", :all => true, :raise_on_error => false
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
- and match(/freeze \nby running `bundle config unset frozen`./m)
+ it "should fail loudly when frozen is set globally" do
+ bundle_config_global "frozen 1"
+ bundle "update", all: true, raise_on_error: false
+ expect(err).to eq <<~ERROR.strip
+ Bundler is unlocking, but the lockfile can't be updated because frozen mode is set
+
+ If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`.
+ ERROR
+ end
+
+ it "should fail loudly when deployment is set globally" do
+ bundle_config_global "deployment true"
+ bundle "update", all: true, raise_on_error: false
+ expect(err).to eq <<~ERROR.strip
+ Bundler is unlocking, but the lockfile can't be updated because frozen mode is set
+
+ If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`.
+ ERROR
end
- it "should suggest different command when frozen is set globally", :bundler => "3" do
- bundle "config set --global deployment true"
- bundle "update", :all => true, :raise_on_error => false
- expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m).
- and match(/freeze \nby running `bundle config unset deployment`./m)
+ it "should not suggest any command to unfreeze bundler if frozen is set through ENV" do
+ bundle "update", all: true, raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" }
+ expect(err).to eq("Bundler is unlocking, but the lockfile can't be updated because frozen mode is set")
end
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)}"
+ source "https://gem.repo2"
gem "activesupport"
G
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)}"
+ source "https://gem.repo2"
gem "activesupport"
G
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
@@ -353,26 +891,13 @@ RSpec.describe "bundle update" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "harry"
gem "fred"
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|
@@ -398,13 +923,13 @@ RSpec.describe "bundle update" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "harry"
gem "fred"
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|
@@ -413,70 +938,198 @@ 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 "shows the previous version of the gem when updated from rubygems source" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ G
+
+ bundle "update", all: true, verbose: 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
+
+ it "only prints `Using` for versions that have changed" do
+ build_repo4 do
+ build_gem "bar"
+ build_gem "foo"
end
- it "should not update the child dependencies of a gem that has the same name as the source", :bundler => "3" do
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "bar"
+ gem "foo"
+ G
+
+ bundle "update", all: true
+ expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/)
+
+ build_repo4 do
+ build_gem "foo", "2.0"
+ end
+
+ bundle "update", all: true
+ expect(out.sub("Removing foo (1.0)\n", "")).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/)
+ end
+
+ it "shows error message when Gemfile.lock is not preset and gem is specified" do
+ gemfile <<-G
+ source "https://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 sources and caching enabled" do
+ before do
+ build_repo2 do
+ build_gem "myrack", "1.0.0"
+
+ build_gem "request_store", "1.0.0" do |s|
+ s.add_dependency "myrack", "1.0.0"
+ end
+ end
+
+ build_repo4 do
+ # set up repo with no gems
+ end
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "request_store"
+
+ source "https://gem.repo4" do
+ end
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack (1.0.0)
+ request_store (1.0.0)
+ myrack (= 1.0.0)
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ request_store
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "works" do
+ bundle :install
+ bundle :cache
+
update_repo2 do
- build_gem "george", "2.0"
- build_gem "harry", "2.0" do |s|
- s.add_dependency "george"
+ build_gem "request_store", "1.1.0" do |s|
+ s.add_dependency "myrack", "1.0.0"
end
end
- bundle "update --source harry"
- expect(the_bundle).to include_gems "harry 1.0", "fred 1.0", "george 1.0"
+ bundle "update request_store"
+
+ expect(out).to include("Bundle updated!")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack (1.0.0)
+ request_store (1.1.0)
+ myrack (= 1.0.0)
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ request_store
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
end
end
RSpec.describe "bundle update in more complicated situations" do
- before :each do
+ before do
build_repo2
end
it "will eagerly unlock dependencies of a specified gem" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "thin"
- gem "rack-obama"
+ gem "myrack-obama"
G
update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
build_gem "thin", "2.0" do |s|
- s.add_dependency "rack"
+ s.add_dependency "myrack"
end
end
bundle "update thin"
- expect(the_bundle).to include_gems "thin 2.0", "rack 1.2", "rack-obama 1.0"
+ expect(the_bundle).to include_gems "thin 2.0", "myrack 1.2", "myrack-obama 1.0"
end
it "will warn when some explicitly updated gems are not updated" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "thin"
- gem "rack-obama"
+ gem "myrack-obama"
G
update_repo2 do
- build_gem("thin", "2.0") {|s| s.add_dependency "rack" }
- build_gem "rack", "10.0"
+ build_gem("thin", "2.0") {|s| s.add_dependency "myrack" }
+ build_gem "myrack", "10.0"
end
- bundle "update thin rack-obama"
- expect(last_command.stdboth).to include "Bundler attempted to update rack-obama but its version stayed the same"
- expect(the_bundle).to include_gems "thin 2.0", "rack 10.0", "rack-obama 1.0"
+ bundle "update thin myrack-obama"
+ expect(stdboth).to include "Bundler attempted to update myrack-obama but its version stayed the same"
+ expect(the_bundle).to include_gems "thin 2.0", "myrack 10.0", "myrack-obama 1.0"
end
it "will not warn when an explicitly updated git gem changes sha but not version" do
build_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
@@ -486,31 +1139,32 @@ RSpec.describe "bundle update in more complicated situations" do
bundle "update foo"
- expect(last_command.stdboth).not_to include "attempted to update"
+ expect(stdboth).not_to include "attempted to update"
end
it "will not warn when changing gem sources but not versions" do
- build_git "rack"
+ build_git "myrack"
install_gemfile <<-G
- gem "rack", :git => '#{lib_path("rack-1.0")}'
+ source "https://gem.repo2"
+ gem "myrack", :git => '#{lib_path("myrack-1.0")}'
G
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "update rack"
+ bundle "update myrack"
- expect(last_command.stdboth).not_to include "attempted to update"
+ expect(stdboth).not_to include "attempted to update"
end
it "will update only from pinned source" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
- source "#{file_uri_for(gem_repo1)}" do
+ source "https://gem.repo1" do
gem "thin"
end
G
@@ -519,12 +1173,12 @@ RSpec.describe "bundle update in more complicated situations" do
build_gem "thin", "2.0"
end
- bundle "update"
+ bundle "update", artifice: "compact_index"
expect(the_bundle).to include_gems "thin 1.0"
end
context "when the lockfile is for a different platform" do
- before do
+ around do |example|
build_repo4 do
build_gem("a", "0.9")
build_gem("a", "0.9") {|s| s.platform = "java" }
@@ -533,13 +1187,13 @@ RSpec.describe "bundle update in more complicated situations" do
end
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem "a"
G
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo4)}
+ remote: https://gem.repo4
specs:
a (0.9-java)
@@ -550,11 +1204,11 @@ RSpec.describe "bundle update in more complicated situations" do
a
L
- simulate_platform linux
+ simulate_platform "x86_64-linux", &example
end
it "allows updating" do
- bundle :update, :all => true
+ bundle :update, all: true
expect(the_bundle).to include_gem "a 1.1"
end
@@ -572,13 +1226,13 @@ RSpec.describe "bundle update in more complicated situations" do
end
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem "a", platform: :jruby
G
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo4)}
+ remote: https://gem.repo4
specs:
a (0.9-java)
@@ -588,14 +1242,14 @@ RSpec.describe "bundle update in more complicated situations" do
DEPENDENCIES
a
L
-
- simulate_platform linux
end
it "is not updated because it is not actually included in the bundle" do
- bundle "update a"
- expect(last_command.stdboth).to include "Bundler attempted to update a but it was not considered because it is for a different platform from the current one"
- expect(the_bundle).to_not include_gem "a"
+ simulate_platform "x86_64-linux" do
+ bundle "update a"
+ expect(stdboth).to include "Bundler attempted to update a but it was not considered because it is for a different platform from the current one"
+ expect(the_bundle).to_not include_gem "a"
+ end
end
end
end
@@ -605,164 +1259,103 @@ RSpec.describe "bundle update without a Gemfile.lock" do
build_repo2
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
- gem "rack", "1.0"
+ gem "myrack", "1.0"
G
- bundle "update", :all => true
+ bundle "update", all: true
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
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
+ s.add_dependency "bundler", "9.9.9"
end
+
+ build_gem "bundler", "9.9.9"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "rails", "3.0.1"
G
end
- it "should explain that bundler conflicted", :bundler => "< 3" do
- bundle "update", :all => true, :raise_on_error => false
- expect(last_command.stdboth).not_to match(/in snapshot/i)
+ it "should explain that bundler conflicted and how to resolve the conflict" do
+ bundle "update", all: true, raise_on_error: false
+ expect(stdboth).not_to match(/in snapshot/i)
expect(err).to match(/current Bundler version/i).
- and match(/perhaps you need to update bundler/i)
- end
-
- it "should warn that the newer version of Bundler would conflict", :bundler => "3" do
- bundle "update", :all => true
- expect(err).to include("rails (3.0.1) has dependency bundler").
- and include("so the dependency is being ignored")
- expect(the_bundle).to include_gem "rails 3.0.1"
- 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)
+ and match(/Install the necessary version with `gem install bundler:9\.9\.9`/i)
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'
- G
- end
-
context "when the Gemfile removes the ruby" do
before do
+ install_gemfile <<-G
+ ruby '~> #{Gem.ruby_version}'
+ source "https://gem.repo1"
+ G
+
gemfile <<-G
- ::RUBY_VERSION = '2.1.4'
- ::RUBY_PATCHLEVEL = 222
+ source "https://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: https://gem.repo1/
specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
-
+ #{checksums_section_when_enabled}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
context "when the Gemfile specified an updated Ruby version" do
before do
+ install_gemfile <<-G
+ ruby '~> #{Gem.ruby_version}'
+ source "https://gem.repo1"
+ G
+
gemfile <<-G
- ::RUBY_VERSION = '2.1.4'
- ::RUBY_PATCHLEVEL = 222
- ruby '~> 2.1.0'
+ ruby '~> #{current_ruby_minor}'
+ source "https://gem.repo1"
G
end
+
it "updates the Gemfile.lock with the latest version" do
bundle "update --ruby"
- lockfile_should_be <<-L
- GEM
- specs:
-
- PLATFORMS
- #{lockfile_platforms}
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo1/
+ specs:
- DEPENDENCIES
+ PLATFORMS
+ #{lockfile_platforms}
- RUBY VERSION
- ruby 2.1.4p222
+ DEPENDENCIES
+ #{checksums_section_when_enabled}
+ RUBY VERSION
+ #{Bundler::RubyVersion.system}
- BUNDLED WITH
+ BUNDLED WITH
#{Bundler::VERSION}
L
end
@@ -770,68 +1363,436 @@ RSpec.describe "bundle update --ruby" do
context "when a different Ruby is being used than has been versioned" do
before do
+ install_gemfile <<-G
+ ruby '~> #{Gem.ruby_version}'
+ source "https://gem.repo1"
+ G
+
gemfile <<-G
- ::RUBY_VERSION = '2.2.2'
- ::RUBY_PATCHLEVEL = 505
ruby '~> 2.1.0'
+ source "https://gem.repo1"
G
end
it "shows a helpful error message" do
- bundle "update --ruby", :raise_on_error => false
+ bundle "update --ruby", raise_on_error: false
- expect(err).to include("Your Ruby version is 2.2.2, but your Gemfile specified ~> 2.1.0")
+ expect(err).to include("Your Ruby version is #{Bundler::RubyVersion.system.gem_version}, but your Gemfile specified ~> 2.1.0")
end
end
context "when updating Ruby version and Gemfile `ruby`" do
before do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ CHECKSUMS
+
+ RUBY VERSION
+ ruby 2.1.4p222
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
gemfile <<-G
- ::RUBY_VERSION = '1.8.3'
- ::RUBY_PATCHLEVEL = 55
- ruby '~> 1.8.0'
+ ruby '~> #{Gem.ruby_version}'
+ source "https://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: https://gem.repo1/
specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
-
+ #{checksums_section_when_enabled}
RUBY VERSION
- ruby 1.8.3p55
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
end
RSpec.describe "bundle update --bundler" do
- it "updates the bundler version in the lockfile without re-resolving" do
+ it "updates the bundler version in the lockfile" do
build_repo4 do
- build_gem "rack", "1.0"
+ build_gem "bundler", "2.5.9"
+ build_gem "myrack", "1.0"
+ end
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo4, "myrack", "1.0")
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "rack"
+ source "https://gem.repo4"
+ gem "myrack"
G
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
- FileUtils.rm_r gem_repo4
+ bundle :update, bundler: true, verbose: true
+ expect(out).to include("Using bundler #{Bundler::VERSION}")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ expect(the_bundle).to include_gem "myrack 1.0"
+ end
+
+ it "updates the bundler version in the lockfile without re-resolving if the highest version is already installed" do
+ build_repo4 do
+ build_gem "bundler", "2.3.9"
+ build_gem "myrack", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+ lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo4, "myrack", "1.0")
+ end
+
+ bundle :update, bundler: true, verbose: true
+ expect(out).to include("Using bundler #{Bundler::VERSION}")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
- bundle :update, :bundler => true, :verbose => true
- expect(the_bundle).to include_gem "rack 1.0"
+ DEPENDENCIES
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
- expect(the_bundle.locked_gems.bundler_version).to eq v(Bundler::VERSION)
+ expect(the_bundle).to include_gem "myrack 1.0"
+ end
+
+ it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo do
+ bundle_config "path.system true"
+
+ pristine_system_gems "bundler-9.0.0"
+
+ build_repo4 do
+ build_gem "myrack", "1.0"
+
+ build_bundler "999.0.0"
+ end
+
+ checksums = checksums_section do |c|
+ c.checksum(gem_repo4, "myrack", "1.0")
+ c.checksum(gem_repo4, "bundler", "999.0.0")
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+
+ bundle :update, bundler: true, verbose: true
+
+ expect(out).to include("Updating bundler to 999.0.0")
+ expect(out).to include("Running `bundle update --bundler \"> 0.a\" --verbose` with bundler 999.0.0")
+ expect(out).not_to include("Installing Bundler 2.99.9 and restarting using that version.")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ 999.0.0
+ L
+
+ expect(the_bundle).to include_gems "bundler 999.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
+ end
+
+ it "does not claim to update to Bundler version to a wrong version when cached gems are present" do
+ pristine_system_gems "bundler-4.99.0"
+
+ build_repo4 do
+ build_gem "myrack", "3.0.9.1"
+
+ build_bundler "4.99.0"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (3.0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+
+ BUNDLED WITH
+ 2.99.0
+ L
+
+ bundle :cache, verbose: true
+
+ bundle :update, bundler: true, verbose: true
+
+ expect(out).not_to include("Updating bundler to")
+ end
+
+ it "does not update the bundler version in the lockfile if the latest version is not compatible with current ruby", :ruby_repo do
+ pristine_system_gems "bundler-9.9.9"
+
+ build_repo4 do
+ build_gem "myrack", "1.0"
+
+ build_bundler "9.9.9"
+ build_bundler "999.0.0" do |s|
+ s.required_ruby_version = "> #{Gem.ruby_version}"
+ end
+ end
+
+ checksums = checksums_section do |c|
+ c.checksum(gem_repo4, "myrack", "1.0")
+ c.checksum(gem_repo4, "bundler", "9.9.9")
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+
+ bundle :update, bundler: true, verbose: true
+
+ expect(out).to include("Using bundler 9.9.9")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ 9.9.9
+ L
+
+ expect(the_bundle).to include_gems "bundler 9.9.9"
+ expect(the_bundle).to include_gems "myrack 1.0"
+ end
+
+ it "errors if the explicit target version does not exist" do
+ pristine_system_gems "bundler-9.9.9"
+
+ build_repo4 do
+ build_gem "myrack", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+
+ bundle :update, bundler: "999.999.999", raise_on_error: false
+
+ expect(last_command).to be_failure
+ expect(err).to eq("The `bundle update --bundler` target version (999.999.999) does not exist")
+ end
+
+ it "errors if the explicit target version does not exist, even if auto switching is disabled" do
+ pristine_system_gems "bundler-9.9.9"
+
+ build_repo4 do
+ build_gem "myrack", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+
+ bundle :update, bundler: "999.999.999", raise_on_error: false, env: { "BUNDLER_VERSION" => "9.9.9" }
+
+ expect(last_command).to be_failure
+ expect(err).to eq("The `bundle update --bundler` target version (999.999.999) does not exist")
+ end
+
+ it "allows updating to development versions if already installed locally" do
+ system_gems "bundler-9.9.9"
+
+ build_repo4 do
+ build_gem "myrack", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+
+ system_gems "bundler-9.0.0.dev", path: local_gem_path
+ bundle :update, bundler: "9.0.0.dev", verbose: "true"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo4, "myrack", "1.0")
+ end
+ checksums.delete("bundler")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ 9.0.0.dev
+ L
+
+ expect(out).to include("Using bundler 9.0.0.dev")
+ end
+
+ it "does not touch the network if not necessary" do
+ system_gems "bundler-9.9.9"
+
+ build_repo4 do
+ build_gem "myrack", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+ system_gems "bundler-9.0.0", path: local_gem_path
+ bundle :update, bundler: "9.0.0", verbose: true
+
+ expect(out).not_to include("Fetching gem metadata from https://rubygems.org/")
+
+ # Only updates properly on modern RubyGems.
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo4, "myrack", "1.0")
+ c.checksum(local_gem_path, "bundler", "9.0.0", Gem::Platform::RUBY, "cache")
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ 9.0.0
+ L
+
+ expect(out).to include("Using bundler 9.0.0")
+ end
+
+ it "prints an error when trying to update bundler in frozen mode" do
+ system_gems "bundler-9.0.0"
+
+ gemfile <<~G
+ source "https://gem.repo2"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ 9.0.0
+ L
+
+ system_gems "bundler-9.9.9", path: local_gem_path
+
+ bundle "update --bundler=9.9.9", env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ expect(err).to include("An update to the version of Bundler itself was requested, but the lockfile can't be updated because frozen mode is set")
end
end
@@ -849,13 +1810,16 @@ RSpec.describe "bundle update conservative" do
build_gem "foo", %w[1.5.1] do |s|
s.add_dependency "bar", "~> 3.0"
end
- build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0]
+ build_gem "foo", %w[2.0.0.pre] do |s|
+ s.add_dependency "bar"
+ end
+ build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 2.1.2.pre 3.0.0 3.1.0.pre 4.0.0.pre]
build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0]
end
# establish a lockfile set to 1.4.3
install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem 'foo', '1.4.3'
gem 'bar', '2.0.3'
gem 'qux', '1.0.0'
@@ -864,7 +1828,7 @@ RSpec.describe "bundle update conservative" do
# remove 1.4.3 requirement and bar altogether
# to setup update specs below
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem 'foo'
gem 'qux'
G
@@ -872,7 +1836,7 @@ RSpec.describe "bundle update conservative" do
context "with patch set as default update level in config" do
it "should do a patch level update" do
- bundle "config set --local prefer_patch true"
+ bundle_config "prefer_patch true"
bundle "update foo"
expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.0"
@@ -887,7 +1851,7 @@ RSpec.describe "bundle update conservative" do
end
it "update all" do
- bundle "update --patch", :all => true
+ bundle "update --patch", all: true
expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.1"
end
@@ -909,11 +1873,37 @@ RSpec.describe "bundle update conservative" do
end
it "minor preferred" do
- bundle "update --minor --strict", :all => true
+ bundle "update --minor --strict", all: true
expect(the_bundle).to include_gems "foo 1.5.0", "bar 2.1.1", "qux 1.1.0"
end
end
+
+ context "pre" do
+ it "defaults to major" do
+ bundle "update --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 2.0.0.pre", "bar 4.0.0.pre", "qux 1.0.0"
+ end
+
+ it "patch preferred" do
+ bundle "update --patch --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.2.pre", "qux 1.0.0"
+ end
+
+ it "minor preferred" do
+ bundle "update --minor --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 1.5.1", "bar 3.1.0.pre", "qux 1.0.0"
+ end
+
+ it "major preferred" do
+ bundle "update --major --pre foo bar"
+
+ expect(the_bundle).to include_gems "foo 2.0.0.pre", "bar 4.0.0.pre", "qux 1.0.0"
+ end
+ end
end
context "eager unlocking" do
@@ -934,16 +1924,16 @@ RSpec.describe "bundle update conservative" do
end
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem 'isolated_owner'
gem 'shared_owner_a'
gem 'shared_owner_b'
G
- lockfile <<-L
+ lockfile <<~L
GEM
- remote: #{file_uri_for(gem_repo4)}
+ remote: https://gem.repo4/
specs:
isolated_dep (2.0.1)
isolated_owner (1.0.1)
@@ -955,15 +1945,17 @@ RSpec.describe "bundle update conservative" do
shared_dep (~> 5.0)
PLATFORMS
- ruby
+ #{local_platform}
DEPENDENCIES
+ isolated_owner
shared_owner_a
shared_owner_b
- isolated_owner
+
+ CHECKSUMS
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -982,12 +1974,55 @@ 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"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "isolated_dep", "2.0.1"
+ c.checksum gem_repo4, "isolated_owner", "1.0.2"
+ c.checksum gem_repo4, "shared_dep", "5.0.1"
+ c.checksum gem_repo4, "shared_owner_a", "3.0.2"
+ c.checksum gem_repo4, "shared_owner_b", "4.0.2"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://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
+ #{local_platform}
+
+ DEPENDENCIES
+ isolated_owner
+ shared_owner_a
+ shared_owner_b
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
it "should match bundle install conservative update behavior when not eagerly unlocking" do
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem 'isolated_owner', '1.0.2'
gem 'shared_owner_a', '3.0.2'
@@ -996,17 +2031,63 @@ RSpec.describe "bundle update conservative" do
bundle "install"
- 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
+ end
+
+ context "when Gemfile dependencies have changed" do
+ before do
+ build_repo4 do
+ build_gem "nokogiri", "1.16.4" do |s|
+ s.platform = "arm64-darwin"
+ end
+
+ build_gem "nokogiri", "1.16.4" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem "prism", "0.25.0"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "nokogiri", ">=1.16.4"
+ gem "prism", ">=0.25.0"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.16.4-arm64-darwin)
+ nokogiri (1.16.4-x86_64-linux)
+
+ PLATFORMS
+ arm64-darwin
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri (>= 1.16.4)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "still works" do
+ simulate_platform "arm64-darwin-23" do
+ bundle "update"
+ end
end
end
context "error handling" do
before do
- gemfile ""
+ gemfile "source 'https://gem.repo1'"
end
it "raises if too many flags are provided" do
- bundle "update --patch --minor", :all => true, :raise_on_error => false
+ bundle "update --patch --minor", all: true, raise_on_error: false
expect(err).to eq "Provide only one of the following options: minor, patch"
end
diff --git a/spec/bundler/commands/version_spec.rb b/spec/bundler/commands/version_spec.rb
index 53d545f5fa..4320ad0611 100644
--- a/spec/bundler/commands/version_spec.rb
+++ b/spec/bundler/commands/version_spec.rb
@@ -4,44 +4,62 @@ require_relative "../support/path"
RSpec.describe "bundle version" do
if Spec::Path.ruby_core?
- COMMIT_HASH = /unknown|[a-fA-F0-9]{7,}/.freeze
+ COMMIT_HASH = /unknown|[a-fA-F0-9]{7,}/
else
- COMMIT_HASH = /[a-fA-F0-9]{7,}/.freeze
+ COMMIT_HASH = /[a-fA-F0-9]{7,}/
end
context "with -v" do
- it "outputs the version", :bundler => "< 3" do
+ it "outputs the version and virtual version if set" do
bundle "-v"
- expect(out).to eq("Bundler version #{Bundler::VERSION}")
- end
+ expect(out).to eq(Bundler::VERSION.to_s)
- it "outputs the version", :bundler => "3" do
+ bundle_config "simulate_version 5"
bundle "-v"
- expect(out).to eq(Bundler::VERSION)
+ expect(out).to eq("#{Bundler::VERSION} (simulating Bundler 5)")
end
end
context "with --version" do
- it "outputs the version", :bundler => "< 3" do
+ it "outputs the version and virtual version if set" do
bundle "--version"
- expect(out).to eq("Bundler version #{Bundler::VERSION}")
- end
+ expect(out).to eq(Bundler::VERSION.to_s)
- it "outputs the version", :bundler => "3" do
+ bundle_config "simulate_version 5"
bundle "--version"
- expect(out).to eq(Bundler::VERSION)
+ expect(out).to eq("#{Bundler::VERSION} (simulating Bundler 5)")
end
end
context "with version" do
- it "outputs the version with build metadata", :bundler => "< 3" do
- bundle "version"
- expect(out).to match(/\ABundler version #{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/)
+ context "when released", :ruby_repo do
+ before do
+ system_gems "bundler-4.9.9", released: true
+ end
+
+ it "outputs the version, virtual version if set, and build metadata" do
+ bundle "version"
+ expect(out).to match(/\A4\.9\.9 \(2100-01-01 commit #{COMMIT_HASH}\)\z/)
+
+ bundle_config "simulate_version 5"
+ bundle "version"
+ expect(out).to match(/\A4\.9\.9 \(simulating Bundler 5\) \(2100-01-01 commit #{COMMIT_HASH}\)\z/)
+ end
end
- it "outputs the version with build metadata", :bundler => "3" do
- bundle "version"
- expect(out).to match(/\A#{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/)
+ context "when not released" do
+ before do
+ system_gems "bundler-4.9.9", released: false
+ end
+
+ it "outputs the version, virtual version if set, and build metadata" do
+ bundle "version"
+ expect(out).to match(/\A4\.9\.9 \(20\d{2}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/)
+
+ bundle_config "simulate_version 5"
+ bundle "version"
+ expect(out).to match(/\A4\.9\.9 \(simulating Bundler 5\) \(20\d{2}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/)
+ end
end
end
end
diff --git a/spec/bundler/commands/viz_spec.rb b/spec/bundler/commands/viz_spec.rb
deleted file mode 100644
index 0efb24b504..0000000000
--- a/spec/bundler/commands/viz_spec.rb
+++ /dev/null
@@ -1,146 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot") do
- before do
- graphviz_version = RUBY_VERSION >= "2.4" ? "1.2.5" : "1.2.4"
-
- realworld_system_gems "ruby-graphviz --version #{graphviz_version}"
- end
-
- it "graphs gems from the Gemfile" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rack-obama"
- G
-
- bundle "viz"
- expect(out).to include("gem_graph.png")
-
- bundle "viz", :format => "debug"
- expect(out).to eq(strip_whitespace(<<-DOT).strip)
- digraph Gemfile {
- concentrate = "true";
- normalize = "true";
- nodesep = "0.55";
- edge[ weight = "2"];
- node[ fontname = "Arial, Helvetica, SansSerif"];
- edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
- default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
- rack [style = "filled", fillcolor = "#B9B9D5", label = "rack"];
- default -> rack [constraint = "false"];
- "rack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "rack-obama"];
- default -> "rack-obama" [constraint = "false"];
- "rack-obama" -> rack;
- }
- debugging bundle viz...
- DOT
- end
-
- it "graphs gems that are prereleases" do
- build_repo2 do
- build_gem "rack", "1.3.pre"
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack", "= 1.3.pre"
- gem "rack-obama"
- G
-
- bundle "viz"
- expect(out).to include("gem_graph.png")
-
- bundle "viz", :format => :debug, :version => true
- expect(out).to eq(strip_whitespace(<<-EOS).strip)
- digraph Gemfile {
- concentrate = "true";
- normalize = "true";
- nodesep = "0.55";
- edge[ weight = "2"];
- node[ fontname = "Arial, Helvetica, SansSerif"];
- edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
- default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
- rack [style = "filled", fillcolor = "#B9B9D5", label = "rack\\n1.3.pre"];
- default -> rack [constraint = "false"];
- "rack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "rack-obama\\n1.0"];
- default -> "rack-obama" [constraint = "false"];
- "rack-obama" -> rack;
- }
- debugging bundle viz...
- EOS
- end
-
- context "with another gem that has a graphviz file" do
- before do
- build_repo4 do
- build_gem "graphviz", "999" do |s|
- s.write("lib/graphviz.rb", "abort 'wrong graphviz gem loaded'")
- end
- end
-
- system_gems "graphviz-999", :gem_repo => gem_repo4
- end
-
- it "loads the correct ruby-graphviz gem" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rack-obama"
- G
-
- bundle "viz", :format => "debug"
- expect(out).to eq(strip_whitespace(<<-DOT).strip)
- digraph Gemfile {
- concentrate = "true";
- normalize = "true";
- nodesep = "0.55";
- edge[ weight = "2"];
- node[ fontname = "Arial, Helvetica, SansSerif"];
- edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
- default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
- rack [style = "filled", fillcolor = "#B9B9D5", label = "rack"];
- default -> rack [constraint = "false"];
- "rack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "rack-obama"];
- default -> "rack-obama" [constraint = "false"];
- "rack-obama" -> rack;
- }
- debugging bundle viz...
- DOT
- end
- end
-
- context "--without option" do
- it "one group" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "activesupport"
-
- group :rails do
- gem "rails"
- end
- G
-
- bundle "viz --without=rails"
- expect(out).to include("gem_graph.png")
- end
-
- it "two groups" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "activesupport"
-
- group :rack do
- gem "rack"
- end
-
- group :rails do
- gem "rails"
- end
- G
-
- bundle "viz --without=rails:rack"
- expect(out).to include("gem_graph.png")
- end
- end
-end
diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb
index d0aa4e4d9e..c7ab7c3d7e 100644
--- a/spec/bundler/install/allow_offline_install_spec.rb
+++ b/spec/bundler/install/allow_offline_install_spec.rb
@@ -1,23 +1,19 @@
# frozen_string_literal: true
-RSpec.describe "bundle install with :allow_offline_install" do
- before do
- bundle "config set allow_offline_install true"
- end
-
+RSpec.describe "bundle install allows offline install" do
context "with no cached data locally" do
it "still installs" do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "http://testgemserver.local"
- gem "rack-obama"
+ gem "myrack-obama"
G
- expect(the_bundle).to include_gem("rack 1.0")
+ expect(the_bundle).to include_gem("myrack 1.0")
end
it "still fails when the network is down" do
- install_gemfile <<-G, :artifice => "fail", :raise_on_error => false
+ install_gemfile <<-G, artifice: "fail", raise_on_error: false
source "http://testgemserver.local"
- gem "rack-obama"
+ gem "myrack-obama"
G
expect(err).to include("Could not reach host testgemserver.local.")
expect(the_bundle).to_not be_locked
@@ -26,34 +22,37 @@ RSpec.describe "bundle install with :allow_offline_install" do
context "with cached data locally" do
it "will install from the compact index" do
- system_gems ["rack-1.0.0"], :path => default_bundle_path
+ system_gems ["myrack-1.0.0"], path: default_bundle_path
- bundle "config set clean false"
- install_gemfile <<-G, :artifice => "compact_index"
+ bundle_config "clean false"
+ install_gemfile <<-G, artifice: "compact_index"
source "http://testgemserver.local"
- gem "rack-obama"
- gem "rack", "< 1.0"
+ gem "myrack-obama"
+ gem "myrack", "< 1.0"
G
- expect(the_bundle).to include_gems("rack-obama 1.0", "rack 0.9.1")
+ expect(the_bundle).to include_gems("myrack-obama 1.0", "myrack 0.9.1")
gemfile <<-G
source "http://testgemserver.local"
- gem "rack-obama"
+ gem "myrack-obama"
G
- bundle :update, :artifice => "fail", :all => true
- expect(last_command.stdboth).to include "Using the cached data for the new index because of a network error"
+ bundle :update, artifice: "fail", all: true
+ expect(stdboth).to include "Using the cached data for the new index because of a network error"
- expect(the_bundle).to include_gems("rack-obama 1.0", "rack 1.0.0")
+ expect(the_bundle).to include_gems("myrack-obama 1.0", "myrack 1.0.0")
end
def break_git_remote_ops!
FileUtils.mkdir_p(tmp("broken_path"))
File.open(tmp("broken_path/git"), "w", 0o755) do |f|
- f.puts strip_whitespace(<<-RUBY)
+ f.puts <<~RUBY
#!/usr/bin/env ruby
- if %w(fetch --force --quiet --tags refs/heads/*:refs/heads/*).-(ARGV).empty? || %w(clone --bare --no-hardlinks --quiet).-(ARGV).empty?
+ fetch_args = %w(fetch --force --quiet --no-tags)
+ clone_args = %w(clone --bare --no-hardlinks --quiet)
+
+ if (fetch_args.-(ARGV).empty? || clone_args.-(ARGV).empty?) && File.exist?(ARGV[ARGV.index("--") + 1])
warn "git remote ops have been disabled"
exit 1
end
@@ -72,18 +71,20 @@ RSpec.describe "bundle install with :allow_offline_install" do
it "will install from a cached git repo" do
skip "doesn't print errors" if Gem.win_platform?
- git = build_git "a", "1.0.0", :path => lib_path("a")
- update_git("a", :path => git.path, :branch => "new_branch")
+ git = build_git "a", "1.0.0", path: lib_path("a")
+ update_git("a", path: git.path, branch: "new_branch")
install_gemfile <<-G
+ source "https://gem.repo1"
gem "a", :git => #{git.path.to_s.dump}
G
- break_git_remote_ops! { bundle :update, :all => true }
+ break_git_remote_ops! { bundle :update, all: true }
expect(err).to include("Using cached git data because of network errors")
expect(the_bundle).to be_locked
break_git_remote_ops! do
install_gemfile <<-G
+ source "https://gem.repo1"
gem "a", :git => #{git.path.to_s.dump}, :branch => "new_branch"
G
end
diff --git a/spec/bundler/install/binstubs_spec.rb b/spec/bundler/install/binstubs_spec.rb
index 6961171f4f..c2eccb3ef2 100644
--- a/spec/bundler/install/binstubs_spec.rb
+++ b/spec/bundler/install/binstubs_spec.rb
@@ -2,20 +2,18 @@
RSpec.describe "bundle install" do
describe "when system_bindir is set" do
- # On OS X, Gem.bindir defaults to /usr/bin, so system_bindir is useful if
- # you want to avoid sudo installs for system gems with OS X's default ruby
it "overrides Gem.bindir" do
- expect(Pathname.new("/usr/bin")).not_to be_writable unless Process.euid == 0
+ expect(Pathname.new("/usr/bin")).not_to be_writable
gemfile <<-G
def Gem.bindir; "/usr/bin"; end
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- config "BUNDLE_SYSTEM_BINDIR" => system_gem_path("altbin").to_s
+ bundle_config "BUNDLE_SYSTEM_BINDIR" => system_gem_path("altbin").to_s
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0"
- expect(system_gem_path("altbin/rackup")).to exist
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ expect(system_gem_path("altbin/myrackup")).to exist
end
end
@@ -23,26 +21,26 @@ RSpec.describe "bundle install" do
before do
build_repo2 do
build_gem "fake", "14" do |s|
- s.executables = "rackup"
+ s.executables = "myrackup"
end
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "fake"
- gem "rack"
+ gem "myrack"
G
end
it "warns about the situation" do
- bundle "exec rackup"
+ bundle "exec myrackup"
expect(last_command.stderr).to include(
- "The `rackup` executable in the `fake` gem is being loaded, but it's also present in other gems (rack).\n" \
+ "The `myrackup` executable in the `fake` gem is being loaded, but it's also present in other gems (myrack).\n" \
"If you meant to run the executable for another gem, make sure you use a project specific binstub (`bundle binstub <gem_name>`).\n" \
"If you plan to use multiple conflicting executables, generate binstubs for them and disambiguate their names."
).or include(
- "The `rackup` executable in the `rack` gem is being loaded, but it's also present in other gems (fake).\n" \
+ "The `myrackup` executable in the `myrack` gem is being loaded, but it's also present in other gems (fake).\n" \
"If you meant to run the executable for another gem, make sure you use a project specific binstub (`bundle binstub <gem_name>`).\n" \
"If you plan to use multiple conflicting executables, generate binstubs for them and disambiguate their names."
)
diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb
index e5352ab6a2..86c22dad55 100644
--- a/spec/bundler/install/bundler_spec.rb
+++ b/spec/bundler/install/bundler_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe "bundle install" do
before(:each) do
build_repo2 do
build_gem "rails", "3.0" do |s|
- s.add_dependency "bundler", ">= 0.9.0.pre"
+ s.add_dependency "bundler", ">= 0.9.0"
end
build_gem "bundler", "0.9.1"
build_gem "bundler", Bundler::VERSION
@@ -14,57 +14,101 @@ RSpec.describe "bundle install" do
it "are forced to the current bundler version" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "rails", "3.0"
G
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"
+ source "https://gem.repo1"
+ gem "myrack"
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" do
- bundle "config set force_ruby_platform true"
+ it "causes a conflict if explicitly requesting a different version of bundler" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
+ gem "rails", "3.0"
+ gem "bundler", "0.9.1"
+ G
+
+ nice_error = <<~E.strip
+ Could not find compatible versions
+
+ Because the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler = 0.9.1
+ and Gemfile depends on bundler = 0.9.1,
+ version solving has failed.
+
+ Your bundle requires a different version of Bundler than the one you're running.
+ Install the necessary version with `gem install bundler:0.9.1` and rerun bundler using `bundle _0.9.1_ install`
+ E
+ expect(err).to include(nice_error)
+ end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
+ it "causes a conflict if explicitly requesting a non matching requirement on bundler" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
+ gem "rails", "3.0"
+ gem "bundler", "~> 0.8"
+ G
+
+ nice_error = <<~E.strip
+ Could not find compatible versions
+
+ Because rails >= 3.0 depends on bundler >= 0.9.0
+ and the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler >= 0.9.0, < 1.A,
+ rails >= 3.0 requires bundler >= 1.A.
+ So, because Gemfile depends on rails = 3.0
+ and Gemfile depends on bundler ~> 0.8,
+ version solving has failed.
+
+ Your bundle requires a different version of Bundler than the one you're running.
+ Install the necessary version with `gem install bundler:0.9.1` and rerun bundler using `bundle _0.9.1_ install`
+ E
+ expect(err).to include(nice_error)
+ end
+
+ it "causes a conflict if explicitly requesting a version of bundler that doesn't exist" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
gem "rails", "3.0"
gem "bundler", "0.9.2"
G
- nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "bundler":
- In Gemfile:
- bundler (= 0.9.2)
+ nice_error = <<~E.strip
+ Could not find compatible versions
- Current Bundler version:
- bundler (#{Bundler::VERSION})
- This Gemfile requires a different version of Bundler.
- Perhaps you need to update Bundler by running `gem install bundler`?
+ Because the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler = 0.9.2
+ and Gemfile depends on bundler = 0.9.2,
+ version solving has failed.
- Could not find gem 'bundler (= 0.9.2)' in any
+ Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.
E
expect(err).to include(nice_error)
end
it "works for gems with multiple versions in its dependencies" do
+ build_repo2 do
+ build_gem "multiple_versioned_deps" do |s|
+ s.add_dependency "weakling", ">= 0.0.1", "< 0.1"
+ end
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "multiple_versioned_deps"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "multiple_versioned_deps"
- gem "rack"
+ gem "myrack"
G
expect(the_bundle).to include_gems "multiple_versioned_deps 1.0.0"
@@ -72,7 +116,7 @@ RSpec.describe "bundle install" do
it "includes bundler in the bundle when it's a child dependency" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "rails", "3.0"
G
@@ -82,8 +126,8 @@ RSpec.describe "bundle install" do
it "allows gem 'bundler' when Bundler is not in the Gemfile or its dependencies" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
+ source "https://gem.repo2"
+ gem "myrack"
G
run "begin; gem 'bundler'; puts 'WIN'; rescue Gem::LoadError => e; puts e.backtrace; end"
@@ -91,54 +135,74 @@ RSpec.describe "bundle install" do
end
it "causes a conflict if child dependencies conflict" do
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
+
+ update_repo2 do
+ build_gem "rails_pinned_to_old_activesupport" do |s|
+ s.add_dependency "activesupport", "= 1.2.3"
+ end
+ end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
gem "activemerchant"
gem "rails_pinned_to_old_activesupport"
G
- nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "activesupport":
- In Gemfile:
- activemerchant was resolved to 1.0, which depends on
- activesupport (>= 2.0.0)
+ nice_error = <<~E.strip
+ Could not find compatible versions
- rails_pinned_to_old_activesupport was resolved to 1.0, which depends on
- activesupport (= 1.2.3)
+ Because every version of rails_pinned_to_old_activesupport depends on activesupport = 1.2.3
+ and every version of activemerchant depends on activesupport >= 2.0.0,
+ every version of rails_pinned_to_old_activesupport is incompatible with activemerchant >= 0.
+ So, because Gemfile depends on activemerchant >= 0
+ and Gemfile depends on rails_pinned_to_old_activesupport >= 0,
+ version solving has failed.
E
expect(err).to include(nice_error)
end
it "causes a conflict if a child dependency conflicts with the Gemfile" do
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
+
+ update_repo2 do
+ build_gem "rails_pinned_to_old_activesupport" do |s|
+ s.add_dependency "activesupport", "= 1.2.3"
+ end
+ end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
gem "rails_pinned_to_old_activesupport"
gem "activesupport", "2.3.5"
G
- nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "activesupport":
- In Gemfile:
- activesupport (= 2.3.5)
+ nice_error = <<~E.strip
+ Could not find compatible versions
- rails_pinned_to_old_activesupport was resolved to 1.0, which depends on
- activesupport (= 1.2.3)
+ Because every version of rails_pinned_to_old_activesupport depends on activesupport = 1.2.3
+ and Gemfile depends on rails_pinned_to_old_activesupport >= 0,
+ activesupport = 1.2.3 is required.
+ So, because Gemfile depends on activesupport = 2.3.5,
+ version solving has failed.
E
expect(err).to include(nice_error)
end
it "does not cause a conflict if new dependencies in the Gemfile require older dependencies than the lockfile" do
+ update_repo2 do
+ build_gem "rails_pinned_to_old_activesupport" do |s|
+ s.add_dependency "activesupport", "= 1.2.3"
+ end
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem 'rails', "2.3.2"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "rails_pinned_to_old_activesupport"
G
@@ -146,13 +210,40 @@ RSpec.describe "bundle install" do
expect(err).to be_empty
end
+ it "prints the previous version when switching to a previously downloaded gem" do
+ build_repo4 do
+ build_gem "rails", "7.0.3"
+ build_gem "rails", "7.0.4"
+ end
+
+ bundle_config "path.system true"
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem 'rails', "7.0.4"
+ G
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem 'rails', "7.0.3"
+ G
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem 'rails', "7.0.4"
+ G
+
+ expect(out).to include("Using rails 7.0.4 (was 7.0.3)")
+ expect(err).to be_empty
+ end
+
it "can install dependencies with newer bundler version with system gems" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
system_gems "bundler-99999999.99.1"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "rails", "3.0"
G
@@ -161,39 +252,17 @@ RSpec.describe "bundle install" do
end
it "can install dependencies with newer bundler version with a local path" do
- bundle "config set path .bundle"
+ bundle_config "path .bundle"
system_gems "bundler-99999999.99.1"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "rails", "3.0"
G
bundle "check"
expect(out).to include("The Gemfile's dependencies are satisfied")
end
-
- context "with allow_bundler_dependency_conflicts set" do
- before { bundle "config set allow_bundler_dependency_conflicts true" }
-
- it "are forced to the current bundler version with warnings when no compatible version is found" do
- build_repo4 do
- build_gem "requires_nonexistant_bundler" do |s|
- s.add_runtime_dependency "bundler", "99.99.99.99"
- end
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
- gem "requires_nonexistant_bundler"
- G
-
- expect(err).to include "requires_nonexistant_bundler (1.0) has dependency bundler (= 99.99.99.99), " \
- "which is unsatisfied by the current bundler version #{Bundler::VERSION}, so the dependency is being ignored"
-
- expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}", "requires_nonexistant_bundler 1.0"
- end
- end
end
end
diff --git a/spec/bundler/install/cooldown_spec.rb b/spec/bundler/install/cooldown_spec.rb
new file mode 100644
index 0000000000..bad7b7cf34
--- /dev/null
+++ b/spec/bundler/install/cooldown_spec.rb
@@ -0,0 +1,433 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with the cooldown setting" do
+ before do
+ build_repo2
+ end
+
+ context "Gemfile DSL" do
+ it "accepts `source ..., cooldown: N` without error" do
+ install_gemfile <<-G, artifice: "compact_index"
+ source "https://gem.repo2", cooldown: 5
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
+ it "accepts `cooldown: 0` to disable cooldown for a source" do
+ install_gemfile <<-G, artifice: "compact_index"
+ source "https://gem.repo2", cooldown: 0
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+ end
+
+ context "CLI flag" do
+ before do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+ end
+
+ it "accepts --cooldown N on install" do
+ bundle "install --cooldown 7", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
+ it "accepts --cooldown 0 as an escape hatch" do
+ bundle "install --cooldown 0", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
+ it "rejects a negative --cooldown value" do
+ bundle "install --cooldown=-7", artifice: "compact_index", raise_on_error: false
+
+ expect(err).to match(/non-negative integer/)
+ end
+ end
+
+ context "configuration" do
+ it "reads BUNDLE_COOLDOWN as an integer" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+
+ bundle "install", env: { "BUNDLE_COOLDOWN" => "7" }, artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
+ it "reads `bundle config set cooldown N`" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+
+ bundle "config set cooldown 7"
+ bundle "install", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+ end
+
+ context "end-to-end with v2 compact index" do
+ before do
+ now = Time.now.utc
+ build_repo3 do
+ build_gem "ripe_gem", "1.0.0" do |s|
+ s.date = now - (30 * 86_400)
+ end
+ build_gem "ripe_gem", "2.0.0" do |s|
+ s.date = now - (1 * 86_400)
+ end
+
+ # parent only resolves with the in-cooldown child 2.0.0
+ build_gem "child", "1.0.0" do |s|
+ s.date = now - (30 * 86_400)
+ end
+ build_gem "child", "2.0.0" do |s|
+ s.date = now - (1 * 86_400)
+ end
+ build_gem "parent", "1.0.0" do |s|
+ s.add_dependency "child", ">= 2.0.0"
+ s.date = now - (30 * 86_400)
+ end
+
+ # a cooldown-eligible version exists above the in-cooldown locked one
+ build_gem "upgradable", "2.0.0" do |s|
+ s.date = now - (1 * 86_400)
+ end
+ build_gem "upgradable", "3.0.0" do |s|
+ s.date = now - (30 * 86_400)
+ end
+ end
+ end
+
+ it "excludes versions within the cooldown window" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ bundle "install --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "selects the latest version when --cooldown 0 is passed" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ bundle "install --cooldown 0", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 2.0.0")
+ end
+
+ it "applies cooldown declared per-source in the Gemfile" do
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 7
+ gem "ripe_gem"
+ G
+
+ bundle "install", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "is overridden by CLI --cooldown when Gemfile sets a different per-source value" do
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 0
+ gem "ripe_gem"
+ G
+
+ bundle "install --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "bypasses cooldown when bundle install uses an existing lockfile" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 2.0.0")
+ end
+
+ it "annotates in-cooldown versions in bundle outdated table output" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem", "1.0.0"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem (= 1.0.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "outdated --cooldown 7", artifice: "compact_index_cooldown", raise_on_error: false
+
+ expect(out).to match(/ripe_gem.*\(cooldown \d+d\)/)
+ end
+
+ it "annotates in-cooldown versions in bundle outdated --parseable output" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem", "1.0.0"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem (= 1.0.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "outdated --cooldown 7 --parseable", artifice: "compact_index_cooldown", raise_on_error: false
+
+ expect(out).to match(/ripe_gem.*in cooldown for \d+ more day/)
+ end
+
+ it "excludes a locally-installed version that is still within the cooldown window" do
+ system_gems "ripe_gem-2.0.0", gem_repo: gem_repo3
+
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ bundle "install --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "selects a locally-installed in-cooldown version when --cooldown 0 bypasses the filter" do
+ system_gems "ripe_gem-2.0.0", gem_repo: gem_repo3
+
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ bundle "install --cooldown 0", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 2.0.0")
+ end
+
+ it "surfaces a cooldown hint when bundle update filters every candidate" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update ripe_gem --cooldown 99999", artifice: "compact_index_cooldown", raise_on_error: false
+
+ expect(err).to match(/excluded by the cooldown setting/)
+ expect(err).to match(/--cooldown 0/)
+ end
+
+ it "keeps an in-cooldown locked version on bundle update --all instead of failing" do
+ # Lockfile written before cooldown was enabled pins the now-in-cooldown
+ # latest version. A full update must not downgrade below it, and cooldown
+ # must not filter it out, otherwise resolution becomes impossible (#9598).
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --all --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 2.0.0")
+ end
+
+ it "does not fail bundle outdated when the locked version is in cooldown" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "outdated --cooldown 7", artifice: "compact_index_cooldown", raise_on_error: false
+
+ # exit 0 means no outdated gems and, crucially, no resolution failure (exit 7)
+ expect(exitstatus).to eq(0)
+ end
+
+ it "still applies cooldown and downgrades a gem that is updated explicitly" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update ripe_gem --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "keeps an in-cooldown transitive dependency on bundle update --all" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "parent"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ child (2.0.0)
+ parent (1.0.0)
+ child (>= 2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parent
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --all --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("parent 1.0.0", "child 2.0.0")
+ end
+
+ it "still upgrades to a cooldown-eligible version above the locked one" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "upgradable"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ upgradable (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ upgradable
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --all --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("upgradable 3.0.0")
+ end
+ end
+end
diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb
index 357f4512f1..a3b4a87ecf 100644
--- a/spec/bundler/install/deploy_spec.rb
+++ b/spec/bundler/install/deploy_spec.rb
@@ -3,63 +3,43 @@
RSpec.describe "install in deployment or frozen mode" do
before do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
- context "with CLI flags", :bundler => "< 3" do
- it "fails without a lockfile and says that --deployment requires a lock" do
- bundle "install --deployment", :raise_on_error => false
- expect(err).to include("The --deployment flag requires a Gemfile.lock")
- end
-
- it "fails without a lockfile and says that --frozen requires a lock" do
- bundle "install --frozen", :raise_on_error => false
- expect(err).to include("The --frozen flag requires a Gemfile.lock")
- end
-
- it "disallows --deployment --system" do
- bundle "install --deployment --system", :raise_on_error => false
- expect(err).to include("You have specified both --deployment")
- expect(err).to include("Please choose only one option")
- expect(exitstatus).to eq(15)
- end
-
- it "disallows --deployment --path --system" do
- bundle "install --deployment --path . --system", :raise_on_error => false
- expect(err).to include("You have specified both --path")
- expect(err).to include("as well as --system")
- expect(err).to include("Please choose only one option")
- expect(exitstatus).to eq(15)
- end
+ it "fails without a lockfile and says that deployment requires a lock" do
+ bundle_config "deployment true"
+ bundle "install", raise_on_error: false
+ expect(err).to include("The deployment setting requires a lockfile")
+ end
- it "doesn't mess up a subsequent `bundle install` after you try to deploy without a lock" do
- bundle "install --deployment", :raise_on_error => false
- bundle :install
- expect(the_bundle).to include_gems "rack 1.0"
- end
+ it "fails without a lockfile and says that frozen requires a lock" do
+ bundle_config "frozen true"
+ bundle "install", raise_on_error: false
+ expect(err).to include("The frozen setting requires a lockfile")
end
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 "install --gemfile #{tmp}/bundled_app/Gemfile", :dir => tmp
- expect(the_bundle).to include_gems "rack 1.0"
+ pristine_system_gems
+ bundle_config "deployment true"
+ bundle_config "path vendor/bundle"
+ bundle "install --gemfile #{tmp}/bundled_app/Gemfile", dir: tmp
+ expect(the_bundle).to include_gems "myrack 1.0"
end
it "works if you exclude a group with a git gem" do
build_git "foo"
gemfile <<-G
+ source "https://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 "deployment true"
+ bundle_config "without test"
bundle :install
end
@@ -67,117 +47,92 @@ 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 "deployment true"
bundle :install
- bundle "exec bundle check", :env => { "PATH" => path }
+ bundle "exec bundle check", env: { "PATH" => path }
end
it "works when using path gems from the same path and the version is specified" do
- build_lib "foo", :path => lib_path("nested/foo")
- build_lib "bar", :path => lib_path("nested/bar")
+ build_lib "foo", path: lib_path("nested/foo")
+ build_lib "bar", path: lib_path("nested/bar")
gemfile <<-G
+ source "https://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 "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 "https://gem.repo1"
+ gem "foo", :path => "#{lib_path("nested/foo")}"
+ gem "foo", :path => "#{lib_path("nested/foo")}"
+ G
+
+ bundle :install
+ bundle_config "deployment true"
bundle :install
end
it "works when there are credentials in the source URL" do
- install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ install_gemfile(<<-G, artifice: "endpoint_strict_basic_authentication", quiet: true)
source "http://user:pass@localgemserver.test/"
- gem "rack-obama", ">= 1.0"
+ gem "myrack-obama", ">= 1.0"
G
- bundle "config --local deployment true"
- bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ bundle_config "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)}" do
- gem "rack"
+ source "https://gem.repo1"
+ source "https://gem.repo1" do
+ gem "myrack"
end
G
- bundle "config --local deployment true"
+ bundle_config "deployment true"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
context "when replacing a host with the same host with credentials" do
before do
- bundle "config --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
gemfile <<-G
- source "http://user_name:password@localgemserver.test/"
- gem "rack"
+ source "http://user_name:password@localgemserver.test/"
+ gem "myrack"
G
lockfile <<-G
- GEM
- remote: http://localgemserver.test/
- specs:
- rack (1.0.0)
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ myrack (1.0.0)
- PLATFORMS
- #{local}
+ PLATFORMS
+ #{generic_local_platform}
- DEPENDENCIES
- rack
+ DEPENDENCIES
+ myrack
G
- bundle "config set --local deployment true"
- end
-
- it "prevents the replace by default" do
- bundle :install, :raise_on_error => false
-
- expect(err).to match(/The list of sources changed/)
+ bundle_config "deployment true"
end
- context "when allow_deployment_source_credential_changes is true" do
- before { bundle "config set allow_deployment_source_credential_changes true" }
-
- it "allows the replace" do
- bundle :install
-
- expect(out).to match(/Bundle complete!/)
- end
- end
-
- context "when allow_deployment_source_credential_changes is false" do
- before { bundle "config set allow_deployment_source_credential_changes false" }
-
- it "prevents the replace" do
- bundle :install, :raise_on_error => false
-
- expect(err).to match(/The list of sources changed/)
- end
- end
-
- context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is true" do
- before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "true" }
-
- it "allows the replace" do
- bundle :install
-
- expect(out).to match(/Bundle complete!/)
- end
- end
-
- context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is false" do
- before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "false" }
-
- it "prevents the replace" do
- bundle :install, :raise_on_error => false
+ it "allows the replace" do
+ bundle :install
- expect(err).to match(/The list of sources changed/)
- end
+ expect(out).to match(/Bundle complete!/)
end
end
@@ -186,40 +141,52 @@ RSpec.describe "install in deployment or frozen mode" do
bundle "install"
end
- it "installs gems by default to vendor/bundle", :bundler => "< 3" do
- bundle "install --deployment"
+ it "installs gems by default to vendor/bundle" do
+ bundle_config "deployment true"
+ expect do
+ bundle "install"
+ end.not_to change { bundled_app_lock.mtime }
expect(out).to include("vendor/bundle")
end
- it "installs gems to custom path if specified", :bundler => "< 3" do
- bundle "install --path vendor/bundle2 --deployment"
+ it "installs gems to custom path if specified" do
+ bundle_config "path vendor/bundle2"
+ bundle_config "deployment true"
+ bundle "install"
expect(out).to include("vendor/bundle2")
end
- it "works with the --deployment flag if you didn't change anything", :bundler => "< 3" do
- bundle "install --deployment"
+ it "installs gems to custom path if specified, even when configured through ENV" do
+ bundle_config "deployment true"
+ bundle "install", env: { "BUNDLE_PATH" => "vendor/bundle2" }
+ expect(out).to include("vendor/bundle2")
end
- it "works with the --frozen flag if you didn't change anything", :bundler => "< 3" do
- bundle "install --frozen"
+ it "works with the `frozen` setting" do
+ bundle_config "frozen true"
+ expect do
+ bundle "install"
+ end.not_to change { bundled_app_lock.mtime }
end
it "works with BUNDLE_FROZEN if you didn't change anything" do
- bundle :install, :env => { "BUNDLE_FROZEN" => "true" }
+ expect do
+ bundle :install, env: { "BUNDLE_FROZEN" => "true" }
+ end.not_to change { bundled_app_lock.mtime }
end
- it "explodes with the --deployment flag if you make a change and don't check in the lockfile" do
+ it "explodes with the `deployment` setting if you make a change and don't check in the lockfile" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rack-obama"
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "myrack-obama"
G
- bundle "config --local deployment true"
- bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle_config "deployment true"
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
- expect(err).to include("* rack-obama")
+ expect(err).to include("* myrack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
expect(err).not_to include("You have changed in the Gemfile")
end
@@ -227,76 +194,125 @@ RSpec.describe "install in deployment or frozen mode" do
it "works if a path gem is missing but is in a without group" do
build_lib "path_gem"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rake"
gem "path_gem", :path => "#{lib_path("path_gem-1.0")}", :group => :development
G
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 :install, :env => { "DEBUG" => "1" }
+ bundle_config "path .bundle"
+ bundle_config "without development"
+ bundle_config "deployment true"
+ bundle :install, env: { "DEBUG" => "1" }
run "puts :WIN"
expect(out).to eq("WIN")
end
+ it "works if a gem is missing, but it's on a different platform" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+
+ source "https://gem.repo1" do
+ gem "rake", platform: :#{not_local_tag}
+ end
+ G
+
+ bundle :install, env: { "BUNDLE_FROZEN" => "true" }
+ expect(last_command).to be_success
+ end
+
+ it "shows a good error if a gem is missing from the lockfile" do
+ build_repo4 do
+ build_gem "foo"
+ build_gem "bar"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "foo"
+ gem "bar"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ bar
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false, artifice: "compact_index"
+ expect(err).to include("Your lockfile is missing \"bar\", but can't be updated because frozen mode is set")
+ end
+
it "explodes if a path gem is missing" do
build_lib "path_gem"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rake"
gem "path_gem", :path => "#{lib_path("path_gem-1.0")}", :group => :development
G
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 :install, :raise_on_error => false
+ bundle_config "path .bundle"
+ bundle_config "deployment true"
+ bundle :install, raise_on_error: false
expect(err).to include("The path `#{lib_path("path_gem-1.0")}` does not exist.")
end
- it "can have --frozen set via an environment variable", :bundler => "< 3" do
+ it "can have --frozen set via an environment variable" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rack-obama"
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "myrack-obama"
G
ENV["BUNDLE_FROZEN"] = "1"
- bundle "install", :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle "install", raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
- expect(err).to include("* rack-obama")
+ expect(err).to include("* myrack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
expect(err).not_to include("You have changed in the Gemfile")
end
it "can have --deployment set via an environment variable" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rack-obama"
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "myrack-obama"
G
ENV["BUNDLE_DEPLOYMENT"] = "true"
- bundle "install", :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle "install", raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
- expect(err).to include("* rack-obama")
+ expect(err).to include("* myrack-obama")
expect(err).not_to include("You have deleted from the Gemfile")
expect(err).not_to include("You have changed in the Gemfile")
end
- it "installs gems by default to vendor/bundle when deployment mode is set via an environment variable", :bundler => "< 3" do
+ it "installs gems by default to vendor/bundle when deployment mode is set via an environment variable" do
ENV["BUNDLE_DEPLOYMENT"] = "true"
bundle "install"
expect(out).to include("vendor/bundle")
end
- it "installs gems to custom path when deployment mode is set via an environment variable ", :bundler => "< 3" do
+ it "installs gems to custom path when deployment mode is set via an environment variable " do
ENV["BUNDLE_DEPLOYMENT"] = "true"
ENV["BUNDLE_PATH"] = "vendor/bundle2"
bundle "install"
@@ -305,87 +321,119 @@ RSpec.describe "install in deployment or frozen mode" do
it "can have --frozen set to false via an environment variable" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rack-obama"
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "myrack-obama"
G
ENV["BUNDLE_FROZEN"] = "false"
ENV["BUNDLE_DEPLOYMENT"] = "false"
bundle "install"
- expect(out).not_to include("deployment mode")
+ expect(out).not_to include("frozen mode")
expect(out).not_to include("You have added to the Gemfile")
- expect(out).not_to include("* rack-obama")
+ expect(out).not_to include("* myrack-obama")
end
- it "explodes if you remove a gem and don't check in the lockfile" do
+ it "explodes if you replace a gem and don't check in the lockfile" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport"
G
- bundle "config --local deployment true"
- bundle :install, :raise_on_error => false
- expect(err).to include("deployment mode")
+ bundle_config "deployment true"
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile:\n* activesupport\n\n")
- expect(err).to include("You have deleted from the Gemfile:\n* rack")
+ expect(err).to include("You have deleted from the Gemfile:\n* myrack")
+ expect(err).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you remove a gem and don't check in the lockfile" do
+ gemfile 'source "https://gem.repo1"'
+
+ bundle_config "deployment true"
+ bundle :install, raise_on_error: false
+ expect(err).to include("Some dependencies were deleted")
+ expect(err).to include("frozen mode")
+ expect(err).to include("You have deleted from the Gemfile:\n* myrack")
expect(err).not_to include("You have changed in the Gemfile")
end
it "explodes if you add a source" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "git://hubz.com"
+ source "https://gem.repo1"
+ gem "myrack", :git => "git://hubz.com"
G
- bundle "config --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 (at master)")
- expect(err).not_to include("You have changed in the Gemfile")
+ bundle_config "deployment true"
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
+ expect(err).not_to include("You have added to the Gemfile")
+ expect(err).to include("You have changed in the Gemfile:\n* myrack from `no specified source` to `git://hubz.com`")
end
- it "explodes if you unpin a source" do
- build_git "rack"
+ it "explodes if you change a source from git to the default" do
+ build_git "myrack"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-1.0")}"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-1.0")}"
G
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "config --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")} (at master@#{revision_for(lib_path("rack-1.0"))[0..6]}")
+ bundle_config "deployment true"
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
+ 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* myrack from `#{lib_path("myrack-1.0")}` to `no specified source`")
+ end
+
+ it "explodes if you change a source from git to the default, in presence of other git sources" do
+ build_lib "foo", path: lib_path("myrack/foo")
+ build_git "myrack", path: lib_path("myrack")
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack")}"
+ gem "foo", :git => "#{lib_path("myrack")}"
+ G
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "foo", :git => "#{lib_path("myrack")}"
+ G
+
+ bundle_config "deployment true"
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
+ expect(err).to include("You have changed in the Gemfile:\n* myrack from `#{lib_path("myrack")}` 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
- it "explodes if you unpin a source, leaving it pinned somewhere else" do
- build_lib "foo", :path => lib_path("rack/foo")
- build_git "rack", :path => lib_path("rack")
+ it "explodes if you change a source from path to git" do
+ build_git "myrack", path: lib_path("myrack")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack")}"
- gem "foo", :git => "#{lib_path("rack")}"
+ source "https://gem.repo1"
+ gem "myrack", :path => "#{lib_path("myrack")}"
G
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "foo", :git => "#{lib_path("rack")}"
+ source "https://gem.repo1"
+ gem "myrack", :git => "https:/my-git-repo-for-myrack"
G
- bundle "config --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")} (at master@#{revision_for(lib_path("rack"))[0..6]})`")
+ bundle_config "frozen true"
+ bundle :install, raise_on_error: false
+ expect(err).to include("frozen mode")
+ expect(err).to include("You have changed in the Gemfile:\n* myrack from `#{lib_path("myrack")}` to `https:/my-git-repo-for-myrack`")
expect(err).not_to include("You have added to the Gemfile")
expect(err).not_to include("You have deleted from the Gemfile")
end
@@ -393,49 +441,49 @@ RSpec.describe "install in deployment or frozen mode" do
it "remembers that the bundle is frozen at runtime" do
bundle :lock
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
- gem "rack-obama"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
+ gem "myrack-obama"
G
- expect(the_bundle).not_to include_gems "rack 1.0.0"
- expect(err).to include strip_whitespace(<<-E).strip
-The dependencies in your gemfile changed
+ run "require 'myrack'", raise_on_error: false
+ expect(err).to include <<~E.strip
+ The dependencies in your gemfile changed, but the lockfile can't be updated because frozen mode is set (Bundler::ProductionError)
-You have added to the Gemfile:
-* rack (= 1.0.0)
-* rack-obama
+ You have added to the Gemfile:
+ * myrack (= 1.0.0)
+ * myrack-obama
-You have deleted from the Gemfile:
-* rack
+ You have deleted from the Gemfile:
+ * myrack
E
end
end
context "with path in Gemfile and packed" do
it "works fine after bundle package and bundle install --local" do
- build_lib "foo", :path => lib_path("foo")
+ build_lib "foo", path: lib_path("foo")
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo")}"
G
bundle :install
expect(the_bundle).to include_gems "foo 1.0"
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo")).to be_directory
bundle "install --local"
expect(out).to include("Updating files in vendor/cache")
- simulate_new_machine
- bundle "config set --local deployment true"
+ pristine_system_gems
+ bundle_config "deployment true"
bundle "install --verbose"
- expect(out).not_to include("You are trying to install in deployment mode after changing your Gemfile")
+ expect(out).not_to include("can't be updated because frozen mode is set")
expect(out).not_to include("You have added to the Gemfile")
expect(out).not_to include("You have deleted from the Gemfile")
expect(out).to include("vendor/cache/foo")
diff --git a/spec/bundler/install/failure_spec.rb b/spec/bundler/install/failure_spec.rb
index d265fafa63..32ca455439 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"
@@ -14,13 +14,13 @@ RSpec.describe "bundle install" do
end
end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://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
@@ -29,116 +29,57 @@ In Gemfile:
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
+ context "because the downloaded .gem was invalid" do
+ before do
+ build_repo4 do
+ build_gem "a"
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
+ gem_repo4("gems", "a-1.0.gem").open("w") {|f| f << "<html></html>" }
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
+ it "removes the downloaded .gem" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo4"
+ gem "a"
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
+ expect(default_bundle_path("cache", "a-1.0.gem")).not_to exist
end
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
+ context "when lockfile dependencies don't match the gemspec" do
+ before do
+ build_repo4 do
+ build_gem "myrack", "1.0.0" do |s|
+ s.add_dependency "myrack-test", "~> 1.0"
end
- end
- build_repo4 do
- build_gem "a"
+ build_gem "myrack-test", "1.0.0"
end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo4)}"
- source "#{file_uri_for(gem_repo2)}" do
- gem "rails"
- end
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
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
- actionmailer was resolved to 2.3.2, which depends on
- activesupport
- M
- end
- context "because the downloaded .gem was invalid" do
- before do
- build_repo4 do
- build_gem "a"
- end
+ # First install to generate lockfile
+ bundle :install
- gem_repo4("gems", "a-1.0.gem").open("w") {|f| f << "<html></html>" }
- end
+ # Manually edit lockfile to have incorrect dependencies
+ lockfile_content = File.read(bundled_app_lock)
+ # Remove the myrack-test dependency from myrack
+ lockfile_content.gsub!(/^ myrack \(1\.0\.0\)\n myrack-test \(~> 1\.0\)\n/, " myrack (1.0.0)\n")
+ File.write(bundled_app_lock, lockfile_content)
+ end
- it "removes the downloaded .gem" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo4)}"
- gem "a"
- G
+ it "reports the mismatch with detailed information" do
+ bundle :install, raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" }
- expect(default_bundle_path("cache", "a-1.0.gem")).not_to exist
- end
+ expect(err).to include("Bundler found incorrect dependencies in the lockfile for myrack-1.0.0")
+ expect(err).to include("myrack-test: gemspec specifies ~> 1.0, not in lockfile")
+ expect(err).to include("Please run `bundle install` to regenerate the lockfile.")
end
end
end
diff --git a/spec/bundler/install/force_spec.rb b/spec/bundler/install/force_spec.rb
new file mode 100644
index 0000000000..e0f6fb6364
--- /dev/null
+++ b/spec/bundler/install/force_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ before :each do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+ end
+
+ shared_examples_for "an option to force reinstalling gems" do
+ it "re-installs installed gems" do
+ myrack_lib = default_bundle_path("gems/myrack-1.0.0/lib/myrack.rb")
+
+ bundle :install
+ myrack_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle :install, flag => true
+
+ expect(out).to include "Installing myrack 1.0.0"
+ expect(myrack_lib.open(&:read)).to eq("MYRACK = '1.0.0'\n")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "works on first bundle install" do
+ bundle :install, flag => true
+
+ expect(out).to include "Installing myrack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ context "with a git gem" do
+ let!(:ref) { build_git("foo", "1.0").ref_for("HEAD", 11) }
+
+ before do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+ end
+
+ it "re-installs installed gems" do
+ foo_lib = default_bundle_path("bundler/gems/foo-1.0-#{ref}/lib/foo.rb")
+
+ bundle :install
+ foo_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle :install, flag => true
+
+ expect(foo_lib.open(&:read)).to eq("FOO = '1.0'\n")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "works on first bundle install" do
+ bundle :install, flag => true
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+ end
+
+ describe "with --force" do
+ it_behaves_like "an option to force reinstalling gems" do
+ let(:flag) { "force" }
+ end
+ end
+
+ describe "with --redownload" do
+ it_behaves_like "an option to force reinstalling gems" do
+ let(:flag) { "redownload" }
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/eval_gemfile_spec.rb b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
index c42ae7ef79..3afa4f5daa 100644
--- a/spec/bundler/install/gemfile/eval_gemfile_spec.rb
+++ b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
before do
- build_lib("gunks", :path => bundled_app.join("gems/gunks")) do |s|
+ build_lib("gunks", path: bundled_app("gems/gunks")) do |s|
s.name = "gunks"
s.version = "0.0.1"
end
@@ -10,30 +10,68 @@ 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
+ gemfile "Gemfile-other", <<-G
+ source "https://gem.repo1"
gemspec :path => 'gems/gunks'
G
end
it "installs the gemspec specified gem" do
install_gemfile <<-G
+ source "https://gem.repo1"
eval_gemfile 'Gemfile-other'
G
expect(out).to include("Resolving dependencies")
expect(out).to include("Bundle complete")
- expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}"
+ expect(the_bundle).to include_gem "gunks 0.0.1", source: "path@#{bundled_app("gems", "gunks")}"
+ 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
+
+ gemfile bundled_app("gems/Gemfile"), <<-G
+ source "https://gem.repo2"
+
+ gemspec :path => "\#{__dir__}/gunks"
+
+ source "https://gem.repo2" do
+ gem "zip-zip"
+ end
+ G
+ end
+
+ it "installs and finds gems correctly" do
+ install_gemfile <<-G
+ source "https://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
+ build_lib("a", path: bundled_app("gems/a"))
+ gemfile bundled_app("nested/Gemfile-nested"), <<-G
+ source "https://gem.repo1"
gem "a", :path => "../gems/a"
G
gemfile <<-G
+ source "https://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 "deployment true"
bundle :install
end
end
@@ -57,27 +95,28 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
it "installs the gemspec specified gem" do
install_gemfile <<-G
+ source "https://gem.repo1"
eval_gemfile 'other/Gemfile-other'
gemspec :path => 'gems/gunks'
G
expect(out).to include("Resolving dependencies")
expect(out).to include("Bundle complete")
- expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}"
+ expect(the_bundle).to include_gem "gunks 0.0.1", source: "path@#{bundled_app("gems", "gunks")}"
end
end
context "eval-ed Gemfile references other gemfiles" do
it "works with relative paths" do
- create_file "other/Gemfile-other", "gem 'rack'"
- create_file "other/Gemfile", "eval_gemfile 'Gemfile-other'"
- create_file "Gemfile-alt", <<-G
- source "#{file_uri_for(gem_repo1)}"
+ gemfile "other/Gemfile-other", "gem 'myrack'"
+ gemfile "other/Gemfile", "eval_gemfile 'Gemfile-other'"
+ gemfile "Gemfile-alt", <<-G
+ source "https://gem.repo1"
eval_gemfile "other/Gemfile"
G
install_gemfile "eval_gemfile File.expand_path('Gemfile-alt')"
- expect(the_bundle).to include_gem "rack 1.0.0"
+ expect(the_bundle).to include_gem "myrack 1.0.0"
end
end
end
diff --git a/spec/bundler/install/gemfile/force_ruby_platform_spec.rb b/spec/bundler/install/gemfile/force_ruby_platform_spec.rb
new file mode 100644
index 0000000000..bcc1f36823
--- /dev/null
+++ b/spec/bundler/install/gemfile/force_ruby_platform_spec.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with force_ruby_platform DSL option", :jruby do
+ context "when no transitive deps" do
+ before do
+ build_repo4 do
+ # Build a gem with platform specific versions
+ build_gem("platform_specific")
+
+ build_gem("platform_specific") do |s|
+ s.platform = Bundler.local_platform
+ end
+
+ # Build the exact same gem with a different name to compare using vs not using the option
+ build_gem("platform_specific_forced")
+
+ build_gem("platform_specific_forced") do |s|
+ s.platform = Bundler.local_platform
+ end
+ end
+ end
+
+ it "pulls the pure ruby variant of the given gem" do
+ install_gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "platform_specific_forced", :force_ruby_platform => true
+ gem "platform_specific"
+ G
+
+ expect(the_bundle).to include_gems "platform_specific_forced 1.0 ruby"
+ expect(the_bundle).to include_gems "platform_specific 1.0 #{Bundler.local_platform}"
+ end
+
+ it "still respects a global `force_ruby_platform` config" do
+ install_gemfile <<-G, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "true" }
+ source "https://gem.repo4"
+
+ gem "platform_specific_forced", :force_ruby_platform => true
+ gem "platform_specific"
+ G
+
+ expect(the_bundle).to include_gems "platform_specific_forced 1.0 ruby"
+ expect(the_bundle).to include_gems "platform_specific 1.0 ruby"
+ end
+ end
+
+ context "when also a transitive dependency" do
+ before do
+ build_repo4 do
+ build_gem("depends_on_platform_specific") {|s| s.add_dependency "platform_specific" }
+
+ build_gem("platform_specific")
+
+ build_gem("platform_specific") do |s|
+ s.platform = Bundler.local_platform
+ end
+ end
+ end
+
+ it "still pulls the ruby variant" do
+ install_gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "depends_on_platform_specific"
+ gem "platform_specific", :force_ruby_platform => true
+ G
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 ruby"
+ end
+ end
+
+ context "with transitive dependencies with platform specific versions" do
+ before do
+ build_repo4 do
+ build_gem("depends_on_platform_specific") do |s|
+ s.add_dependency "platform_specific"
+ end
+
+ build_gem("depends_on_platform_specific") do |s|
+ s.add_dependency "platform_specific"
+ s.platform = Bundler.local_platform
+ end
+
+ build_gem("platform_specific")
+
+ build_gem("platform_specific") do |s|
+ s.platform = Bundler.local_platform
+ end
+ end
+ end
+
+ it "ignores ruby variants for the transitive dependencies" do
+ install_gemfile <<-G, env: { "DEBUG_RESOLVER" => "true" }
+ source "https://gem.repo4"
+
+ gem "depends_on_platform_specific", :force_ruby_platform => true
+ G
+
+ expect(the_bundle).to include_gems "depends_on_platform_specific 1.0 ruby"
+ expect(the_bundle).to include_gems "platform_specific 1.0 #{Bundler.local_platform}"
+ end
+
+ it "reinstalls the ruby variant when a platform specific variant is already installed, the lockile has only ruby platform, and :force_ruby_platform is used in the Gemfile" do
+ skip "Can't simulate platform reliably on JRuby, installing a platform specific gem fails to activate io-wait because only the -java version is present, and we're simulating a different platform" if RUBY_ENGINE == "jruby"
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4
+ specs:
+ platform_specific (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86-darwin-100" do
+ system_gems "platform_specific-1.0-x86-darwin-100", path: default_bundle_path
+
+ install_gemfile <<-G, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, artifice: "compact_index"
+ source "https://gem.repo4"
+
+ gem "platform_specific", :force_ruby_platform => true
+ G
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 ruby"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb
index 8e13ac05a9..e51fc9247d 100644
--- a/spec/bundler/install/gemfile/gemspec_spec.rb
+++ b/spec/bundler/install/gemfile/gemspec_spec.rb
@@ -9,35 +9,35 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "should install runtime and development dependencies" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp("foo")) do |s|
s.write("Gemfile", "source :rubygems\ngemspec")
s.add_dependency "bar", "=1.0.0"
s.add_development_dependency "bar-dev", "=1.0.0"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gemspec :path => '#{tmp.join("foo")}'
+ source "https://gem.repo2"
+ gemspec :path => '#{tmp("foo")}'
G
expect(the_bundle).to include_gems "bar 1.0.0"
- expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :development
end
it "that is hidden should install runtime and development dependencies" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp("foo")) do |s|
s.write("Gemfile", "source :rubygems\ngemspec")
s.add_dependency "bar", "=1.0.0"
s.add_development_dependency "bar-dev", "=1.0.0"
end
- FileUtils.mv tmp.join("foo", "foo.gemspec"), tmp.join("foo", ".gemspec")
+ FileUtils.mv tmp("foo", "foo.gemspec"), tmp("foo", ".gemspec")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gemspec :path => '#{tmp.join("foo")}'
+ source "https://gem.repo2"
+ gemspec :path => '#{tmp("foo")}'
G
expect(the_bundle).to include_gems "bar 1.0.0"
- expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :development
end
it "should handle a list of requirements" do
@@ -46,153 +46,153 @@ RSpec.describe "bundle install from an existing gemspec" do
build_gem "baz", "1.1"
end
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp("foo")) do |s|
s.write("Gemfile", "source :rubygems\ngemspec")
s.add_dependency "baz", ">= 1.0", "< 1.1"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gemspec :path => '#{tmp.join("foo")}'
+ source "https://gem.repo2"
+ gemspec :path => '#{tmp("foo")}'
G
expect(the_bundle).to include_gems "baz 1.0"
end
it "should raise if there are no gemspecs available" do
- build_lib("foo", :path => tmp.join("foo"), :gemspec => false)
+ build_lib("foo", path: tmp("foo"), gemspec: false)
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
- gemspec :path => '#{tmp.join("foo")}'
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
+ gemspec :path => '#{tmp("foo")}'
G
- expect(err).to match(/There are no gemspecs at #{tmp.join('foo')}/)
+ expect(err).to match(/There are no gemspecs at #{tmp("foo")}/)
end
it "should raise if there are too many gemspecs available" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp("foo")) do |s|
s.write("foo2.gemspec", build_spec("foo", "4.0").first.to_ruby)
end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
- gemspec :path => '#{tmp.join("foo")}'
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
+ gemspec :path => '#{tmp("foo")}'
G
- expect(err).to match(/There are multiple gemspecs at #{tmp.join('foo')}/)
+ expect(err).to match(/There are multiple gemspecs at #{tmp("foo")}/)
end
it "should pick a specific gemspec" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp("foo")) do |s|
s.write("foo2.gemspec", "")
s.add_dependency "bar", "=1.0.0"
s.add_development_dependency "bar-dev", "=1.0.0"
end
install_gemfile(<<-G)
- source "#{file_uri_for(gem_repo2)}"
- gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ source "https://gem.repo2"
+ gemspec :path => '#{tmp("foo")}', :name => 'foo'
G
expect(the_bundle).to include_gems "bar 1.0.0"
- expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :development
end
it "should use a specific group for development dependencies" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp("foo")) do |s|
s.write("foo2.gemspec", "")
s.add_dependency "bar", "=1.0.0"
s.add_development_dependency "bar-dev", "=1.0.0"
end
install_gemfile(<<-G)
- source "#{file_uri_for(gem_repo2)}"
- gemspec :path => '#{tmp.join("foo")}', :name => 'foo', :development_group => :dev
+ source "https://gem.repo2"
+ gemspec :path => '#{tmp("foo")}', :name => 'foo', :development_group => :dev
G
expect(the_bundle).to include_gems "bar 1.0.0"
- expect(the_bundle).not_to include_gems "bar-dev 1.0.0", :groups => :development
- expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :dev
+ expect(the_bundle).not_to include_gems "bar-dev 1.0.0", groups: :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :dev
end
it "should match a lockfile even if the gemspec defines development dependencies" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
- s.write("Gemfile", "source '#{file_uri_for(gem_repo1)}'\ngemspec")
+ build_lib("foo", path: tmp("foo")) do |s|
+ s.write("Gemfile", "source 'https://gem.repo1'\ngemspec")
s.add_dependency "actionpack", "=2.3.2"
- s.add_development_dependency "rake", "=13.0.1"
+ s.add_development_dependency "rake", rake_version
end
- bundle "install", :dir => tmp.join("foo")
+ bundle "install", dir: tmp("foo"), artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
# This should really be able to rely on $stderr, but, it's not written
# right, so we can't. In fact, this is a bug negation test, and so it'll
# ghost pass in future, and will only catch a regression if the message
# doesn't change. Exit codes should be used correctly (they can be more
# than just 0 and 1).
- bundle "config set --local deployment true"
- output = bundle("install", :dir => tmp.join("foo"))
+ bundle_config "deployment true"
+ output = bundle("install", dir: tmp("foo"), artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s })
expect(output).not_to match(/You have added to the Gemfile/)
expect(output).not_to match(/You have deleted from the Gemfile/)
- expect(output).not_to match(/install in deployment mode after changing/)
+ expect(output).not_to match(/the lockfile can't be updated because frozen mode is set/)
end
it "should match a lockfile without needing to re-resolve" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
- s.add_dependency "rack"
+ build_lib("foo", path: tmp("foo")) do |s|
+ s.add_dependency "myrack"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gemspec :path => '#{tmp.join("foo")}'
+ source "https://gem.repo1"
+ gemspec :path => '#{tmp("foo")}'
G
- bundle "install", :verbose => true
+ bundle "install", verbose: true
message = "Found no changes, using resolution from the lockfile"
expect(out.scan(message).size).to eq(1)
end
it "should match a lockfile without needing to re-resolve with development dependencies" do
- simulate_platform java
-
- build_lib("foo", :path => tmp.join("foo")) do |s|
- s.add_dependency "rack"
- s.add_development_dependency "thin"
- end
+ simulate_platform "java" do
+ build_lib("foo", path: tmp("foo")) do |s|
+ s.add_dependency "myrack"
+ s.add_development_dependency "thin"
+ end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gemspec :path => '#{tmp.join("foo")}'
- G
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gemspec :path => '#{tmp("foo")}'
+ G
- bundle "install", :verbose => true
+ bundle "install", verbose: true
- message = "Found no changes, using resolution from the lockfile"
- expect(out.scan(message).size).to eq(1)
+ message = "Found no changes, using resolution from the lockfile"
+ expect(out.scan(message).size).to eq(1)
+ end
end
- it "should match a lockfile on non-ruby platforms with a transitive platform dependency", :jruby do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ it "should match a lockfile on non-ruby platforms with a transitive platform dependency", :jruby_only do
+ build_lib("foo", path: tmp("foo")) do |s|
s.add_dependency "platform_specific"
end
- system_gems "platform_specific-1.0-java", :path => default_bundle_path
+ system_gems "platform_specific-1.0-java", path: default_bundle_path
install_gemfile <<-G
- gemspec :path => '#{tmp.join("foo")}'
+ gemspec :path => '#{tmp("foo")}'
G
- bundle "update --bundler", :verbose => true
- expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 JAVA"
+ bundle "update --bundler", artifice: "compact_index", verbose: true
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 java"
end
it "should evaluate the gemspec in its directory" do
- build_lib("foo", :path => tmp.join("foo"))
- File.open(tmp.join("foo/foo.gemspec"), "w") do |s|
- s.write "raise 'ahh' unless Dir.pwd == '#{tmp.join("foo")}'"
+ build_lib("foo", path: tmp("foo"))
+ File.open(tmp("foo/foo.gemspec"), "w") do |s|
+ s.write "raise 'ahh' unless Dir.pwd == '#{tmp("foo")}'"
end
- install_gemfile <<-G, :raise_on_error => false
- gemspec :path => '#{tmp.join("foo")}'
+ install_gemfile <<-G, raise_on_error: false
+ gemspec :path => '#{tmp("foo")}'
G
- expect(last_command.stdboth).not_to include("ahh")
+ expect(stdboth).not_to include("ahh")
end
it "allows the gemspec to activate other gems" do
@@ -201,15 +201,16 @@ RSpec.describe "bundle install from an existing gemspec" do
#
# issue was caused by rubygems having an unresolved gem during a require,
# so emulate that
- system_gems %w[rack-1.0.0 rack-0.9.1 rack-obama-1.0]
+ system_gems %w[myrack-1.0.0 myrack-0.9.1 myrack-obama-1.0]
- build_lib("foo", :path => bundled_app)
+ build_lib("foo", path: bundled_app)
gemspec = bundled_app("foo.gemspec").read
bundled_app("foo.gemspec").open("w") do |f|
- f.write "#{gemspec.strip}.tap { gem 'rack-obama'; require 'rack/obama' }"
+ f.write "#{gemspec.strip}.tap { gem 'myrack-obama'; require 'myrack/obama' }"
end
install_gemfile <<-G
+ source "https://gem.repo1"
gemspec
G
@@ -217,26 +218,26 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "allows conflicts" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp("foo")) do |s|
s.version = "1.0.0"
s.add_dependency "bar", "= 1.0.0"
end
- build_gem "deps", :to_bundle => true do |s|
+ build_gem "deps", to_bundle: true do |s|
s.add_dependency "foo", "= 0.0.1"
end
- build_gem "foo", "0.0.1", :to_bundle => true
+ build_gem "foo", "0.0.1", to_bundle: true
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "deps"
- gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ gemspec :path => '#{tmp("foo")}', :name => 'foo'
G
expect(the_bundle).to include_gems "foo 1.0.0"
end
it "does not break Gem.finish_resolve with conflicts" do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp("foo")) do |s|
s.version = "1.0.0"
s.add_dependency "bar", "= 1.0.0"
end
@@ -248,9 +249,9 @@ RSpec.describe "bundle install from an existing gemspec" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "deps"
- gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ gemspec :path => '#{tmp("foo")}', :name => 'foo'
G
expect(the_bundle).to include_gems "foo 1.0.0"
@@ -259,16 +260,36 @@ RSpec.describe "bundle install from an existing gemspec" do
expect(out).to eq("WIN")
end
- it "works with only_update_to_newer_versions" do
- build_lib "omg", "2.0", :path => lib_path("omg")
+ it "does not make Gem.try_activate warn when local gem has extensions" do
+ build_lib("foo", path: tmp("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_c_extension
+ end
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gemspec :path => '#{tmp("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0"
+
+ run "Gem.try_activate('irb/lc/es/error.rb'); puts 'WIN'"
+ expect(out).to eq("WIN")
+ expect(err).to be_empty
+ end
+
+ it "handles downgrades" do
+ build_lib "omg", "2.0", path: lib_path("omg")
install_gemfile <<-G
+ source "https://gem.repo1"
gemspec :path => "#{lib_path("omg")}"
G
- build_lib "omg", "1.0", :path => lib_path("omg")
+ 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
@@ -276,23 +297,23 @@ RSpec.describe "bundle install from an existing gemspec" do
context "in deployment mode" do
context "when the lockfile was not updated after a change to the gemspec's dependencies" do
it "reports that installation failed" do
- build_lib "cocoapods", :path => bundled_app do |s|
+ build_lib "cocoapods", path: bundled_app do |s|
s.add_dependency "activesupport", ">= 1"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gemspec
G
expect(the_bundle).to include_gems("cocoapods 1.0", "activesupport 2.3.5")
- build_lib "cocoapods", :path => bundled_app do |s|
+ build_lib "cocoapods", path: bundled_app do |s|
s.add_dependency "activesupport", ">= 1.0.1"
end
- bundle "config --local deployment true"
- bundle :install, :raise_on_error => false
+ bundle_config "deployment true"
+ bundle :install, raise_on_error: false
expect(err).to include("changed")
end
@@ -302,107 +323,93 @@ RSpec.describe "bundle install from an existing gemspec" do
context "when child gemspecs conflict with a released gemspec" do
before do
# build the "parent" gem that depends on another gem in the same repo
- build_lib "source_conflict", :path => bundled_app do |s|
- s.add_dependency "rack_middleware"
+ build_lib "source_conflict", path: bundled_app do |s|
+ s.add_dependency "myrack_middleware"
end
# build the "child" gem that is the same version as a released gem, but
# has completely different and conflicting dependency requirements
- build_lib "rack_middleware", "1.0", :path => bundled_app("rack_middleware") do |s|
- s.add_dependency "rack", "1.0" # anything other than 0.9.1
+ build_lib "myrack_middleware", "1.0", path: bundled_app("myrack_middleware") do |s|
+ s.add_dependency "myrack", "1.0" # anything other than 0.9.1
end
end
it "should install the child gemspec's deps" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gemspec
G
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
context "with a lockfile and some missing dependencies" do
let(:source_uri) { "http://localgemserver.test" }
- context "previously bundled for Ruby" do
- let(:platform) { "ruby" }
-
- before do
- skip "not installing for some reason" if Gem.win_platform?
-
- build_lib("foo", :path => tmp.join("foo")) do |s|
- s.add_dependency "rack", "=1.0.0"
- end
-
- gemfile <<-G
- source "#{source_uri}"
- gemspec :path => "../foo"
- G
-
- lockfile <<-L
- PATH
- remote: ../foo
- specs:
- foo (1.0)
- rack (= 1.0.0)
-
- GEM
- remote: #{source_uri}
- specs:
- rack (1.0.0)
-
- PLATFORMS
- #{generic_local_platform}
+ before do
+ build_lib("foo", path: tmp("foo")) do |s|
+ s.add_dependency "myrack", "=1.0.0"
+ end
- DEPENDENCIES
- foo!
+ gemfile <<-G
+ source "#{source_uri}"
+ gemspec :path => "../foo"
+ G
- BUNDLED WITH
- #{Bundler::VERSION}
- L
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
end
- context "using JRuby with explicit platform", :jruby do
- before do
- create_file(
- tmp.join("foo", "foo-java.gemspec"),
- build_spec("foo", "1.0", "java") do
- dep "rack", "=1.0.0"
- @spec.authors = "authors"
- @spec.summary = "summary"
- end.first.to_ruby
- )
- end
+ lockfile <<-L
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+ myrack (= 1.0.0)
+
+ GEM
+ remote: #{source_uri}
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
- it "should install" do
- results = bundle "install", :artifice => "endpoint"
- expect(results).to include("Installing rack 1.0.0")
- expect(the_bundle).to include_gems "rack 1.0.0"
- end
+ context "using JRuby with explicit platform", :jruby_only do
+ before do
+ create_file(
+ tmp("foo", "foo-java.gemspec"),
+ build_spec("foo", "1.0", "java") do
+ dep "myrack", "=1.0.0"
+ @spec.authors = "authors"
+ @spec.summary = "summary"
+ end.first.to_ruby
+ )
end
- context "using JRuby", :jruby do
- it "should install" do
- results = bundle "install", :artifice => "endpoint"
- expect(results).to include("Installing rack 1.0.0")
- expect(the_bundle).to include_gems "rack 1.0.0"
- end
+ it "should install" do
+ results = bundle "install", artifice: "endpoint"
+ expect(results).to include("Installing myrack 1.0.0")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
+ end
- context "using Windows" do
- it "should install" do
- simulate_windows do
- results = bundle "install", :artifice => "endpoint"
- expect(results).to include("Installing rack 1.0.0")
- expect(the_bundle).to include_gems "rack 1.0.0"
- end
- end
- end
+ it "should install", :jruby do
+ results = bundle "install", artifice: "endpoint"
+ expect(results).to include("Installing myrack 1.0.0")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- context "bundled for ruby and jruby" do
+ context "bundled for multiple platforms" do
let(:platform_specific_type) { :runtime }
let(:dependency) { "platform_specific" }
before do
@@ -412,38 +419,49 @@ RSpec.describe "bundle install from an existing gemspec" do
end
end
- build_lib "foo", :path => bundled_app do |s|
- if platform_specific_type == :runtime
+ build_lib "foo", path: bundled_app do |s|
+ case platform_specific_type
+ when :runtime
s.add_runtime_dependency dependency
- elsif platform_specific_type == :development
+ when :development
s.add_development_dependency dependency
else
- raise "wrong dependency type #{platform_specific_type}, can only be :development or :runtime"
+ raise ArgumentError, "wrong dependency type #{platform_specific_type}, can only be :development or :runtime"
end
end
- bundle "config specific_platform false"
+ gemfile <<-G
+ source "https://gem.repo2"
+ gemspec
+ G
- %w[ruby jruby].each do |platform|
- simulate_platform(platform) do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gemspec
- G
- end
- end
+ bundle_config "force_ruby_platform true"
+ bundle "install"
+
+ simulate_new_machine
+ simulate_platform("jruby") { bundle "install" }
+ expect(lockfile).to include("platform_specific (1.0-java)")
+ simulate_platform("x64-mingw-ucrt") { bundle "install" }
end
context "on ruby" do
before do
- simulate_platform("ruby")
+ bundle_config "force_ruby_platform true"
bundle :install
end
context "as a runtime dependency" do
- it "keeps java dependencies in the lockfile" do
- expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
- expect(lockfile).to eq strip_whitespace(<<-L)
+ it "keeps all platform dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 ruby"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0", "java"
+ c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw-ucrt"
+ end
+
+ expect(lockfile).to eq <<~L
PATH
remote: .
specs:
@@ -451,20 +469,22 @@ RSpec.describe "bundle install from an existing gemspec" do
platform_specific
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
platform_specific (1.0)
platform_specific (1.0-java)
+ platform_specific (1.0-x64-mingw-ucrt)
PLATFORMS
java
ruby
+ x64-mingw-ucrt
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -472,30 +492,40 @@ RSpec.describe "bundle install from an existing gemspec" do
context "as a development dependency" do
let(:platform_specific_type) { :development }
- it "keeps java dependencies in the lockfile" do
- expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
- expect(lockfile).to eq strip_whitespace(<<-L)
+ it "keeps all platform dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 ruby"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0", "java"
+ c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw-ucrt"
+ end
+
+ expect(lockfile).to eq <<~L
PATH
remote: .
specs:
foo (1.0)
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
platform_specific (1.0)
platform_specific (1.0-java)
+ platform_specific (1.0-x64-mingw-ucrt)
PLATFORMS
java
ruby
+ x64-mingw-ucrt
DEPENDENCIES
foo!
platform_specific
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -504,32 +534,43 @@ RSpec.describe "bundle install from an existing gemspec" do
let(:platform_specific_type) { :development }
let(:dependency) { "indirect_platform_specific" }
- it "keeps java dependencies in the lockfile" do
- expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY"
- expect(lockfile).to eq strip_whitespace(<<-L)
+ it "keeps all platform dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 ruby"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo2, "indirect_platform_specific", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0"
+ c.checksum gem_repo2, "platform_specific", "1.0", "java"
+ c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw-ucrt"
+ end
+
+ expect(lockfile).to eq <<~L
PATH
remote: .
specs:
foo (1.0)
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
indirect_platform_specific (1.0)
platform_specific
platform_specific (1.0)
platform_specific (1.0-java)
+ platform_specific (1.0-x64-mingw-ucrt)
PLATFORMS
java
ruby
+ x64-mingw-ucrt
DEPENDENCIES
foo!
indirect_platform_specific
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -539,35 +580,158 @@ RSpec.describe "bundle install from an existing gemspec" do
context "with multiple platforms" do
before do
- build_lib("foo", :path => tmp.join("foo")) do |s|
+ build_lib("foo", path: tmp("foo")) do |s|
s.version = "1.0.0"
- s.add_development_dependency "rack"
- s.write "foo-universal-java.gemspec", build_spec("foo", "1.0.0", "universal-java") {|sj| sj.runtime "rack", "1.0.0" }.first.to_ruby
+ s.add_development_dependency "myrack"
+ s.write "foo-universal-java.gemspec", build_spec("foo", "1.0.0", "universal-java") {|sj| sj.runtime "myrack", "1.0.0" }.first.to_ruby
end
end
it "installs the ruby platform gemspec" do
- simulate_platform "ruby"
+ bundle_config "force_ruby_platform true"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ source "https://gem.repo1"
+ gemspec :path => '#{tmp("foo")}', :name => 'foo'
G
- expect(the_bundle).to include_gems "foo 1.0.0", "rack 1.0.0"
+ expect(the_bundle).to include_gems "foo 1.0.0", "myrack 1.0.0"
end
it "installs the ruby platform gemspec and skips dev deps with `without development` configured" do
- simulate_platform "ruby"
+ bundle_config "force_ruby_platform true"
- bundle "config --local without development"
+ bundle_config "without development"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ source "https://gem.repo1"
+ gemspec :path => '#{tmp("foo")}', :name => 'foo'
G
expect(the_bundle).to include_gem "foo 1.0.0"
- expect(the_bundle).not_to include_gem "rack"
+ expect(the_bundle).not_to include_gem "myrack"
+ end
+ end
+
+ context "with multiple platforms and resolving for more specific platforms" do
+ before do
+ build_lib("chef", path: tmp("chef")) do |s|
+ s.version = "17.1.17"
+ s.write "chef-universal-mingw-ucrt.gemspec", build_spec("chef", "17.1.17", "universal-mingw-ucrt") {|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-mingw-ucrt"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gemspec :path => "../chef"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "chef", "17.1.17"
+ c.no_checksum "chef", "17.1.17", "universal-mingw-ucrt"
+ c.checksum gem_repo4, "win32-api", "1.5.3", "universal-mingw-ucrt"
+ end
+
+ initial_lockfile = <<~L
+ PATH
+ remote: ../chef
+ specs:
+ chef (17.1.17)
+ chef (17.1.17-universal-mingw-ucrt)
+ win32-api (~> 1.5.3)
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ win32-api (1.5.3-universal-mingw-ucrt)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby", "x64-mingw-ucrt", "x86-mingw32")}
+
+ DEPENDENCIES
+ chef!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile initial_lockfile
+
+ bundle "update"
+
+ expect(lockfile).to eq initial_lockfile
+ end
+ end
+
+ context "with multiple locked platforms" do
+ before do
+ build_lib("activeadmin", path: tmp("activeadmin")) do |s|
+ s.version = "2.9.0"
+ s.add_dependency "railties", ">= 5.2", "< 6.2"
+ end
+
+ build_repo4 do
+ build_gem "railties", "6.1.4"
+
+ build_gem "jruby-openssl", "0.10.7" do |s|
+ s.platform = "java"
+ end
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gemspec :path => "../activeadmin"
+ gem "jruby-openssl", :platform => :jruby
+ G
+
+ bundle "lock --add-platform java"
+ end
+
+ it "does not remove the platform specific specs from the lockfile when re-resolving due to gemspec changes" do
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "activeadmin", "2.9.0"
+ c.checksum gem_repo4, "jruby-openssl", "0.10.7", "java"
+ c.checksum gem_repo4, "railties", "6.1.4"
+ end
+
+ expect(lockfile).to eq <<~L
+ PATH
+ remote: ../activeadmin
+ specs:
+ activeadmin (2.9.0)
+ railties (>= 5.2, < 6.2)
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ jruby-openssl (0.10.7-java)
+ railties (6.1.4)
+
+ PLATFORMS
+ #{lockfile_platforms("java")}
+
+ DEPENDENCIES
+ activeadmin!
+ jruby-openssl
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemspec = tmp("activeadmin/activeadmin.gemspec")
+ File.write(gemspec, File.read(gemspec).sub(">= 5.2", ">= 6.0"))
+
+ previous_lockfile = lockfile
+
+ bundle "install --local"
+
+ expect(lockfile).to eq(previous_lockfile.sub(">= 5.2", ">= 6.0"))
end
end
end
diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb
index c0e2510acd..b2a82caf01 100644
--- a/spec/bundler/install/gemfile/git_spec.rb
+++ b/spec/bundler/install/gemfile/git_spec.rb
@@ -1,21 +1,26 @@
# frozen_string_literal: true
RSpec.describe "bundle install with git sources" do
- describe "when floating on master" do
- before :each do
- build_git "foo" do |s|
- s.executables = "foobar"
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ describe "when floating on main" do
+ let(:base_gemfile) do
+ <<-G
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
G
end
+ let(:install_base_gemfile) do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile base_gemfile
+ end
+
it "fetches gems" do
+ install_base_gemfile
expect(the_bundle).to include_gems("foo 1.0")
run <<-RUBY
@@ -26,18 +31,69 @@ RSpec.describe "bundle install with git sources" do
expect(out).to eq("WIN")
end
- it "caches the git repo", :bundler => "< 3" do
- expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes :size => 1
+ it "does not (yet?) enforce CHECKSUMS" do
+ build_git "foo"
+ revision = revision_for(lib_path("foo-1.0"))
+
+ bundle_config "lockfile_checksums true"
+ gemfile base_gemfile
+
+ lockfile <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{revision}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ CHECKSUMS
+ foo (1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle_config "frozen true"
+
+ bundle "install"
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "caches the git repo" do
+ install_base_gemfile
+ expect(Dir["#{default_cache_path}/git/foo-1.0-*"]).to have_attributes size: 1
end
- it "caches the git repo globally" do
- simulate_new_machine
- bundle "config set global_gem_cache true"
+ it "does not write to cache on bundler/setup" do
+ install_base_gemfile
+ FileUtils.rm_r(default_cache_path)
+ ruby "require 'bundler/setup'"
+ expect(default_cache_path).not_to exist
+ end
+
+ it "caches the git repo globally and properly uses the cached repo on the next invocation" do
+ install_base_gemfile
+ pristine_system_gems
+ bundle_config "global_gem_cache true"
bundle :install
- expect(Dir["#{home}/.bundle/cache/git/foo-1.0-*"]).to have_attributes :size => 1
+ expect(Dir["#{home}/.bundle/cache/git/foo-1.0-*"]).to have_attributes size: 1
+
+ bundle "install --verbose"
+ expect(err).to be_empty
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")}")
end
it "caches the evaluated gemspec" do
+ install_base_gemfile
git = update_git "foo" do |s|
s.executables = ["foobar"] # we added this the first time, so keep it now
s.files = ["bin/foobar"] # updating git nukes the files list
@@ -47,17 +103,20 @@ RSpec.describe "bundle install with git sources" do
bundle "update foo"
- sha = git.ref_for("master", 11)
- spec_file = default_bundle_path.join("bundler/gems/foo-1.0-#{sha}/foo.gemspec").to_s
- ruby_code = Gem::Specification.load(spec_file).to_ruby
+ sha = git.ref_for("main", 11)
+ spec_file = default_bundle_path("bundler/gems/foo-1.0-#{sha}/foo.gemspec")
+ expect(spec_file).to exist
+ ruby_code = Gem::Specification.load(spec_file.to_s).to_ruby
file_code = File.read(spec_file)
expect(file_code).to eq(ruby_code)
end
it "does not update the git source implicitly" do
+ install_base_gemfile
update_git "foo"
- install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2
+ install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
@@ -72,6 +131,7 @@ RSpec.describe "bundle install with git sources" do
end
it "sets up git gem executables on the path" do
+ install_base_gemfile
bundle "exec foobar"
expect(out).to eq("1.0")
end
@@ -79,32 +139,30 @@ RSpec.describe "bundle install with git sources" do
it "complains if pinned specs don't exist in the git repo" do
build_git "foo"
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
gem "foo", "1.1", :git => "#{lib_path("foo-1.0")}"
G
- expect(err).to include("The source contains 'foo' at: 1.0")
+ expect(err).to include("The source contains the following gems matching 'foo':\n * foo-1.0")
end
- it "complains with version and platform if pinned specs don't exist in the git repo" do
- simulate_platform "java"
-
+ it "complains with version and platform if pinned specs don't exist in the git repo", :jruby_only do
build_git "only_java" do |s|
s.platform = "java"
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
platforms :jruby do
gem "only_java", "1.2", :git => "#{lib_path("only_java-1.0-java")}"
end
G
- expect(err).to include("The source contains 'only_java' at: 1.0 java")
+ expect(err).to include("The source contains the following gems matching 'only_java':\n * only_java-1.0-java")
end
- it "complains with multiple versions and platforms if pinned specs don't exist in the git repo" do
- simulate_platform "java"
-
+ it "complains with multiple versions and platforms if pinned specs don't exist in the git repo", :jruby_only do
build_git "only_java", "1.0" do |s|
s.platform = "java"
end
@@ -114,44 +172,45 @@ RSpec.describe "bundle install with git sources" do
s.write "only_java1-0.gemspec", File.read("#{lib_path("only_java-1.0-java")}/only_java.gemspec")
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
platforms :jruby do
gem "only_java", "1.2", :git => "#{lib_path("only_java-1.1-java")}"
end
G
- expect(err).to include("The source contains 'only_java' at: 1.0 java, 1.1 java")
+ expect(err).to include("The source contains the following gems matching 'only_java':\n * only_java-1.0-java\n * only_java-1.1-java")
end
it "still works after moving the application directory" do
- bundle "config --local path vendor/bundle"
- bundle "install"
+ bundle_config "path vendor/bundle"
+ install_base_gemfile
FileUtils.mv bundled_app, tmp("bundled_app.bck")
- expect(the_bundle).to include_gems "foo 1.0", :dir => tmp("bundled_app.bck")
+ expect(the_bundle).to include_gems "foo 1.0", dir: tmp("bundled_app.bck")
end
it "can still install after moving the application directory" do
- bundle "config --local path vendor/bundle"
- bundle "install"
+ bundle_config "path vendor/bundle"
+ install_base_gemfile
FileUtils.mv bundled_app, tmp("bundled_app.bck")
- update_git "foo", "1.1", :path => lib_path("foo-1.0")
+ update_git "foo", "1.1", path: lib_path("foo-1.0")
gemfile tmp("bundled_app.bck/Gemfile"), <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
- gem "rack", "1.0"
+ gem "myrack", "1.0"
G
- bundle "update foo", :dir => tmp("bundled_app.bck")
+ bundle "update foo", dir: tmp("bundled_app.bck")
- expect(the_bundle).to include_gems "foo 1.1", "rack 1.0", :dir => tmp("bundled_app.bck")
+ expect(the_bundle).to include_gems "foo 1.1", "myrack 1.0", dir: tmp("bundled_app.bck")
end
end
@@ -159,8 +218,8 @@ RSpec.describe "bundle install with git sources" do
before do
build_git "foo"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
git "#{lib_path("foo-1.0")}" do
# this page left intentionally blank
@@ -170,7 +229,7 @@ RSpec.describe "bundle install with git sources" do
it "does not explode" do
bundle "install"
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
@@ -183,10 +242,12 @@ RSpec.describe "bundle install with git sources" do
it "works" do
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}", :ref => "#{@revision}" do
gem "foo"
end
G
+ expect(err).to be_empty
run <<-RUBY
require 'foo'
@@ -198,6 +259,7 @@ RSpec.describe "bundle install with git sources" do
it "works when the revision is a symbol" do
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}", :ref => #{@revision.to_sym.inspect} do
gem "foo"
end
@@ -212,20 +274,60 @@ RSpec.describe "bundle install with git sources" do
expect(out).to eq("WIN")
end
+ it "works when an abbreviated revision is added after an initial, potentially shallow clone" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ git "#{lib_path("foo-1.0")}", :ref => #{@revision[0..7].inspect} do
+ gem "foo"
+ end
+ G
+ end
+
+ it "works when a tag that does not look like a commit hash is used as the value of :ref" do
+ build_git "foo"
+ @remote = build_git("bar", bare: true)
+ update_git "foo", remote: @remote.path
+ update_git "foo", push: "main"
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'foo', :git => "#{@remote.path}"
+ G
+
+ # Create a new tag on the remote that needs fetching
+ update_git "foo", tag: "v1.0.0"
+ update_git "foo", push: "v1.0.0"
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'foo', :git => "#{@remote.path}", :ref => "v1.0.0"
+ G
+
+ expect(err).to be_empty
+ end
+
it "works when the revision is a non-head ref" do
- # want to ensure we don't fallback to master
- update_git "foo", :path => lib_path("foo-1.0") do |s|
+ # want to ensure we don't fallback to main
+ update_git "foo", path: lib_path("foo-1.0") do |s|
s.write("lib/foo.rb", "raise 'FAIL'")
end
- sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 master~1", :dir => lib_path("foo-1.0"))
+ git("update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", lib_path("foo-1.0"))
# 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'")
+ update_git "foo", path: lib_path("foo-1.0"), branch: "rando" do |s|
+ s.write("lib/foo.rb", "raise 'FAIL_FROM_RANDO'")
end
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}", :ref => "refs/bundler/1" do
gem "foo"
end
@@ -242,24 +344,26 @@ 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 "https://gem.repo1"
git "#{lib_path("foo-1.0")}" do
gem "foo"
end
G
- # want to ensure we don't fallback to master
- update_git "foo", :path => lib_path("foo-1.0") do |s|
+ # want to ensure we don't fallback to main
+ update_git "foo", path: lib_path("foo-1.0") do |s|
s.write("lib/foo.rb", "raise 'FAIL'")
end
- sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 master~1", :dir => lib_path("foo-1.0"))
+ git("update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", lib_path("foo-1.0"))
# 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'")
+ update_git "foo", path: lib_path("foo-1.0"), branch: "rando" do |s|
+ s.write("lib/foo.rb", "raise 'FAIL_FROM_RANDO'")
end
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}", :ref => "refs/bundler/1" do
gem "foo"
end
@@ -275,20 +379,21 @@ RSpec.describe "bundle install with git sources" do
end
it "does not download random non-head refs" do
- sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 master~1", :dir => lib_path("foo-1.0"))
+ git("update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", lib_path("foo-1.0"))
- bundle "config set global_gem_cache true"
+ bundle_config "global_gem_cache true"
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}" do
gem "foo"
end
G
# ensure we also git fetch after cloning
- bundle :update, :all => true
+ bundle :update, all: true
- sys_exec("git ls-remote .", :dir => Dir[home(".bundle/cache/git/foo-*")].first)
+ git("ls-remote .", Dir[home(".bundle/cache/git/foo-*")].first)
expect(out).not_to include("refs/bundler/1")
end
@@ -299,9 +404,10 @@ RSpec.describe "bundle install with git sources" do
let(:repo) { build_git("foo").path }
it "works" do
- update_git("foo", :path => repo, :branch => branch)
+ update_git("foo", path: repo, branch: branch)
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{repo}", :branch => #{branch.dump} do
gem "foo"
end
@@ -315,9 +421,10 @@ RSpec.describe "bundle install with git sources" do
it "works" do
skip "git does not accept this" if Gem.win_platform?
- update_git("foo", :path => repo, :branch => branch)
+ update_git("foo", path: repo, branch: branch)
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{repo}", :branch => #{branch.dump} do
gem "foo"
end
@@ -332,9 +439,10 @@ RSpec.describe "bundle install with git sources" do
it "works" do
skip "git does not accept this" if Gem.win_platform?
- update_git("foo", :path => repo, :branch => branch)
+ update_git("foo", path: repo, branch: branch)
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{repo}", :branch => #{branch.dump} do
gem "foo"
end
@@ -350,9 +458,10 @@ RSpec.describe "bundle install with git sources" do
let(:repo) { build_git("foo").path }
it "works" do
- update_git("foo", :path => repo, :tag => tag)
+ update_git("foo", path: repo, tag: tag)
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{repo}", :tag => #{tag.dump} do
gem "foo"
end
@@ -366,9 +475,10 @@ RSpec.describe "bundle install with git sources" do
it "works" do
skip "git does not accept this" if Gem.win_platform?
- update_git("foo", :path => repo, :tag => tag)
+ update_git("foo", path: repo, tag: tag)
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{repo}", :tag => #{tag.dump} do
gem "foo"
end
@@ -383,9 +493,10 @@ RSpec.describe "bundle install with git sources" do
it "works" do
skip "git does not accept this" if Gem.win_platform?
- update_git("foo", :path => repo, :tag => tag)
+ update_git("foo", path: repo, tag: tag)
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{repo}", :tag => #{tag.dump} do
gem "foo"
end
@@ -398,103 +509,100 @@ RSpec.describe "bundle install with git sources" do
describe "when specifying local override" do
it "uses the local repository instead of checking a new one out" do
- # We don't generate it because we actually don't need it
- # build_git "rack", "0.8"
-
- build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
- s.write "lib/rack.rb", "puts :LOCAL"
+ build_git "myrack", "0.8", path: lib_path("local-myrack") do |s|
+ s.write "lib/myrack.rb", "puts :LOCAL"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
bundle :install
- run "require 'rack'"
+ run "require 'myrack'"
expect(out).to eq("LOCAL")
end
it "chooses the local repository on runtime" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
- update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
- s.write "lib/rack.rb", "puts :LOCAL"
+ update_git "myrack", "0.8", path: lib_path("local-myrack") do |s|
+ s.write "lib/myrack.rb", "puts :LOCAL"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
- run "require 'rack'"
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
+ run "require 'myrack'"
expect(out).to eq("LOCAL")
end
it "unlocks the source when the dependencies have changed while switching to the local" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
- update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
- s.write "rack.gemspec", build_spec("rack", "0.8") { runtime "rspec", "> 0" }.first.to_ruby
- s.write "lib/rack.rb", "puts :LOCAL"
+ update_git "myrack", "0.8", path: lib_path("local-myrack") do |s|
+ s.write "myrack.gemspec", build_spec("myrack", "0.8") { runtime "rspec", "> 0" }.first.to_ruby
+ s.write "lib/myrack.rb", "puts :LOCAL"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
bundle :install
- run "require 'rack'"
+ run "require 'myrack'"
expect(out).to eq("LOCAL")
end
it "updates specs on runtime" do
system_gems "nokogiri-1.4.2"
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
lockfile0 = File.read(bundled_app_lock)
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
- update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
+ update_git "myrack", "0.8", path: lib_path("local-myrack") do |s|
s.add_dependency "nokogiri", "1.4.2"
end
- bundle %(config set local.rack #{lib_path("local-rack")})
- run "require 'rack'"
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
+ run "require 'myrack'"
lockfile1 = File.read(bundled_app_lock)
expect(lockfile1).not_to eq(lockfile0)
end
it "updates ref on install" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
lockfile0 = File.read(bundled_app_lock)
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
- update_git "rack", "0.8", :path => lib_path("local-rack")
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
+ update_git "myrack", "0.8", path: lib_path("local-myrack")
- bundle %(config set local.rack #{lib_path("local-rack")})
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
bundle :install
lockfile1 = File.read(bundled_app_lock)
@@ -502,18 +610,18 @@ RSpec.describe "bundle install with git sources" do
end
it "explodes and gives correct solution if given path does not exist on install" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
- bundle :install, :raise_on_error => false
- expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/)
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
+ bundle :install, raise_on_error: false
+ expect(err).to match(/Cannot use local override for myrack-0.8 because #{Regexp.escape(lib_path("local-myrack").to_s)} does not exist/)
- solution = "config unset local.rack"
+ solution = "config unset local.myrack"
expect(err).to match(/Run `bundle #{solution}` to remove the local override/)
bundle solution
@@ -523,19 +631,19 @@ RSpec.describe "bundle install with git sources" do
end
it "explodes and gives correct solution if branch is not given on install" do
- build_git "rack", "0.8"
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ build_git "myrack", "0.8"
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
- bundle :install, :raise_on_error => false
- expect(err).to match(/Cannot use local override for rack-0.8 at #{Regexp.escape(lib_path('local-rack').to_s)} because :branch is not specified in Gemfile/)
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
+ bundle :install, raise_on_error: false
+ expect(err).to match(/Cannot use local override for myrack-0.8 at #{Regexp.escape(lib_path("local-myrack").to_s)} because :branch is not specified in Gemfile/)
- solution = "config unset local.rack"
+ solution = "config unset local.myrack"
expect(err).to match(/Specify a branch or run `bundle #{solution}` to remove the local override/)
bundle solution
@@ -545,55 +653,73 @@ RSpec.describe "bundle install with git sources" do
end
it "does not explode if disable_local_branch_check is given" do
- build_git "rack", "0.8"
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ build_git "myrack", "0.8"
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
bundle %(config set disable_local_branch_check true)
bundle :install
expect(out).to match(/Bundle complete!/)
end
it "explodes on different branches on install" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
- update_git "rack", "0.8", :path => lib_path("local-rack"), :branch => "another" do |s|
- s.write "lib/rack.rb", "puts :LOCAL"
+ update_git "myrack", "0.8", path: lib_path("local-myrack"), branch: "another" do |s|
+ s.write "lib/myrack.rb", "puts :LOCAL"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
- bundle :install, :raise_on_error => false
- expect(err).to match(/is using branch another but Gemfile specifies master/)
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
+ bundle :install, raise_on_error: false
+ expect(err).to match(/is using branch another but Gemfile specifies main/)
end
it "explodes on invalid revision on install" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
- build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
- s.write "lib/rack.rb", "puts :LOCAL"
+ build_git "myrack", "0.8", path: lib_path("local-myrack") do |s|
+ s.write "lib/myrack.rb", "puts :LOCAL"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
- bundle :install, :raise_on_error => false
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
+ bundle :install, raise_on_error: false
expect(err).to match(/The Gemfile lock is pointing to revision \w+/)
end
+
+ it "does not explode on invalid revision on install" do
+ build_git "myrack", "0.8"
+
+ build_git "myrack", "0.8", path: lib_path("local-myrack") do |s|
+ s.write "lib/myrack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
+ G
+
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
+ bundle %(config set disable_local_revision_check true)
+ bundle :install
+ expect(out).to match(/Bundle complete!/)
+ end
end
describe "specified inline" do
@@ -614,77 +740,76 @@ RSpec.describe "bundle install with git sources" do
# end
it "installs from git even if a newer gem is available elsewhere" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}"
G
- expect(the_bundle).to include_gems "rack 0.8"
+ expect(the_bundle).to include_gems "myrack 0.8"
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"
+ system_gems "myrack-1.0.0"
- build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
- s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
+ build_lib "myrack", "1.0", path: lib_path("nested/bar") do |s|
+ s.write "lib/myrack.rb", "puts 'WIN OVERRIDE'"
end
- build_git "foo", :path => lib_path("nested") do |s|
- s.add_dependency "rack", "= 1.0"
+ build_git "foo", path: lib_path("nested") do |s|
+ s.add_dependency "myrack", "= 1.0"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("nested")}"
G
- run "require 'rack'"
+ run "require 'myrack'"
expect(out).to eq("WIN OVERRIDE")
end
it "correctly unlocks when changing to a git source" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "0.9.1"
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
G
- build_git "rack", :path => lib_path("rack")
+ build_git "myrack", path: lib_path("myrack")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0", :git => "#{lib_path("rack")}"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0", :git => "#{lib_path("myrack")}"
G
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "correctly unlocks when changing to a git source without versions" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- build_git "rack", "1.2", :path => lib_path("rack")
+ build_git "myrack", "1.2", path: lib_path("myrack")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack")}"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack")}"
G
- expect(the_bundle).to include_gems "rack 1.2"
+ expect(the_bundle).to include_gems "myrack 1.2"
end
end
describe "block syntax" do
it "pulls all gems from a git block" do
- build_lib "omg", :path => lib_path("hi2u/omg")
- build_lib "hi2u", :path => lib_path("hi2u")
+ build_lib "omg", path: lib_path("hi2u/omg")
+ build_lib "hi2u", path: lib_path("hi2u")
install_gemfile <<-G
+ source "https://gem.repo1"
path "#{lib_path("hi2u")}" do
gem "omg"
gem "hi2u"
@@ -701,6 +826,7 @@ RSpec.describe "bundle install with git sources" do
update_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{@revision}"
G
@@ -718,7 +844,7 @@ RSpec.describe "bundle install with git sources" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
gem "rails", "2.3.2"
G
@@ -728,7 +854,7 @@ RSpec.describe "bundle install with git sources" do
end
it "runs the gemspec in the context of its parent directory" do
- build_lib "bar", :path => lib_path("foo/bar"), :gemspec => false do |s|
+ build_lib "bar", path: lib_path("foo/bar"), gemspec: false do |s|
s.write lib_path("foo/bar/lib/version.rb"), %(BAR_VERSION = '1.0')
s.write "bar.gemspec", <<-G
$:.unshift Dir.pwd
@@ -743,12 +869,12 @@ RSpec.describe "bundle install with git sources" do
G
end
- build_git "foo", :path => lib_path("foo") do |s|
+ build_git "foo", path: lib_path("foo") do |s|
s.write "bin/foo", ""
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "bar", :git => "#{lib_path("foo")}"
gem "rails", "2.3.2"
G
@@ -757,14 +883,41 @@ RSpec.describe "bundle install with git sources" do
expect(the_bundle).to include_gems "rails 2.3.2"
end
+ it "runs the gemspec in the context of its parent directory, when using local overrides" do
+ build_git "foo", path: lib_path("foo"), gemspec: false do |s|
+ s.write lib_path("foo/lib/foo/version.rb"), %(FOO_VERSION = '1.0')
+ s.write "foo.gemspec", <<-G
+ $:.unshift Dir.pwd
+ require 'lib/foo/version'
+ Gem::Specification.new do |s|
+ s.name = 'foo'
+ s.author = 'no one'
+ s.version = FOO_VERSION
+ s.summary = 'Foo'
+ s.files = Dir["lib/**/*.rb"]
+ end
+ G
+ end
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => "https://github.com/gems/foo", branch: "main"
+ G
+
+ bundle %(config set local.foo #{lib_path("foo")})
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
it "installs from git even if a rubygems gem is present" do
- build_gem "foo", "1.0", :path => lib_path("fake_foo"), :to_system => true do |s|
+ build_gem "foo", "1.0", path: lib_path("fake_foo"), to_system: true do |s|
s.write "lib/foo.rb", "raise 'FAIL'"
end
build_git "foo", "1.0"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", "1.0", :git => "#{lib_path("foo-1.0")}"
G
@@ -772,10 +925,10 @@ RSpec.describe "bundle install with git sources" do
end
it "fakes the gem out if there is no gemspec" do
- build_git "foo", :gemspec => false
+ build_git "foo", gemspec: false
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", "1.0", :git => "#{lib_path("foo-1.0")}"
gem "rails", "2.3.2"
G
@@ -786,10 +939,11 @@ RSpec.describe "bundle install with git sources" do
it "catches git errors and spits out useful output" do
gemfile <<-G
+ source "https://gem.repo1"
gem "foo", "1.0", :git => "omgomg"
G
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to include("Git error:")
expect(err).to include("fatal")
@@ -797,9 +951,10 @@ RSpec.describe "bundle install with git sources" do
end
it "works when the gem path has spaces in it" do
- build_git "foo", :path => lib_path("foo space-1.0")
+ build_git "foo", path: lib_path("foo space-1.0")
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo space-1.0")}"
G
@@ -810,6 +965,7 @@ RSpec.describe "bundle install with git sources" do
build_git "forced", "1.0"
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{lib_path("forced-1.0")}" do
gem 'forced'
end
@@ -820,42 +976,50 @@ RSpec.describe "bundle install with git sources" do
s.write "lib/forced.rb", "FORCED = '1.1'"
end
- bundle "update", :all => true
+ bundle "update", all: true
expect(the_bundle).to include_gems "forced 1.1"
- sys_exec("git reset --hard HEAD^", :dir => lib_path("forced-1.0"))
+ git("reset --hard HEAD^", lib_path("forced-1.0"))
- bundle "update", :all => true
+ bundle "update", all: true
expect(the_bundle).to include_gems "forced 1.0"
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"
end
- sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0")
- sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
+ git "submodule add #{lib_path("submodule-1.0")} submodule-1.0", lib_path("has_submodule-1.0")
+ git "commit -m \"submodulator\"", lib_path("has_submodule-1.0")
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
git "#{lib_path("has_submodule-1.0")}" do
gem "has_submodule"
end
G
- expect(err).to match(/could not find gem 'submodule/i)
+ expect(err).to match(%r{submodule >= 0 could not be found in rubygems repository https://gem.repo1/ or installed locally})
expect(the_bundle).not_to include_gems "has_submodule 1.0"
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"
end
- sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0")
- sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
+ git "submodule add #{lib_path("submodule-1.0")} submodule-1.0", lib_path("has_submodule-1.0")
+ git "commit -m \"submodulator\"", lib_path("has_submodule-1.0")
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{lib_path("has_submodule-1.0")}", :submodules => true do
gem "has_submodule"
end
@@ -864,10 +1028,33 @@ RSpec.describe "bundle install with git sources" do
expect(the_bundle).to include_gems "has_submodule 1.0"
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"
+
+ git "submodule add #{lib_path("submodule-1.0")} submodule-1.0", lib_path("has_submodule-1.0")
+ git "commit -m \"submodulator\"", lib_path("has_submodule-1.0")
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ git "#{lib_path("has_submodule-1.0")}" do
+ gem "has_submodule"
+ end
+ G
+ expect(err).to be_empty
+
+ expect(the_bundle).to include_gems "has_submodule 1.0"
+ expect(the_bundle).to_not include_gems "submodule 1.0"
+ end
+
it "handles implicit updates when modifying the source info" do
git = build_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}" do
gem "foo"
end
@@ -877,6 +1064,7 @@ RSpec.describe "bundle install with git sources" do
update_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}", :ref => "#{git.ref_for("HEAD^")}" do
gem "foo"
end
@@ -890,14 +1078,15 @@ RSpec.describe "bundle install with git sources" do
expect(out).to eq("WIN")
end
- it "does not to a remote fetch if the revision is cached locally" do
+ it "does not do a remote fetch if the revision is cached locally" do
build_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- FileUtils.rm_rf(lib_path("foo-1.0"))
+ FileUtils.rm_r(lib_path("foo-1.0"))
bundle "install"
expect(out).not_to match(/updating/i)
@@ -907,6 +1096,7 @@ RSpec.describe "bundle install with git sources" do
build_git "foo"
gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -915,31 +1105,31 @@ RSpec.describe "bundle install with git sources" do
end
it "prints a friendly error if a file blocks the git repo" do
- skip "drive letter is not detected correctly in error message" if Gem.win_platform?
-
build_git "foo"
FileUtils.mkdir_p(default_bundle_path)
FileUtils.touch(default_bundle_path("bundler"))
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- expect(exitstatus).to_not eq(0)
+ expect(last_command).to be_failure
expect(err).to include("Bundler could not install a gem because it " \
"needs to create a directory, but a file exists " \
"- #{default_bundle_path("bundler")}")
end
it "does not duplicate git gem sources" do
- build_lib "foo", :path => lib_path("nested/foo")
- build_lib "bar", :path => lib_path("nested/bar")
+ build_lib "foo", path: lib_path("nested/foo")
+ build_lib "bar", path: lib_path("nested/bar")
- build_git "foo", :path => lib_path("nested")
- build_git "bar", :path => lib_path("nested")
+ build_git "foo", path: lib_path("nested")
+ build_git "bar", path: lib_path("nested")
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("nested")}"
gem "bar", :git => "#{lib_path("nested")}"
G
@@ -949,21 +1139,21 @@ RSpec.describe "bundle install with git sources" do
describe "switching sources" do
it "doesn't explode when switching Path to Git sources" do
- build_gem "foo", "1.0", :to_system => true do |s|
+ build_gem "foo", "1.0", to_system: true do |s|
s.write "lib/foo.rb", "raise 'fail'"
end
- build_lib "foo", "1.0", :path => lib_path("bar/foo")
- build_git "bar", "1.0", :path => lib_path("bar") do |s|
+ build_lib "foo", "1.0", path: lib_path("bar/foo")
+ build_git "bar", "1.0", path: lib_path("bar") do |s|
s.add_dependency "foo"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "bar", :path => "#{lib_path("bar")}"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "bar", :git => "#{lib_path("bar")}"
G
@@ -972,24 +1162,67 @@ RSpec.describe "bundle install with git sources" do
it "doesn't explode when switching Gem to Git source" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack-obama"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack-obama"
+ gem "myrack", "1.0.0"
G
- build_git "rack", "1.0" do |s|
+ build_git "myrack", "1.0" do |s|
s.write "lib/new_file.rb", "puts 'USING GIT'"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack-obama"
- gem "rack", "1.0.0", :git => "#{lib_path("rack-1.0")}"
+ source "https://gem.repo1"
+ gem "myrack-obama"
+ gem "myrack", "1.0.0", :git => "#{lib_path("myrack-1.0")}"
G
run "require 'new_file'"
expect(out).to eq("USING GIT")
end
+
+ it "doesn't explode when removing an explicit exact version from a git gem with dependencies" do
+ build_lib "activesupport", "7.1.4", path: lib_path("rails/activesupport")
+ build_git "rails", "7.1.4", path: lib_path("rails") do |s|
+ s.add_dependency "activesupport", "= 7.1.4"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails", "7.1.4", :git => "#{lib_path("rails")}"
+ G
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails", :git => "#{lib_path("rails")}"
+ G
+
+ expect(the_bundle).to include_gem "rails 7.1.4", "activesupport 7.1.4"
+ end
+
+ it "doesn't explode when adding an explicit ref to a git gem with dependencies" do
+ lib_root = lib_path("rails")
+
+ build_lib "activesupport", "7.1.4", path: lib_root.join("activesupport")
+ build_git "rails", "7.1.4", path: lib_root do |s|
+ s.add_dependency "activesupport", "= 7.1.4"
+ end
+
+ old_revision = revision_for(lib_root)
+ update_git "rails", "7.1.4", path: lib_root
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails", "7.1.4", :git => "#{lib_root}"
+ G
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails", :git => "#{lib_root}", :ref => "#{old_revision}"
+ G
+
+ expect(the_bundle).to include_gem "rails 7.1.4", "activesupport 7.1.4"
+ end
end
describe "bundle install after the remote has been updated" do
@@ -997,7 +1230,8 @@ RSpec.describe "bundle install with git sources" do
build_git "valim"
install_gemfile <<-G
- gem "valim", :git => "#{file_uri_for(lib_path("valim-1.0"))}"
+ source "https://gem.repo1"
+ gem "valim", :git => "#{lib_path("valim-1.0")}"
G
old_revision = revision_for(lib_path("valim-1.0"))
@@ -1022,29 +1256,42 @@ RSpec.describe "bundle install with git sources" do
revision = revision_for(lib_path("foo-1.0"))
install_gemfile <<-G
- gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :ref => "#{revision}"
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{revision}"
G
expect(out).to_not match(/Revision.*does not exist/)
- install_gemfile <<-G, :raise_on_error => false
- gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :ref => "deadbeef"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "deadbeef"
G
expect(err).to include("Revision deadbeef does not exist in the repository")
end
+
+ it "gives a helpful error message when the remote branch no longer exists" do
+ build_git "foo"
+
+ install_gemfile <<-G, env: { "LANG" => "en" }, raise_on_error: false
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "deadbeef"
+ G
+
+ expect(err).to include("Revision deadbeef does not exist in the repository")
+ end
end
describe "bundle install with deployment mode configured and git sources" do
it "works" do
- build_git "valim", :path => lib_path("valim")
+ build_git "valim", path: lib_path("valim")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "valim", "= 1.0", :git => "#{lib_path("valim")}"
G
- simulate_new_machine
+ pristine_system_gems
- bundle "config --local deployment true"
+ bundle_config "deployment true"
bundle :install
end
end
@@ -1053,6 +1300,7 @@ RSpec.describe "bundle install with git sources" do
it "runs pre-install hooks" do
build_git "foo"
gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1065,13 +1313,14 @@ RSpec.describe "bundle install with git sources" do
end
bundle :install,
- :requires => [lib_path("install_hooks.rb")]
+ requires: [lib_path("install_hooks.rb")]
expect(err_without_deprecations).to eq("Ran pre-install hook: foo-1.0")
end
it "runs post-install hooks" do
build_git "foo"
gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1084,13 +1333,14 @@ RSpec.describe "bundle install with git sources" do
end
bundle :install,
- :requires => [lib_path("install_hooks.rb")]
+ requires: [lib_path("install_hooks.rb")]
expect(err_without_deprecations).to eq("Ran post-install hook: foo-1.0")
end
it "complains if the install hook fails" do
build_git "foo"
gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1102,7 +1352,7 @@ RSpec.describe "bundle install with git sources" do
H
end
- bundle :install, :requires => [lib_path("install_hooks.rb")], :raise_on_error => false
+ bundle :install, requires: [lib_path("install_hooks.rb")], raise_on_error: false
expect(err).to include("failed for foo-1.0")
end
end
@@ -1114,7 +1364,7 @@ RSpec.describe "bundle install with git sources" do
s.extensions << "Rakefile"
s.write "Rakefile", <<-RUBY
task :default do
- path = File.expand_path("../lib", __FILE__)
+ path = File.expand_path("lib", __dir__)
FileUtils.mkdir_p(path)
File.open("\#{path}/foo.rb", "w") do |f|
f.puts "FOO = 'YES'"
@@ -1124,7 +1374,7 @@ RSpec.describe "bundle install with git sources" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1140,8 +1390,8 @@ RSpec.describe "bundle install with git sources" do
expect(out).to include(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s)
end
- it "does not use old extension after ref changes", :ruby_repo do
- git_reader = build_git "foo", :no_default => true do |s|
+ it "does not use old extension after ref changes" do
+ git_reader = build_git "foo", no_default: true do |s|
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-RUBY
require "mkmf"
@@ -1154,16 +1404,16 @@ RSpec.describe "bundle install with git sources" do
File.open(git_reader.path.join("ext/foo.c"), "w") do |file|
file.write <<-C
#include "ruby.h"
- VALUE foo() { return INT2FIX(#{i}); }
+ VALUE foo(VALUE self) { return INT2FIX(#{i}); }
void Init_foo() { rb_define_global_function("foo", &foo, 0); }
C
end
- sys_exec("git commit -m \"commit for iteration #{i}\" ext/foo.c", :dir => git_reader.path)
+ git("commit -m \"commit for iteration #{i}\" ext/foo.c", git_reader.path)
git_commit_sha = git_reader.ref_for("HEAD")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{git_commit_sha}"
G
@@ -1187,8 +1437,8 @@ RSpec.describe "bundle install with git sources" do
RUBY
end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1207,7 +1457,7 @@ In Gemfile:
s.extensions << "Rakefile"
s.write "Rakefile", <<-RUBY
task :default do
- path = File.expand_path("../lib", __FILE__)
+ path = File.expand_path("lib", __dir__)
FileUtils.mkdir_p(path)
cur_time = Time.now.to_f.to_s
File.open("\#{path}/foo.rb", "w") do |f|
@@ -1218,7 +1468,7 @@ In Gemfile:
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1231,7 +1481,7 @@ In Gemfile:
expect(installed_time).to match(/\A\d+\.\d+\z/)
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1248,7 +1498,7 @@ In Gemfile:
s.extensions << "Rakefile"
s.write "Rakefile", <<-RUBY
task :default do
- path = File.expand_path("../lib", __FILE__)
+ path = File.expand_path("lib", __dir__)
FileUtils.mkdir_p(path)
cur_time = Time.now.to_f.to_s
File.open("\#{path}/foo.rb", "w") do |f|
@@ -1259,8 +1509,8 @@ In Gemfile:
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "0.9.1"
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1273,8 +1523,8 @@ In Gemfile:
expect(installed_time).to match(/\A\d+\.\d+\z/)
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1291,7 +1541,7 @@ In Gemfile:
s.extensions << "Rakefile"
s.write "Rakefile", <<-RUBY
task :default do
- path = File.expand_path("../lib", __FILE__)
+ path = File.expand_path("lib", __dir__)
FileUtils.mkdir_p(path)
cur_time = Time.now.to_f.to_s
File.open("\#{path}/foo.rb", "w") do |f|
@@ -1302,7 +1552,7 @@ In Gemfile:
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1313,12 +1563,12 @@ In Gemfile:
installed_time = out
- update_git("foo", :branch => "branch2")
+ update_git("foo", branch: "branch2")
expect(installed_time).to match(/\A\d+\.\d+\z/)
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}", :branch => "branch2"
G
@@ -1351,7 +1601,7 @@ In Gemfile:
ENV["GIT_WORK_TREE"] = "bar"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
git "#{lib_path("xxxxxx-1.0")}" do
gem 'xxxxxx'
end
@@ -1363,42 +1613,79 @@ In Gemfile:
end
describe "without git installed" do
- it "prints a better error message" do
+ it "prints a better error message when installing" do
+ gemfile <<-G
+ source "https://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 "https://gem.repo1"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
G
with_path_as("") do
- bundle "update", :all => true, :raise_on_error => false
+ bundle "update", all: true, 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 "installs a packaged git gem successfully" do
+ it "doesn't need git in the new machine if an installed git gem is copied to another machine" do
build_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
G
- bundle "config set cache_all true"
- bundle :cache
- simulate_new_machine
+ bundle_config_global "path vendor/bundle"
+ bundle :install
+ pristine_system_gems
- bundle "install", :env => { "PATH" => "" }
+ bundle "install", env: { "PATH" => "" }
expect(out).to_not include("You need to install git to be able to use gems from git repositories.")
end
end
describe "when the git source is overridden with a local git repo" do
before do
- bundle "config set --global local.foo #{lib_path("foo")}"
+ bundle_config_global "local.foo #{lib_path("foo")}"
end
describe "and git output is colorized" do
@@ -1409,10 +1696,11 @@ In Gemfile:
end
it "installs successfully" do
- build_git "foo", "1.0", :path => lib_path("foo")
+ build_git "foo", "1.0", path: lib_path("foo")
gemfile <<-G
- gem "foo", :git => "#{lib_path("foo")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo")}", :branch => "main"
G
bundle :install
@@ -1426,13 +1714,14 @@ In Gemfile:
let(:credentials) { "user1:password1" }
it "does not display the password" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
git "https://#{credentials}@github.com/company/private-repo" do
gem "foo"
end
G
- expect(last_command.stdboth).to_not include("password1")
+ expect(stdboth).to_not include("password1")
expect(out).to include("Fetching https://user1@github.com/company/private-repo")
end
end
@@ -1441,13 +1730,14 @@ In Gemfile:
let(:credentials) { "oauth_token" }
it "displays the oauth scheme but not the oauth token" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
git "https://#{credentials}:x-oauth-basic@github.com/company/private-repo" do
gem "foo"
end
G
- expect(last_command.stdboth).to_not include("oauth_token")
+ expect(stdboth).to_not include("oauth_token")
expect(out).to include("Fetching https://x-oauth-basic@github.com/company/private-repo")
end
end
diff --git a/spec/bundler/install/gemfile/groups_spec.rb b/spec/bundler/install/gemfile/groups_spec.rb
index 4e7484ddbd..4013b112ec 100644
--- a/spec/bundler/install/gemfile/groups_spec.rb
+++ b/spec/bundler/install/gemfile/groups_spec.rb
@@ -4,8 +4,8 @@ RSpec.describe "bundle install with groups" do
describe "installing with no options" do
before :each do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
group :emo do
gem "activesupport", "2.3.5"
end
@@ -14,7 +14,7 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems in the default group" do
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "installs gems in a group block into that group" do
@@ -25,7 +25,7 @@ RSpec.describe "bundle install with groups" do
puts ACTIVESUPPORT
R
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- activesupport/)
end
it "installs gems with inline :groups into those groups" do
@@ -36,11 +36,11 @@ RSpec.describe "bundle install with groups" do
puts THIN
R
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- thin/)
end
it "sets up everything if Bundler.setup is used with no groups" do
- output = run("require 'rack'; puts RACK")
+ output = run("require 'myrack'; puts MYRACK")
expect(output).to eq("1.0.0")
output = run("require 'activesupport'; puts ACTIVESUPPORT")
@@ -57,7 +57,7 @@ RSpec.describe "bundle install with groups" do
puts THIN
RUBY
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- thin/)
end
it "sets up old groups when they have previously been removed" do
@@ -74,8 +74,8 @@ RSpec.describe "bundle install with groups" do
describe "with gems assigned to a single group" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
group :emo do
gem "activesupport", "2.3.5"
end
@@ -86,58 +86,58 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems in the default group" do
- bundle "config --local without emo"
+ bundle_config "without emo"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ expect(the_bundle).to include_gems "myrack 1.0.0", groups: [:default]
end
it "respects global `without` configuration, but does not save it locally" do
- bundle "config without emo"
+ bundle_config_global "without emo"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ expect(the_bundle).to include_gems "myrack 1.0.0", groups: [:default]
bundle "config list"
expect(out).not_to include("Set for your local app (#{bundled_app(".bundle/config")}): [:emo]")
expect(out).to include("Set for the current user (#{home(".bundle/config")}): [:emo]")
end
- it "does not install gems from the excluded group" do
- bundle "config --local without emo"
+ it "allows running application where groups where configured by a different user" do
+ bundle_config "without emo"
bundle :install
- expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+ bundle "exec ruby -e 'puts 42'", env: { "BUNDLE_USER_HOME" => tmp("new_home").to_s }
+ expect(out).to include("42")
end
- it "remembers previous exclusion with `--without`", :bundler => "< 3" do
- bundle "install --without emo"
- expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ it "does not install gems from the excluded group" do
+ bundle_config "without emo"
bundle :install
- expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", groups: [:default]
end
it "does not say it installed gems from the excluded group" do
- bundle "config --local without emo"
+ bundle_config "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 "without emo"
bundle :install
- run("require 'rack'; puts RACK", :default)
+ run("require 'myrack'; puts MYRACK", :default)
expect(out).to eq("1.0.0")
end
it "does not effect the resolve" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport"
group :emo do
gem "rails", "2.3.2"
end
G
- bundle "config --local without emo"
+ bundle_config "without emo"
bundle :install
- expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => [:default]
+ expect(the_bundle).to include_gems "activesupport 2.3.2", groups: [:default]
end
it "still works when BUNDLE_WITHOUT is set" do
@@ -146,40 +146,19 @@ RSpec.describe "bundle install with groups" do
bundle :install
expect(out).not_to include("activesupport")
- expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
- expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+ expect(the_bundle).to include_gems "myrack 1.0.0", groups: [:default]
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", groups: [:default]
ENV["BUNDLE_WITHOUT"] = nil
end
- it "clears --without when passed an empty list", :bundler => "< 3" do
- bundle "install --without emo"
-
- bundle "install --without ''"
- expect(the_bundle).to include_gems "activesupport 2.3.5"
- end
-
- it "doesn't clear without when nothing is passed", :bundler => "< 3" do
- bundle "install --without emo"
-
- bundle :install
- expect(the_bundle).not_to include_gems "activesupport 2.3.5"
- end
-
it "does not install gems from the optional group" do
bundle :install
expect(the_bundle).not_to include_gems "thin 1.0"
end
it "installs gems from the optional group when requested" do
- bundle "config --local with debugging"
- bundle :install
- expect(the_bundle).to include_gems "thin 1.0"
- end
-
- it "installs gems from the previously requested group", :bundler => "< 3" do
- bundle "install --with debugging"
- expect(the_bundle).to include_gems "thin 1.0"
+ bundle_config "with debugging"
bundle :install
expect(the_bundle).to include_gems "thin 1.0"
end
@@ -191,30 +170,6 @@ RSpec.describe "bundle install with groups" do
ENV["BUNDLE_WITH"] = nil
end
- it "clears --with when passed an empty list", :bundler => "< 3" do
- bundle "install --with debugging"
- bundle "install --with ''"
- expect(the_bundle).not_to include_gems "thin 1.0"
- end
-
- it "removes groups from without when passed at --with", :bundler => "< 3" do
- bundle "config --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 "install --without debugging", :raise_on_error => false
- expect(the_bundle).not_to include_gem "thin 1.0"
- end
-
- it "errors out when passing a group to with and without via CLI flags", :bundler => "< 3" do
- bundle "install --with emo debugging --without emo", :raise_on_error => false
- expect(last_command).to be_failure
- expect(err).to include("The offending groups are: emo")
- end
-
it "allows the BUNDLE_WITH setting to override BUNDLE_WITHOUT" do
ENV["BUNDLE_WITH"] = "debugging"
@@ -228,20 +183,14 @@ RSpec.describe "bundle install with groups" do
expect(the_bundle).to include_gem "thin 1.0"
end
- it "can add and remove a group at the same time", :bundler => "< 3" do
- bundle "install --with debugging --without emo"
- expect(the_bundle).to include_gems "thin 1.0"
- expect(the_bundle).not_to include_gems "activesupport 2.3.5"
- end
-
it "has no effect when listing a not optional group in with" do
- bundle "config --local with emo"
+ bundle_config "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 "without debugging"
bundle :install
expect(the_bundle).not_to include_gems "thin 1.0"
end
@@ -250,8 +199,8 @@ RSpec.describe "bundle install with groups" do
describe "with gems assigned to multiple groups" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
group :emo, :lolercoaster do
gem "activesupport", "2.3.5"
end
@@ -259,22 +208,22 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems in the default group" do
- bundle "config --local without emo lolercoaster"
+ bundle_config "without emo lolercoaster"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "installs the gem if any of its groups are installed" do
- bundle "config --local without emo"
+ bundle_config "without emo"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ expect(the_bundle).to include_gems "myrack 1.0.0", "activesupport 2.3.5"
end
describe "with a gem defined multiple times in different groups" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
group :emo do
gem "activesupport", "2.3.5"
@@ -287,19 +236,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 "without emo"
bundle :install
expect(the_bundle).to include_gems "activesupport 2.3.5"
- bundle "config --local without lolercoaster"
+ bundle_config "without lolercoaster"
bundle :install
expect(the_bundle).to include_gems "activesupport 2.3.5"
- bundle "config --local without emo lolercoaster"
+ bundle_config "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
@@ -309,8 +258,8 @@ RSpec.describe "bundle install with groups" do
describe "nesting groups" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
group :emo do
group :lolercoaster do
gem "activesupport", "2.3.5"
@@ -320,15 +269,15 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems in the default group" do
- bundle "config --local without emo lolercoaster"
+ bundle_config "without emo lolercoaster"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "installs the gem if any of its groups are installed" do
- bundle "config --local without emo"
+ bundle_config "without emo"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ expect(the_bundle).to include_gems "myrack 1.0.0", "activesupport 2.3.5"
end
end
end
@@ -336,16 +285,16 @@ RSpec.describe "bundle install with groups" do
describe "when loading only the default group" do
it "should not load all groups" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
gem "activesupport", :groups => :development
G
ruby <<-R
- require "#{lib_dir}/bundler"
+ require "bundler"
Bundler.setup :default
Bundler.require :default
- puts RACK
+ puts MYRACK
begin
require "activesupport"
rescue LoadError
@@ -358,39 +307,39 @@ RSpec.describe "bundle install with groups" do
end
end
- describe "when locked and installed with `without` option" do
+ describe "when locked and installed with `without` setting" do
before(:each) do
build_repo2
- system_gems "rack-0.9.1"
+ system_gems "myrack-0.9.1"
- bundle "config --local without rack"
+ bundle_config "without myrack"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
+ source "https://gem.repo2"
+ gem "myrack"
- group :rack do
- gem "rack_middleware"
+ group :myrack do
+ gem "myrack_middleware"
end
G
end
- it "uses the correct versions even if --without was used on the original" do
- expect(the_bundle).to include_gems "rack 0.9.1"
- expect(the_bundle).not_to include_gems "rack_middleware 1.0"
+ it "uses versions from excluded gems in a machine without the without configuration" do
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
simulate_new_machine
bundle :install
- expect(the_bundle).to include_gems "rack 0.9.1"
- expect(the_bundle).to include_gems "rack_middleware 1.0"
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ expect(the_bundle).to include_gems "myrack_middleware 1.0"
end
it "does not hit the remote a second time" do
- FileUtils.rm_rf gem_repo2
- bundle "config --local without rack"
- bundle :install, :verbose => true
- expect(last_command.stdboth).not_to match(/fetching/i)
+ FileUtils.rm_r gem_repo2
+ bundle_config "without myrack"
+ bundle :install, verbose: true
+ expect(stdboth).not_to match(/fetching/i)
end
end
end
diff --git a/spec/bundler/install/gemfile/install_if_spec.rb b/spec/bundler/install/gemfile/install_if_spec.rb
index 786e0e9258..05a6d15129 100644
--- a/spec/bundler/install/gemfile/install_if_spec.rb
+++ b/spec/bundler/install/gemfile/install_if_spec.rb
@@ -3,7 +3,7 @@
RSpec.describe "bundle install with install_if conditionals" do
it "follows the install_if DSL" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
install_if(lambda { true }) do
gem "activesupport", "2.3.5"
end
@@ -11,22 +11,29 @@ RSpec.describe "bundle install with install_if conditionals" do
install_if(lambda { false }) do
gem "foo"
end
- gem "rack"
+ gem "myrack"
G
- expect(the_bundle).to include_gems("rack 1.0", "activesupport 2.3.5")
+ expect(the_bundle).to include_gems("myrack 1.0", "activesupport 2.3.5")
expect(the_bundle).not_to include_gems("thin")
expect(the_bundle).not_to include_gems("foo")
- lockfile_should_be <<-L
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo1, "activesupport", "2.3.5"
+ c.checksum gem_repo1, "foo", "1.0"
+ c.checksum gem_repo1, "myrack", "1.0.0"
+ c.checksum gem_repo1, "thin", "1.0"
+ end
+
+ expect(lockfile).to eq <<~L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
activesupport (2.3.5)
foo (1.0)
- rack (1.0.0)
+ myrack (1.0.0)
thin (1.0)
- rack
+ myrack
PLATFORMS
#{lockfile_platforms}
@@ -34,11 +41,11 @@ RSpec.describe "bundle install with install_if conditionals" do
DEPENDENCIES
activesupport (= 2.3.5)
foo
- rack
+ myrack
thin
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
diff --git a/spec/bundler/install/gemfile/lockfile_spec.rb b/spec/bundler/install/gemfile/lockfile_spec.rb
index 313e99d0b8..19bd7074b2 100644
--- a/spec/bundler/install/gemfile/lockfile_spec.rb
+++ b/spec/bundler/install/gemfile/lockfile_spec.rb
@@ -2,13 +2,14 @@
RSpec.describe "bundle install with a lockfile present" do
let(:gf) { <<-G }
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
G
- subject do
+ it "touches the lockfile on install even when nothing has changed" do
install_gemfile(gf)
+ expect { bundle :install }.to change { bundled_app_lock.mtime }
end
context "gemfile evaluation" do
@@ -16,32 +17,25 @@ RSpec.describe "bundle install with a lockfile present" do
context "with plugins disabled" do
before do
- bundle "config set plugins false"
- subject
+ bundle_config "plugins false"
end
- it "does not evaluate the gemfile twice" do
+ it "does not evaluate the gemfile twice when the gem is already installed" do
+ install_gemfile(gf)
bundle :install
- with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "rack 1.0.0" }
+ with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "myrack 1.0.0" }
- # The first eval is from the initial install, we're testing that the
- # second install doesn't double-eval
expect(bundled_app("evals").read.lines.to_a.size).to eq(2)
end
- context "when the gem is not installed" do
- before { FileUtils.rm_rf bundled_app(".bundle") }
-
- it "does not evaluate the gemfile twice" do
- bundle :install
+ it "does not evaluate the gemfile twice when the gem is not installed" do
+ gemfile(gf)
+ bundle :install
- with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "rack 1.0.0" }
+ with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "myrack 1.0.0" }
- # The first eval is from the initial install, we're testing that the
- # second install doesn't double-eval
- expect(bundled_app("evals").read.lines.to_a.size).to eq(2)
- end
+ expect(bundled_app("evals").read.lines.to_a.size).to eq(1)
end
end
end
diff --git a/spec/bundler/install/gemfile/override_spec.rb b/spec/bundler/install/gemfile/override_spec.rb
new file mode 100644
index 0000000000..02b0e7d772
--- /dev/null
+++ b/spec/bundler/install/gemfile/override_spec.rb
@@ -0,0 +1,401 @@
+# frozen_string_literal: true
+
+RSpec.describe "override DSL" do
+ context "with a version: string operation" do
+ it "replaces a direct dependency requirement with the override version spec" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 0.9.1"
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ end
+
+ it "replaces a transitive dependency requirement" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 1.0.0"
+ gem "myrack_middleware"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0", "myrack_middleware 1.0"
+ end
+
+ it "replaces the requirement even when the Gemfile pins a different version" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 0.9.1"
+ gem "myrack", "= 1.0.0"
+ G
+
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ end
+
+ it "applies the override against an existing lockfile" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 0.9.1"
+ gem "myrack"
+ G
+
+ bundle :install
+
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ end
+
+ it "pins a prerelease version that the Gemfile dependency would otherwise filter out" do
+ build_repo2 do
+ build_gem "has_prerelease", "1.0"
+ build_gem "has_prerelease", "1.1.pre"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ override "has_prerelease", version: "= 1.1.pre"
+ gem "has_prerelease"
+ G
+
+ expect(the_bundle).to include_gems "has_prerelease 1.1.pre"
+ end
+ end
+
+ context "with a version: :ignore_upper operation" do
+ it "strips a < upper bound on a direct dependency" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: :ignore_upper
+ gem "myrack", "< 1.0"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "folds ~> into >= so newer versions become reachable" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: :ignore_upper
+ gem "myrack", "~> 0.9.1"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+ end
+
+ context "with a version: nil operation" do
+ it "drops a direct dependency's pin entirely" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: nil
+ gem "myrack", "= 0.9.1"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "drops a transitive dependency's pin entirely" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: nil
+ gem "myrack_middleware"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0", "myrack_middleware 1.0"
+ end
+
+ it "applies a transitive-only override against an existing lockfile" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack_middleware"
+ G
+
+ expect(the_bundle).to include_gems "myrack 0.9.1", "myrack_middleware 1.0"
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 1.0.0"
+ gem "myrack_middleware"
+ G
+
+ bundle :install
+
+ expect(the_bundle).to include_gems "myrack 1.0.0", "myrack_middleware 1.0"
+ end
+ end
+
+ context "lockfile contents" do
+ it "does not record the override directive in Gemfile.lock" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 0.9.1"
+ gem "myrack"
+ G
+
+ expect(lockfile).not_to match(/override/i)
+ end
+ end
+
+ context "with a required_ruby_version: operation" do
+ it "lets the resolver pick a gem whose required_ruby_version excludes the current Ruby with :ignore_upper" do
+ build_repo2 do
+ build_gem "needs_old_ruby", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_ruby", required_ruby_version: :ignore_upper
+ gem "needs_old_ruby"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("needs_old_ruby (1.0)")
+ end
+
+ it "lets the resolver pick the gem with required_ruby_version: nil" do
+ build_repo2 do
+ build_gem "needs_old_ruby", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_ruby", required_ruby_version: nil
+ gem "needs_old_ruby"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("needs_old_ruby (1.0)")
+ end
+
+ it "applies to a transitive dependency's required_ruby_version" do
+ build_repo2 do
+ build_gem "needs_old_ruby", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ build_gem "wraps_old", "1.0" do |s|
+ s.add_dependency "needs_old_ruby"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_ruby", required_ruby_version: :ignore_upper
+ gem "wraps_old"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("needs_old_ruby (1.0)")
+ expect(lockfile).to include("wraps_old (1.0)")
+ end
+
+ it "re-resolves a direct dep when a metadata override is added against an existing lockfile" do
+ build_repo2 do
+ build_gem "selectable", "1.0"
+ build_gem "selectable", "2.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "selectable"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("selectable (1.0)")
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "selectable", required_ruby_version: :ignore_upper
+ gem "selectable"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("selectable (2.0)")
+ end
+ end
+
+ context "with a required_rubygems_version: operation" do
+ it "lets the resolver pick a gem whose required_rubygems_version excludes the current RubyGems with :ignore_upper" do
+ build_repo2 do
+ build_gem "needs_old_rubygems", "1.0" do |s|
+ s.required_rubygems_version = "< #{Gem.rubygems_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_rubygems", required_rubygems_version: :ignore_upper
+ gem "needs_old_rubygems"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("needs_old_rubygems (1.0)")
+ end
+ end
+
+ context "with an :all target" do
+ it "applies required_ruby_version: :ignore_upper to every gem" do
+ build_repo2 do
+ build_gem "needs_old_ruby_a", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ build_gem "needs_old_ruby_b", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override :all, required_ruby_version: :ignore_upper
+ gem "needs_old_ruby_a"
+ gem "needs_old_ruby_b"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("needs_old_ruby_a (1.0)")
+ expect(lockfile).to include("needs_old_ruby_b (1.0)")
+ end
+
+ it "is overridden by a per-gem override on the same field" do
+ build_repo2 do
+ build_gem "permissive", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ build_gem "still_blocked", "1.0" do |s|
+ s.required_ruby_version = "= #{Gem.ruby_version}.999"
+ end
+ end
+
+ # :all says ignore_upper (would unblock both), but per-gem on
+ # still_blocked nails it to a hard requirement that still fails.
+ gemfile <<-G
+ source "https://gem.repo2"
+ override :all, required_ruby_version: :ignore_upper
+ override "still_blocked", required_ruby_version: "= #{Gem.ruby_version}.999"
+ gem "permissive"
+ gem "still_blocked"
+ G
+
+ bundle :lock, raise_on_error: false
+ expect(err).to include("still_blocked")
+ end
+
+ it "preserves locked versions when an :all metadata override is added without bundle update" do
+ build_repo2 do
+ build_gem "selectable", "1.0"
+ build_gem "selectable", "2.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "selectable"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("selectable (1.0)")
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override :all, required_ruby_version: :ignore_upper
+ gem "selectable"
+ G
+
+ # :all override alone does not pre-unlock locked specs; narrow change
+ # should not trigger unrelated lockfile churn.
+ bundle :lock
+ expect(lockfile).to include("selectable (1.0)")
+
+ # bundle update opts the user into re-resolution under the override.
+ bundle "update selectable"
+ expect(lockfile).to include("selectable (2.0)")
+ end
+ end
+
+ context "diagnostic on resolve failure" do
+ it "lists active overrides with their Gemfile location" do
+ build_repo2 do
+ build_gem "needs_old_ruby", "1.0" do |s|
+ s.required_ruby_version = "= #{Gem.ruby_version}.999"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_ruby", required_ruby_version: "= #{Gem.ruby_version}.999"
+ gem "needs_old_ruby"
+ G
+
+ bundle :lock, raise_on_error: false
+ expect(err).to include("Bundler applied the following overrides")
+ expect(err).to include("override \"needs_old_ruby\", required_ruby_version:")
+ expect(err).to match(/declared at Gemfile:\d+/)
+ end
+ end
+
+ context "install-time compatibility" do
+ it "installs a gem whose required_ruby_version excludes the current Ruby when an override removes the constraint" do
+ build_repo2 do
+ build_gem "needs_old_ruby", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_ruby", required_ruby_version: nil
+ gem "needs_old_ruby"
+ G
+
+ expect(the_bundle).to include_gems "needs_old_ruby 1.0"
+ end
+
+ it "installs a gem whose required_rubygems_version excludes the current RubyGems when an override removes it" do
+ build_repo2 do
+ build_gem "needs_old_rubygems", "1.0" do |s|
+ s.required_rubygems_version = "< #{Gem.rubygems_version}"
+ end
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_rubygems", required_rubygems_version: nil
+ gem "needs_old_rubygems"
+ G
+
+ expect(the_bundle).to include_gems "needs_old_rubygems 1.0"
+ end
+
+ it "installs every gem when :all required_ruby_version override is in effect" do
+ build_repo2 do
+ build_gem "needs_old_ruby_a", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ build_gem "needs_old_ruby_b", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ override :all, required_ruby_version: :ignore_upper
+ gem "needs_old_ruby_a"
+ gem "needs_old_ruby_b"
+ G
+
+ expect(the_bundle).to include_gems "needs_old_ruby_a 1.0", "needs_old_ruby_b 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb
index 07722d09dd..b069488531 100644
--- a/spec/bundler/install/gemfile/path_spec.rb
+++ b/spec/bundler/install/gemfile/path_spec.rb
@@ -1,17 +1,6 @@
# frozen_string_literal: true
RSpec.describe "bundle install with explicit source paths" do
- it "fetches gems with a global path source", :bundler => "< 3" do
- build_lib "foo"
-
- install_gemfile <<-G
- path "#{lib_path("foo-1.0")}"
- gem 'foo'
- G
-
- expect(the_bundle).to include_gems("foo 1.0")
- end
-
it "fetches gems" do
build_lib "foo"
@@ -65,7 +54,7 @@ RSpec.describe "bundle install with explicit source paths" do
username = "some_unexisting_user"
relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("/home/#{username}").expand_path)
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
gem 'foo', :path => "~#{username}/#{relative_path}"
G
expect(err).to match("There was an error while trying to use the path `~#{username}/#{relative_path}`.")
@@ -73,25 +62,29 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "expands paths relative to Bundler.root" do
- build_lib "foo", :path => bundled_app("foo-1.0")
+ build_lib "foo", path: bundled_app("foo-1.0")
install_gemfile <<-G
gem 'foo', :path => "./foo-1.0"
G
- expect(the_bundle).to include_gems("foo 1.0", :dir => bundled_app("subdir").mkpath)
+ expect(the_bundle).to include_gems("foo 1.0", dir: bundled_app("subdir").mkpath)
end
it "sorts paths consistently on install and update when they start with ./" do
- build_lib "demo", :path => lib_path("demo")
- build_lib "aaa", :path => lib_path("demo/aaa")
+ build_lib "demo", path: lib_path("demo")
+ build_lib "aaa", path: lib_path("demo/aaa")
- gemfile = <<-G
+ gemfile lib_path("demo/Gemfile"), <<-G
+ source "https://gem.repo1"
gemspec
gem "aaa", :path => "./aaa"
G
- File.open(lib_path("demo/Gemfile"), "w") {|f| f.puts gemfile }
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "aaa", "1.0"
+ c.no_checksum "demo", "1.0"
+ end
lockfile = <<~L
PATH
@@ -105,6 +98,7 @@ RSpec.describe "bundle install with explicit source paths" do
aaa (1.0)
GEM
+ remote: https://gem.repo1/
specs:
PLATFORMS
@@ -113,60 +107,57 @@ RSpec.describe "bundle install with explicit source paths" do
DEPENDENCIES
aaa!
demo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
- bundle :install, :dir => lib_path("demo")
- expect(lib_path("demo/Gemfile.lock")).to have_lockfile(lockfile)
- bundle :update, :all => true, :dir => lib_path("demo")
- expect(lib_path("demo/Gemfile.lock")).to have_lockfile(lockfile)
+ bundle :install, dir: lib_path("demo")
+ 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 read_as(lockfile)
end
it "expands paths when comparing locked paths to Gemfile paths" do
- build_lib "foo", :path => bundled_app("foo-1.0")
+ build_lib "foo", path: bundled_app("foo-1.0")
install_gemfile <<-G
- gem 'foo', :path => File.expand_path("../foo-1.0", __FILE__)
+ gem 'foo', :path => File.expand_path("foo-1.0", __dir__)
G
- bundle "config --local frozen true"
+ bundle_config "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 "myrack-1.0.0"
- system_gems "rack-1.0.0"
-
- build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
- s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
+ build_lib "myrack", "1.0", path: lib_path("nested/bar") do |s|
+ s.write "lib/myrack.rb", "puts 'WIN OVERRIDE'"
end
- build_lib "foo", :path => lib_path("nested") do |s|
- s.add_dependency "rack", "= 1.0"
+ build_lib "foo", path: lib_path("nested") do |s|
+ s.add_dependency "myrack", "= 1.0"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
gem "foo", :path => "#{lib_path("nested")}"
G
- run "require 'rack'"
+ run "require 'myrack'"
expect(out).to eq("WIN OVERRIDE")
end
it "works" do
- build_gem "foo", "1.0.0", :to_system => true do |s|
+ build_gem "foo", "1.0.0", to_system: true do |s|
s.write "lib/foo.rb", "puts 'FAIL'"
end
- build_lib "omg", "1.0", :path => lib_path("omg") do |s|
+ build_lib "omg", "1.0", path: lib_path("omg") do |s|
s.add_dependency "foo"
end
- build_lib "foo", "1.0.0", :path => lib_path("omg/foo")
+ build_lib "foo", "1.0.0", path: lib_path("omg/foo")
install_gemfile <<-G
gem "omg", :path => "#{lib_path("omg")}"
@@ -175,22 +166,88 @@ RSpec.describe "bundle install with explicit source paths" do
expect(the_bundle).to include_gems "foo 1.0"
end
- it "works with only_update_to_newer_versions" do
- build_lib "omg", "2.0", :path => lib_path("omg")
+ it "works when using prereleases of 0.0.0" do
+ build_lib "foo", "0.0.0.dev", path: lib_path("foo")
+
+ gemfile <<~G
+ source "https://gem.repo1"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ lockfile <<~L
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (0.0.0.dev)
+
+ GEM
+ remote: https://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 "https://gem.repo1"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ lockfile <<~L
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (0.0.0.SNAPSHOT)
+
+ GEM
+ remote: https://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
gem "omg", :path => "#{lib_path("omg")}"
G
- build_lib "omg", "1.0", :path => lib_path("omg")
+ 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
it "prefers gemspecs closer to the path root" do
- build_lib "premailer", "1.0.0", :path => lib_path("premailer") do |s|
+ build_lib "premailer", "1.0.0", path: lib_path("premailer") do |s|
s.write "gemfiles/ruby187.gemspec", <<-G
Gem::Specification.new do |s|
s.name = 'premailer'
@@ -222,90 +279,143 @@ RSpec.describe "bundle install with explicit source paths" do
G
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
- expect(err).to_not include("Your Gemfile has no gem server sources.")
expect(err).to match(/is not valid. Please fix this gemspec./)
expect(err).to match(/The validation error was 'missing value for attribute version'/)
expect(err).to match(/You have one or more invalid gemspecs that need to be fixed/)
end
it "supports gemspec syntax" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
- s.add_dependency "rack", "1.0"
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack", "1.0"
end
- gemfile = <<-G
- source "#{file_uri_for(gem_repo1)}"
+ gemfile lib_path("foo/Gemfile"), <<-G
+ source "https://gem.repo1"
gemspec
G
- File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts gemfile }
+ bundle "install", dir: lib_path("foo")
+ expect(the_bundle).to include_gems "foo 1.0", dir: lib_path("foo")
+ expect(the_bundle).to include_gems "myrack 1.0", dir: lib_path("foo")
+ end
+
+ it "does not unlock dependencies of path sources" do
+ build_repo4 do
+ build_gem "graphql", "2.0.15"
+ build_gem "graphql", "2.0.16"
+ end
+
+ build_lib "foo", "0.1.0", path: lib_path("foo") do |s|
+ s.add_dependency "graphql", "~> 2.0"
+ end
+
+ gemfile_path = lib_path("foo/Gemfile")
+
+ gemfile gemfile_path, <<-G
+ source "https://gem.repo4"
+ gemspec
+ G
+
+ lockfile_path = lib_path("foo/Gemfile.lock")
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "0.1.0"
+ c.checksum gem_repo4, "graphql", "2.0.15"
+ end
+
+ original_lockfile = <<~L
+ PATH
+ remote: .
+ specs:
+ foo (0.1.0)
+ graphql (~> 2.0)
- bundle "install", :dir => lib_path("foo")
- expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo")
- expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo")
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ graphql (2.0.15)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile lockfile_path, original_lockfile
+
+ build_lib "foo", "0.1.1", path: lib_path("foo") do |s|
+ s.add_dependency "graphql", "~> 2.0"
+ end
+
+ bundle "install", dir: lib_path("foo")
+ expect(lockfile_path).to read_as(original_lockfile.gsub("foo (0.1.0)", "foo (0.1.1)"))
end
it "supports gemspec syntax with an alternative path" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
- s.add_dependency "rack", "1.0"
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack", "1.0"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gemspec :path => "#{lib_path("foo")}"
G
expect(the_bundle).to include_gems "foo 1.0"
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
it "doesn't automatically unlock dependencies when using the gemspec syntax" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
- s.add_dependency "rack", ">= 1.0"
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack", ">= 1.0"
end
- install_gemfile lib_path("foo/Gemfile"), <<-G, :dir => lib_path("foo")
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile lib_path("foo/Gemfile"), <<-G, dir: lib_path("foo")
+ source "https://gem.repo1"
gemspec
G
- build_gem "rack", "1.0.1", :to_system => true
+ build_gem "myrack", "1.0.1", to_system: true
- bundle "install", :dir => lib_path("foo")
+ bundle "install", dir: lib_path("foo")
- expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo")
- expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo")
+ expect(the_bundle).to include_gems "foo 1.0", dir: lib_path("foo")
+ expect(the_bundle).to include_gems "myrack 1.0", dir: lib_path("foo")
end
it "doesn't automatically unlock dependencies when using the gemspec syntax and the gem has development dependencies" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
- s.add_dependency "rack", ">= 1.0"
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack", ">= 1.0"
s.add_development_dependency "activesupport"
end
- install_gemfile lib_path("foo/Gemfile"), <<-G, :dir => lib_path("foo")
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile lib_path("foo/Gemfile"), <<-G, dir: lib_path("foo")
+ source "https://gem.repo1"
gemspec
G
- build_gem "rack", "1.0.1", :to_system => true
+ build_gem "myrack", "1.0.1", to_system: true
- bundle "install", :dir => lib_path("foo")
+ bundle "install", dir: lib_path("foo")
- expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo")
- expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo")
+ expect(the_bundle).to include_gems "foo 1.0", dir: lib_path("foo")
+ expect(the_bundle).to include_gems "myrack 1.0", dir: lib_path("foo")
end
it "raises if there are multiple gemspecs" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.write "bar.gemspec", build_spec("bar", "1.0").first.to_ruby
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
gemspec :path => "#{lib_path("foo")}"
G
@@ -314,7 +424,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "allows :name to be specified to resolve ambiguity" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.write "bar.gemspec"
end
@@ -330,11 +440,12 @@ RSpec.describe "bundle install with explicit source paths" do
s.executables = "foobar"
end
- install_gemfile <<-G
+ install_gemfile <<-G, verbose: true
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"
@@ -343,7 +454,7 @@ RSpec.describe "bundle install with explicit source paths" do
it "handles directories in bin/" do
build_lib "foo"
- lib_path("foo-1.0").join("foo.gemspec").rmtree
+ FileUtils.rm_rf lib_path("foo-1.0").join("foo.gemspec")
lib_path("foo-1.0").join("bin/performance").mkpath
install_gemfile <<-G
@@ -379,9 +490,9 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "keeps source pinning" do
- build_lib "foo", "1.0", :path => lib_path("foo")
- build_lib "omg", "1.0", :path => lib_path("omg")
- build_lib "foo", "1.0", :path => lib_path("omg/foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo")
+ build_lib "omg", "1.0", path: lib_path("omg")
+ build_lib "foo", "1.0", path: lib_path("omg/foo") do |s|
s.write "lib/foo.rb", "puts 'FAIL'"
end
@@ -394,7 +505,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "works when the path does not have a gemspec" do
- build_lib "foo", :gemspec => false
+ build_lib "foo", gemspec: false
gemfile <<-G
gem "foo", "1.0", :path => "#{lib_path("foo-1.0")}"
@@ -410,9 +521,6 @@ RSpec.describe "bundle install with explicit source paths" do
PATH
remote: vendor/bar
specs:
-
- GEM
- remote: http://rubygems.org
L
FileUtils.mkdir_p(bundled_app("vendor/bar"))
@@ -425,34 +533,34 @@ RSpec.describe "bundle install with explicit source paths" do
context "existing lockfile" do
it "rubygems gems don't re-resolve without changes" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack-obama', '1.0'
+ source "https://gem.repo1"
+ gem 'myrack-obama', '1.0'
gem 'net-ssh', '1.0'
G
- bundle :check, :env => { "DEBUG" => "1" }
+ bundle :check, env: { "DEBUG" => "1" }
expect(out).to match(/using resolution from the lockfile/)
- expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0"
+ expect(the_bundle).to include_gems "myrack-obama 1.0", "net-ssh 1.0"
end
it "source path gems w/deps don't re-resolve without changes" do
- build_lib "rack-obama", "1.0", :path => lib_path("omg") do |s|
+ build_lib "myrack-obama", "1.0", path: lib_path("omg") do |s|
s.add_dependency "yard"
end
- build_lib "net-ssh", "1.0", :path => lib_path("omg") do |s|
+ build_lib "net-ssh", "1.0", path: lib_path("omg") do |s|
s.add_dependency "yard"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack-obama', :path => "#{lib_path("omg")}"
+ source "https://gem.repo1"
+ gem 'myrack-obama', :path => "#{lib_path("omg")}"
gem 'net-ssh', :path => "#{lib_path("omg")}"
G
- bundle :check, :env => { "DEBUG" => "1" }
+ bundle :check, env: { "DEBUG" => "1" }
expect(out).to match(/using resolution from the lockfile/)
- expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0"
+ expect(the_bundle).to include_gems "myrack-obama 1.0", "net-ssh 1.0"
end
end
@@ -471,10 +579,10 @@ RSpec.describe "bundle install with explicit source paths" do
describe "when the gem version in the path is updated" do
before :each do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
s.add_dependency "bar"
end
- build_lib "bar", "1.0", :path => lib_path("foo/bar")
+ build_lib "bar", "1.0", path: lib_path("foo/bar")
install_gemfile <<-G
gem "foo", :path => "#{lib_path("foo")}"
@@ -482,7 +590,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "unlocks all gems when the top level gem is updated" do
- build_lib "foo", "2.0", :path => lib_path("foo") do |s|
+ build_lib "foo", "2.0", path: lib_path("foo") do |s|
s.add_dependency "bar"
end
@@ -492,7 +600,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "unlocks all gems when a child dependency gem is updated" do
- build_lib "bar", "2.0", :path => lib_path("foo/bar")
+ build_lib "bar", "2.0", path: lib_path("foo/bar")
bundle "install"
@@ -502,104 +610,280 @@ RSpec.describe "bundle install with explicit source paths" do
describe "when dependencies in the path are updated" do
before :each do
- build_lib "foo", "1.0", :path => lib_path("foo")
+ build_lib "foo", "1.0", path: lib_path("foo")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo")}"
G
end
it "gets dependencies that are updated in the path" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
- s.add_dependency "rack"
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack"
end
bundle "install"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "keeps using the same version if it's compatible" do
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
- s.add_dependency "rack", "0.9.1"
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack", "0.9.1"
end
bundle "install"
- expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo1, "myrack", "0.9.1"
+ end
+
+ expect(lockfile).to eq <<~G
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ myrack (= 0.9.1)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (0.9.1)
- lockfile_should_be <<-G
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack"
+ end
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
specs:
foo (1.0)
- rack (= 0.9.1)
+ myrack
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (0.9.1)
+ myrack (0.9.1)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ end
+
+ it "keeps using the same version even when another dependency is added" do
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack", "0.9.1"
+ end
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.checksum gem_repo1, "myrack", "0.9.1"
+ end
+ expect(lockfile).to eq <<~G
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ myrack (= 0.9.1)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
- build_lib "foo", "1.0", :path => lib_path("foo") do |s|
- s.add_dependency "rack"
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack"
+ s.add_dependency "rake", rake_version
end
bundle "install"
- lockfile_should_be <<-G
+ checksums.checksum gem_repo1, "rake", rake_version
+
+ expect(lockfile).to eq <<~G
PATH
remote: #{lib_path("foo")}
specs:
foo (1.0)
- rack
+ myrack
+ rake (= #{rake_version})
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (0.9.1)
+ myrack (0.9.1)
+ rake (#{rake_version})
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ end
+
+ it "does not remove existing ruby platform" do
+ build_lib "foo", "1.0", path: lib_path("foo") do |s|
+ s.add_dependency "myrack", "0.9.1"
+ end
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
+ lockfile <<~L
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+ checksums.checksum gem_repo1, "myrack", "0.9.1"
+
+ expect(lockfile).to eq <<~G
+ PATH
+ remote: #{lib_path("foo")}
+ specs:
+ foo (1.0)
+ myrack (= 0.9.1)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ foo!
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
+ end
+ end
- expect(the_bundle).to include_gems "rack 0.9.1"
+ context "when platform specific version locked, and having less dependencies that the generic version that's actually installed" do
+ before do
+ build_repo4 do
+ build_gem "racc", "1.8.1"
+ build_gem "mini_portile2", "2.8.2"
+ end
+
+ build_lib "nokogiri", "1.18.9", path: lib_path("nokogiri") do |s|
+ s.add_dependency "mini_portile2", "~> 2.8.2"
+ s.add_dependency "racc", "~> 1.4"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri", path: "#{lib_path("nokogiri")}"
+ G
+
+ lockfile <<~L
+ PATH
+ remote: #{lib_path("nokogiri")}
+ specs:
+ nokogiri (1.18.9)
+ mini_portile2 (~> 2.8.2)
+ racc (~> 1.4)
+ nokogiri (1.18.9-arm64-darwin)
+ racc (~> 1.4)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ racc (1.8.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ nokogiri!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "works" do
+ bundle "install"
end
end
describe "switching sources" do
it "doesn't switch pinned git sources to rubygems when pinning the parent gem to a path source" do
- build_gem "foo", "1.0", :to_system => true do |s|
+ build_gem "foo", "1.0", to_system: true do |s|
s.write "lib/foo.rb", "raise 'fail'"
end
- build_lib "foo", "1.0", :path => lib_path("bar/foo")
- build_git "bar", "1.0", :path => lib_path("bar") do |s|
+ build_lib "foo", "1.0", path: lib_path("bar/foo")
+ build_git "bar", "1.0", path: lib_path("bar") do |s|
s.add_dependency "foo"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
gem "bar", :git => "#{lib_path("bar")}"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
gem "bar", :path => "#{lib_path("bar")}"
G
@@ -607,23 +891,23 @@ RSpec.describe "bundle install with explicit source paths" do
end
it "switches the source when the gem existed in rubygems and the path was already being used for another gem" do
- build_lib "foo", "1.0", :path => lib_path("foo")
- build_gem "bar", "1.0", :to_bundle => true do |s|
+ build_lib "foo", "1.0", path: lib_path("foo")
+ build_gem "bar", "1.0", to_bundle: true do |s|
s.write "lib/bar.rb", "raise 'fail'"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "bar"
path "#{lib_path("foo")}" do
gem "foo"
end
G
- build_lib "bar", "1.0", :path => lib_path("foo/bar")
+ build_lib "bar", "1.0", path: lib_path("foo/bar")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
path "#{lib_path("foo")}" do
gem "foo"
gem "bar"
@@ -636,21 +920,17 @@ 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
+ build_lib "private_lib", "2.2", path: lib_path("private_lib")
+ gemfile lib_path("private_lib/Gemfile"), <<-G
source "http://localgemserver.test"
gemspec
- gem 'rack'
+ gem 'myrack'
G
- File.open(lib_path("private_lib/Gemfile"), "w") {|f| f.puts gemfile }
-
- bundle :install, :env => { "DEBUG" => "1" }, :artifice => "endpoint", :dir => lib_path("private_lib")
- expect(out).to match(%r{^HTTP GET http://localgemserver\.test/api/v1/dependencies\?gems=rack$})
+ bundle :install, env: { "DEBUG" => "1" }, artifice: "endpoint", dir: lib_path("private_lib")
+ expect(out).to match(%r{^HTTP GET http://localgemserver\.test/api/v1/dependencies\?gems=myrack$})
expect(out).not_to match(/^HTTP GET.*private_lib/)
- expect(the_bundle).to include_gems "private_lib 2.2", :dir => lib_path("private_lib")
- expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("private_lib")
+ expect(the_bundle).to include_gems "private_lib 2.2", dir: lib_path("private_lib")
+ expect(the_bundle).to include_gems "myrack 1.0", dir: lib_path("private_lib")
end
end
@@ -670,7 +950,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
bundle :install,
- :requires => [lib_path("install_hooks.rb")]
+ requires: [lib_path("install_hooks.rb")]
expect(err_without_deprecations).to eq("Ran pre-install hook: foo-1.0")
end
@@ -689,7 +969,7 @@ RSpec.describe "bundle install with explicit source paths" do
end
bundle :install,
- :requires => [lib_path("install_hooks.rb")]
+ requires: [lib_path("install_hooks.rb")]
expect(err_without_deprecations).to eq("Ran post-install hook: foo-1.0")
end
@@ -707,7 +987,7 @@ RSpec.describe "bundle install with explicit source paths" do
H
end
- bundle :install, :requires => [lib_path("install_hooks.rb")], :raise_on_error => false
+ bundle :install, requires: [lib_path("install_hooks.rb")], raise_on_error: false
expect(err).to include("failed for foo-1.0")
end
@@ -718,11 +998,11 @@ RSpec.describe "bundle install with explicit source paths" do
expect(bar_file).not_to be_file
build_lib "foo" do |s|
- s.write("lib/rubygems_plugin.rb", "FileUtils.touch('#{foo_file}')")
+ s.write("lib/rubygems_plugin.rb", "require 'fileutils'; FileUtils.touch('#{foo_file}')")
end
build_git "bar" do |s|
- s.write("lib/rubygems_plugin.rb", "FileUtils.touch('#{bar_file}')")
+ s.write("lib/rubygems_plugin.rb", "require 'fileutils'; FileUtils.touch('#{bar_file}')")
end
install_gemfile <<-G
diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb
index dd58aef29b..e12933ebcf 100644
--- a/spec/bundler/install/gemfile/platform_spec.rb
+++ b/spec/bundler/install/gemfile/platform_spec.rb
@@ -4,30 +4,30 @@ RSpec.describe "bundle install across platforms" do
it "maintains the same lockfile if all gems are compatible across platforms" do
lockfile <<-G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (0.9.1)
+ myrack (0.9.1)
PLATFORMS
#{not_local}
DEPENDENCIES
- rack
+ myrack
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
G
- expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).to include_gems "myrack 0.9.1"
end
it "pulls in the correct platform specific gem" do
lockfile <<-G
GEM
- remote: #{file_uri_for(gem_repo1)}
+ remote: https://gem.repo1
specs:
platform_specific (1.0)
platform_specific (1.0-java)
@@ -40,37 +40,139 @@ RSpec.describe "bundle install across platforms" do
platform_specific
G
- simulate_platform "java"
+ simulate_platform "java" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "platform_specific"
+ G
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 java"
+ end
+ end
+
+ it "pulls the pure ruby version on jruby if the java platform is not present in the lockfile and bundler is run in frozen mode", :jruby_only do
+ lockfile <<-G
+ GEM
+ remote: https://gem.repo1
+ specs:
+ platform_specific (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ platform_specific
+ G
+
+ bundle_config "frozen true"
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "platform_specific"
G
- expect(the_bundle).to include_gems "platform_specific 1.0 JAVA"
+ expect(the_bundle).to include_gems "platform_specific 1.0 ruby"
+ expect(err).to be_empty
+ end
+
+ context "on universal Rubies" do
+ before do
+ build_repo4 do
+ build_gem "darwin_single_arch" do |s|
+ s.platform = "ruby"
+ end
+ build_gem "darwin_single_arch" do |s|
+ s.platform = "arm64-darwin"
+ end
+ build_gem "darwin_single_arch" do |s|
+ s.platform = "x86_64-darwin"
+ end
+ end
+ end
+
+ it "pulls in the correct architecture gem" do
+ lockfile <<-G
+ GEM
+ remote: https://gem.repo4
+ specs:
+ darwin_single_arch (1.0)
+ darwin_single_arch (1.0-arm64-darwin)
+ darwin_single_arch (1.0-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ darwin_single_arch
+ G
+
+ simulate_platform "universal-darwin-21" do
+ simulate_ruby_platform "universal.x86_64-darwin21" do
+ install_gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "darwin_single_arch"
+ G
+
+ expect(the_bundle).to include_gems "darwin_single_arch 1.0 x86_64-darwin"
+ end
+ end
+ end
+
+ it "pulls in the correct architecture gem on arm64e macOS Ruby" do
+ lockfile <<-G
+ GEM
+ remote: https://gem.repo4
+ specs:
+ darwin_single_arch (1.0)
+ darwin_single_arch (1.0-arm64-darwin)
+ darwin_single_arch (1.0-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ darwin_single_arch
+ G
+
+ simulate_platform "universal-darwin-21" do
+ simulate_ruby_platform "universal.arm64e-darwin21" do
+ install_gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "darwin_single_arch"
+ G
+
+ expect(the_bundle).to include_gems "darwin_single_arch 1.0 arm64-darwin"
+ end
+ end
+ end
end
it "works with gems that have different dependencies" do
- simulate_platform "java"
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ simulate_platform "java" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
- gem "nokogiri"
- G
+ gem "nokogiri"
+ G
- expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3"
+ expect(the_bundle).to include_gems "nokogiri 1.4.2 java", "weakling 0.0.3"
- simulate_new_machine
+ pristine_system_gems
+ bundle_config "force_ruby_platform true"
+ bundle "install"
- simulate_platform "ruby"
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ expect(the_bundle).not_to include_gems "weakling"
- gem "nokogiri"
- G
+ simulate_new_machine
+ bundle "install"
- expect(the_bundle).to include_gems "nokogiri 1.4.2"
- expect(the_bundle).not_to include_gems "weakling"
+ expect(the_bundle).to include_gems "nokogiri 1.4.2 java", "weakling 0.0.3"
+ end
end
it "does not keep unneeded platforms for gems that are used" do
@@ -78,207 +180,255 @@ RSpec.describe "bundle install across platforms" do
build_gem "empyrean", "0.1.0"
build_gem "coderay", "1.1.2"
build_gem "method_source", "0.9.0"
- build_gem("spoon", "0.0.6") {|s| s.add_runtime_dependency "ffi" }
+ build_gem("spoon", "0.0.6") {|s| s.add_dependency "ffi" }
build_gem "pry", "0.11.3" do |s|
s.platform = "java"
- s.add_runtime_dependency "coderay", "~> 1.1.0"
- s.add_runtime_dependency "method_source", "~> 0.9.0"
- s.add_runtime_dependency "spoon", "~> 0.0"
+ s.add_dependency "coderay", "~> 1.1.0"
+ s.add_dependency "method_source", "~> 0.9.0"
+ s.add_dependency "spoon", "~> 0.0"
end
build_gem "pry", "0.11.3" do |s|
- s.add_runtime_dependency "coderay", "~> 1.1.0"
- s.add_runtime_dependency "method_source", "~> 0.9.0"
+ s.add_dependency "coderay", "~> 1.1.0"
+ s.add_dependency "method_source", "~> 0.9.0"
end
build_gem("ffi", "1.9.23") {|s| s.platform = "java" }
build_gem("ffi", "1.9.23")
end
- simulate_platform java
+ simulate_platform "java" do
+ install_gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "empyrean", "0.1.0"
+ gem "pry"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "coderay", "1.1.2"
+ c.checksum gem_repo4, "empyrean", "0.1.0"
+ c.checksum gem_repo4, "ffi", "1.9.23", "java"
+ c.checksum gem_repo4, "method_source", "0.9.0"
+ c.checksum gem_repo4, "pry", "0.11.3", "java"
+ c.checksum gem_repo4, "spoon", "0.0.6"
+ end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ coderay (1.1.2)
+ empyrean (0.1.0)
+ ffi (1.9.23-java)
+ method_source (0.9.0)
+ pry (0.11.3-java)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ spoon (~> 0.0)
+ spoon (0.0.6)
+ ffi
+
+ PLATFORMS
+ java
+
+ DEPENDENCIES
+ empyrean (= 0.1.0)
+ pry
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --add-platform ruby"
+
+ checksums.checksum gem_repo4, "pry", "0.11.3"
+
+ good_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ coderay (1.1.2)
+ empyrean (0.1.0)
+ ffi (1.9.23-java)
+ method_source (0.9.0)
+ pry (0.11.3)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ pry (0.11.3-java)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ spoon (~> 0.0)
+ spoon (0.0.6)
+ ffi
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ empyrean (= 0.1.0)
+ pry
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ expect(lockfile).to eq good_lockfile
+
+ bad_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ coderay (1.1.2)
+ empyrean (0.1.0)
+ ffi (1.9.23)
+ ffi (1.9.23-java)
+ method_source (0.9.0)
+ pry (0.11.3)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ pry (0.11.3-java)
+ coderay (~> 1.1.0)
+ method_source (~> 0.9.0)
+ spoon (~> 0.0)
+ spoon (0.0.6)
+ ffi
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ empyrean (= 0.1.0)
+ pry
+ #{checksums}
+ BUNDLED WITH
+ 1.16.1
+ L
+
+ aggregate_failures do
+ lockfile bad_lockfile
+ bundle :install, env: { "BUNDLER_VERSION" => Bundler::VERSION }
+ expect(lockfile).to eq good_lockfile
+
+ lockfile bad_lockfile
+ bundle :update, all: true, env: { "BUNDLER_VERSION" => Bundler::VERSION }
+ expect(lockfile).to eq good_lockfile
+
+ lockfile bad_lockfile
+ bundle "update ffi", env: { "BUNDLER_VERSION" => Bundler::VERSION }
+ expect(lockfile).to eq good_lockfile
+
+ lockfile bad_lockfile
+ bundle "update empyrean", env: { "BUNDLER_VERSION" => Bundler::VERSION }
+ expect(lockfile).to eq good_lockfile
+
+ lockfile bad_lockfile
+ bundle :lock, env: { "BUNDLER_VERSION" => Bundler::VERSION }
+ expect(lockfile).to eq good_lockfile
+ end
+ end
+ end
- gem "empyrean", "0.1.0"
- gem "pry"
- G
+ it "works with gems with platform-specific dependency having different requirements order" do
+ simulate_platform "x86_64-darwin-15" do
+ update_repo2 do
+ build_gem "fspath", "3"
+ build_gem "image_optim_pack", "1.2.3" do |s|
+ s.add_dependency "fspath", ">= 2.1", "< 4"
+ end
+ build_gem "image_optim_pack", "1.2.3" do |s|
+ s.platform = "universal-darwin"
+ s.add_dependency "fspath", "< 4", ">= 2.1"
+ end
+ end
- lockfile_should_be <<-L
- GEM
- remote: #{file_uri_for(gem_repo4)}/
- specs:
- coderay (1.1.2)
- empyrean (0.1.0)
- ffi (1.9.23-java)
- method_source (0.9.0)
- pry (0.11.3-java)
- coderay (~> 1.1.0)
- method_source (~> 0.9.0)
- spoon (~> 0.0)
- spoon (0.0.6)
- ffi
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ G
- PLATFORMS
- java
+ install_gemfile <<-G
+ source "https://gem.repo2"
- DEPENDENCIES
- empyrean (= 0.1.0)
- pry
+ gem "image_optim_pack"
+ G
- BUNDLED WITH
- #{Bundler::VERSION}
- L
+ expect(err).not_to include "Unable to use the platform-specific"
- bundle "lock --add-platform ruby"
+ expect(the_bundle).to include_gem "image_optim_pack 1.2.3 universal-darwin"
+ end
+ end
- good_lockfile = strip_whitespace(<<-L)
- GEM
- remote: #{file_uri_for(gem_repo4)}/
- specs:
- coderay (1.1.2)
- empyrean (0.1.0)
- ffi (1.9.23-java)
- method_source (0.9.0)
- pry (0.11.3)
- coderay (~> 1.1.0)
- method_source (~> 0.9.0)
- pry (0.11.3-java)
- coderay (~> 1.1.0)
- method_source (~> 0.9.0)
- spoon (~> 0.0)
- spoon (0.0.6)
- ffi
+ it "fetches gems again after changing the version of Ruby" do
+ gemfile <<-G
+ source "https://gem.repo1"
- PLATFORMS
- java
- ruby
+ gem "myrack", "1.0.0"
+ G
- DEPENDENCIES
- empyrean (= 0.1.0)
- pry
+ bundle_config "path vendor/bundle"
+ bundle :install
- BUNDLED WITH
- #{Bundler::VERSION}
- L
+ FileUtils.mv(vendored_gems, bundled_app("vendor/bundle", Gem.ruby_engine, "1.8"))
- lockfile_should_be good_lockfile
+ bundle :install
+ expect(vendored_gems("gems/myrack-1.0.0")).to exist
+ end
- bad_lockfile = strip_whitespace <<-L
+ it "keeps existing platforms when installing with force_ruby_platform" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo1, "platform_specific", "1.0"
+ c.checksum gem_repo1, "platform_specific", "1.0", "java"
+ end
+
+ lockfile <<-G
GEM
- remote: #{file_uri_for(gem_repo4)}/
+ remote: https://gem.repo1/
specs:
- coderay (1.1.2)
- empyrean (0.1.0)
- ffi (1.9.23)
- ffi (1.9.23-java)
- method_source (0.9.0)
- pry (0.11.3)
- coderay (~> 1.1.0)
- method_source (~> 0.9.0)
- pry (0.11.3-java)
- coderay (~> 1.1.0)
- method_source (~> 0.9.0)
- spoon (~> 0.0)
- spoon (0.0.6)
- ffi
+ platform_specific (1.0-java)
PLATFORMS
java
- ruby
DEPENDENCIES
- empyrean (= 0.1.0)
- pry
-
- BUNDLED WITH
- #{Bundler::VERSION}
- L
-
- aggregate_failures do
- lockfile bad_lockfile
- bundle :install
- lockfile_should_be good_lockfile
-
- lockfile bad_lockfile
- bundle :update, :all => true
- lockfile_should_be good_lockfile
-
- lockfile bad_lockfile
- bundle "update ffi"
- lockfile_should_be good_lockfile
-
- lockfile bad_lockfile
- bundle "update empyrean"
- lockfile_should_be good_lockfile
-
- lockfile bad_lockfile
- bundle :lock
- lockfile_should_be 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"
+ platform_specific
+ #{checksums}
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 that have extra platform-specific runtime dependencies", :bundler => "< 3" do
- simulate_platform x64_mac
-
- update_repo2 do
- build_gem "facter", "2.4.6"
- build_gem "facter", "2.4.6" do |s|
- s.platform = "universal-darwin"
- s.add_runtime_dependency "CFPropertyList"
- end
- build_gem "CFPropertyList"
- end
+ bundle_config "force_ruby_platform true"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
-
- gem "facter"
+ source "https://gem.repo1"
+ gem "platform_specific"
G
- expect(err).to include "Unable to use the platform-specific (universal-darwin) version of facter (2.4.6) " \
- "because it has different dependencies from the ruby version. " \
- "To use the platform-specific version of the gem, run `bundle config set specific_platform true` and install again."
-
- expect(the_bundle).to include_gem "facter 2.4.6"
- expect(the_bundle).not_to include_gem "CFPropertyList"
- end
-
- it "fetches gems again after changing the version of Ruby" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ checksums.checksum gem_repo1, "platform_specific", "1.0"
- gem "rack", "1.0.0"
- G
+ expect(the_bundle).to include_gem "platform_specific 1.0 ruby"
- bundle "config --local path vendor/bundle"
- bundle :install
+ expect(lockfile).to eq <<~G
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
- FileUtils.mv(vendored_gems, bundled_app("vendor/bundle", Gem.ruby_engine, "1.8"))
+ PLATFORMS
+ java
+ ruby
- bundle :install
- expect(vendored_gems("gems/rack-1.0.0")).to exist
+ DEPENDENCIES
+ platform_specific
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
end
end
RSpec.describe "bundle install with platform conditionals" do
it "installs gems tagged w/ the current platforms" do
- skip "platform issues" if Gem.win_platform?
-
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
platforms :#{local_tag} do
gem "nokogiri"
@@ -290,22 +440,63 @@ RSpec.describe "bundle install with platform conditionals" do
it "does not install gems tagged w/ another platforms" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
platforms :#{not_local_tag} do
gem "nokogiri"
end
G
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
end
- it "installs gems tagged w/ the current platforms inline" do
- skip "platform issues" if Gem.win_platform?
+ 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 "https://gem.repo4"
+
+ gem "activesupport"
+
+ platforms :#{not_local_tag} do
+ gem "tzinfo", "~> 1.2"
+ end
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ activesupport (6.1.4.1)
+ tzinfo (~> 2.0)
+ tzinfo (2.0.4)
+
+ PLATFORMS
+ #{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
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "nokogiri", :platforms => :#{local_tag}
G
expect(the_bundle).to include_gems "nokogiri 1.4.2"
@@ -313,19 +504,17 @@ RSpec.describe "bundle install with platform conditionals" do
it "does not install gems tagged w/ another platforms inline" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
gem "nokogiri", :platforms => :#{not_local_tag}
G
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
end
it "installs gems tagged w/ the current platform inline" do
- skip "platform issues" if Gem.win_platform?
-
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "nokogiri", :platform => :#{local_tag}
G
expect(the_bundle).to include_gems "nokogiri 1.4.2"
@@ -333,7 +522,7 @@ RSpec.describe "bundle install with platform conditionals" do
it "doesn't install gems tagged w/ another platform inline" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "nokogiri", :platform => :#{not_local_tag}
G
expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
@@ -343,6 +532,7 @@ RSpec.describe "bundle install with platform conditionals" do
build_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
platform :#{not_local_tag} do
gem "foo", :git => "#{lib_path("foo-1.0")}"
end
@@ -352,10 +542,10 @@ RSpec.describe "bundle install with platform conditionals" do
end
it "does not attempt to install gems from :rbx when using --local" do
- simulate_platform "ruby"
+ bundle_config "force_ruby_platform true"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "some_gem", :platform => :rbx
G
@@ -364,64 +554,85 @@ RSpec.describe "bundle install with platform conditionals" do
end
it "does not attempt to install gems from other rubies when using --local" do
- simulate_platform "ruby"
- other_ruby_version_tag = RUBY_VERSION =~ /^1\.8/ ? :ruby_19 : :ruby_18
-
+ bundle_config "force_ruby_platform true"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "some_gem", platform: :#{other_ruby_version_tag}
+ source "https://gem.repo1"
+ gem "some_gem", platform: :ruby_22
G
bundle "install --local"
expect(out).not_to match(/Could not find gem 'some_gem/)
end
- it "resolves all platforms by default and without warning messages" do
- simulate_platform "ruby"
+ it "does not print a warning when a dependency is unused on a platform different from the current one" do
+ bundle_config "force_ruby_platform true"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby]
+ gem "myrack", :platform => [:windows, :jruby]
G
bundle "install"
expect(err).to be_empty
- lockfile_should_be <<-L
+ expect(lockfile).to eq <<~L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (1.0.0)
PLATFORMS
- java
ruby
- x64-mingw32
- x86-mingw32
- x86-mswin32
DEPENDENCIES
- rack
-
+ myrack
+ #{checksums_section_when_enabled}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
+
+ it "resolves fine when a dependency is unused on a platform different from the current one, but reintroduced transitively" do
+ bundle_config "force_ruby_platform true"
+
+ build_repo4 do
+ build_gem "listen", "3.7.1" do |s|
+ s.add_dependency "ffi"
+ end
+
+ build_gem "ffi", "1.15.5"
+ end
+
+ install_gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "listen"
+ gem "ffi", :platform => :windows
+ G
+ expect(err).to be_empty
+ end
end
RSpec.describe "when a gem has no architecture" do
it "still installs correctly" do
- simulate_platform mswin
+ simulate_platform "x86-mswin32" do
+ build_repo2 do
+ # The rcov gem is platform mswin32, but has no arch
+ build_gem "rcov" do |s|
+ s.platform = Gem::Platform.new([nil, "mswin32", nil])
+ s.write "lib/rcov.rb", "RCOV = '1.0.0'"
+ end
+ end
- gemfile <<-G
- # Try to install gem with nil arch
- source "http://localgemserver.test/"
- gem "rcov"
- G
+ gemfile <<-G
+ # Try to install gem with nil arch
+ source "http://localgemserver.test/"
+ gem "rcov"
+ G
- bundle :install, :artifice => "windows"
- expect(the_bundle).to include_gems "rcov 1.0.0"
+ bundle :install, artifice: "windows", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
end
end
diff --git a/spec/bundler/install/gemfile/ruby_spec.rb b/spec/bundler/install/gemfile/ruby_spec.rb
index fd4300c042..d937abd714 100644
--- a/spec/bundler/install/gemfile/ruby_spec.rb
+++ b/spec/bundler/install/gemfile/ruby_spec.rb
@@ -10,100 +10,148 @@ RSpec.describe "ruby requirement" do
# requirement. This test verifies the fix, committed in bfbad5c5.
it "allows adding gems" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "#{RUBY_VERSION}"
- gem "rack"
+ source "https://gem.repo1"
+ ruby "#{Gem.ruby_version}"
+ gem "myrack"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "#{RUBY_VERSION}"
- gem "rack"
- gem "rack-obama"
+ source "https://gem.repo1"
+ ruby "#{Gem.ruby_version}"
+ gem "myrack"
+ gem "myrack-obama"
G
- expect(the_bundle).to include_gems "rack-obama 1.0"
+ expect(the_bundle).to include_gems "myrack-obama 1.0"
end
it "allows removing the ruby version requirement" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "~> #{RUBY_VERSION}"
- gem "rack"
+ source "https://gem.repo1"
+ ruby "~> #{Gem.ruby_version}"
+ gem "myrack"
G
expect(lockfile).to include("RUBY VERSION")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
expect(lockfile).not_to include("RUBY VERSION")
end
it "allows changing the ruby version requirement to something compatible" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby ">= 1.0.0"
- gem "rack"
+ source "https://gem.repo1"
+ ruby ">= #{current_ruby_minor}"
+ gem "myrack"
G
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
- simulate_ruby_version "5100"
-
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby ">= 1.0.1"
- gem "rack"
+ source "https://gem.repo1"
+ ruby ">= #{Gem.ruby_version}"
+ gem "myrack"
G
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
end
it "allows changing the ruby version requirement to something incompatible" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
ruby ">= 1.0.0"
- gem "rack"
+ gem "myrack"
G
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+
+ RUBY VERSION
+ ruby 2.1.4
- simulate_ruby_version "5100"
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby ">= 5000.0"
- gem "rack"
+ source "https://gem.repo1"
+ ruby ">= #{current_ruby_minor}"
+ gem "myrack"
G
- expect(the_bundle).to include_gems "rack 1.0.0"
- expect(locked_ruby_version.versions).to eq(["5100"])
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
end
it "allows requirements with trailing whitespace" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "#{RUBY_VERSION}\\n \t\\n"
- gem "rack"
+ source "https://gem.repo1"
+ ruby "#{Gem.ruby_version}\\n \t\\n"
+ gem "myrack"
G
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "fails gracefully with malformed requirements" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
ruby ">= 0", "-.\\0"
- gem "rack"
+ gem "myrack"
G
expect(err).to include("There was an error parsing") # i.e. DSL error, not error template
end
+
+ it "allows picking up ruby version from a file" do
+ create_file ".ruby-version", Gem.ruby_version.to_s
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ ruby file: ".ruby-version"
+ gem "myrack"
+ G
+
+ expect(lockfile).to include("RUBY VERSION")
+ end
+
+ it "reads the ruby version file from the right folder when nested Gemfiles are involved" do
+ create_file ".ruby-version", Gem.ruby_version.to_s
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ ruby file: ".ruby-version"
+ gem "myrack"
+ G
+
+ nested_dir = bundled_app(".ruby-lsp")
+
+ FileUtils.mkdir nested_dir
+
+ gemfile ".ruby-lsp/Gemfile", <<-G
+ eval_gemfile(File.expand_path("../Gemfile", __dir__))
+ G
+
+ bundle "install", dir: nested_dir
+
+ expect(bundled_app(".ruby-lsp/Gemfile.lock").read).to include("RUBY VERSION")
+ end
end
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
index be41857043..654d638e1f 100644
--- a/spec/bundler/install/gemfile/sources_spec.rb
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -2,434 +2,514 @@
RSpec.describe "bundle install with gems on multiple sources" do
# repo1 is built automatically before all of the specs run
- # it contains rack-obama 1.0.0 and rack 0.9.1 & 1.0.0 amongst other gems
-
- context "without source affinity" do
- before do
- # Oh no! Someone evil is trying to hijack rack :(
- # need this to be broken to check for correct source ordering
- build_repo gem_repo3 do
- build_gem "rack", repo3_rack_version do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
- end
- end
- end
-
- context "with multiple toplevel sources" do
- let(:repo3_rack_version) { "1.0.0" }
-
- before do
- gemfile <<-G
- source "#{file_uri_for(gem_repo3)}"
- source "#{file_uri_for(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
-
- 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(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
- expect(err).to include("Each source after the first must include a block")
- expect(exitstatus).to eq(4)
- end
- end
-
- context "when different versions of the same gem are in multiple sources" do
- let(:repo3_rack_version) { "1.2" }
-
- before do
- gemfile <<-G
- source "#{file_uri_for(gem_repo3)}"
- source "#{file_uri_for(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
- 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(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
- expect(err).to include("Each source after the first must include a block")
- expect(exitstatus).to eq(4)
- end
- end
- end
+ # it contains myrack-obama 1.0.0 and myrack 0.9.1 & 1.0.0 amongst other gems
context "with source affinity" do
context "with sources given by a block" do
before do
- # Oh no! Someone evil is trying to hijack rack :(
+ # Oh no! Someone evil is trying to hijack myrack :(
# need this to be broken to check for correct source ordering
- build_repo gem_repo3 do
- build_gem "rack", "1.0.0" do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
+ build_repo3 do
+ build_gem "myrack", "1.0.0" do |s|
+ s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
end
- build_gem "rack-obama" do |s|
- s.add_dependency "rack"
+ build_gem "myrack-obama" do |s|
+ s.add_dependency "myrack"
end
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"
+ gem "myrack"
end
- gem "rack-obama" # shoud come from repo3!
+ gem "myrack-obama" # should come from repo3!
G
end
it "installs the gems without any warning" do
- bundle :install
- expect(out).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")
+ bundle :install, artifice: "compact_index"
+ expect(err).not_to include("Warning")
+ expect(the_bundle).to include_gems("myrack-obama 1.0.0")
+ expect(the_bundle).to include_gems("myrack 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
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/myrack-obama-1.0.gem")).to exist
- bundle "config --local deployment true"
- bundle :install
+ bundle_config "deployment true"
+ bundle :install, artifice: "compact_index"
- expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
+ expect(the_bundle).to include_gems("myrack-obama 1.0.0", "myrack 1.0.0")
end
end
context "with sources set by an option" do
before do
- # Oh no! Someone evil is trying to hijack rack :(
+ # Oh no! Someone evil is trying to hijack myrack :(
# need this to be broken to check for correct source ordering
- build_repo gem_repo3 do
- build_gem "rack", "1.0.0" do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
+ build_repo3 do
+ build_gem "myrack", "1.0.0" do |s|
+ s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
end
- build_gem "rack-obama" do |s|
- s.add_dependency "rack"
+ build_gem "myrack-obama" do |s|
+ s.add_dependency "myrack"
end
end
- gemfile <<-G
- source "#{file_uri_for(gem_repo3)}"
- gem "rack-obama" # should come from repo3!
- gem "rack", :source => "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, artifice: "compact_index"
+ source "https://gem.repo3"
+ gem "myrack-obama" # should come from repo3!
+ gem "myrack", :source => "https://gem.repo1"
G
end
it "installs the gems without any warning" do
- bundle :install
- expect(out).not_to include("Warning")
- expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
+ expect(err).not_to include("Warning")
+ expect(the_bundle).to include_gems("myrack-obama 1.0.0", "myrack 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"
+ build_repo3 do
+ build_gem "depends_on_myrack", "1.0.1" do |s|
+ s.add_dependency "myrack"
end
end
- end
-
- context "when the indirect dependency is in the pinned source" do
- before do
- # we need a working rack gem in repo3
- update_repo gem_repo3 do
- build_gem "rack", "1.0.0"
- end
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- source "#{file_uri_for(gem_repo3)}" do
- gem "depends_on_rack"
- end
- G
+ # we need a working myrack gem in repo3
+ update_repo gem_repo3 do
+ build_gem "myrack", "1.0.0"
end
- context "and not in any other sources" do
- before do
- build_repo(gem_repo2) {}
+ gemfile <<-G
+ source "https://gem.repo2"
+ source "https://gem.repo3" do
+ gem "depends_on_myrack"
end
+ G
+ 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
+ context "and not in any other sources" do
+ before do
+ 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_myrack 1.0.1", "myrack 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
+ 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 "myrack", "1.0.0" do |s|
+ s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
end
end
+ end
- context "when disable_multisource is set" do
- before do
- bundle "config set disable_multisource true"
- end
+ it "installs from the same source without any warning" do
+ bundle :install, artifice: "compact_index"
- it "installs from the same source without any warning" do
- bundle :install
+ expect(err).not_to include("Warning: the gem 'myrack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0", source: "remote3")
- 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")
+ # In https://github.com/bundler/bundler/issues/3585 this failed
+ # when there is already a lockfile, and the gems are missing, so try again
+ system_gems []
+ 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 'myrack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0", source: "remote3")
+ end
+ end
+ 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")
- end
+ context "when a pinned gem has an indirect dependency in a different source" do
+ before do
+ # In these tests, we need a working myrack gem in repo2 and not repo3
+
+ build_repo3 do
+ build_gem "depends_on_myrack", "1.0.1" do |s|
+ s.add_dependency "myrack"
end
end
+
+ build_repo gem_repo2 do
+ build_gem "myrack", "1.0.0"
+ end
end
- context "when the indirect dependency is in a different source" do
+ context "and not in any other sources" 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
+ install_gemfile <<-G, artifice: "compact_index"
+ source "https://gem.repo2"
+ source "https://gem.repo3" do
+ gem "depends_on_myrack"
+ end
+ G
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
+ it "installs from the other source without any warning" do
+ expect(err).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0")
+ end
+ end
+ end
+
+ context "when a top-level gem can only be found in an scoped source" do
+ before do
+ build_repo2
+
+ build_repo3 do
+ build_gem "private_gem_1", "1.0.0"
+ build_gem "private_gem_2", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ gem "private_gem_1"
+
+ source "https://gem.repo3" do
+ gem "private_gem_2"
end
+ G
+ end
- 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")
+ 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 a top-level gem has an indirect dependency" do
+ before do
+ build_repo gem_repo2 do
+ build_gem "depends_on_myrack", "1.0.1" do |s|
+ s.add_dependency "myrack"
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
- end
+ build_repo3 do
+ build_gem "unrelated_gem", "1.0.0"
+ 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")
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ gem "depends_on_myrack"
+
+ 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 "myrack", "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_myrack 1.0.1", "myrack 1.0.0", "unrelated_gem 1.0.0")
+ expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 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 "myrack", "1.0.0" do |s|
+ s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
end
+ end
+ end
- gemfile <<-G
- source "#{file_uri_for(gem_repo3)}" # contains depends_on_rack
- source "#{file_uri_for(gem_repo2)}" # contains broken rack
+ it "does not find the dependency" do
+ bundle :install, artifice: "compact_index", raise_on_error: false
+ expect(err).to end_with <<~E.strip
+ Could not find compatible versions
+
+ Because every version of depends_on_myrack depends on myrack >= 0
+ and myrack >= 0 could not be found in rubygems repository https://gem.repo2/ or installed locally,
+ depends_on_myrack cannot be used.
+ So, because Gemfile depends on depends_on_myrack >= 0,
+ version solving has failed.
+ E
+ end
+ end
- gem "depends_on_rack" # installed from gem_repo3
- gem "rack", :source => "#{file_uri_for(gem_repo1)}"
- G
+ context "and the dependency is in both the top-level and a pinned source" do
+ before do
+ update_repo gem_repo2 do
+ build_gem "myrack", "1.0.0"
end
- it "installs the dependency from the pinned source without warning", :bundler => "2" do
- bundle :install
+ update_repo gem_repo3 do
+ build_gem "myrack", "1.0.0" do |s|
+ s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
+ end
+ end
+ 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")
+ 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 'myrack'; puts MYRACK")).to eq("1.0.0")
+ expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0", "unrelated_gem 1.0.0")
+ expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0", source: "remote2")
+ expect(the_bundle).to include_gems("unrelated_gem 1.0.0", source: "remote3")
+ end
+ 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
+ context "when a scoped gem has a deeply nested indirect dependency" do
+ before do
+ build_repo3 do
+ build_gem "depends_on_depends_on_myrack", "1.0.1" do |s|
+ s.add_dependency "depends_on_myrack"
+ 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_myrack", "1.0.1" do |s|
+ s.add_dependency "myrack"
end
+ 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)
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ source "https://gem.repo3" do
+ gem "depends_on_depends_on_myrack"
end
+ G
+ end
+
+ context "and the dependency is only in the top-level source" do
+ before do
+ update_repo gem_repo2 do
+ build_gem "myrack", "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_myrack 1.0.1", "depends_on_myrack 1.0.1", "myrack 1.0.0")
+ expect(the_bundle).to include_gems("myrack 1.0.0", source: "remote2")
+ expect(the_bundle).to include_gems("depends_on_depends_on_myrack 1.0.1", "depends_on_myrack 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 "myrack", "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_myrack 1.0.1", "depends_on_myrack 1.0.1", "myrack 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 "myrack", "1.0.0" do |s|
+ s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
end
end
- build_repo gem_repo3 do
- build_gem "unrelated_gem", "1.0.0"
+ update_repo gem_repo3 do
+ build_gem "myrack", "1.0.0"
end
+ end
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
-
- gem "depends_on_rack"
+ 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_myrack 1.0.1", "depends_on_myrack 1.0.1", "myrack 1.0.0", source: "remote3")
+ end
+ end
+ end
- source "#{file_uri_for(gem_repo3)}" do
- gem "unrelated_gem"
- end
- G
+ 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
- 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
+ build_repo gem_repo2 do
+ build_gem "activesupport", "6.1.2"
- 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 "webpacker", "5.2.1" do |s|
+ s.add_dependency "activesupport", ">= 5.2"
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
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ 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_repo3 do
+ build_gem "handsoap", "0.2.5.5" do |s|
+ s.add_dependency "nokogiri", ">= 1.2.3"
end
+ 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")
+ 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
- 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
+ source "https://gem.repo3" do
+ gem "handsoap"
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")
- end
+ gem "nokogiri"
+ G
+ end
+
+ it "installs from the default source without any warnings or errors and generates a proper lockfile" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo3, "handsoap", "0.2.5.5"
+ c.checksum gem_repo2, "nokogiri", "1.11.1"
+ c.checksum gem_repo2, "racca", "1.5.2"
end
+
+ 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
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ handsoap!
+ nokogiri
+ #{checksums}
+ 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
context "with a gem that is only found in the wrong source" do
before do
- build_repo gem_repo3 do
+ build_repo3 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
context "with an existing lockfile" do
before do
- system_gems "rack-0.9.1", "rack-1.0.0", :path => default_bundle_path
+ system_gems "myrack-0.9.1", "myrack-1.0.0", path: default_bundle_path
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo1)}
- remote: #{file_uri_for(gem_repo3)}
+ remote: https://gem.repo1
specs:
- rack (0.9.1)
+
+ GEM
+ remote: https://gem.repo3
+ specs:
+ myrack (0.9.1)
PLATFORMS
- ruby
+ #{lockfile_platforms}
DEPENDENCIES
- rack!
+ myrack!
L
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- source "#{file_uri_for(gem_repo3)}" do
- gem 'rack'
+ source "https://gem.repo1"
+ source "https://gem.repo3" do
+ gem 'myrack'
end
G
end
# Reproduction of https://github.com/rubygems/bundler/issues/3298
it "does not unlock the installed gem on exec" do
- expect(the_bundle).to include_gems("rack 0.9.1")
+ expect(the_bundle).to include_gems("myrack 0.9.1")
end
end
@@ -438,13 +518,14 @@ RSpec.describe "bundle install with gems on multiple sources" do
build_lib "foo"
gemfile <<-G
- gem "rack", :source => "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
+ gem "myrack", :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"')
@@ -455,18 +536,17 @@ RSpec.describe "bundle install with gems on multiple sources" do
context "when an older version of the same gem also ships with Ruby" do
before do
- system_gems "rack-0.9.1"
+ system_gems "myrack-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 "myrack" # 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")
+ expect(the_bundle).to include_gems("myrack 1.0.0")
end
end
@@ -480,17 +560,17 @@ RSpec.describe "bundle install with gems on multiple sources" do
# Installing this gemfile...
gemfile <<-G
- source '#{file_uri_for(gem_repo1)}'
- gem 'rack'
- gem 'foo', '~> 0.1', :source => '#{file_uri_for(gem_repo4)}'
- gem 'bar', '~> 0.1', :source => '#{file_uri_for(gem_repo4)}'
+ source 'https://gem.repo1'
+ gem 'myrack'
+ 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 "path ../gems/system"
+ bundle :install, artifice: "compact_index"
# And then we add some new versions...
- update_repo4 do
+ build_repo4 do
build_gem "foo", "0.2"
build_gem "bar", "0.3"
end
@@ -498,11 +578,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)}'
- gem 'rack'
- gem 'foo', '~> 0.2', :source => '#{file_uri_for(gem_repo4)}'
- gem 'bar', '~> 0.1', :source => '#{file_uri_for(gem_repo4)}'
+ install_gemfile <<-G, artifice: "compact_index"
+ source 'https://gem.repo1'
+ gem 'myrack'
+ 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,18 +593,21 @@ 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_repo3 do
+ build_gem "myrack"
+ 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
- gem "rack"
+ source "https://gem.repo3" do
+ gem "myrack"
end
gem "path1", :path => "#{lib_path("path1-1.0")}"
@@ -535,7 +618,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,106 +627,687 @@ 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)}"
- gem "rack"
+ install_gemfile <<-G, artifice: "compact_index"
+ source "https://gem.repo1"
+ gem "myrack"
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 "myrack", "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
- gem "rack", "2.0.1.1.forked"
+ source "https://gem.repo4" do
+ gem "myrack", "2.0.1.1.forked"
gem "thor"
end
- gem "rack-obama"
+ gem "myrack-obama"
G
# Then we change the Gemfile by adding a version to thor
gemfile <<-G
- source "#{source_uri}"
+ source "https://gem.repo1"
- source "#{second_uri}" do
- gem "rack", "2.0.1.1.forked"
+ source "https://gem.repo4" do
+ gem "myrack", "2.0.1.1.forked"
gem "thor", "0.19.1.1.forked"
end
- gem "rack-obama"
+ gem "myrack-obama"
G
- # But we should still be able to find rack 2.0.1.1.forked and install it
- bundle :install
+ # But we should still be able to find myrack 2.0.1.1.forked and install it
+ 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"
+ gem "myrack"
G
build_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
build_gem "bar"
end
- build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s|
+ build_lib("gemspec_test", path: tmp("gemspec_test")) do |s|
s.add_dependency "bar", "=1.0.0"
end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
- gemspec :path => "#{tmp.join("gemspec_test")}"
+ install_gemfile <<-G, artifice: "compact_index"
+ source "https://gem.repo2"
+ gem "myrack"
+ gemspec :path => "#{tmp("gemspec_test")}"
G
end
- it "keeps the old version", :bundler => "2" do
- expect(the_bundle).to include_gems("rack 1.0.0")
+ it "conservatively installs the existing locked version" do
+ expect(the_bundle).to include_gems("myrack 1.0.0")
end
+ end
+
+ context "when Gemfile overrides a gemspec development dependency to change the default source" do
+ before do
+ build_repo4 do
+ build_gem "bar"
+ end
- it "installs the higher version in the new repo", :bundler => "3" do
- expect(the_bundle).to include_gems("rack 1.2")
+ build_lib("gemspec_test", path: tmp("gemspec_test")) do |s|
+ s.add_development_dependency "bar"
+ end
+
+ install_gemfile <<-G, artifice: "compact_index"
+ source "https://gem.repo1"
+
+ source "https://gem.repo4" do
+ gem "bar"
+ end
+
+ gemspec :path => "#{tmp("gemspec_test")}"
+ G
+ end
+
+ it "does not print warnings" do
+ expect(err).to be_empty
+ 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
- context "when a gem is available from multiple ambiguous sources", :bundler => "3" do
+ it "fails immediately 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
+
+ ruby <<~R, raise_on_error: false
+ require 'bundler/setup'
+ R
+
+ expect(last_command).to be_failure
+ expect(err).to include("Could not find gem 'example' in locally installed gems.")
+ end
+
+ it "fails immediately 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" do
it "raises, suggesting a source block" do
build_repo4 do
- build_gem "depends_on_rack" do |s|
- s.add_dependency "rack"
+ build_gem "depends_on_myrack" do |s|
+ s.add_dependency "myrack"
end
- build_gem "rack"
+ build_gem "myrack"
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_extra_api", raise_on_error: false
+ source "https://global.source"
+
+ source "https://scoped.source/extra" do
+ gem "depends_on_myrack"
+ end
+
+ source "https://scoped.source" 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
+ expect(err).to eq <<~EOS.strip
+ The gem 'myrack' was found in multiple relevant sources.
+ * rubygems repository https://scoped.source/
+ * rubygems repository https://scoped.source/extra/
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 default source includes old gems with nil required_ruby_version" do
+ before do
+ build_repo2 do
+ build_gem "ruport", "1.7.0.3" do |s|
+ s.add_dependency "pdf-writer", "1.1.8"
+ end
+ end
+
+ build_repo gem_repo4 do
+ build_gem "pdf-writer", "1.1.8"
+ end
+
+ path = "#{gem_repo4}/#{Gem::MARSHAL_SPEC_DIR}/pdf-writer-1.1.8.gemspec.rz"
+ spec = Marshal.load(Bundler.rubygems.inflate(File.binread(path)))
+ spec.instance_variable_set(:@required_ruby_version, nil)
+ File.open(path, "wb") do |f|
+ f.write Gem.deflate(Marshal.dump(spec))
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "ruport", "= 1.7.0.3", :source => "https://gem.repo4/extra"
+ G
+ end
+
+ it "handles that fine" do
+ bundle "install", artifice: "compact_index_extra"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "pdf-writer", "1.1.8"
+ c.checksum gem_repo2, "ruport", "1.7.0.3"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ pdf-writer (1.1.8)
+
+ GEM
+ remote: https://gem.repo4/extra/
+ specs:
+ ruport (1.7.0.3)
+ pdf-writer (= 1.1.8)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ruport (= 1.7.0.3)!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when default source includes old gems with nil required_rubygems_version" do
+ before do
+ build_repo2 do
+ build_gem "ruport", "1.7.0.3" do |s|
+ s.add_dependency "pdf-writer", "1.1.8"
+ end
+ end
+
+ build_repo gem_repo4 do
+ build_gem "pdf-writer", "1.1.8"
+ end
+
+ path = "#{gem_repo4}/#{Gem::MARSHAL_SPEC_DIR}/pdf-writer-1.1.8.gemspec.rz"
+ spec = Marshal.load(Bundler.rubygems.inflate(File.binread(path)))
+ spec.instance_variable_set(:@required_rubygems_version, nil)
+ File.open(path, "wb") do |f|
+ f.write Gem.deflate(Marshal.dump(spec))
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "ruport", "= 1.7.0.3", :source => "https://gem.repo4/extra"
+ G
+ end
+
+ it "handles that fine" do
+ bundle "install", artifice: "compact_index_extra"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "pdf-writer", "1.1.8"
+ c.checksum gem_repo2, "ruport", "1.7.0.3"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ pdf-writer (1.1.8)
+
+ GEM
+ remote: https://gem.repo4/extra/
+ specs:
+ ruport (1.7.0.3)
+ pdf-writer (= 1.1.8)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ruport (= 1.7.0.3)!
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when default source uses the old API and includes old gems with nil required_rubygems_version" do
+ before do
+ build_repo4 do
+ build_gem "pdf-writer", "1.1.8"
+ end
+
+ path = "#{gem_repo4}/#{Gem::MARSHAL_SPEC_DIR}/pdf-writer-1.1.8.gemspec.rz"
+ spec = Marshal.load(Bundler.rubygems.inflate(File.binread(path)))
+ spec.instance_variable_set(:@required_rubygems_version, nil)
+ File.open(path, "wb") do |f|
+ f.write Gem.deflate(Marshal.dump(spec))
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "pdf-writer", "= 1.1.8"
+ G
+ end
+
+ it "handles that fine" do
+ bundle "install --verbose", artifice: "endpoint"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "pdf-writer", "1.1.8"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ pdf-writer (1.1.8)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ pdf-writer (= 1.1.8)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when mistakenly adding a top level gem already depended on and cached under the wrong source" do
+ before do
+ build_repo4 do
+ build_gem "some_private_gem", "0.1.0" do |s|
+ s.add_dependency "example", "~> 1.0"
+ end
+ end
+
+ build_repo2 do
+ build_gem "example", "1.0.0"
+ end
+
+ install_gemfile <<~G, artifice: "compact_index"
+ source "https://gem.repo2"
+
+ source "https://gem.repo4" do
+ gem "some_private_gem"
+ end
+ G
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ source "https://gem.repo4" do
+ gem "some_private_gem"
+ gem "example" # MISTAKE, example is not available at gem.repo4
+ end
+ G
+ end
+
+ it "shows a proper error message and does not generate a corrupted lockfile" do
+ expect do
+ bundle :install, artifice: "compact_index", raise_on_error: false, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ end.not_to change { lockfile }
+
+ expect(err).to include("Could not find gem 'example' in rubygems repository https://gem.repo4/")
+ end
+ end
+
+ context "when a gem has versions in two sources, but only the locked one has updates" do
+ let(:original_lockfile) do
+ <<~L
+ GEM
+ remote: https://main.source/
+ specs:
+ activesupport (1.0)
+ bigdecimal
+ bigdecimal (1.0.0)
+
+ GEM
+ remote: https://main.source/extra/
+ specs:
+ foo (1.0)
+ bigdecimal
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ activesupport
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ before do
+ build_repo3 do
+ build_gem "activesupport" do |s|
+ s.add_dependency "bigdecimal"
+ end
+
+ build_gem "bigdecimal", "1.0.0"
+ build_gem "bigdecimal", "3.3.1"
+ end
+
+ build_repo4 do
+ build_gem "foo" do |s|
+ s.add_dependency "bigdecimal"
+ end
+
+ build_gem "bigdecimal", "1.0.0"
+ end
+
+ gemfile <<~G
+ source "https://main.source"
+
+ gem "activesupport"
+
+ source "https://main.source/extra" do
+ gem "foo"
+ end
+ G
+
+ lockfile original_lockfile
+ end
+
+ it "properly upgrades the lockfile when updating that specific gem" do
+ bundle "update bigdecimal --conservative", artifice: "compact_index_extra_api", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo3.to_s }
+
+ expect(lockfile).to eq original_lockfile.gsub("bigdecimal (1.0.0)", "bigdecimal (3.3.1)")
+ end
+ end
+
+ context "when switching a gem with components from rubygems to git source" do
+ before do
+ build_repo2 do
+ build_gem "rails", "7.0.0" do |s|
+ s.add_dependency "actionpack", "7.0.0"
+ s.add_dependency "activerecord", "7.0.0"
+ end
+ build_gem "actionpack", "7.0.0"
+ build_gem "activerecord", "7.0.0"
+ # propshaft also depends on actionpack, creating the conflict
+ build_gem "propshaft", "1.0.0" do |s|
+ s.add_dependency "actionpack", ">= 7.0.0"
+ end
+ end
+
+ build_git "rails", "7.0.0", path: lib_path("rails") do |s|
+ s.add_dependency "actionpack", "7.0.0"
+ s.add_dependency "activerecord", "7.0.0"
+ end
+
+ build_git "actionpack", "7.0.0", path: lib_path("rails")
+ build_git "activerecord", "7.0.0", path: lib_path("rails")
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "rails", "7.0.0"
+ gem "propshaft"
+ G
+ end
+
+ it "moves component gems to the git source in the lockfile" do
+ expect(lockfile).to include("remote: https://gem.repo2")
+ expect(lockfile).to include("rails (7.0.0)")
+ expect(lockfile).to include("actionpack (7.0.0)")
+ expect(lockfile).to include("activerecord (7.0.0)")
+ expect(lockfile).to include("propshaft (1.0.0)")
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "rails", git: "#{lib_path("rails")}"
+ gem "propshaft"
+ G
+
+ bundle "install"
+
+ expect(lockfile).to include("remote: #{lib_path("rails")}")
+ expect(lockfile).to include("rails (7.0.0)")
+ expect(lockfile).to include("actionpack (7.0.0)")
+ expect(lockfile).to include("activerecord (7.0.0)")
+
+ # Component gems should NOT remain in the GEM section
+ # Extract just the GEM section by splitting on GIT first, then GEM
+ gem_section = lockfile.split("GEM\n").last.split(/\n(PLATFORMS|DEPENDENCIES)/)[0]
+ expect(gem_section).not_to include("actionpack (7.0.0)")
+ expect(gem_section).not_to include("activerecord (7.0.0)")
+ end
+ end
+
+ context "when switching a gem with components from rubygems to path source" do
+ before do
+ build_repo2 do
+ build_gem "rails", "7.0.0" do |s|
+ s.add_dependency "actionpack", "7.0.0"
+ s.add_dependency "activerecord", "7.0.0"
+ end
+ build_gem "actionpack", "7.0.0"
+ build_gem "activerecord", "7.0.0"
+ # propshaft also depends on actionpack, creating the conflict
+ build_gem "propshaft", "1.0.0" do |s|
+ s.add_dependency "actionpack", ">= 7.0.0"
+ end
+ end
+
+ build_lib "rails", "7.0.0", path: lib_path("rails") do |s|
+ s.add_dependency "actionpack", "7.0.0"
+ s.add_dependency "activerecord", "7.0.0"
+ end
+
+ build_lib "actionpack", "7.0.0", path: lib_path("rails")
+ build_lib "activerecord", "7.0.0", path: lib_path("rails")
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "rails", "7.0.0"
+ gem "propshaft"
+ G
+ end
+
+ it "moves component gems to the path source in the lockfile" do
+ expect(lockfile).to include("remote: https://gem.repo2")
+ expect(lockfile).to include("rails (7.0.0)")
+ expect(lockfile).to include("actionpack (7.0.0)")
+ expect(lockfile).to include("activerecord (7.0.0)")
+ expect(lockfile).to include("propshaft (1.0.0)")
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "rails", path: "#{lib_path("rails")}"
+ gem "propshaft"
+ G
+
+ bundle "install"
+
+ expect(lockfile).to include("remote: #{lib_path("rails")}")
+ expect(lockfile).to include("rails (7.0.0)")
+ expect(lockfile).to include("actionpack (7.0.0)")
+ expect(lockfile).to include("activerecord (7.0.0)")
+
+ # Component gems should NOT remain in the GEM section
+ # Extract just the GEM section by splitting appropriately
+ gem_section = lockfile.split("GEM\n").last.split(/\n(PLATFORMS|DEPENDENCIES)/)[0]
+ expect(gem_section).not_to include("actionpack (7.0.0)")
+ expect(gem_section).not_to include("activerecord (7.0.0)")
+ end
+ end
+
+ context "when a scoped rubygems source is missing a transitive dependency" do
+ before do
+ build_repo2 do
+ build_gem "fallback_dep", "1.0.0"
+ build_gem "foo", "1.0.0"
+ end
+
+ build_repo3 do
+ build_gem "private_parent", "1.0.0" do |s|
+ s.add_dependency "fallback_dep"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ gem "foo"
+
+ source "https://gem.repo3" do
+ gem "private_parent", "1.0.0"
+ end
+ G
+
+ bundle :install, artifice: "compact_index"
+ end
+
+ it "falls back to the default rubygems source for that dependency" do
+ build_repo2 do
+ build_gem "foo", "2.0.0"
+ end
+
+ system_gems []
+
+ bundle "update foo", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("private_parent 1.0.0", "fallback_dep 1.0.0", "foo 2.0.0")
+ expect(the_bundle).to include_gems("private_parent 1.0.0", source: "remote3")
+ expect(the_bundle).to include_gems("fallback_dep 1.0.0", source: "remote2")
+ end
+ end
+
+ context "when a path gem has a transitive dependency that does not exist in the path source" do
+ before do
+ build_repo2 do
+ build_gem "missing_dep", "1.0.0"
+ build_gem "foo", "1.0.0"
+ end
+
+ build_lib "parent_gem", "1.0.0", path: lib_path("parent_gem") do |s|
+ s.add_dependency "missing_dep"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ gem "foo"
+
+ gem "parent_gem", path: "#{lib_path("parent_gem")}"
+ G
+
+ bundle :install, artifice: "compact_index"
+ end
+
+ it "falls back to the default rubygems source for that dependency when updating" do
+ build_repo2 do
+ build_gem "foo", "2.0.0"
+ end
+
+ system_gems []
+
+ bundle "update foo", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("parent_gem 1.0.0", "missing_dep 1.0.0", "foo 2.0.0")
+ expect(the_bundle).to include_gems("parent_gem 1.0.0", source: "path@#{lib_path("parent_gem")}")
+ expect(the_bundle).to include_gems("missing_dep 1.0.0", source: "remote2")
+ end
+ end
+
+ context "when a git gem has a transitive dependency that does not exist in the git source" do
+ before do
+ build_repo2 do
+ build_gem "missing_dep", "1.0.0"
+ build_gem "foo", "1.0.0"
+ end
+
+ build_git "parent_gem", "1.0.0", path: lib_path("parent_gem") do |s|
+ s.add_dependency "missing_dep"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ gem "foo"
+
+ gem "parent_gem", git: "#{lib_path("parent_gem")}"
+ G
+
+ bundle :install, artifice: "compact_index"
+ end
+
+ it "falls back to the default rubygems source for that dependency when updating" do
+ build_repo2 do
+ build_gem "foo", "2.0.0"
+ end
+
+ system_gems []
+
+ bundle "update foo", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("parent_gem 1.0.0", "missing_dep 1.0.0", "foo 2.0.0")
+ expect(the_bundle).to include_gems("parent_gem 1.0.0", source: "git@#{lib_path("parent_gem")}")
+ expect(the_bundle).to include_gems("missing_dep 1.0.0", source: "remote2")
+ end
+ end
end
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
index 959ffbf615..97b1d233bf 100644
--- a/spec/bundler/install/gemfile/specific_platform_spec.rb
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -1,119 +1,1972 @@
# frozen_string_literal: true
-RSpec.describe "bundle install with specific_platform enabled" do
- before do
- bundle "config set specific_platform true"
-
- build_repo2 do
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1")
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86_64-linux" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-mingw32" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-linux" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x64-mingw32" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "universal-darwin" }
+RSpec.describe "bundle install with specific platforms" do
+ let(:google_protobuf) { <<-G }
+ source "https://gem.repo2"
+ gem "google-protobuf"
+ G
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86_64-linux" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-linux" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x64-mingw32" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-mingw32" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5")
+ it "locks to the specific darwin platform" do
+ simulate_platform "x86_64-darwin-15" do
+ setup_multiplatform_gem
+ install_gemfile(google_protobuf)
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ expect(the_bundle.locked_platforms).to include("universal-darwin")
+ expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(
+ "google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin"
+ )
+ end
+ end
- build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "universal-darwin" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86_64-linux" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-mingw32" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-linux" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x64-mingw32" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.4")
-
- build_gem("google-protobuf", "3.0.0.alpha.5.0.3")
- build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86_64-linux" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-mingw32" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-linux" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x64-mingw32" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "universal-darwin" }
+ it "still installs the platform specific variant when locked only to ruby, and the platform specific variant has different dependencies" do
+ simulate_platform "x86_64-darwin-15" do
+ build_repo4 do
+ build_gem("sass-embedded", "1.72.0") do |s|
+ s.add_dependency "rake"
+ end
- build_gem("google-protobuf", "3.0.0.alpha.4.0")
- build_gem("google-protobuf", "3.0.0.alpha.3.1.pre")
- build_gem("google-protobuf", "3.0.0.alpha.3")
- build_gem("google-protobuf", "3.0.0.alpha.2.0")
- build_gem("google-protobuf", "3.0.0.alpha.1.1")
- build_gem("google-protobuf", "3.0.0.alpha.1.0")
+ build_gem("sass-embedded", "1.72.0") do |s|
+ s.platform = "x86_64-darwin-15"
+ end
- build_gem("facter", "2.4.6")
- build_gem("facter", "2.4.6") do |s|
- s.platform = "universal-darwin"
- s.add_runtime_dependency "CFPropertyList"
+ build_gem "rake"
end
- build_gem("CFPropertyList")
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sass-embedded"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ rake (1.0)
+ sass-embedded (1.72.0)
+ rake
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ sass-embedded
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose"
+ expect(err).to include("The following platform specific gems are getting installed, yet the lockfile includes only their generic ruby version")
+ expect(out).to include("Installing sass-embedded 1.72.0 (x86_64-darwin-15)")
+
+ expect(the_bundle).to include_gem("sass-embedded 1.72.0 x86_64-darwin-15")
end
end
- let(:google_protobuf) { <<-G }
- source "#{file_uri_for(gem_repo2)}"
- gem "google-protobuf"
- G
+ it "understands that a non-platform specific gem in a old lockfile doesn't necessarily mean installing the non-specific variant" do
+ simulate_platform "x86_64-darwin-15" do
+ setup_multiplatform_gem
- context "when on a darwin machine" do
- before { simulate_platform "x86_64-darwin-15" }
+ # Consistent location to install and look for gems
+ bundle_config "path vendor/bundle"
- it "locks to both the specific darwin platform and ruby" do
install_gemfile(google_protobuf)
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- expect(the_bundle.locked_gems.platforms).to eq([pl("ruby"), pl("x86_64-darwin-15")])
+
+ # simulate lockfile created with old bundler, which only locks for ruby platform
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ google-protobuf (3.0.0.alpha.5.0.5.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ google-protobuf
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ # force strict usage of the lockfile by setting frozen mode
+ bundle_config "frozen true"
+
+ # make sure the platform that got actually installed with the old bundler is used
+ expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
+ end
+ end
+
+ 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
+ simulate_platform "x86_64-darwin-15" do
+ setup_multiplatform_gem
+
+ # Consistent location to install and look for gems
+ bundle_config "path vendor/bundle"
+
+ gemfile google_protobuf
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "google-protobuf", "3.0.0.alpha.4.0"
+ end
+
+ # simulate lockfile created with old bundler, which only locks for ruby platform
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ google-protobuf (3.0.0.alpha.4.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ google-protobuf
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update"
+ expect(err).to include("The following platform specific gems are getting installed, yet the lockfile includes only their generic ruby version")
+
+ checksums.checksum gem_repo2, "google-protobuf", "3.0.0.alpha.5.0.5.1"
+
+ # make sure the platform that the platform specific dependency is used, since we're only locked to ruby
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
- google-protobuf-3.0.0.alpha.5.0.5.1
- google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
- ])
+
+ # make sure we're still only locked to ruby
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ google-protobuf (3.0.0.alpha.5.0.5.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ google-protobuf
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
+ end
- it "caches both the universal-darwin and ruby gems when --all-platforms is passed" do
+ context "when running on a legacy lockfile locked only to ruby" do
+ # Exercises the legacy lockfile path (use_exact_resolved_specifications? = false)
+ # because most_specific_locked_platform is ruby, matching the generic platform.
+ # Key insight: when target (arm64-darwin-22) != platform (ruby), the code tries
+ # both platforms before falling back, preserving lockfile integrity.
+
+ around do |example|
+ build_repo4 do
+ build_gem "nokogiri", "1.3.10"
+ build_gem "nokogiri", "1.3.10" do |s|
+ s.platform = "arm64-darwin"
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.3.10)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "arm64-darwin-22", &example
+ end
+
+ it "still installs the generic ruby variant if necessary" do
+ bundle "install"
+ expect(the_bundle).to include_gem("nokogiri 1.3.10")
+ expect(the_bundle).not_to include_gem("nokogiri 1.3.10 arm64-darwin")
+ end
+
+ it "still installs the generic ruby variant if necessary, even in frozen mode" do
+ bundle "install", env: { "BUNDLE_FROZEN" => "true" }
+ expect(the_bundle).to include_gem("nokogiri 1.3.10")
+ expect(the_bundle).not_to include_gem("nokogiri 1.3.10 arm64-darwin")
+ end
+ end
+
+ context "when platform-specific gem has incompatible required_ruby_version" do
+ # Key insight: candidate_platforms tries [target, platform, ruby] in order.
+ # Ruby platform is last since it requires compilation, but works when
+ # precompiled gems are incompatible with the current Ruby version.
+ #
+ # Note: This fix requires the lockfile to include both ruby and platform-
+ # specific variants (typical after `bundle lock --add-platform`). If the
+ # lockfile only has platform-specific gems, frozen mode cannot help because
+ # Bundler.setup would still expect the locked (incompatible) gem.
+
+ # Exercises the exact spec path (use_exact_resolved_specifications? = true)
+ # because lockfile has platform-specific entry as most_specific_locked_platform
+ it "falls back to ruby platform in frozen mode when lockfile includes both variants" do
+ build_repo4 do
+ build_gem "nokogiri", "1.18.10"
+ build_gem "nokogiri", "1.18.10" do |s|
+ s.platform = "x86_64-linux"
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ # Lockfile has both ruby and platform-specific gem (typical after `bundle lock --add-platform`)
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.18.10)
+ nokogiri (1.18.10-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "install", env: { "BUNDLE_FROZEN" => "true" }
+ expect(the_bundle).to include_gem("nokogiri 1.18.10")
+ expect(the_bundle).not_to include_gem("nokogiri 1.18.10 x86_64-linux")
+ end
+ end
+ end
+
+ it "doesn't discard previously installed platform specific gem and fall back to ruby on subsequent bundles" do
+ simulate_platform "x86_64-darwin-15" 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_dependency "libv8"
+ end
+ end
+
+ # Consistent location to install and look for gems
+ bundle_config "path vendor/bundle"
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "libv8"
+ G
+
+ # simulate lockfile created with old bundler, which only locks for ruby platform
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ libv8 (8.4.255.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ libv8
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose"
+ expect(err).to include("The following platform specific gems are getting installed, yet the lockfile includes only their generic ruby version")
+ expect(out).to include("Installing libv8 8.4.255.0 (universal-darwin)")
+
+ bundle "add mini_racer --verbose"
+ expect(out).to include("Using libv8 8.4.255.0 (universal-darwin)")
+ end
+ end
+
+ it "chooses platform specific gems even when resolving upon materialization and the API returns more specific platforms first" do
+ simulate_platform "x86_64-darwin-15" do
+ build_repo4 do
+ build_gem("grpc", "1.50.0")
+ build_gem("grpc", "1.50.0") {|s| s.platform = "universal-darwin" }
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "grpc"
+ G
+
+ # simulate lockfile created with old bundler, which only locks for ruby platform
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ grpc (1.50.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ grpc
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose", artifice: "compact_index_precompiled_before"
+ expect(err).to include("The following platform specific gems are getting installed, yet the lockfile includes only their generic ruby version")
+ expect(out).to include("Installing grpc 1.50.0 (universal-darwin)")
+ end
+ end
+
+ it "caches the universal-darwin gem when --all-platforms is passed and properly picks it up on further bundler invocations" do
+ simulate_platform "x86_64-darwin-15" do
+ setup_multiplatform_gem
gemfile(google_protobuf)
- bundle "package --all-platforms"
- expect([cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1"), cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin")]).
- to all(exist)
+ bundle "cache --all-platforms"
+ expect(cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin")).to exist
+
+ bundle "install --verbose"
+ expect(err).to be_empty
end
+ end
- it "uses the platform-specific gem with extra dependencies" do
+ it "caches the universal-darwin gem when cache_all_platforms is configured and properly picks it up on further bundler invocations" do
+ simulate_platform "x86_64-darwin-15" do
+ setup_multiplatform_gem
+ gemfile(google_protobuf)
+ bundle_config "cache_all_platforms true"
+ bundle "cache"
+ expect(cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin")).to exist
+
+ bundle "install --verbose"
+ expect(err).to be_empty
+ end
+ end
+
+ it "caches multiplatform git gems with a single gemspec when --all-platforms is passed" do
+ git = build_git "pg_array_parser", "1.0"
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "pg_array_parser", :git => "#{lib_path("pg_array_parser-1.0")}"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("pg_array_parser-1.0")}
+ revision: #{git.ref_for("main")}
+ specs:
+ pg_array_parser (1.0-java)
+ pg_array_parser (1.0)
+
+ GEM
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms("java")}
+
+ DEPENDENCIES
+ pg_array_parser!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "cache --all-platforms"
+
+ expect(err).to be_empty
+ end
+
+ it "uses the platform-specific gem with extra dependencies" do
+ simulate_platform "x86_64-darwin-15" do
+ setup_multiplatform_gem_with_different_dependencies_per_platform
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "facter"
G
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- expect(the_bundle.locked_gems.platforms).to eq([pl("ruby"), pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_platforms).to include("universal-darwin")
expect(the_bundle).to include_gems("facter 2.4.6 universal-darwin", "CFPropertyList 1.0")
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(["CFPropertyList-1.0",
- "facter-2.4.6",
- "facter-2.4.6-universal-darwin"])
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include("CFPropertyList-1.0",
+ "facter-2.4.6-universal-darwin")
end
+ end
- context "when adding a platform via lock --add_platform" do
- before do
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- end
+ context "when adding a platform via lock --add_platform" do
+ before do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ end
- it "adds the foreign platform" do
+ it "adds the foreign platform" do
+ simulate_platform "x86_64-darwin-15" do
+ setup_multiplatform_gem
install_gemfile(google_protobuf)
- bundle "lock --add-platform=#{x64_mingw}"
+ bundle "lock --add-platform=x64-mingw-ucrt"
- expect(the_bundle.locked_gems.platforms).to eq([rb, x64_mingw, pl("x86_64-darwin-15")])
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
- google-protobuf-3.0.0.alpha.5.0.5.1
+ expect(the_bundle.locked_platforms).to include("x64-mingw-ucrt", "universal-darwin")
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(*%w[
google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
- google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32
+ google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw-ucrt
])
end
+ end
- it "falls back on plain ruby when that version doesnt have a platform-specific gem" do
+ it "falls back on plain ruby when that version doesn't have a platform-specific gem" do
+ simulate_platform "x86_64-darwin-15" do
+ setup_multiplatform_gem
install_gemfile(google_protobuf)
- bundle "lock --add-platform=#{java}"
+ bundle "lock --add-platform=java"
- expect(the_bundle.locked_gems.platforms).to eq([java, rb, pl("x86_64-darwin-15")])
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
- google-protobuf-3.0.0.alpha.5.0.5.1
- google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
- ])
+ expect(the_bundle.locked_platforms).to include("java", "universal-darwin")
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(
+ "google-protobuf-3.0.0.alpha.5.0.5.1",
+ "google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin"
+ )
+ end
+ end
+ end
+
+ it "installs sorbet-static, which does not provide a pure ruby variant, in absence of a lockfile, just fine", :truffleruby do
+ skip "does not apply to Windows" if Gem.win_platform?
+
+ build_repo2 do
+ build_gem("sorbet-static", "0.5.6403") {|s| s.platform = Bundler.local_platform }
+ end
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "sorbet-static", "0.5.6403"
+ G
+
+ bundle "install --verbose"
+ end
+
+ it "installs sorbet-static, which does not provide a pure ruby variant, in presence of a lockfile, just fine", :truffleruby do
+ skip "does not apply to Windows" if Gem.win_platform?
+
+ build_repo2 do
+ build_gem("sorbet-static", "0.5.6403") {|s| s.platform = Bundler.local_platform }
+ end
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "sorbet-static", "0.5.6403"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ sorbet-static (0.5.6403-#{Bundler.local_platform})
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ sorbet-static (= 0.5.6403)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose"
+ end
+
+ it "does not resolve if the current platform does not match any of available platform specific variants for a top level dependency" do
+ build_repo4 do
+ build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "x86_64-linux" }
+ build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "universal-darwin-20" }
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet-static", "0.5.6433"
+ G
+
+ error_message = <<~ERROR.strip
+ Could not find gem 'sorbet-static (= 0.5.6433)' with platform 'arm64-darwin-21' in rubygems repository https://gem.repo4/ or installed locally.
+
+ The source contains the following gems matching 'sorbet-static (= 0.5.6433)':
+ * sorbet-static-0.5.6433-universal-darwin-20
+ * sorbet-static-0.5.6433-x86_64-linux
+ ERROR
+
+ simulate_platform "arm64-darwin-21" do
+ bundle "lock", raise_on_error: false
+ end
+
+ expect(err).to include(error_message).once
+
+ # Make sure it doesn't print error twice in verbose mode
+
+ simulate_platform "arm64-darwin-21" do
+ bundle "lock --verbose", raise_on_error: false
+ end
+
+ expect(err).to include(error_message).once
+ end
+
+ it "shows a platform mismatch hint when the current platform is not in the lockfile's platforms" do
+ build_repo4 do
+ build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "x86_64-linux-musl" }
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet-static", "0.5.6433"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet-static (0.5.6433-x86_64-linux-musl)
+
+ PLATFORMS
+ x86_64-linux-musl
+
+ DEPENDENCIES
+ sorbet-static (= 0.5.6433)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "install", raise_on_error: false
+ end
+
+ expect(err).to include("Your current platform (x86_64-linux) is not included in the lockfile's platforms (x86_64-linux-musl)")
+ expect(err).to include("bundle lock --add-platform x86_64-linux")
+ end
+
+ it "does not resolve if the current platform does not match any of available platform specific variants for a transitive dependency" do
+ build_repo4 do
+ build_gem("sorbet", "0.5.6433") {|s| s.add_dependency "sorbet-static", "= 0.5.6433" }
+ build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "x86_64-linux" }
+ build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "universal-darwin-20" }
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet", "0.5.6433"
+ G
+
+ error_message = <<~ERROR.strip
+ Could not find compatible versions
+
+ Because every version of sorbet depends on sorbet-static = 0.5.6433
+ and sorbet-static = 0.5.6433 could not be found in rubygems repository https://gem.repo4/ or installed locally for any resolution platforms (arm64-darwin-21),
+ sorbet cannot be used.
+ So, because Gemfile depends on sorbet = 0.5.6433,
+ version solving has failed.
+
+ The source contains the following gems matching 'sorbet-static (= 0.5.6433)':
+ * sorbet-static-0.5.6433-universal-darwin-20
+ * sorbet-static-0.5.6433-x86_64-linux
+ ERROR
+
+ simulate_platform "arm64-darwin-21" do
+ bundle "lock", raise_on_error: false
+ end
+
+ expect(err).to include(error_message).once
+
+ # Make sure it doesn't print error twice in verbose mode
+
+ simulate_platform "arm64-darwin-21" do
+ bundle "lock --verbose", raise_on_error: false
+ end
+
+ expect(err).to include(error_message).once
+ end
+
+ it "does not generate a lockfile if ruby platform is forced and some gem has no ruby variant available" do
+ build_repo4 do
+ build_gem("sorbet-static", "0.5.9889") {|s| s.platform = Gem::Platform.local }
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet-static", "0.5.9889"
+ G
+
+ bundle "lock", raise_on_error: false, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "true" }
+
+ expect(err).to include <<~ERROR.rstrip
+ Could not find gem 'sorbet-static (= 0.5.9889)' with platform 'ruby' in rubygems repository https://gem.repo4/ or installed locally.
+
+ The source contains the following gems matching 'sorbet-static (= 0.5.9889)':
+ * sorbet-static-0.5.9889-#{Gem::Platform.local}
+ ERROR
+ end
+
+ it "automatically fixes the lockfile if ruby platform is locked and some gem has no ruby variant available" do
+ build_repo4 do
+ build_gem("sorbet-static-and-runtime", "0.5.10160") do |s|
+ s.add_dependency "sorbet", "= 0.5.10160"
+ s.add_dependency "sorbet-runtime", "= 0.5.10160"
+ end
+
+ build_gem("sorbet", "0.5.10160") do |s|
+ s.add_dependency "sorbet-static", "= 0.5.10160"
+ end
+
+ build_gem("sorbet-runtime", "0.5.10160")
+
+ build_gem("sorbet-static", "0.5.10160") do |s|
+ s.platform = Gem::Platform.local
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet-static-and-runtime"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-runtime (0.5.10160)
+ sorbet-static (0.5.10160-#{Gem::Platform.local})
+ sorbet-static-and-runtime (0.5.10160)
+ sorbet (= 0.5.10160)
+ sorbet-runtime (= 0.5.10160)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ sorbet-static-and-runtime
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "sorbet", "0.5.10160"
+ c.checksum gem_repo4, "sorbet-runtime", "0.5.10160"
+ c.checksum gem_repo4, "sorbet-static", "0.5.10160", Gem::Platform.local
+ c.checksum gem_repo4, "sorbet-static-and-runtime", "0.5.10160"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-runtime (0.5.10160)
+ sorbet-static (0.5.10160-#{Gem::Platform.local})
+ sorbet-static-and-runtime (0.5.10160)
+ sorbet (= 0.5.10160)
+ sorbet-runtime (= 0.5.10160)
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ sorbet-static-and-runtime
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile if both ruby platform and a more specific platform are locked, and some gem has no ruby variant available" do
+ build_repo4 do
+ build_gem "nokogiri", "1.12.0"
+ build_gem "nokogiri", "1.12.0" do |s|
+ s.platform = "x86_64-darwin"
+ end
+
+ build_gem "nokogiri", "1.13.0"
+ build_gem "nokogiri", "1.13.0" do |s|
+ s.platform = "x86_64-darwin"
+ end
+
+ build_gem("sorbet-static", "0.5.10601") do |s|
+ s.platform = "x86_64-darwin"
+ end
+ end
+
+ simulate_platform "x86_64-darwin-22" do
+ install_gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+ end
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.13.0", "x86_64-darwin"
+ c.checksum gem_repo4, "sorbet-static", "0.5.10601", "x86_64-darwin"
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.12.0)
+ nokogiri (1.12.0-x86_64-darwin)
+ sorbet-static (0.5.10601-x86_64-darwin)
+
+ PLATFORMS
+ ruby
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-darwin-22" do
+ bundle "update --conservative nokogiri"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.13.0-x86_64-darwin)
+ sorbet-static (0.5.10601-x86_64-darwin)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile if only ruby platform is locked and some gem has no ruby variant available" do
+ build_repo4 do
+ build_gem("sorbet-static-and-runtime", "0.5.10160") do |s|
+ s.add_dependency "sorbet", "= 0.5.10160"
+ s.add_dependency "sorbet-runtime", "= 0.5.10160"
+ end
+
+ build_gem("sorbet", "0.5.10160") do |s|
+ s.add_dependency "sorbet-static", "= 0.5.10160"
+ end
+
+ build_gem("sorbet-runtime", "0.5.10160")
+
+ build_gem("sorbet-static", "0.5.10160") do |s|
+ s.platform = Gem::Platform.local
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet-static-and-runtime"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-runtime (0.5.10160)
+ sorbet-static (0.5.10160-#{Gem::Platform.local})
+ sorbet-static-and-runtime (0.5.10160)
+ sorbet (= 0.5.10160)
+ sorbet-runtime (= 0.5.10160)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ sorbet-static-and-runtime
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "sorbet", "0.5.10160"
+ c.checksum gem_repo4, "sorbet-runtime", "0.5.10160"
+ c.checksum gem_repo4, "sorbet-static", "0.5.10160", Gem::Platform.local
+ c.checksum gem_repo4, "sorbet-static-and-runtime", "0.5.10160"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-runtime (0.5.10160)
+ sorbet-static (0.5.10160-#{Gem::Platform.local})
+ sorbet-static-and-runtime (0.5.10160)
+ sorbet (= 0.5.10160)
+ sorbet-runtime (= 0.5.10160)
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ sorbet-static-and-runtime
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile when adding a gem that introduces dependencies with no ruby platform variants transitively" do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.18.2"
+
+ build_gem "nokogiri", "1.18.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem("sorbet", "0.5.11835") do |s|
+ s.add_dependency "sorbet-static", "= 0.5.11835"
+ end
+
+ build_gem "sorbet-static", "0.5.11835" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ gem "sorbet"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.18.2)
+ nokogiri (1.18.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.18.2", "x86_64-linux"
+ c.checksum gem_repo4, "sorbet", "0.5.11835"
+ c.checksum gem_repo4, "sorbet-static", "0.5.11835", "x86_64-linux"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.18.2)
+ nokogiri (1.18.2-x86_64-linux)
+ sorbet (0.5.11835)
+ sorbet-static (= 0.5.11835)
+ sorbet-static (0.5.11835-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sorbet
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "automatically fixes the lockfile if multiple platforms locked, but no valid versions of direct dependencies for all of them" do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "arm-linux"
+ end
+
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.0-arm-linux)
+ nokogiri (1.14.0-x86_64-linux)
+ sorbet-static (0.5.10696-x86_64-linux)
+
+ PLATFORMS
+ aarch64-linux
+ arm-linux
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.0", "x86_64-linux"
+ c.checksum gem_repo4, "sorbet-static", "0.5.10696", "x86_64-linux"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.0-x86_64-linux)
+ sorbet-static (0.5.10696-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "automatically fixes the lockfile without removing other variants if it's missing platform gems, but they are installed locally" do
+ simulate_platform "x86_64-darwin-21" do
+ build_repo4 do
+ build_gem("sorbet-static", "0.5.10549") do |s|
+ s.platform = "universal-darwin-20"
+ end
+
+ build_gem("sorbet-static", "0.5.10549") do |s|
+ s.platform = "universal-darwin-21"
+ end
+ end
+
+ # Make sure sorbet-static-0.5.10549-universal-darwin-21 is installed
+ install_gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet-static", "= 0.5.10549"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "sorbet-static", "0.5.10549", "universal-darwin-20"
+ c.checksum gem_repo4, "sorbet-static", "0.5.10549", "universal-darwin-21"
+ end
+
+ # Make sure the lockfile is missing sorbet-static-0.5.10549-universal-darwin-21
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet-static (0.5.10549-universal-darwin-20)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ sorbet-static (= 0.5.10549)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet-static (0.5.10549-universal-darwin-20)
+ sorbet-static (0.5.10549-universal-darwin-21)
+
+ PLATFORMS
+ x86_64-darwin
+
+ DEPENDENCIES
+ sorbet-static (= 0.5.10549)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "automatically fixes the lockfile if locked only to ruby, and some locked specs don't meet locked dependencies" do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem("ibandit", "0.7.0") do |s|
+ s.add_dependency "i18n", "~> 0.7.0"
+ end
+
+ build_gem("i18n", "0.7.0.beta1")
+ build_gem("i18n", "0.7.0")
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "ibandit", "~> 0.7.0"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ i18n (0.7.0.beta1)
+ ibandit (0.7.0)
+ i18n (~> 0.7.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ ibandit (~> 0.7.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update i18n"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ i18n (0.7.0)
+ ibandit (0.7.0)
+ i18n (~> 0.7.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ ibandit (~> 0.7.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "does not remove ruby if gems for other platforms, and not present in the lockfile, exist in the Gemfile" do
+ build_repo4 do
+ build_gem "nokogiri", "1.13.8"
+ build_gem "nokogiri", "1.13.8" do |s|
+ s.platform = Gem::Platform.local
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+
+ gem "tzinfo", "~> 1.2", platform: :#{not_local_tag}
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.13.8"
+ c.checksum gem_repo4, "nokogiri", "1.13.8", Gem::Platform.local
+ end
+
+ original_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.13.8)
+ nokogiri (1.13.8-#{Gem::Platform.local})
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ nokogiri
+ tzinfo (~> 1.2)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile original_lockfile
+
+ bundle "lock --update"
+
+ expect(lockfile).to eq(original_lockfile)
+ end
+
+ it "does not remove ruby if gems for other platforms, and not present in the lockfile, exist in the Gemfile, and the lockfile only has ruby" do
+ build_repo4 do
+ build_gem "nokogiri", "1.13.8"
+ build_gem "nokogiri", "1.13.8" do |s|
+ s.platform = "arm64-darwin"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+
+ gem "tzinfo", "~> 1.2", platforms: %i[windows jruby]
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.13.8"
+ end
+
+ original_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.13.8)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ nokogiri
+ tzinfo (~> 1.2)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile original_lockfile
+
+ simulate_platform "arm64-darwin-23" do
+ bundle "lock --update"
+ end
+
+ expect(lockfile).to eq(original_lockfile)
+ end
+
+ it "does not remove ruby when adding a new gem to the Gemfile" do
+ build_repo4 do
+ build_gem "concurrent-ruby", "1.2.2"
+ build_gem "myrack", "3.0.7"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "concurrent-ruby"
+ gem "myrack"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "concurrent-ruby", "1.2.2"
+ c.checksum gem_repo4, "myrack", "3.0.7"
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ concurrent-ruby (1.2.2)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ concurrent-ruby
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ concurrent-ruby (1.2.2)
+ myrack (3.0.7)
+
+ PLATFORMS
+ #{lockfile_platforms(generic_default_locked_platform || local_platform, defaults: ["ruby"])}
+
+ DEPENDENCIES
+ concurrent-ruby
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "can fallback to a source gem when platform gems are incompatible with current ruby version" do
+ setup_multiplatform_gem_with_source_gem
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "my-precompiled-gem"
+ G
+
+ # simulate lockfile which includes both a precompiled gem with:
+ # - Gem the current platform (with incompatible ruby version)
+ # - A source gem with compatible ruby version
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ my-precompiled-gem (3.0.0)
+ my-precompiled-gem (3.0.0-#{Bundler.local_platform})
+
+ PLATFORMS
+ ruby
+ #{Bundler.local_platform}
+
+ DEPENDENCIES
+ my-precompiled-gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install
+ end
+
+ it "automatically adds the ruby variant to the lockfile if the specific platform is locked and we move to a newer ruby version for which a native package is not available" do
+ #
+ # Given an existing application using native gems (e.g., nokogiri)
+ # And a lockfile generated with a stable ruby version
+ # When want test the application against ruby-head and `bundle install`
+ # Then bundler should fall back to the generic ruby platform gem
+ #
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0"
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri", "1.14.0"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.0", "x86_64-linux"
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.0-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri (= 1.14.0)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle :install
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.0"
+ c.checksum gem_repo4, "nokogiri", "1.14.0", "x86_64-linux"
+ end
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.0)
+ nokogiri (1.14.0-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri (= 1.14.0)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "automatically fixes the lockfile when only ruby platform locked, and adding a dependency with subdependencies not valid for ruby" do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem("sorbet", "0.5.10160") do |s|
+ s.add_dependency "sorbet-static", "= 0.5.10160"
+ end
+
+ build_gem("sorbet-static", "0.5.10160") do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet (0.5.10160)
+ sorbet-static (= 0.5.10160)
+ sorbet-static (0.5.10160-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ sorbet
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "locks specific platforms automatically" do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0"
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "arm-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x64-mingw-ucrt"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "java"
+ end
+
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "universal-darwin-22"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ bundle "lock"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.14.0"
+ c.checksum gem_repo4, "nokogiri", "1.14.0", "arm-linux"
+ c.checksum gem_repo4, "nokogiri", "1.14.0", "x86_64-linux"
+ end
+
+ # locks all compatible platforms, excluding Java and Windows
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.0)
+ nokogiri (1.14.0-arm-linux)
+ nokogiri (1.14.0-x86_64-linux)
+
+ PLATFORMS
+ arm-linux
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+
+ FileUtils.rm bundled_app_lock
+
+ bundle "lock"
+
+ checksums.delete "nokogiri", "arm-linux"
+ checksums.checksum gem_repo4, "sorbet-static", "0.5.10696", "universal-darwin-22"
+ checksums.checksum gem_repo4, "sorbet-static", "0.5.10696", "x86_64-linux"
+
+ # locks only platforms compatible with all gems in the bundle
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.0)
+ nokogiri (1.14.0-x86_64-linux)
+ sorbet-static (0.5.10696-universal-darwin-22)
+ sorbet-static (0.5.10696-x86_64-linux)
+
+ PLATFORMS
+ universal-darwin-22
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "does not fail when a platform variant is incompatible with the current ruby and another equivalent platform specific variant is part of the resolution" do
+ build_repo4 do
+ build_gem "nokogiri", "1.15.5"
+
+ build_gem "nokogiri", "1.15.5" do |s|
+ s.platform = "x86_64-linux"
+ s.required_ruby_version = "< #{current_ruby_minor}.dev"
+ end
+
+ build_gem "sass-embedded", "1.69.5"
+
+ build_gem "sass-embedded", "1.69.5" do |s|
+ s.platform = "x86_64-linux-gnu"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ gem "sass-embedded"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.15.5"
+ c.checksum gem_repo4, "sass-embedded", "1.69.5"
+ c.checksum gem_repo4, "sass-embedded", "1.69.5", "x86_64-linux-gnu"
+ end
+
+ simulate_platform "x86_64-linux" do
+ bundle "install --verbose"
+
+ # locks all compatible platforms, excluding Java and Windows
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.15.5)
+ sass-embedded (1.69.5)
+ sass-embedded (1.69.5-x86_64-linux-gnu)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sass-embedded
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "does not add ruby platform gem if it brings extra dependencies not resolved originally" do
+ build_repo4 do
+ build_gem "nokogiri", "1.15.5" do |s|
+ s.add_dependency "mini_portile2", "~> 2.8.2"
+ end
+
+ build_gem "nokogiri", "1.15.5" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.15.5", "x86_64-linux"
+ end
+
+ simulate_platform "x86_64-linux" do
+ bundle "install --verbose"
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.15.5-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ ["x86_64-linux", "x86_64-linux-musl"].each do |host_platform|
+ describe "on host platform #{host_platform}" do
+ it "adds current musl platform" do
+ build_repo4 do
+ build_gem "rcee_precompiled", "0.5.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem "rcee_precompiled", "0.5.0" do |s|
+ s.platform = "x86_64-linux-musl"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "rcee_precompiled", "0.5.0"
+ G
+
+ simulate_platform host_platform do
+ bundle "lock"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "rcee_precompiled", "0.5.0", "x86_64-linux"
+ c.checksum gem_repo4, "rcee_precompiled", "0.5.0", "x86_64-linux-musl"
+ end
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ rcee_precompiled (0.5.0-x86_64-linux)
+ rcee_precompiled (0.5.0-x86_64-linux-musl)
+
+ PLATFORMS
+ x86_64-linux
+ x86_64-linux-musl
+
+ DEPENDENCIES
+ rcee_precompiled (= 0.5.0)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+ end
+ end
+
+ it "adds current musl platform, when there are also gnu variants" do
+ build_repo4 do
+ build_gem "rcee_precompiled", "0.5.0" do |s|
+ s.platform = "x86_64-linux-gnu"
+ end
+
+ build_gem "rcee_precompiled", "0.5.0" do |s|
+ s.platform = "x86_64-linux-musl"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "rcee_precompiled", "0.5.0"
+ G
+
+ simulate_platform "x86_64-linux-musl" do
+ bundle "lock"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "rcee_precompiled", "0.5.0", "x86_64-linux-gnu"
+ c.checksum gem_repo4, "rcee_precompiled", "0.5.0", "x86_64-linux-musl"
+ end
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ rcee_precompiled (0.5.0-x86_64-linux-gnu)
+ rcee_precompiled (0.5.0-x86_64-linux-musl)
+
+ PLATFORMS
+ x86_64-linux-gnu
+ x86_64-linux-musl
+
+ DEPENDENCIES
+ rcee_precompiled (= 0.5.0)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "does not add current platform if there's an equivalent less specific platform among the ones resolved" do
+ build_repo4 do
+ build_gem "rcee_precompiled", "0.5.0" do |s|
+ s.platform = "universal-darwin"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "rcee_precompiled", "0.5.0"
+ G
+
+ simulate_platform "x86_64-darwin-15" do
+ bundle "lock"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "rcee_precompiled", "0.5.0", "universal-darwin"
+ end
+
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ rcee_precompiled (0.5.0-universal-darwin)
+
+ PLATFORMS
+ universal-darwin
+
+ DEPENDENCIES
+ rcee_precompiled (= 0.5.0)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "does not re-resolve when a specific platform, but less specific than the current platform, is locked" do
+ build_repo4 do
+ build_gem "nokogiri"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.0)
+
+ PLATFORMS
+ arm64-darwin
+
+ DEPENDENCIES
+ nokogiri!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "arm64-darwin-23" do
+ bundle "install --verbose"
+
+ expect(out).to include("Found no changes, using resolution from the lockfile")
+ end
+ end
+
+ it "does not remove generic platform gems locked for a specific platform from lockfile when unlocking an unrelated gem" do
+ build_repo4 do
+ build_gem "ffi"
+
+ build_gem "ffi" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem "nokogiri"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "ffi"
+ gem "nokogiri"
+ G
+
+ original_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ ffi (1.0)
+ nokogiri (1.0)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ ffi
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile original_lockfile
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --update nokogiri"
+
+ expect(lockfile).to eq(original_lockfile)
+ end
+ end
+
+ it "does not remove generic platform gems locked for a specific platform from lockfile when unlocking an unrelated gem, and variants for other platform also locked" do
+ build_repo4 do
+ build_gem "ffi"
+
+ build_gem "ffi" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem "ffi" do |s|
+ s.platform = "java"
+ end
+
+ build_gem "nokogiri"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "ffi"
+ gem "nokogiri"
+ G
+
+ original_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ ffi (1.0)
+ ffi (1.0-java)
+ nokogiri (1.0)
+
+ PLATFORMS
+ java
+ x86_64-linux
+
+ DEPENDENCIES
+ ffi
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile original_lockfile
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --update nokogiri"
+
+ expect(lockfile).to eq(original_lockfile)
+ end
+ end
+
+ it "does not remove platform specific gems from lockfile when using a ruby version that does not match their ruby requirements, since they may be useful in other rubies" do
+ build_repo4 do
+ build_gem("google-protobuf", "3.25.5")
+ build_gem("google-protobuf", "3.25.5") do |s|
+ s.required_ruby_version = "< #{current_ruby_minor}.dev"
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "google-protobuf", "~> 3.0"
+ G
+
+ original_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ google-protobuf (3.25.5)
+ google-protobuf (3.25.5-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ google-protobuf (~> 3.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile original_lockfile
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --update"
+ end
+
+ expect(lockfile).to eq(original_lockfile)
+ end
+
+ private
+
+ def setup_multiplatform_gem
+ build_repo2 do
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1")
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x64-mingw-ucrt" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "universal-darwin" }
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x64-mingw-ucrt" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5")
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "universal-darwin" }
+
+ build_gem("google-protobuf", "3.0.0.alpha.4.0")
+ build_gem("google-protobuf", "3.0.0.alpha.3.1.pre")
+ end
+ end
+
+ def setup_multiplatform_gem_with_different_dependencies_per_platform
+ build_repo2 do
+ build_gem("facter", "2.4.6")
+ build_gem("facter", "2.4.6") do |s|
+ s.platform = "universal-darwin"
+ s.add_dependency "CFPropertyList"
+ end
+ build_gem("CFPropertyList")
+ end
+ end
+
+ def setup_multiplatform_gem_with_source_gem
+ build_repo2 do
+ build_gem("my-precompiled-gem", "3.0.0")
+ build_gem("my-precompiled-gem", "3.0.0") do |s|
+ s.platform = Bundler.local_platform
+
+ # purposely unresolvable
+ s.required_ruby_version = ">= 1000.0.0"
end
end
end
diff --git a/spec/bundler/install/gemfile_spec.rb b/spec/bundler/install/gemfile_spec.rb
index ffbb2e3a61..83875a3d0e 100644
--- a/spec/bundler/install/gemfile_spec.rb
+++ b/spec/bundler/install/gemfile_spec.rb
@@ -3,7 +3,9 @@
RSpec.describe "bundle install" do
context "with duplicated gems" do
it "will display a warning" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+
gem 'rails', '~> 4.0.0'
gem 'rails', '~> 4.0.0'
G
@@ -14,58 +16,134 @@ RSpec.describe "bundle install" do
context "with --gemfile" do
it "finds the gemfile" do
gemfile bundled_app("NotGemfile"), <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
- bundle :install, :gemfile => bundled_app("NotGemfile")
+ bundle :install, gemfile: bundled_app("NotGemfile")
# Specify BUNDLE_GEMFILE for `the_bundle`
# to retrieve the proper Gemfile
ENV["BUNDLE_GEMFILE"] = "NotGemfile"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "respects lockfile and BUNDLE_LOCKFILE" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ lockfile "ReallyNotGemfile.lock"
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+
+ bundle :install, gemfile: bundled_app("NotGemfile")
+
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ ENV["BUNDLE_LOCKFILE"] = "ReallyNotGemfile.lock"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "respects BUNDLE_LOCKFILE during bundle install" do
+ ENV["BUNDLE_LOCKFILE"] = "ReallyNotGemfile.lock"
+
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+
+ bundle :install, gemfile: bundled_app("NotGemfile")
+ expect(bundled_app("ReallyNotGemfile.lock")).to exist
+
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
context "with gemfile set via config" do
before do
gemfile bundled_app("NotGemfile"), <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
- bundle "config set --local gemfile #{bundled_app("NotGemfile")}"
+ bundle_config "gemfile #{bundled_app("NotGemfile")}"
end
it "uses the gemfile to install" do
bundle "install"
bundle "list"
- expect(out).to include("rack (1.0.0)")
+ expect(out).to include("myrack (1.0.0)")
end
it "uses the gemfile while in a subdirectory" do
bundled_app("subdir").mkpath
- bundle "install", :dir => bundled_app("subdir")
- bundle "list", :dir => bundled_app("subdir")
+ bundle "install", dir: bundled_app("subdir")
+ bundle "list", dir: bundled_app("subdir")
- expect(out).to include("rack (1.0.0)")
+ expect(out).to include("myrack (1.0.0)")
end
end
- context "with deprecated features" do
- it "reports that lib is an invalid option" do
- gemfile <<-G
- gem "rack", :lib => "rack"
- G
+ it "reports that lib is an invalid option" do
+ gemfile <<-G
+ source "https://gem.repo1"
- bundle :install, :raise_on_error => false
- expect(err).to match(/You passed :lib as an option for gem 'rack', but it is invalid/)
+ gem "myrack", :lib => "myrack"
+ G
+
+ bundle :install, raise_on_error: false
+ expect(err).to match(/You passed :lib as an option for gem 'myrack', but it is invalid/)
+ end
+
+ it "reports that type is an invalid option" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "myrack", :type => "development"
+ G
+
+ bundle :install, raise_on_error: false
+ expect(err).to match(/You passed :type as an option for gem 'myrack', but it is invalid/)
+ end
+
+ it "reports that gemfile is an invalid option" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "myrack", :gemfile => "foo"
+ G
+
+ bundle :install, raise_on_error: false
+ expect(err).to match(/You passed :gemfile as an option for gem 'myrack', but it is invalid/)
+ end
+
+ context "when an internal error happens" do
+ let(:bundler_bug) do
+ create_file("bundler_bug.rb", <<~RUBY)
+ require "bundler"
+
+ module Bundler
+ class Dsl
+ def source(source, *args, &blk)
+ nil.name
+ end
+ end
+ end
+ RUBY
+
+ bundled_app("bundler_bug.rb").to_s
+ end
+
+ it "shows culprit file and line" do
+ skip "ruby-core test setup has always \"lib\" in $LOAD_PATH so `require \"bundler\"` always activates the local version rather than using RubyGems gem activation stuff, causing conflicts" if ruby_core?
+
+ install_gemfile "source 'https://gem.repo1'", requires: [bundler_bug], artifice: nil, raise_on_error: false
+ expect(err).to include("bundler_bug.rb:6")
end
end
- context "with engine specified in symbol", :jruby do
+ context "with engine specified in symbol", :jruby_only do
it "does not raise any error parsing Gemfile" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
ruby "#{RUBY_VERSION}", :engine => :jruby, :engine_version => "#{RUBY_ENGINE_VERSION}"
G
@@ -74,18 +152,20 @@ RSpec.describe "bundle install" do
it "installation succeeds" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
ruby "#{RUBY_VERSION}", :engine => :jruby, :engine_version => "#{RUBY_ENGINE_VERSION}"
- gem "rack"
+ gem "myrack"
G
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
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 "https://gem.repo1"
+
str = "Il était une fois ..."
puts "The source encoding is: " + str.encoding.name
G
@@ -99,6 +179,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 "https://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 6e5177c60d..9db73b84b5 100644
--- a/spec/bundler/install/gems/compact_index_spec.rb
+++ b/spec/bundler/install/gems/compact_index_spec.rb
@@ -7,12 +7,27 @@ RSpec.describe "compact index api" do
it "should use the API" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "compact_index"
+ 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"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "has a debug mode" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "myrack"
+ G
+
+ bundle :install, artifice: "compact_index", env: { "DEBUG_COMPACT_INDEX" => "true" }
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(err).to include("[Bundler::CompactIndexClient] available?")
+ expect(err).to include("[Bundler::CompactIndexClient] fetching versions")
+ expect(err).to include("[Bundler::CompactIndexClient] info(myrack)")
+ expect(err).to include("[Bundler::CompactIndexClient] fetching info/myrack")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "should URI encode gem names" do
@@ -21,7 +36,7 @@ RSpec.describe "compact index api" do
gem " sinatra"
G
- bundle :install, :artifice => "compact_index", :raise_on_error => false
+ bundle :install, artifice: "compact_index", raise_on_error: false
expect(err).to include("' sinatra' is not a valid gem name because it contains whitespace.")
end
@@ -31,7 +46,7 @@ RSpec.describe "compact index api" do
gem "rails"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems(
"rails 2.3.2",
@@ -44,23 +59,23 @@ RSpec.describe "compact index api" do
end
it "should handle case sensitivity conflicts" do
- build_repo4 do
- build_gem "rack", "1.0" do |s|
- s.add_runtime_dependency("Rack", "0.1")
+ build_repo4(build_compact_index: false) do
+ build_gem "myrack", "1.0" do |s|
+ s.add_dependency("Myrack", "0.1")
end
- build_gem "Rack", "0.1"
+ build_gem "Myrack", "0.1"
end
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
source "#{source_uri}"
- gem "rack", "1.0"
- gem "Rack", "0.1"
+ gem "myrack", "1.0"
+ gem "Myrack", "0.1"
G
# can't use `include_gems` here since the `require` will conflict on a
# case-insensitive FS
- run "Bundler.require; puts Gem.loaded_specs.values_at('rack', 'Rack').map(&:full_name)"
- expect(out).to eq("rack-1.0\nRack-0.1")
+ run "Bundler.require; puts Gem.loaded_specs.values_at('myrack', 'Myrack').map(&:full_name)"
+ expect(out).to eq("myrack-1.0\nMyrack-0.1")
end
it "should handle multiple gem dependencies on the same gem" do
@@ -69,22 +84,21 @@ RSpec.describe "compact index api" do
gem "net-sftp"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems "net-sftp 1.1.1"
end
it "should use the endpoint when using deployment mode" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
- bundle "config --local deployment true"
- bundle "config --local path vendor/bundle"
- bundle :install, :artifice => "compact_index"
+ bundle_config "deployment true"
+ 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"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "handles git dependencies that are in rubygems" do
@@ -95,12 +109,12 @@ RSpec.describe "compact index api" do
gemfile <<-G
source "#{source_uri}"
- git "#{file_uri_for(lib_path("foo-1.0"))}" do
+ git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("rails 2.3.2")
end
@@ -113,13 +127,13 @@ RSpec.describe "compact index api" do
gemfile <<-G
source "#{source_uri}"
- gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index"
- bundle "config --local deployment true"
- bundle :install, :artifice => "compact_index"
+ bundle_config "deployment true"
+ bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("rails 2.3.2")
end
@@ -128,52 +142,58 @@ RSpec.describe "compact index api" do
build_git "foo"
gemfile <<-G
source "#{source_uri}"
- gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
G
- bundle "install", :artifice => "compact_index"
- bundle "config --local deployment true"
- bundle :install, :artifice => "compact_index"
+ bundle "install", artifice: "compact_index"
+ bundle_config "deployment true"
+ bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("foo 1.0")
end
- it "falls back when the API errors out" do
- simulate_platform mswin
-
+ it "falls back when the API URL returns 403 Forbidden" do
gemfile <<-G
source "#{source_uri}"
- gem "rcov"
+ gem "myrack"
G
- bundle :install, :artifice => "windows"
- expect(out).to include("Fetching source index from #{source_uri}")
- expect(the_bundle).to include_gems "rcov 1.0.0"
+ bundle :install, verbose: true, artifice: "compact_index_forbidden"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "falls back when the API URL returns 403 Forbidden" do
+ it "falls back when the versions endpoint has a checksum mismatch" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :verbose => true, :artifice => "compact_index_forbidden"
+ bundle :install, verbose: true, artifice: "compact_index_checksum_mismatch"
expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(out).to include("The checksum of /versions does not match the checksum provided by the server!")
+ expect(out).to include("Calculated checksums #{{ "sha-256" => "8KfZiM/fszVkqhP/m5s9lvE6M9xKu4I1bU4Izddp5Ms=" }.inspect} did not match expected #{{ "sha-256" => "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=" }.inspect}")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "falls back when the versions endpoint has a checksum mismatch" do
+ it "shows proper path when permission errors happen", :permissions do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :verbose => true, :artifice => "compact_index_checksum_mismatch"
- expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(out).to include <<-'WARN'
-The checksum of /versions does not match the checksum provided by the server! Something is wrong (local checksum is "\"d41d8cd98f00b204e9800998ecf8427e\"", was expecting "\"123\"").
- WARN
- expect(the_bundle).to include_gems "rack 1.0.0"
+ versions = compact_index_cache_path.join(
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
+ )
+ versions.dirname.mkpath
+ versions.write("created_at")
+ FileUtils.chmod("-r", versions)
+
+ bundle :install, artifice: "compact_index", raise_on_error: false
+
+ expect(err).to include(
+ "There was an error while trying to read from `#{versions}`. It is likely that you need to grant read permissions for that path."
+ )
end
it "falls back when the user's home directory does not exist or is not writable" do
@@ -181,28 +201,28 @@ The checksum of /versions does not match the checksum provided by the server! So
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "compact_index"
+ 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"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "handles host redirects" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "compact_index_host_redirect"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle :install, artifice: "compact_index_host_redirect"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "handles host redirects without Net::HTTP::Persistent" do
+ it "handles host redirects without Gem::Net::HTTP::Persistent" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
FileUtils.mkdir_p lib_path
@@ -218,18 +238,18 @@ The checksum of /versions does not match the checksum provided by the server! So
H
end
- bundle :install, :artifice => "compact_index_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ bundle :install, artifice: "compact_index_host_redirect", requires: [lib_path("disable_net_http_persistent.rb")]
expect(out).to_not match(/Too many redirects/)
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "times out when Bundler::Fetcher redirects too much" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "compact_index_redirects", :raise_on_error => false
+ bundle :install, artifice: "compact_index_redirects", raise_on_error: false
expect(err).to match(/Too many redirects/)
end
@@ -237,69 +257,74 @@ The checksum of /versions does not match the checksum provided by the server! So
it "should use the modern index for install" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle "install --full-index", :artifice => "compact_index"
+ bundle "install --full-index", artifice: "compact_index"
expect(out).to include("Fetching source index from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "should use the modern index for update" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle "update --full-index", :artifice => "compact_index", :all => true
+ bundle "update --full-index", artifice: "compact_index", all: true
expect(out).to include("Fetching source index from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
it "does not double check for gems that are only installed locally" do
- system_gems %w[rack-1.0.0 thin-1.0 net_a-1.0]
- bundle "config set --local path.system true"
- ENV["BUNDLER_SPEC_ALL_REQUESTS"] = strip_whitespace(<<-EOS).strip
- #{source_uri}/versions
- #{source_uri}/info/rack
- EOS
-
- install_gemfile <<-G, :artifice => "compact_index", :verbose => true
- source "#{source_uri}"
- gem "rack"
- G
-
- expect(last_command.stdboth).not_to include "Double checking"
- end
-
- it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 3" do
build_repo2 do
- build_gem "back_deps" do |s|
- s.add_dependency "foo"
+ build_gem "net_a" do |s|
+ s.add_dependency "net_b"
+ s.add_dependency "net_build_extensions"
+ end
+
+ build_gem "net_b"
+
+ build_gem "net_build_extensions" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("lib", __dir__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/net_build_extensions.rb", "w") do |f|
+ f.puts "NET_BUILD_EXTENSIONS = 'YES'"
+ end
+ end
+ RUBY
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
- gemfile <<-G
+ system_gems %w[myrack-1.0.0 thin-1.0 net_a-1.0], gem_repo: gem_repo2
+ bundle_config "path.system true"
+ ENV["BUNDLER_SPEC_ALL_REQUESTS"] = <<~EOS.strip
+ #{source_uri}/versions
+ #{source_uri}/info/myrack
+ EOS
+
+ install_gemfile <<-G, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
source "#{source_uri}"
- source "#{source_uri}/extra"
- gem "back_deps"
+ gem "myrack"
G
- bundle :install, :artifice => "compact_index_extra"
- expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
+ expect(stdboth).not_to include "Double checking"
end
- it "fetches again when more dependencies are found in subsequent sources with source blocks" do
+ it "fetches again when more dependencies are found in subsequent sources" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
- install_gemfile <<-G, :artifice => "compact_index_extra", :verbose => true
+ install_gemfile <<-G, artifice: "compact_index_extra", verbose: true
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -312,55 +337,33 @@ The checksum of /versions does not match the checksum provided by the server! So
it "fetches gem versions even when those gems are already installed" do
gemfile <<-G
source "#{source_uri}"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
G
- bundle :install, :artifice => "compact_index_extra_api"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle :install, artifice: "compact_index_extra_api"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
build_repo4 do
- build_gem "rack", "1.2" do |s|
- s.executables = "rackup"
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
end
end
gemfile <<-G
source "#{source_uri}" do; end
source "#{source_uri}/extra"
- gem "rack", "1.2"
- G
- bundle :install, :artifice => "compact_index_extra_api"
- expect(the_bundle).to include_gems "rack 1.2"
- end
-
- it "considers all possible versions of dependencies from all api gem sources", :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"
- gem 'somegem', '1.0.0'
+ gem "myrack", "1.2"
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"
+ bundle :install, artifice: "compact_index_extra_api"
+ expect(the_bundle).to include_gems "myrack 1.2"
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.
+ it "resolves indirect dependencies to the most scoped source that includes them" 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 tries to
+ # use the version in the most scoped source, even if not compatible, and
+ # gives a resolution error
build_repo4 do
build_gem "activesupport", "1.2.0"
build_gem "somegem", "1.0.0" do |s|
@@ -375,10 +378,9 @@ The checksum of /versions does not match the checksum provided by the server! So
end
G
- bundle :install, :artifice => "compact_index_extra_api"
+ bundle :install, artifice: "compact_index_extra_api", raise_on_error: false
- expect(the_bundle).to include_gems "somegem 1.0.0"
- expect(the_bundle).to include_gems "activesupport 1.2.3"
+ expect(err).to include("Could not find compatible versions")
end
it "prints API output properly with back deps" do
@@ -386,7 +388,7 @@ The checksum of /versions does not match the checksum provided by the server! So
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
@@ -396,25 +398,23 @@ The checksum of /versions does not match the checksum provided by the server! So
end
G
- bundle :install, :artifice => "compact_index_extra"
+ bundle :install, artifice: "compact_index_extra"
expect(out).to include("Fetching gem metadata from http://localgemserver.test/")
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
end
- it "does not fetch every spec if the index of gems is large when doing back deps" do
+ it "does not fetch every spec when doing back deps" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
build_gem "missing"
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
- 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"
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -424,19 +424,17 @@ The checksum of /versions does not match the checksum provided by the server! So
expect(the_bundle).to include_gems "back_deps 1.0"
end
- it "does not fetch every spec if the index of gems is large when doing back deps & everything is the compact index" do
+ it "does not fetch every spec when doing back deps & everything is the compact index" do
build_repo4 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
build_gem "missing"
- FileUtils.rm_rf Dir[gem_repo4("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo4("gems/foo-*.gem")]
end
- 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"
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
@@ -453,36 +451,16 @@ The checksum of /versions does not match the checksum provided by the server! So
gem 'foo'
G
- bundle :install, :artifice => "compact_index_api_missing"
+ bundle :install, artifice: "compact_index_api_missing"
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" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
- end
-
- gemfile <<-G
- source "#{source_uri}"
- source "#{source_uri}/extra"
- gem "back_deps"
- G
-
- bundle :install, :artifice => "compact_index_extra"
-
- bundle "install --deployment", :artifice => "compact_index_extra"
- expect(the_bundle).to include_gems "back_deps 1.0"
- end
-
- it "fetches again when more dependencies are found in subsequent sources using deployment mode with blocks" do
- build_repo2 do
- build_gem "back_deps" do |s|
- s.add_dependency "foo"
- end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
@@ -492,95 +470,55 @@ The checksum of /versions does not match the checksum provided by the server! So
end
G
- bundle :install, :artifice => "compact_index_extra"
- bundle "config --local deployment true"
- bundle :install, :artifice => "compact_index_extra"
+ bundle :install, artifice: "compact_index_extra"
+ bundle_config "deployment true"
+ bundle :install, artifice: "compact_index_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
it "does not refetch if the only unmet dependency is bundler" do
+ build_repo2 do
+ build_gem "bundler_dep" do |s|
+ s.add_dependency "bundler"
+ end
+ end
+
gemfile <<-G
source "#{source_uri}"
gem "bundler_dep"
G
- bundle :install, :artifice => "compact_index"
+ bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
- it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
- sudo "mkdir -p #{system_gem_path("bin")}"
- sudo "chown -R root #{system_gem_path("bin")}"
-
- gemfile <<-G
- source "#{source_uri}"
- gem "rails"
- G
- bundle :install, :artifice => "compact_index"
- expect(the_bundle).to include_gems "rails 2.3.2"
- end
-
- it "installs the binstubs", :bundler => "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "rack"
- G
-
- bundle "install --binstubs", :artifice => "compact_index"
-
- gembin "rackup"
- expect(out).to eq("1.0.0")
- end
-
- it "installs the bins when using --path and uses autoclean", :bundler => "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "rack"
- G
-
- bundle "install --path vendor/bundle", :artifice => "compact_index"
-
- expect(vendored_gems("bin/rackup")).to exist
- end
-
- it "installs the bins when using --path and uses bundle clean", :bundler => "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "rack"
- G
-
- bundle "install --path vendor/bundle --no-clean", :artifice => "compact_index"
-
- expect(vendored_gems("bin/rackup")).to exist
- end
-
it "prints post_install_messages" do
gemfile <<-G
source "#{source_uri}"
- gem 'rack-obama'
+ gem 'myrack-obama'
G
- bundle :install, :artifice => "compact_index"
- expect(out).to include("Post-install message from rack:")
+ bundle :install, artifice: "compact_index"
+ expect(out).to include("Post-install message from myrack:")
end
it "should display the post install message for a dependency" do
gemfile <<-G
source "#{source_uri}"
- gem 'rack_middleware'
+ gem 'myrack_middleware'
G
- bundle :install, :artifice => "compact_index"
- expect(out).to include("Post-install message from rack:")
- expect(out).to include("Rack's post install message")
+ bundle :install, artifice: "compact_index"
+ expect(out).to include("Post-install message from myrack:")
+ expect(out).to include("Myrack's post install message")
end
context "when using basic authentication" do
let(:user) { "user" }
let(:password) { "pass" }
let(:basic_auth_source_uri) do
- uri = Bundler::URI.parse(source_uri)
+ uri = Gem::URI.parse(source_uri)
uri.user = user
uri.password = password
@@ -590,114 +528,100 @@ The checksum of /versions does not match the checksum provided by the server! So
it "passes basic authentication details and strips out creds" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "compact_index_basic_authentication"
+ bundle :install, artifice: "compact_index_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "strips http basic authentication creds for modern index" do
+ it "passes basic authentication details and strips out creds also in verbose mode" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endopint_marshal_fail_basic_authentication"
+ 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 it can't reach the server" do
- gemfile <<-G
- source "#{basic_auth_source_uri}"
- gem "rack"
- G
-
- bundle :install, :artifice => "endpoint_500", :raise_on_error => false
- expect(out).not_to include("#{user}:#{password}")
- end
-
- it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 3" do
- gemfile <<-G
- source "#{basic_auth_source_uri}"
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- bundle :install, :artifice => "compact_index_basic_authentication"
- expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(err).not_to include("#{user}:#{password}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "does not pass the user / password to different hosts on redirect" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "compact_index_creds_diff_host"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle :install, artifice: "compact_index_creds_diff_host"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
describe "with authentication details in bundle config" do
before do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
end
it "reads authentication details by host name from bundle config" do
bundle "config set #{source_hostname} #{user}:#{password}"
- bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ bundle :install, artifice: "compact_index_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "reads authentication details by full url from bundle config" do
# The trailing slash is necessary here; Fetcher canonicalizes the URI.
bundle "config set #{source_uri}/ #{user}:#{password}"
- bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ bundle :install, artifice: "compact_index_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "should use the API" do
bundle "config set #{source_hostname} #{user}:#{password}"
- bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ bundle :install, artifice: "compact_index_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "prefers auth supplied in the source uri" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
bundle "config set #{source_hostname} otheruser:wrong"
- bundle :install, :artifice => "compact_index_strict_basic_authentication"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle :install, artifice: "compact_index_strict_basic_authentication"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "shows instructions if auth is not provided for the source" do
- bundle :install, :artifice => "compact_index_strict_basic_authentication", :raise_on_error => false
- expect(err).to include("bundle config set #{source_hostname} username:password")
+ bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false
+ expect(err).to include("bundle config set --global #{source_hostname} username:password")
end
it "fails if authentication has already been provided, but failed" do
bundle "config set #{source_hostname} #{user}:wrong"
- bundle :install, :artifice => "compact_index_strict_basic_authentication", :raise_on_error => false
+ bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false
+ expect(err).to include("Bad username or password")
+ end
+
+ it "does not fallback to old dependency API if bad authentication is provided" do
+ bundle "config set #{source_hostname} #{user}:wrong"
+
+ bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false, verbose: true
expect(err).to include("Bad username or password")
+ expect(out).to include("HTTP 401 Unauthorized http://user@localgemserver.test/versions")
+ expect(out).not_to include("HTTP 401 Unauthorized http://user@localgemserver.test/api/v1/dependencies")
end
end
@@ -707,11 +631,11 @@ The checksum of /versions does not match the checksum provided by the server! So
it "passes basic authentication details" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "compact_index_basic_authentication"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle :install, artifice: "compact_index_basic_authentication"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
end
@@ -729,14 +653,14 @@ The checksum of /versions does not match the checksum provided by the server! So
end
end
- it "explains what to do to get it" do
+ it "explains what to do to get it, and includes original error" do
gemfile <<-G
source "#{source_uri.gsub(/http/, "https")}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :env => { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, :raise_on_error => false
- expect(err).to include("OpenSSL")
+ bundle :install, env: { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }, raise_on_error: false, artifice: nil
+ expect(err).to include("recompile Ruby").and include("cannot load such file")
end
end
@@ -745,17 +669,17 @@ The checksum of /versions does not match the checksum provided by the server! So
# Install a monkeypatch that reproduces the effects of openssl raising
# a certificate validation error when RubyGems tries to connect.
gemfile <<-G
- class Net::HTTP
+ class Gem::Net::HTTP
def start
raise OpenSSL::SSL::SSLError, "certificate verify failed"
end
end
source "#{source_uri.gsub(/http/, "https")}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to match(/could not verify the SSL certificate/i)
end
end
@@ -763,98 +687,203 @@ The checksum of /versions does not match the checksum provided by the server! So
context ".gemrc with sources is present" do
it "uses other sources declared in the Gemfile" do
File.open(home(".gemrc"), "w") do |file|
- file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
+ file.puts({ sources: ["https://rubygems.org"] }.to_yaml)
end
begin
gemfile <<-G
source "#{source_uri}"
- gem 'rack'
+ gem 'myrack'
G
- bundle :install, :artifice => "compact_index_forbidden"
+ bundle :install, artifice: "compact_index_forbidden"
ensure
- home(".gemrc").rmtree
+ FileUtils.rm_rf home(".gemrc")
end
end
end
- it "performs partial update with a non-empty range" do
- skip "HTTP_RANGE not set" if Gem.win_platform?
+ it "performs update with etag not-modified" do
+ versions_etag = compact_index_cache_path.join(
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions.etag"
+ )
+ expect(versions_etag.file?).to eq(false)
gemfile <<-G
source "#{source_uri}"
- gem 'rack', '0.9.1'
+ gem 'myrack', '0.9.1'
G
- # Initial install creates the cached versions file
- bundle :install, :artifice => "compact_index"
+ # Initial install creates the cached versions file and etag file
+ bundle :install, artifice: "compact_index"
+
+ expect(versions_etag.file?).to eq(true)
+ previous_content = versions_etag.binread
# Update the Gemfile so we can check subsequent install was successful
gemfile <<-G
source "#{source_uri}"
- gem 'rack', '1.0.0'
+ gem 'myrack', '1.0.0'
G
- # Second install should make only a partial request to /versions
- bundle :install, :artifice => "compact_index_partial_update"
+ # Second install should match etag
+ bundle :install, artifice: "compact_index_etag_match"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(versions_etag.binread).to eq(previous_content)
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "performs full update when range is ignored" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'myrack', '0.9.1'
+ G
+
+ # Initial install creates the cached versions file and etag file
+ bundle :install, artifice: "compact_index"
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'myrack', '1.0.0'
+ G
+
+ versions = compact_index_cache_path.join(
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
+ )
+ # Modify the cached file. The ranged request will be based on this but,
+ # in this test, the range is ignored so this gets overwritten, allowing install.
+ versions.write "ruining this file"
+
+ bundle :install, artifice: "compact_index_range_ignored"
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "performs partial update with a non-empty range" do
+ build_repo4 do
+ build_gem "myrack", "0.9.1"
+ end
+
+ # Initial install creates the cached versions file
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'myrack', '0.9.1'
+ G
+
+ build_repo4 do
+ build_gem "myrack", "1.0.0"
+ end
+
+ install_gemfile <<-G, artifice: "compact_index_partial_update", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'myrack', '1.0.0'
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "performs partial update while local cache is updated by another process" do
gemfile <<-G
source "#{source_uri}"
- gem 'rack'
+ gem 'myrack'
G
- # Create an empty file to trigger a partial download
- versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
- "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions")
- FileUtils.mkdir_p(File.dirname(versions))
- FileUtils.touch(versions)
+ # Create a partial cache versions file
+ versions = compact_index_cache_path.join(
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
+ )
+ versions.dirname.mkpath
+ versions.write("created_at")
- bundle :install, :artifice => "compact_index_concurrent_download"
+ bundle :install, artifice: "compact_index_concurrent_download"
- expect(File.read(versions)).to start_with("created_at")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(versions.read).to start_with("created_at")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "performs a partial update that fails digest check, then a full update" do
+ build_repo4 do
+ build_gem "myrack", "0.9.1"
+ end
+
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'myrack', '0.9.1'
+ G
+
+ build_repo4 do
+ build_gem "myrack", "1.0.0"
+ end
+
+ install_gemfile <<-G, artifice: "compact_index_partial_update_bad_digest", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'myrack', '1.0.0'
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "performs full update if server endpoints serve partial content responses but don't have incremental content and provide no digest" do
+ build_repo4 do
+ build_gem "myrack", "0.9.1"
+ end
+
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'myrack', '0.9.1'
+ G
+
+ build_repo4 do
+ build_gem "myrack", "1.0.0"
+ end
+
+ install_gemfile <<-G, artifice: "compact_index_partial_update_no_digest_not_incremental", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ source "#{source_uri}"
+ gem 'myrack', '1.0.0'
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "performs full update of compact index info cache if range is not satisfiable" do
gemfile <<-G
source "#{source_uri}"
- gem 'rack', '0.9.1'
+ gem 'myrack', '0.9.1'
G
- rake_info_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
- "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info", "rack")
+ bundle :install, artifice: "compact_index"
- bundle :install, :artifice => "compact_index"
+ cache_path = compact_index_cache_path.join("localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5")
- expected_rack_info_content = File.read(rake_info_path)
+ # We must remove the etag so that we don't ignore the range and get a 304 Not Modified.
+ myrack_info_etag_path = File.join(cache_path, "info-etags", "myrack-92f3313ce5721296f14445c3a6b9c073")
+ File.unlink(myrack_info_etag_path) if File.exist?(myrack_info_etag_path)
- # Modify the cache files. We expect them to be reset to the normal ones when we re-run :install
- File.open(rake_info_path, "w") {|f| f << (expected_rack_info_content + "this is different") }
+ myrack_info_path = File.join(cache_path, "info", "myrack")
+ expected_myrack_info_content = File.read(myrack_info_path)
+
+ # Modify the cache files to make the range not satisfiable
+ File.open(myrack_info_path, "a") {|f| f << "0.9.2 |checksum:c55b525b421fd833a93171ad3d7f04528ca8e87d99ac273f8933038942a5888c" }
# Update the Gemfile so the next install does its normal things
gemfile <<-G
source "#{source_uri}"
- gem 'rack', '1.0.0'
+ gem 'myrack', '1.0.0'
G
# The cache files now being longer means the requested range is going to be not satisfiable
# Bundler must end up requesting the whole file to fix things up.
- bundle :install, :artifice => "compact_index_range_not_satisfiable"
+ bundle :install, artifice: "compact_index_range_not_satisfiable"
- resulting_rack_info_content = File.read(rake_info_path)
+ resulting_myrack_info_content = File.read(myrack_info_path)
- expect(resulting_rack_info_content).to eq(expected_rack_info_content)
+ expect(resulting_myrack_info_content).to eq(expected_myrack_info_content)
end
it "fails gracefully when the source URI has an invalid scheme" do
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "htps://rubygems.org"
- gem "rack"
+ gem "myrack"
G
expect(exitstatus).to eq(15)
expect(err).to end_with(<<-E.strip)
@@ -863,77 +892,121 @@ The checksum of /versions does not match the checksum provided by the server! So
end
describe "checksum validation" do
+ before do
+ lockfile <<-L
+ GEM
+ remote: #{source_uri}
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ #{checksums_section}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "handles checksums from the server in base64" do
+ api_checksum = checksum_digest(gem_repo1, "myrack", "1.0.0")
+ myrack_checksum = [[api_checksum].pack("H*")].pack("m0")
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_MYRACK_CHECKSUM" => myrack_checksum }
+ source "#{source_uri}"
+ gem "myrack"
+ G
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
it "raises when the checksum does not match" do
- install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum", :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum", raise_on_error: false
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- expect(exitstatus).to eq(19)
- expect(err).
- to include("Bundler cannot continue installing rack (1.0.0).").
- and include("The checksum for the downloaded `rack-1.0.0.gem` does not match the checksum given by the server.").
- and include("This means the contents of the downloaded gem is different from what was uploaded to the server, and could be a potential security issue.").
- and include("To resolve this issue:").
- and include("1. delete the downloaded gem located at: `#{default_bundle_path}/gems/rack-1.0.0/rack-1.0.0.gem`").
- and include("2. run `bundle install`").
- and include("If you wish to continue installing the downloaded gem, and are certain it does not pose a security issue despite the mismatching checksum, do the following:").
- and include("1. run `bundle config set disable_checksum_validation true` to turn off checksum verification").
- and include("2. run `bundle install`").
- and match(/\(More info: The expected SHA256 checksum was "#{"ab" * 22}", but the checksum for the downloaded gem was ".+?"\.\)/)
+ gem_path = default_cache_path.dirname.join("myrack-1.0.0.gem")
+
+ expect(exitstatus).to eq(37)
+ expect(err).to eq <<~E.strip
+ Bundler found mismatched checksums. This is a potential security risk.
+ myrack (1.0.0) sha256=2222222222222222222222222222222222222222222222222222222222222222
+ from the API at http://localgemserver.test/
+ #{checksum_to_lock(gem_repo1, "myrack", "1.0.0")}
+ from the gem at #{gem_path}
+
+ If you trust the API at http://localgemserver.test/, to resolve this issue you can:
+ 1. remove the gem at #{gem_path}
+ 2. run `bundle install`
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
end
it "raises when the checksum is the wrong length" do
- install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum", :env => { "BUNDLER_SPEC_RACK_CHECKSUM" => "checksum!", "DEBUG" => "1" }, :verbose => true, :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum", env: { "BUNDLER_SPEC_MYRACK_CHECKSUM" => "checksum!", "DEBUG" => "1" }, verbose: true, raise_on_error: false
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- expect(exitstatus).to eq(5)
- expect(err).to include("The given checksum for rack-1.0.0 (\"checksum!\") is not a valid SHA256 hexdigest nor base64digest")
+ expect(exitstatus).to eq(14)
+ expect(err).to include('Invalid checksum for myrack-0.9.1: "checksum!" is not a valid SHA256 hex or base64 digest')
end
it "does not raise when disable_checksum_validation is set" do
- bundle "config set disable_checksum_validation true"
- install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum"
+ bundle_config "disable_checksum_validation true"
+ install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum"
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
end
end
it "works when cache dir is world-writable" do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
File.umask(0000)
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
end
it "doesn't explode when the API dependencies are wrong" do
- install_gemfile <<-G, :artifice => "compact_index_wrong_dependencies", :env => { "DEBUG" => "true" }, :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index_wrong_dependencies", env: { "DEBUG" => "true" }, raise_on_error: false
source "#{source_uri}"
gem "rails"
G
- deps = [Gem::Dependency.new("rake", "= 13.0.1"),
+ deps = [Gem::Dependency.new("rake", "= #{rake_version}"),
Gem::Dependency.new("actionpack", "= 2.3.2"),
Gem::Dependency.new("activerecord", "= 2.3.2"),
Gem::Dependency.new("actionmailer", "= 2.3.2"),
Gem::Dependency.new("activeresource", "= 2.3.2")]
- expect(out).to include(<<-E.strip).and include("rails-2.3.2 from rubygems remote at #{source_uri}/ has either corrupted API or lockfile dependencies")
-Bundler::APIResponseMismatchError: Downloading rails-2.3.2 revealed dependencies not in the API or the lockfile (#{deps.map(&:to_s).join(", ")}).
-Either installing with `--full-index` or running `bundle update rails` should fix the problem.
+ expect(out).to include("rails-2.3.2 from rubygems remote at #{source_uri}/ has corrupted API dependencies")
+ expect(err).to include(<<-E.strip)
+Bundler::APIResponseMismatchError: Downloading rails-2.3.2 revealed dependencies not in the API (#{deps.map(&:to_s).join(", ")}).
+Running `bundle update rails` should fix the problem.
E
end
it "does not duplicate specs in the lockfile when updating and a dependency is not installed" do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
+ source "https://gem.repo1"
source "#{source_uri}" do
gem "rails"
gem "activemerchant"
end
G
- gem_command "uninstall activemerchant"
- bundle "update rails", :artifice => "compact_index"
- expect(lockfile.scan(/activemerchant \(/).size).to eq(1)
+ uninstall_gem("activemerchant")
+ bundle "update rails", artifice: "compact_index"
+ count = lockfile.match?("CHECKSUMS") ? 2 : 1 # Once in the specs, and once in CHECKSUMS
+ expect(lockfile.scan(/activemerchant \(/).size).to eq(count)
+ end
+
+ it "handles an API that does not provide checksums info (undocumented, support may get removed)" do
+ install_gemfile <<-G, artifice: "compact_index_no_checksums"
+ source "https://gem.repo1"
+ gem "rake"
+ G
end
end
diff --git a/spec/bundler/install/gems/dependency_api_fallback_spec.rb b/spec/bundler/install/gems/dependency_api_fallback_spec.rb
new file mode 100644
index 0000000000..c7b0c537e4
--- /dev/null
+++ b/spec/bundler/install/gems/dependency_api_fallback_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+RSpec.describe "gemcutter's dependency API" do
+ context "when Gemcutter API takes too long to respond" do
+ before do
+ bundle_config "timeout 1"
+ end
+
+ it "times out and falls back on the modern index" do
+ install_gemfile <<-G, artifice: "endpoint_timeout"
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ expect(out).to include("Fetching source index from https://gem.repo1/")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb
index 765b5e5195..32a1b98b6d 100644
--- a/spec/bundler/install/gems/dependency_api_spec.rb
+++ b/spec/bundler/install/gems/dependency_api_spec.rb
@@ -7,12 +7,12 @@ RSpec.describe "gemcutter's dependency API" do
it "should use the API" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "should URI encode gem names" do
@@ -21,7 +21,7 @@ RSpec.describe "gemcutter's dependency API" do
gem " sinatra"
G
- bundle :install, :artifice => "endpoint", :raise_on_error => false
+ bundle :install, artifice: "endpoint", raise_on_error: false
expect(err).to include("' sinatra' is not a valid gem name because it contains whitespace.")
end
@@ -31,7 +31,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "rails"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}/...")
expect(the_bundle).to include_gems(
"rails 2.3.2",
@@ -49,22 +49,21 @@ RSpec.describe "gemcutter's dependency API" do
gem "net-sftp"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(the_bundle).to include_gems "net-sftp 1.1.1"
end
it "should use the endpoint when using deployment mode" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
- bundle "config --local deployment true"
- bundle "config --local path vendor/bundle"
- bundle :install, :artifice => "endpoint"
+ bundle_config "deployment true"
+ bundle :install, artifice: "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "handles git dependencies that are in rubygems" do
@@ -75,12 +74,12 @@ RSpec.describe "gemcutter's dependency API" do
gemfile <<-G
source "#{source_uri}"
- git "#{file_uri_for(lib_path("foo-1.0"))}" do
+ git "#{lib_path("foo-1.0")}" do
gem 'foo'
end
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
expect(the_bundle).to include_gems("rails 2.3.2")
end
@@ -93,13 +92,13 @@ RSpec.describe "gemcutter's dependency API" do
gemfile <<-G
source "#{source_uri}"
- gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint"
- bundle "config --local deployment true"
- bundle :install, :artifice => "endpoint"
+ bundle_config "deployment true"
+ bundle :install, artifice: "endpoint"
expect(the_bundle).to include_gems("rails 2.3.2")
end
@@ -108,27 +107,35 @@ RSpec.describe "gemcutter's dependency API" do
build_git "foo"
gemfile <<-G
source "#{source_uri}"
- gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
G
- bundle "install", :artifice => "endpoint"
- bundle "config --local deployment true"
- bundle :install, :artifice => "endpoint"
+ bundle "install", artifice: "endpoint"
+ bundle_config "deployment true"
+ bundle :install, artifice: "endpoint"
expect(the_bundle).to include_gems("foo 1.0")
end
it "falls back when the API errors out" do
- simulate_platform mswin
+ simulate_platform "x86-mswin32" do
+ build_repo2 do
+ # The rcov gem is platform mswin32, but has no arch
+ build_gem "rcov" do |s|
+ s.platform = Gem::Platform.new([nil, "mswin32", nil])
+ s.write "lib/rcov.rb", "RCOV = '1.0.0'"
+ end
+ end
- gemfile <<-G
- source "#{source_uri}"
- gem "rcov"
- G
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rcov"
+ G
- bundle :install, :artifice => "windows"
- expect(out).to include("Fetching source index from #{source_uri}")
- expect(the_bundle).to include_gems "rcov 1.0.0"
+ bundle :install, artifice: "windows", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
end
it "falls back when hitting the Gemcutter Dependency Limit" do
@@ -139,10 +146,10 @@ RSpec.describe "gemcutter's dependency API" do
gem "actionmailer"
gem "activeresource"
gem "thin"
- gem "rack"
+ gem "myrack"
gem "rails"
G
- bundle :install, :artifice => "endpoint_fallback"
+ bundle :install, artifice: "endpoint_fallback"
expect(out).to include("Fetching source index from #{source_uri}")
expect(the_bundle).to include_gems(
@@ -152,7 +159,7 @@ RSpec.describe "gemcutter's dependency API" do
"activeresource 2.3.2",
"activesupport 2.3.2",
"thin 1.0.0",
- "rack 1.0.0",
+ "myrack 1.0.0",
"rails 2.3.2"
)
end
@@ -160,39 +167,39 @@ RSpec.describe "gemcutter's dependency API" do
it "falls back when Gemcutter API doesn't return proper Marshal format" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :verbose => true, :artifice => "endpoint_marshal_fail"
+ bundle :install, verbose: true, artifice: "endpoint_marshal_fail"
expect(out).to include("could not fetch from the dependency API, trying the full index")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "falls back when the API URL returns 403 Forbidden" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :verbose => true, :artifice => "endpoint_api_forbidden"
+ bundle :install, verbose: true, artifice: "endpoint_api_forbidden"
expect(out).to include("Fetching source index from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "handles host redirects" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endpoint_host_redirect"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle :install, artifice: "endpoint_host_redirect"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "handles host redirects without Net::HTTP::Persistent" do
+ it "handles host redirects without Gem::Net::HTTP::Persistent" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
FileUtils.mkdir_p lib_path
@@ -208,18 +215,18 @@ RSpec.describe "gemcutter's dependency API" do
H
end
- bundle :install, :artifice => "endpoint_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ bundle :install, artifice: "endpoint_host_redirect", requires: [lib_path("disable_net_http_persistent.rb")]
expect(out).to_not match(/Too many redirects/)
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "timeouts when Bundler::Fetcher redirects too much" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endpoint_redirect", :raise_on_error => false
+ bundle :install, artifice: "endpoint_redirect", raise_on_error: false
expect(err).to match(/Too many redirects/)
end
@@ -227,50 +234,32 @@ RSpec.describe "gemcutter's dependency API" do
it "should use the modern index for install" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle "install --full-index", :artifice => "endpoint"
+ bundle "install --full-index", artifice: "endpoint"
expect(out).to include("Fetching source index from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "should use the modern index for update" do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle "update --full-index", :artifice => "endpoint", :all => true
+ bundle "update --full-index", artifice: "endpoint", all: true
expect(out).to include("Fetching source index from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
- it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 3" do
+ it "fetches again when more dependencies are found in subsequent sources" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
- end
-
- gemfile <<-G
- source "#{source_uri}"
- source "#{source_uri}/extra"
- gem "back_deps"
- G
-
- bundle :install, :artifice => "endpoint_extra"
- expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
- end
-
- it "fetches again when more dependencies are found in subsequent sources using blocks" do
- build_repo2 do
- build_gem "back_deps" do |s|
- s.add_dependency "foo"
- end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
@@ -280,61 +269,39 @@ RSpec.describe "gemcutter's dependency API" do
end
G
- bundle :install, :artifice => "endpoint_extra"
+ bundle :install, artifice: "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
end
it "fetches gem versions even when those gems are already installed" do
gemfile <<-G
source "#{source_uri}"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
G
- bundle :install, :artifice => "endpoint_extra_api"
+ bundle :install, artifice: "endpoint_extra_api"
build_repo4 do
- build_gem "rack", "1.2" do |s|
- s.executables = "rackup"
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
end
end
gemfile <<-G
source "#{source_uri}" do; end
source "#{source_uri}/extra"
- gem "rack", "1.2"
+ gem "myrack", "1.2"
G
- bundle :install, :artifice => "endpoint_extra_api"
- expect(the_bundle).to include_gems "rack 1.2"
+ bundle :install, artifice: "endpoint_extra_api"
+ expect(the_bundle).to include_gems "myrack 1.2"
end
- it "considers all possible versions of dependencies from all api gem sources", :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"
- gem 'somegem', '1.0.0'
- 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 "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.
+ it "resolves indirect dependencies to the most scoped source that includes them" 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 tries to
+ # use the version in the most scoped source, even if not compatible, and
+ # gives a resolution error
build_repo4 do
build_gem "activesupport", "1.2.0"
build_gem "somegem", "1.0.0" do |s|
@@ -349,10 +316,9 @@ RSpec.describe "gemcutter's dependency API" do
end
G
- bundle :install, :artifice => "endpoint_extra_api"
+ bundle :install, artifice: "compact_index_extra_api", raise_on_error: false
- expect(the_bundle).to include_gems "somegem 1.0.0"
- expect(the_bundle).to include_gems "activesupport 1.2.3"
+ expect(err).to include("Could not find compatible versions")
end
it "prints API output properly with back deps" do
@@ -360,7 +326,7 @@ RSpec.describe "gemcutter's dependency API" do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
@@ -370,191 +336,112 @@ RSpec.describe "gemcutter's dependency API" do
end
G
- bundle :install, :artifice => "endpoint_extra"
+ bundle :install, artifice: "endpoint_extra"
expect(out).to include("Fetching gem metadata from http://localgemserver.test/.")
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
end
- it "does not fetch every spec if the index of gems is large when doing back deps", :bundler => "< 3" do
+ it "does not fetch every spec when doing back deps" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
build_gem "missing"
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
- 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"
source "#{source_uri}"
- source "#{source_uri}/extra"
- gem "back_deps"
+ source "#{source_uri}/extra" do
+ gem "back_deps"
+ end
G
expect(the_bundle).to include_gems "back_deps 1.0"
end
- it "does not fetch every spec if the index of gems is large when doing back deps using blocks" do
+ it "fetches again when more dependencies are found in subsequent sources using deployment mode" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- build_gem "missing"
-
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
- 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)
+ gemfile <<-G
source "#{source_uri}"
source "#{source_uri}/extra" do
gem "back_deps"
end
G
+ bundle :install, artifice: "endpoint_extra"
+ bundle_config "deployment true"
+ bundle "install", artifice: "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
- it "uses the endpoint if all sources support it" do
- gemfile <<-G
- source "#{source_uri}"
-
- gem 'foo'
- G
-
- bundle :install, :artifice => "endpoint_api_missing"
- 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 "does not fetch all marshaled specs" do
build_repo2 do
- build_gem "back_deps" do |s|
- s.add_dependency "foo"
- end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ build_gem "foo", "1.0"
+ build_gem "foo", "2.0"
end
- gemfile <<-G
+ install_gemfile <<-G, artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, verbose: true
source "#{source_uri}"
- source "#{source_uri}/extra"
- gem "back_deps"
- G
- bundle :install, :artifice => "endpoint_extra"
+ gem "foo"
+ G
- bundle "install --deployment", :artifice => "endpoint_extra"
- expect(the_bundle).to include_gems "back_deps 1.0"
+ expect(out).to include("foo-2.0.gemspec.rz")
+ expect(out).not_to include("foo-1.0.gemspec.rz")
end
- it "fetches again when more dependencies are found in subsequent sources using deployment mode with blocks" do
+ it "does not refetch if the only unmet dependency is bundler" do
build_repo2 do
- build_gem "back_deps" do |s|
- s.add_dependency "foo"
+ build_gem "bundler_dep" do |s|
+ s.add_dependency "bundler"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
source "#{source_uri}"
- source "#{source_uri}/extra" do
- gem "back_deps"
- end
- G
-
- bundle :install, :artifice => "endpoint_extra"
-
- bundle "config --local deployment true"
- bundle "install", :artifice => "endpoint_extra"
- expect(the_bundle).to include_gems "back_deps 1.0"
- end
-
- it "does not refetch if the only unmet dependency is bundler" do
- gemfile <<-G
- source "#{source_uri}"
gem "bundler_dep"
G
- bundle :install, :artifice => "endpoint"
+ bundle :install, artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
- it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
- sudo "mkdir -p #{system_gem_path("bin")}"
- sudo "chown -R root #{system_gem_path("bin")}"
-
- gemfile <<-G
- source "#{source_uri}"
- gem "rails"
- G
- bundle :install, :artifice => "endpoint"
- expect(the_bundle).to include_gems "rails 2.3.2"
- end
-
- it "installs the binstubs", :bundler => "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "rack"
- G
-
- bundle "install --binstubs", :artifice => "endpoint"
-
- gembin "rackup"
- expect(out).to eq("1.0.0")
- end
-
- it "installs the bins when using --path and uses autoclean", :bundler => "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "rack"
- G
-
- bundle "install --path vendor/bundle", :artifice => "endpoint"
-
- expect(vendored_gems("bin/rackup")).to exist
- end
-
- it "installs the bins when using --path and uses bundle clean", :bundler => "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "rack"
- G
-
- bundle "install --path vendor/bundle --no-clean", :artifice => "endpoint"
-
- expect(vendored_gems("bin/rackup")).to exist
- end
-
it "prints post_install_messages" do
gemfile <<-G
source "#{source_uri}"
- gem 'rack-obama'
+ gem 'myrack-obama'
G
- bundle :install, :artifice => "endpoint"
- expect(out).to include("Post-install message from rack:")
+ bundle :install, artifice: "endpoint"
+ expect(out).to include("Post-install message from myrack:")
end
it "should display the post install message for a dependency" do
gemfile <<-G
source "#{source_uri}"
- gem 'rack_middleware'
+ gem 'myrack_middleware'
G
- bundle :install, :artifice => "endpoint"
- expect(out).to include("Post-install message from rack:")
- expect(out).to include("Rack's post install message")
+ bundle :install, artifice: "endpoint"
+ expect(out).to include("Post-install message from myrack:")
+ expect(out).to include("Myrack's post install message")
end
context "when using basic authentication" do
let(:user) { "user" }
let(:password) { "pass" }
let(:basic_auth_source_uri) do
- uri = Bundler::URI.parse(source_uri)
+ uri = Gem::URI.parse(source_uri)
uri.user = user
uri.password = password
@@ -564,113 +451,127 @@ RSpec.describe "gemcutter's dependency API" do
it "passes basic authentication details and strips out creds" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endpoint_basic_authentication"
+ bundle :install, artifice: "endpoint_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "strips http basic authentication creds for modern index" do
+ it "passes basic authentication details and strips out creds also in verbose mode" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endopint_marshal_fail_basic_authentication"
+ 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"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "strips http basic auth creds when it can't reach the server" do
+ it "strips http basic authentication creds for modern index" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endpoint_500", :raise_on_error => false
+ bundle :install, artifice: "endpoint_marshal_fail_basic_authentication"
expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 3" do
+ it "strips http basic auth creds when it can't reach the server" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endpoint_basic_authentication"
- expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(err).not_to include("#{user}:#{password}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle :install, artifice: "endpoint_500", raise_on_error: false
+ expect(out).not_to include("#{user}:#{password}")
end
it "does not pass the user / password to different hosts on redirect" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endpoint_creds_diff_host"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle :install, artifice: "endpoint_creds_diff_host"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ describe "with host including dashes" do
+ before do
+ gemfile <<-G
+ source "http://local-gemserver.test"
+ gem "myrack"
+ 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 "myrack 1.0.0"
+ end
end
describe "with authentication details in bundle config" do
before do
gemfile <<-G
source "#{source_uri}"
- gem "rack"
+ gem "myrack"
G
end
it "reads authentication details by host name from bundle config" do
bundle "config set #{source_hostname} #{user}:#{password}"
- bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ bundle :install, artifice: "endpoint_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "reads authentication details by full url from bundle config" do
# The trailing slash is necessary here; Fetcher canonicalizes the URI.
bundle "config set #{source_uri}/ #{user}:#{password}"
- bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ bundle :install, artifice: "endpoint_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "should use the API" do
bundle "config set #{source_hostname} #{user}:#{password}"
- bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ bundle :install, artifice: "endpoint_strict_basic_authentication"
expect(out).to include("Fetching gem metadata from #{source_uri}")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "prefers auth supplied in the source uri" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
bundle "config set #{source_hostname} otheruser:wrong"
- bundle :install, :artifice => "endpoint_strict_basic_authentication"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle :install, artifice: "endpoint_strict_basic_authentication"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "shows instructions if auth is not provided for the source" do
- bundle :install, :artifice => "endpoint_strict_basic_authentication", :raise_on_error => false
- expect(err).to include("bundle config set #{source_hostname} username:password")
+ bundle :install, artifice: "endpoint_strict_basic_authentication", raise_on_error: false
+ expect(err).to include("bundle config set --global #{source_hostname} username:password")
end
it "fails if authentication has already been provided, but failed" do
bundle "config set #{source_hostname} #{user}:wrong"
- bundle :install, :artifice => "endpoint_strict_basic_authentication", :raise_on_error => false
+ bundle :install, artifice: "endpoint_strict_basic_authentication", raise_on_error: false
expect(err).to include("Bad username or password")
end
end
@@ -681,11 +582,11 @@ RSpec.describe "gemcutter's dependency API" do
it "passes basic authentication details" do
gemfile <<-G
source "#{basic_auth_source_uri}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "endpoint_basic_authentication"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ bundle :install, artifice: "endpoint_basic_authentication"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
end
@@ -703,14 +604,14 @@ RSpec.describe "gemcutter's dependency API" do
end
end
- it "explains what to do to get it" do
+ it "explains what to do to get it, and includes original error" do
gemfile <<-G
source "#{source_uri.gsub(/http/, "https")}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :env => { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, :raise_on_error => false
- expect(err).to include("OpenSSL")
+ bundle :install, artifice: "fail", env: { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }, raise_on_error: false
+ expect(err).to include("recompile Ruby").and include("cannot load such file")
end
end
@@ -719,17 +620,17 @@ RSpec.describe "gemcutter's dependency API" do
# Install a monkeypatch that reproduces the effects of openssl raising
# a certificate validation error when RubyGems tries to connect.
gemfile <<-G
- class Net::HTTP
+ class Gem::Net::HTTP
def start
raise OpenSSL::SSL::SSLError, "certificate verify failed"
end
end
source "#{source_uri.gsub(/http/, "https")}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
expect(err).to match(/could not verify the SSL certificate/i)
end
end
@@ -737,18 +638,18 @@ RSpec.describe "gemcutter's dependency API" do
context ".gemrc with sources is present" do
it "uses other sources declared in the Gemfile" do
File.open(home(".gemrc"), "w") do |file|
- file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
+ file.puts({ sources: ["https://rubygems.org"] }.to_yaml)
end
begin
gemfile <<-G
source "#{source_uri}"
- gem 'rack'
+ gem 'myrack'
G
- bundle "install", :artifice => "endpoint_marshal_fail"
+ bundle "install", artifice: "endpoint_marshal_fail"
ensure
- home(".gemrc").rmtree
+ FileUtils.rm_rf home(".gemrc")
end
end
end
diff --git a/spec/bundler/install/gems/env_spec.rb b/spec/bundler/install/gems/env_spec.rb
index a6dfadcfc8..6d5aa456fe 100644
--- a/spec/bundler/install/gems/env_spec.rb
+++ b/spec/bundler/install/gems/env_spec.rb
@@ -4,104 +4,104 @@ RSpec.describe "bundle install with ENV conditionals" do
describe "when just setting an ENV key as a string" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
env "BUNDLER_TEST" do
- gem "rack"
+ gem "myrack"
end
G
end
it "excludes the gems when the ENV variable is not set" do
bundle :install
- expect(the_bundle).not_to include_gems "rack"
+ expect(the_bundle).not_to include_gems "myrack"
end
it "includes the gems when the ENV variable is set" do
ENV["BUNDLER_TEST"] = "1"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
describe "when just setting an ENV key as a symbol" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
env :BUNDLER_TEST do
- gem "rack"
+ gem "myrack"
end
G
end
it "excludes the gems when the ENV variable is not set" do
bundle :install
- expect(the_bundle).not_to include_gems "rack"
+ expect(the_bundle).not_to include_gems "myrack"
end
it "includes the gems when the ENV variable is set" do
ENV["BUNDLER_TEST"] = "1"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
describe "when setting a string to match the env" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
env "BUNDLER_TEST" => "foo" do
- gem "rack"
+ gem "myrack"
end
G
end
it "excludes the gems when the ENV variable is not set" do
bundle :install
- expect(the_bundle).not_to include_gems "rack"
+ expect(the_bundle).not_to include_gems "myrack"
end
it "excludes the gems when the ENV variable is set but does not match the condition" do
ENV["BUNDLER_TEST"] = "1"
bundle :install
- expect(the_bundle).not_to include_gems "rack"
+ expect(the_bundle).not_to include_gems "myrack"
end
it "includes the gems when the ENV variable is set and matches the condition" do
ENV["BUNDLER_TEST"] = "foo"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
describe "when setting a regex to match the env" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
env "BUNDLER_TEST" => /foo/ do
- gem "rack"
+ gem "myrack"
end
G
end
it "excludes the gems when the ENV variable is not set" do
bundle :install
- expect(the_bundle).not_to include_gems "rack"
+ expect(the_bundle).not_to include_gems "myrack"
end
it "excludes the gems when the ENV variable is set but does not match the condition" do
ENV["BUNDLER_TEST"] = "fo"
bundle :install
- expect(the_bundle).not_to include_gems "rack"
+ expect(the_bundle).not_to include_gems "myrack"
end
it "includes the gems when the ENV variable is set and matches the condition" do
ENV["BUNDLER_TEST"] = "foobar"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
end
diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb
index 858c40c07c..a30b53d6ad 100644
--- a/spec/bundler/install/gems/flex_spec.rb
+++ b/spec/bundler/install/gems/flex_spec.rb
@@ -3,30 +3,30 @@
RSpec.describe "bundle flex_install" do
it "installs the gems as expected" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
expect(the_bundle).to be_locked
end
it "installs even when the lockfile is invalid" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
expect(the_bundle).to be_locked
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack', '1.0'
+ source "https://gem.repo1"
+ gem 'myrack', '1.0'
G
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
expect(the_bundle).to be_locked
end
@@ -34,19 +34,19 @@ RSpec.describe "bundle flex_install" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack-obama"
+ source "https://gem.repo2"
+ gem "myrack-obama"
G
- expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0", "myrack-obama 1.0.0"
update_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack-obama", "1.0"
+ source "https://gem.repo2"
+ gem "myrack-obama", "1.0"
G
- expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0", "myrack-obama 1.0.0"
end
describe "adding new gems" do
@@ -54,38 +54,38 @@ RSpec.describe "bundle flex_install" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem 'rack'
+ source "https://gem.repo2"
+ gem 'myrack'
G
update_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem 'rack'
+ source "https://gem.repo2"
+ gem 'myrack'
gem 'activesupport', '2.3.5'
G
- expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ expect(the_bundle).to include_gems "myrack 1.0.0", "activesupport 2.3.5"
end
it "keeps child dependencies pinned" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack-obama"
+ source "https://gem.repo2"
+ gem "myrack-obama"
G
update_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack-obama"
+ source "https://gem.repo2"
+ gem "myrack-obama"
gem "thin"
G
- expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0", "thin 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0", "myrack-obama 1.0", "thin 1.0"
end
end
@@ -93,43 +93,43 @@ RSpec.describe "bundle flex_install" do
it "removes gems without changing the versions of remaining gems" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem 'rack'
+ source "https://gem.repo2"
+ gem 'myrack'
gem 'activesupport', '2.3.5'
G
update_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem 'rack'
+ source "https://gem.repo2"
+ gem 'myrack'
G
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem 'rack'
+ source "https://gem.repo2"
+ gem 'myrack'
gem 'activesupport', '2.3.2'
G
- expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.2"
+ expect(the_bundle).to include_gems "myrack 1.0.0", "activesupport 2.3.2"
end
it "removes top level dependencies when removed from the Gemfile while leaving other dependencies intact" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem 'rack'
+ source "https://gem.repo2"
+ gem 'myrack'
gem 'activesupport', '2.3.5'
G
update_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem 'rack'
+ source "https://gem.repo2"
+ gem 'myrack'
G
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
@@ -138,176 +138,227 @@ RSpec.describe "bundle flex_install" do
it "removes child dependencies" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem 'rack-obama'
+ source "https://gem.repo2"
+ gem 'myrack-obama'
gem 'activesupport'
G
- expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0", "activesupport 2.3.5"
+ expect(the_bundle).to include_gems "myrack 1.0.0", "myrack-obama 1.0.0", "activesupport 2.3.5"
update_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem 'activesupport'
G
expect(the_bundle).to include_gems "activesupport 2.3.5"
- expect(the_bundle).not_to include_gems "rack-obama", "rack"
+ expect(the_bundle).not_to include_gems "myrack-obama", "myrack"
end
end
- describe "when Gemfile conflicts with lockfile" do
+ describe "when running bundle install and Gemfile conflicts with lockfile" do
before(:each) do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack_middleware"
+ source "https://gem.repo2"
+ gem "myrack_middleware"
G
- expect(the_bundle).to include_gems "rack_middleware 1.0", "rack 0.9.1"
+ expect(the_bundle).to include_gems "myrack_middleware 1.0", "myrack 0.9.1"
- build_repo2
- update_repo2 do
- build_gem "rack-obama", "2.0" do |s|
- s.add_dependency "rack", "=1.2"
+ build_repo2 do
+ build_gem "myrack-obama", "2.0" do |s|
+ s.add_dependency "myrack", "=1.2"
end
- build_gem "rack_middleware", "2.0" do |s|
- s.add_dependency "rack", ">=1.0"
+ build_gem "myrack_middleware", "2.0" do |s|
+ s.add_dependency "myrack", ">=1.0"
end
end
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack-obama", "2.0"
- gem "rack_middleware"
+ source "https://gem.repo2"
+ gem "myrack-obama", "2.0"
+ gem "myrack_middleware"
G
end
it "does not install gems whose dependencies are not met" do
- bundle :install, :raise_on_error => false
- ruby <<-RUBY, :raise_on_error => false
+ bundle :install, raise_on_error: false
+ ruby <<-RUBY, raise_on_error: false
require 'bundler/setup'
RUBY
- expect(err).to match(/could not find gem 'rack-obama/i)
+ expect(err).to match(/could not find gem 'myrack-obama/i)
end
- it "suggests bundle update when the Gemfile requires different versions than the lock" do
- bundle "config set force_ruby_platform true"
+ it "discards the locked gems when the Gemfile requires different versions than the lock" do
+ bundle_config "force_ruby_platform true"
- nice_error = <<-E.strip.gsub(/^ {8}/, "")
- Bundler could not find compatible versions for gem "rack":
- In snapshot (Gemfile.lock):
- rack (= 0.9.1)
+ nice_error = <<~E.strip
+ Could not find compatible versions
- In Gemfile:
- rack-obama (= 2.0) was resolved to 2.0, which depends on
- rack (= 1.2)
+ Because myrack-obama >= 2.0 depends on myrack = 1.2
+ and myrack = 1.2 could not be found in rubygems repository https://gem.repo2/ or installed locally,
+ myrack-obama >= 2.0 cannot be used.
+ So, because Gemfile depends on myrack-obama = 2.0,
+ version solving has failed.
+ E
+
+ bundle :install, retry: 0, raise_on_error: false
+ expect(err).to end_with(nice_error)
+ end
- rack_middleware was resolved to 1.0, which depends on
- rack (= 0.9.1)
+ it "does not include conflicts with a single requirement tree, because that can't possibly be a conflict" do
+ bundle_config "force_ruby_platform true"
- Running `bundle update` will rebuild your snapshot from scratch, using only
- the gems in your Gemfile, which may resolve the conflict.
+ bad_error = <<~E.strip
+ Bundler could not find compatible versions for gem "myrack-obama":
+ In Gemfile:
+ myrack-obama (= 2.0)
E
- bundle :install, :retry => 0, :raise_on_error => false
- expect(err).to end_with(nice_error)
+ bundle "update myrack_middleware", retry: 0, raise_on_error: false
+ expect(err).not_to end_with(bad_error)
end
end
- describe "subtler cases" do
- before :each do
+ describe "when running bundle update and Gemfile conflicts with lockfile" do
+ before(:each) do
+ build_repo4 do
+ build_gem "jekyll-feed", "0.16.0"
+ build_gem "jekyll-feed", "0.15.1"
+
+ build_gem "github-pages", "226" do |s|
+ s.add_dependency "jekyll-feed", "0.15.1"
+ end
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "rack-obama"
+ source "https://gem.repo4"
+ gem "jekyll-feed", "~> 0.12"
G
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "0.9.1"
- gem "rack-obama"
+ source "https://gem.repo4"
+ gem "github-pages", "~> 226"
+ gem "jekyll-feed", "~> 0.12"
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")
- end
-
- it "should work when you update" do
- bundle "update rack"
+ it "discards the conflicting lockfile information and resolves properly" do
+ bundle :update, raise_on_error: false, all: true
+ expect(err).to be_empty
end
end
- describe "when adding a new source" do
- it "updates the lockfile", :bundler => "< 3" do
- build_repo2
+ describe "subtler cases" do
+ before :each do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "myrack-obama"
G
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
+ gem "myrack-obama"
G
+ end
+
+ it "should work when you install" do
+ bundle "install"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo1, "myrack", "0.9.1"
+ c.checksum gem_repo1, "myrack-obama", "1.0"
+ end
- lockfile_should_be <<-L
- GEM
- remote: #{file_uri_for(gem_repo1)}/
- remote: #{file_uri_for(gem_repo2)}/
- specs:
- rack (1.0.0)
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (0.9.1)
+ myrack-obama (1.0)
+ myrack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack (= 0.9.1)
+ myrack-obama
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
- PLATFORMS
- #{lockfile_platforms}
+ it "should work when you update" do
+ bundle "update myrack"
- DEPENDENCIES
- rack
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo1, "myrack", "0.9.1"
+ c.checksum gem_repo1, "myrack-obama", "1.0"
+ end
- BUNDLED WITH
- #{Bundler::VERSION}
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (0.9.1)
+ myrack-obama (1.0)
+ myrack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack (= 0.9.1)
+ myrack-obama
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
+ end
- it "updates the lockfile", :bundler => "3" do
+ describe "when adding a new source" do
+ it "updates the lockfile" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- source "#{file_uri_for(gem_repo2)}" do
+ source "https://gem.repo1"
+ source "https://gem.repo2" do
end
- gem "rack"
+ gem "myrack"
G
- lockfile_should_be <<-L
- GEM
- remote: #{file_uri_for(gem_repo1)}/
- specs:
- rack (1.0.0)
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo1, "myrack", "1.0.0"
+ end
- GEM
- remote: #{file_uri_for(gem_repo2)}/
- specs:
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (1.0.0)
- PLATFORMS
- #{lockfile_platforms}
+ GEM
+ remote: https://gem.repo2/
+ specs:
- DEPENDENCIES
- rack
+ PLATFORMS
+ #{lockfile_platforms}
- BUNDLED WITH
- #{Bundler::VERSION}
+ DEPENDENCIES
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
end
@@ -317,37 +368,36 @@ RSpec.describe "bundle flex_install" do
before(:each) do
build_repo2 do
build_gem "capybara", "0.3.9" do |s|
- s.add_dependency "rack", ">= 1.0.0"
+ s.add_dependency "myrack", ">= 1.0.0"
end
- build_gem "rack", "1.1.0"
+ build_gem "myrack", "1.1.0"
build_gem "rails", "3.0.0.rc4" do |s|
- s.add_dependency "rack", "~> 1.1.0"
+ s.add_dependency "myrack", "~> 1.1.0"
end
- build_gem "rack", "1.2.1"
+ build_gem "myrack", "1.2.1"
build_gem "rails", "3.0.0" do |s|
- s.add_dependency "rack", "~> 1.2.1"
+ s.add_dependency "myrack", "~> 1.2.1"
end
end
end
- it "prints the correct error message" do
+ it "resolves them" do
# install Rails 3.0.0.rc
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "rails", "3.0.0.rc4"
gem "capybara", "0.3.9"
G
# upgrade Rails to 3.0.0 and then install again
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
+ install_gemfile <<-G
+ source "https://gem.repo2"
gem "rails", "3.0.0"
gem "capybara", "0.3.9"
G
-
- expect(err).to include("Gemfile.lock")
+ expect(err).to be_empty
end
end
end
diff --git a/spec/bundler/install/gems/fund_spec.rb b/spec/bundler/install/gems/fund_spec.rb
new file mode 100644
index 0000000000..8a3a51270a
--- /dev/null
+++ b/spec/bundler/install/gems/fund_spec.rb
@@ -0,0 +1,164 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install" do
+ context "with gem sources" do
+ before do
+ build_repo2 do
+ build_gem "has_funding_and_other_metadata" do |s|
+ s.metadata = {
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ }
+ end
+
+ build_gem "has_funding", "1.2.3" do |s|
+ s.metadata = {
+ "funding_uri" => "https://example.com/has_funding/funding",
+ }
+ end
+
+ build_gem "gem_with_dependent_funding", "1.0" do |s|
+ s.add_dependency "has_funding"
+ end
+ end
+ end
+
+ context "when gems include a fund URI" do
+ it "displays the plural fund message after installing" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'has_funding_and_other_metadata'
+ gem 'has_funding'
+ gem 'myrack-obama'
+ G
+
+ expect(out).to include("2 installed gems you directly depend on are looking for funding.")
+ end
+
+ it "displays the singular fund message after installing" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'has_funding'
+ gem 'myrack-obama'
+ G
+
+ expect(out).to include("1 installed gem you directly depend on is looking for funding.")
+ end
+ end
+
+ context "when gems include a fund URI but `ignore_funding_requests` is configured" do
+ before do
+ bundle_config "ignore_funding_requests true"
+ end
+
+ it "does not display the plural fund message after installing" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'has_funding_and_other_metadata'
+ gem 'has_funding'
+ gem 'myrack-obama'
+ G
+
+ expect(out).not_to include("2 installed gems you directly depend on are looking for funding.")
+ end
+
+ it "does not display the singular fund message after installing" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'has_funding'
+ gem 'myrack-obama'
+ G
+
+ expect(out).not_to include("1 installed gem you directly depend on is looking for funding.")
+ end
+ end
+
+ context "when gems do not include fund messages" do
+ it "does not display any fund messages" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "activesupport"
+ G
+
+ expect(out).not_to include("gem you depend on")
+ end
+ end
+
+ context "when a dependency includes a fund message" do
+ it "does not display the fund message" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem 'gem_with_dependent_funding'
+ G
+
+ expect(out).not_to include("gem you depend on")
+ end
+ end
+ end
+
+ context "with git sources" do
+ context "when gems include fund URI" do
+ it "displays the fund message after installing" do
+ build_git "also_has_funding" do |s|
+ s.metadata = {
+ "funding_uri" => "https://example.com/also_has_funding/funding",
+ }
+ end
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'also_has_funding', :git => '#{lib_path("also_has_funding-1.0")}'
+ G
+
+ expect(out).to include("1 installed gem you directly depend on is looking for funding.")
+ end
+
+ it "displays the fund message if repo is updated" do
+ build_git "also_has_funding" do |s|
+ s.metadata = {
+ "funding_uri" => "https://example.com/also_has_funding/funding",
+ }
+ end
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'also_has_funding', :git => '#{lib_path("also_has_funding-1.0")}'
+ G
+
+ build_git "also_has_funding", "1.1" do |s|
+ s.metadata = {
+ "funding_uri" => "https://example.com/also_has_funding/funding",
+ }
+ end
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem 'also_has_funding', :git => '#{lib_path("also_has_funding-1.1")}'
+ G
+
+ expect(out).to include("1 installed gem you directly depend on is looking for funding.")
+ end
+
+ it "displays the fund message if repo is not updated" do
+ build_git "also_has_funding" do |s|
+ s.metadata = {
+ "funding_uri" => "https://example.com/also_has_funding/funding",
+ }
+ end
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'also_has_funding', :git => '#{lib_path("also_has_funding-1.0")}'
+ G
+
+ bundle :install
+ expect(out).to include("1 installed gem you directly depend on is looking for funding.")
+
+ bundle :install
+ expect(out).to include("1 installed gem you directly depend on is looking for funding.")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/gemfile_source_header_spec.rb b/spec/bundler/install/gems/gemfile_source_header_spec.rb
new file mode 100644
index 0000000000..dc35c8d741
--- /dev/null
+++ b/spec/bundler/install/gems/gemfile_source_header_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+RSpec.describe "fetching dependencies with a mirrored source" do
+ let(:mirror) { "https://server.example.org" }
+
+ before do
+ build_repo2
+
+ gemfile <<-G
+ source "#{mirror}"
+ gem 'weakling'
+ G
+
+ bundle_config "mirror.#{mirror} https://gem.repo2"
+ end
+
+ it "sets the 'X-Gemfile-Source' and 'User-Agent' headers and bundles successfully" do
+ bundle :install, artifice: "endpoint_mirror_source"
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+end
diff --git a/spec/bundler/install/gems/mirror_probe_spec.rb b/spec/bundler/install/gems/mirror_probe_spec.rb
new file mode 100644
index 0000000000..564062ccf6
--- /dev/null
+++ b/spec/bundler/install/gems/mirror_probe_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+RSpec.describe "fetching dependencies with a not available mirror" do
+ before do
+ build_repo2
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem 'weakling'
+ G
+ end
+
+ context "with a specific fallback timeout" do
+ before do
+ bundle_config_global("BUNDLE_MIRROR__HTTPS://GEM__REPO2/__FALLBACK_TIMEOUT/" => "true",
+ "BUNDLE_MIRROR__HTTPS://GEM__REPO2/" => "https://gem.mirror")
+ end
+
+ it "install a gem using the original uri when the mirror is not responding" do
+ bundle :install, env: { "BUNDLER_SPEC_FAKE_RESOLVE" => "gem.mirror" }, verbose: true
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+ end
+
+ context "with a global fallback timeout" do
+ before do
+ bundle_config_global("BUNDLE_MIRROR__ALL__FALLBACK_TIMEOUT/" => "1",
+ "BUNDLE_MIRROR__ALL" => "https://gem.mirror")
+ end
+
+ it "install a gem using the original uri when the mirror is not responding" do
+ bundle :install, env: { "BUNDLER_SPEC_FAKE_RESOLVE" => "gem.mirror" }
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+ end
+
+ context "with a specific mirror without a fallback timeout" do
+ before do
+ bundle_config_global("BUNDLE_MIRROR__HTTPS://GEM__REPO2/" => "https://gem.mirror")
+ end
+
+ it "fails to install the gem with a timeout error when the mirror is not responding" do
+ bundle :install, artifice: "compact_index_mirror_down", raise_on_error: false
+
+ expect(out).to be_empty
+ expect(err).to eq("Could not reach host gem.mirror. Check your network connection and try again.")
+ end
+ end
+
+ context "with a global mirror without a fallback timeout" do
+ before do
+ bundle_config_global("BUNDLE_MIRROR__ALL" => "https://gem.mirror")
+ end
+
+ it "fails to install the gem with a timeout error when the mirror is not responding" do
+ bundle :install, artifice: "compact_index_mirror_down", raise_on_error: false
+
+ expect(out).to be_empty
+ expect(err).to eq("Could not reach host gem.mirror. Check your network connection and try again.")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/mirror_spec.rb b/spec/bundler/install/gems/mirror_spec.rb
index 9611973701..e1fbeac454 100644
--- a/spec/bundler/install/gems/mirror_spec.rb
+++ b/spec/bundler/install/gems/mirror_spec.rb
@@ -4,17 +4,17 @@ RSpec.describe "bundle install with a mirror configured" do
describe "when the mirror does not match the gem source" do
before :each do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
G
- bundle "config set --local mirror.http://gems.example.org http://gem-mirror.example.org"
+ bundle_config "mirror.http://gems.example.org http://gem-mirror.example.org"
end
it "installs from the normal location" do
bundle :install
- expect(out).to include("Fetching source index from #{file_uri_for(gem_repo1)}")
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(out).to include("Fetching gem metadata from https://gem.repo1")
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
@@ -22,18 +22,18 @@ RSpec.describe "bundle install with a mirror configured" do
before :each do
gemfile <<-G
# This source is bogus and doesn't have the gem we're looking for
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
- gem "rack"
+ gem "myrack"
G
- bundle "config set --local mirror.#{file_uri_for(gem_repo2)} #{file_uri_for(gem_repo1)}"
+ bundle_config "mirror.https://gem.repo2 https://gem.repo1"
end
it "installs the gem from the mirror" do
- bundle :install
- expect(out).to include("Fetching source index from #{file_uri_for(gem_repo1)}")
- expect(out).not_to include("Fetching source index from #{file_uri_for(gem_repo2)}")
- expect(the_bundle).to include_gems "rack 1.0"
+ bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ expect(out).to include("Fetching gem metadata from https://gem.repo1")
+ expect(out).not_to include("Fetching gem metadata from https://gem.repo2")
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
end
diff --git a/spec/bundler/install/gems/native_extensions_spec.rb b/spec/bundler/install/gems/native_extensions_spec.rb
index 8a4de3cf92..d5b10d2c8f 100644
--- a/spec/bundler/install/gems/native_extensions_spec.rb
+++ b/spec/bundler/install/gems/native_extensions_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "installing a gem with native extensions", :ruby_repo do
+RSpec.describe "installing a gem with native extensions" do
it "installs" do
build_repo2 do
build_gem "c_extension" do |s|
@@ -9,7 +9,7 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
require "mkmf"
name = "c_extension_bundle"
dir_config(name)
- raise "OMG" unless with_config("c_extension") == "hello"
+ raise ArgumentError unless with_config("c_extension") == "hello"
create_makefile(name)
E
@@ -33,11 +33,11 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
end
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "c_extension"
G
- bundle "config set build.c_extension --with-c_extension=hello"
+ bundle_config "build.c_extension --with-c_extension=hello"
bundle "install"
expect(out).to include("Installing c_extension 1.0 with native extensions")
@@ -53,7 +53,7 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
require "mkmf"
name = "c_extension_bundle"
dir_config(name)
- raise "OMG" unless with_config("c_extension") == "hello"
+ raise ArgumentError unless with_config("c_extension") == "hello"
create_makefile(name)
E
@@ -75,9 +75,10 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
C
end
- bundle "config set build.c_extension --with-c_extension=hello"
+ bundle_config "build.c_extension --with-c_extension=hello"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "c_extension", :git => #{lib_path("c_extension-1.0").to_s.dump}
G
@@ -90,13 +91,13 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
it "installs correctly from git when multiple gems with extensions share one repository" do
build_repo2 do
["one", "two"].each do |n|
- build_lib "c_extension_#{n}", "1.0", :path => lib_path("gems/c_extension_#{n}") do |s|
+ build_lib "c_extension_#{n}", "1.0", path: lib_path("gems/c_extension_#{n}") do |s|
s.extensions = ["ext/extconf.rb"]
s.write "ext/extconf.rb", <<-E
require "mkmf"
name = "c_extension_bundle_#{n}"
dir_config(name)
- raise "OMG" unless with_config("c_extension_#{n}") == "#{n}"
+ raise ArgumentError unless with_config("c_extension_#{n}") == "#{n}"
create_makefile(name)
E
@@ -118,19 +119,21 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
C
end
end
- build_git "gems", :path => lib_path("gems"), :gemspec => false
+ build_git "gems", path: lib_path("gems"), gemspec: false
end
- bundle "config set build.c_extension_one --with-c_extension_one=one"
- bundle "config set build.c_extension_two --with-c_extension_two=two"
+ bundle_config "build.c_extension_one --with-c_extension_one=one"
+ bundle_config "build.c_extension_two --with-c_extension_two=two"
# 1st time, require only one gem -- only one of the extensions gets built.
install_gemfile <<-G
+ source "https://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 "https://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
@@ -146,7 +149,7 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
require "mkmf"
name = "c_extension_bundle"
dir_config(name)
- raise "OMG" unless with_config("c_extension") == "hello" && with_config("c_extension_bundle-dir") == "hola"
+ raise ArgumentError unless with_config("c_extension") == "hello" && with_config("c_extension_bundle-dir") == "hola"
create_makefile(name)
E
@@ -168,9 +171,10 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do
C
end
- bundle "config set build.c_extension --with-c_extension=hello --with-c_extension_bundle-dir=hola"
+ bundle_config "build.c_extension --with-c_extension=hello --with-c_extension_bundle-dir=hola"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "c_extension", :git => #{lib_path("c_extension-1.0").to_s.dump}
G
diff --git a/spec/bundler/install/gems/no_build_extension_spec.rb b/spec/bundler/install/gems/no_build_extension_spec.rb
new file mode 100644
index 0000000000..31f0170433
--- /dev/null
+++ b/spec/bundler/install/gems/no_build_extension_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with --no-build-extension" do
+ before do
+ build_repo2 do
+ build_gem "with_extension" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("lib", __dir__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/with_extension.rb", "w") do |f|
+ f.puts "WITH_EXTENSION = 'YES'"
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ it "skips building native extensions and warns when no_build_extension is set" do
+ bundle_config "no_build_extension true"
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_extension"
+ gem "rake"
+ G
+
+ bundle :install
+
+ build_complete = default_bundle_path("extensions").join(
+ Gem::Platform.local.to_s,
+ Gem.extension_api_version.to_s,
+ "with_extension-1.0",
+ "gem.build_complete"
+ )
+ expect(build_complete).not_to exist
+ expect(err).to include("with_extension-1.0 contains native extensions that were not built")
+ expect(err).to include("unset no_build_extension and run `bundle pristine with_extension`")
+ end
+
+ it "builds native extensions by default" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_extension"
+ gem "rake"
+ G
+
+ bundle :install
+
+ expect(out).to include("Installing with_extension 1.0 with native extensions")
+ end
+end
diff --git a/spec/bundler/install/gems/no_install_plugin_spec.rb b/spec/bundler/install/gems/no_install_plugin_spec.rb
new file mode 100644
index 0000000000..e040e6b813
--- /dev/null
+++ b/spec/bundler/install/gems/no_install_plugin_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with --no-install-plugin" do
+ before do
+ build_repo2 do
+ build_gem "with_plugin", "1.0" do |s|
+ s.write "lib/rubygems_plugin.rb", "# plugin code"
+ end
+
+ build_gem "with_plugin", "2.0"
+ end
+ end
+
+ let(:plugin_path) { default_bundle_path("plugins", "with_plugin_plugin.rb") }
+
+ it "does not generate the plugin wrapper and warns when no_install_plugin is set" do
+ bundle_config "no_install_plugin true"
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_plugin", "1.0"
+ G
+
+ expect(plugin_path).not_to exist
+ expect(err).to include("with_plugin-1.0 contains plugins that were not installed")
+ expect(err).to include("unset no_install_plugin and run `bundle pristine with_plugin`")
+ end
+
+ it "removes a stale plugin wrapper from a prior version when no_install_plugin is set" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_plugin", "1.0"
+ G
+ expect(plugin_path).to exist
+
+ bundle_config "no_install_plugin true"
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_plugin", "2.0"
+ G
+
+ expect(plugin_path).not_to exist
+ end
+
+ it "generates the plugin wrapper by default" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_plugin", "1.0"
+ G
+
+ expect(plugin_path).to exist
+ end
+end
diff --git a/spec/bundler/install/gems/post_install_spec.rb b/spec/bundler/install/gems/post_install_spec.rb
index 3f6d7ce42c..e49fd2a9a3 100644
--- a/spec/bundler/install/gems/post_install_spec.rb
+++ b/spec/bundler/install/gems/post_install_spec.rb
@@ -5,26 +5,26 @@ RSpec.describe "bundle install" do
context "when gems include post install messages" do
it "should display the post-install messages after installing" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
gem 'thin'
- gem 'rack-obama'
+ gem 'myrack-obama'
G
bundle :install
- expect(out).to include("Post-install message from rack:")
- expect(out).to include("Rack's post install message")
+ expect(out).to include("Post-install message from myrack:")
+ expect(out).to include("Myrack's post install message")
expect(out).to include("Post-install message from thin:")
expect(out).to include("Thin's post install message")
- expect(out).to include("Post-install message from rack-obama:")
- expect(out).to include("Rack-obama's post install message")
+ expect(out).to include("Post-install message from myrack-obama:")
+ expect(out).to include("Myrack-obama's post install message")
end
end
context "when gems do not include post install messages" do
it "should not display any post-install messages" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport"
G
@@ -33,16 +33,16 @@ 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)}"
- gem 'rack_middleware'
+ source "https://gem.repo1"
+ gem 'myrack_middleware'
G
bundle :install
- expect(out).to include("Post-install message from rack:")
- expect(out).to include("Rack's post install message")
+ expect(out).to include("Post-install message from myrack:")
+ expect(out).to include("Myrack's post install message")
end
end
end
@@ -54,7 +54,7 @@ RSpec.describe "bundle install" do
s.post_install_message = "Foo's post install message"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem 'foo', :git => '#{lib_path("foo-1.0")}'
G
@@ -68,7 +68,7 @@ RSpec.describe "bundle install" do
s.post_install_message = "Foo's post install message"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem 'foo', :git => '#{lib_path("foo-1.0")}'
G
bundle :install
@@ -77,7 +77,7 @@ RSpec.describe "bundle install" do
s.post_install_message = "Foo's 1.1 post install message"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem 'foo', :git => '#{lib_path("foo-1.1")}'
G
bundle :install
@@ -91,7 +91,7 @@ RSpec.describe "bundle install" do
s.post_install_message = "Foo's post install message"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem 'foo', :git => '#{lib_path("foo-1.0")}'
G
@@ -110,7 +110,7 @@ RSpec.describe "bundle install" do
s.post_install_message = nil
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem 'foo', :git => '#{lib_path("foo-1.0")}'
G
@@ -123,11 +123,11 @@ RSpec.describe "bundle install" do
context "when ignore post-install messages for gem is set" do
it "doesn't display any post-install messages" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "config set ignore_messages.rack true"
+ bundle_config "ignore_messages.myrack true"
bundle :install
expect(out).not_to include("Post-install message")
@@ -137,11 +137,11 @@ RSpec.describe "bundle install" do
context "when ignore post-install messages for all gems" do
it "doesn't display any post-install messages" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "config set ignore_messages true"
+ bundle_config "ignore_messages true"
bundle :install
expect(out).not_to include("Post-install message")
diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb
index f72220ef21..111d361aab 100644
--- a/spec/bundler/install/gems/resolving_spec.rb
+++ b/spec/bundler/install/gems/resolving_spec.rb
@@ -1,9 +1,72 @@
# frozen_string_literal: true
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", __dir__)
+ 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", __dir__)
+ 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"
+ s.add_dependency "net_build_extensions"
+ end
+
+ build_gem "net_b"
+
+ build_gem "net_build_extensions" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("lib", __dir__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/net_build_extensions.rb", "w") do |f|
+ f.puts "NET_BUILD_EXTENSIONS = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ build_gem "net_c" do |s|
+ s.add_dependency "net_a"
+ s.add_dependency "net_d"
+ end
+
+ build_gem "net_d"
+
+ build_gem "net_e" do |s|
+ s.add_dependency "net_d"
+ end
+ end
+ end
+
it "installs gems with implicit rake dependencies" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "with_implicit_rake_dep"
gem "another_implicit_rake_dep"
gem "rake"
@@ -18,32 +81,51 @@ RSpec.describe "bundle install with install-time dependencies" do
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?
+ it "installs gems with implicit rake dependencies without rake previously installed" do
+ with_path_as("") do
+ install_gemfile <<-G
+ source "https://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 "does not install gems with a dependency with no type" do
build_repo2
path = "#{gem_repo2}/#{Gem::MARSHAL_SPEC_DIR}/actionpack-2.3.2.gemspec.rz"
spec = Marshal.load(Bundler.rubygems.inflate(File.binread(path)))
spec.dependencies.each do |d|
- d.instance_variable_set(:@type, :fail)
+ d.instance_variable_set(:@type, "fail")
end
- File.open(path, "w") do |f|
+ File.open(path, "wb") do |f|
f.write Gem.deflate(Marshal.dump(spec))
end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
gem "actionpack", "2.3.2"
G
- expect(the_bundle).to include_gems "actionpack 2.3.2", "activesupport 2.3.2"
+ expect(err).to include("Downloading actionpack-2.3.2 revealed dependencies not in the API (activesupport (= 2.3.2)).")
+
+ expect(the_bundle).not_to include_gems "actionpack 2.3.2", "activesupport 2.3.2"
end
describe "with crazy rubygem plugin stuff" do
it "installs plugins" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "net_b"
G
@@ -51,8 +133,8 @@ RSpec.describe "bundle install with install-time dependencies" do
end
it "installs plugins depended on by other plugins" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, env: { "DEBUG" => "1" }
+ source "https://gem.repo2"
gem "net_a"
G
@@ -60,8 +142,8 @@ RSpec.describe "bundle install with install-time dependencies" do
end
it "installs multiple levels of dependencies" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, env: { "DEBUG" => "1" }
+ source "https://gem.repo2"
gem "net_c"
gem "net_e"
G
@@ -72,115 +154,485 @@ RSpec.describe "bundle install with install-time dependencies" do
context "with ENV['BUNDLER_DEBUG_RESOLVER'] set" do
it "produces debug output" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "net_c"
gem "net_e"
G
- bundle :install, :env => { "BUNDLER_DEBUG_RESOLVER" => "1" }
+ bundle :install, env: { "BUNDLER_DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
- expect(err).to include("BUNDLER: Starting resolution")
+ expect(out).to include("Resolving dependencies...")
end
end
context "with ENV['DEBUG_RESOLVER'] set" do
it "produces debug output" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "net_c"
gem "net_e"
G
- bundle :install, :env => { "DEBUG_RESOLVER" => "1" }
+ bundle :install, env: { "DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
- expect(err).to include("BUNDLER: Starting resolution")
+ expect(out).to include("Resolving dependencies...")
end
end
context "with ENV['DEBUG_RESOLVER_TREE'] set" do
it "produces debug output" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "net_c"
gem "net_e"
G
- bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1" }
+ bundle :install, env: { "DEBUG_RESOLVER_TREE" => "1", "DEBUG" => "1" }
- expect(err).to include(" net_b").
- and include("BUNDLER: Starting resolution").
- and include("BUNDLER: Finished resolution").
- and include("Attempting to activate")
+ expect(out).to include(" net_b").
+ and include("Resolving dependencies...").
+ and include("Solution found after 1 attempts:").
+ and include("selected net_b 1.0")
end
end
end
describe "when a required ruby version" do
context "allows only an older version" do
- before do
- skip "gem not found" if Gem.win_platform?
+ it "installs the older version" do
+ build_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
+ build_gem "myrack", "9001.0.0" do |s|
+ s.required_ruby_version = "> 9000"
+ end
+ end
+
+ install_gemfile <<-G
+ ruby "#{Gem.ruby_version}"
+ source "https://gem.repo2"
+ gem 'myrack'
+ G
+
+ expect(err).to_not include("myrack-9001.0.0 requires ruby version > 9000")
+ expect(the_bundle).to include_gems("myrack 1.2")
end
- it "installs the older version" do
+ it "installs the older version when using servers not implementing the compact index API" do
build_repo2 do
- build_gem "rack", "9001.0.0" do |s|
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+
+ build_gem "myrack", "9001.0.0" do |s|
s.required_ruby_version = "> 9000"
end
end
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
- ruby "#{RUBY_VERSION}"
- source "http://localgemserver.test/"
- gem 'rack'
+ install_gemfile <<-G, artifice: "endpoint"
+ ruby "#{Gem.ruby_version}"
+ source "https://gem.repo2"
+ gem 'myrack'
+ G
+
+ expect(err).to_not include("myrack-9001.0.0 requires ruby version > 9000")
+ expect(the_bundle).to include_gems("myrack 1.2")
+ end
+
+ context "when there is a lockfile using the newer incompatible version" do
+ before do
+ build_repo2 do
+ build_gem "parallel_tests", "3.7.0" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+ end
+
+ build_gem "parallel_tests", "3.8.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem 'parallel_tests'
+ G
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo2, "parallel_tests", "3.8.0"
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ parallel_tests (3.8.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parallel_tests
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically updates lockfile to use the older version" do
+ bundle "install --verbose"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "parallel_tests", "3.7.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ parallel_tests (3.7.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parallel_tests
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "gives a meaningful error if we're in frozen mode" do
+ expect do
+ bundle "install", env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ end.not_to change { lockfile }
+
+ expect(err).to eq("parallel_tests-3.8.0 requires ruby version >= #{next_ruby_minor}, which is incompatible with the current version, #{Gem.ruby_version}")
+ end
+ end
+
+ context "with transitive dependencies in a lockfile" do
+ before do
+ build_repo2 do
+ build_gem "rubocop", "1.28.2" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+
+ s.add_dependency "rubocop-ast", ">= 1.17.0", "< 2.0"
+ end
+
+ build_gem "rubocop", "1.35.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+
+ s.add_dependency "rubocop-ast", ">= 1.20.1", "< 2.0"
+ end
+
+ build_gem "rubocop-ast", "1.17.0" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+ end
+
+ build_gem "rubocop-ast", "1.21.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem 'rubocop'
+ G
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo2, "rubocop", "1.35.0"
+ c.checksum gem_repo2, "rubocop-ast", "1.21.0"
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ rubocop (1.35.0)
+ rubocop-ast (>= 1.20.1, < 2.0)
+ rubocop-ast (1.21.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rubocop
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically updates lockfile to use the older compatible versions" do
+ bundle "install --verbose"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "rubocop", "1.28.2"
+ c.checksum gem_repo2, "rubocop-ast", "1.17.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ rubocop (1.28.2)
+ rubocop-ast (>= 1.17.0, < 2.0)
+ rubocop-ast (1.17.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rubocop
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with a Gemfile and lockfile that don't resolve under the current platform" do
+ before do
+ build_repo4 do
+ build_gem "sorbet", "0.5.10554" do |s|
+ s.add_dependency "sorbet-static", "0.5.10554"
+ end
+
+ build_gem "sorbet-static", "0.5.10554" do |s|
+ s.platform = "universal-darwin-21"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem 'sorbet', '= 0.5.10554'
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet (0.5.10554)
+ sorbet-static (= 0.5.10554)
+ sorbet-static (0.5.10554-universal-darwin-21)
+
+ PLATFORMS
+ arm64-darwin-21
+
+ DEPENDENCIES
+ sorbet (= 0.5.10554)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a proper error" do
+ simulate_platform "aarch64-linux" do
+ bundle "install", raise_on_error: false
+ end
+
+ nice_error = <<~E.strip
+ Could not find gems matching 'sorbet-static (= 0.5.10554)' valid for all resolution platforms (arm64-darwin-21, aarch64-linux) in rubygems repository https://gem.repo4/ or installed locally.
+
+ The source contains the following gems matching 'sorbet-static (= 0.5.10554)':
+ * sorbet-static-0.5.10554-universal-darwin-21
+ E
+ expect(err).to include(nice_error)
+ expect(err).to include("Your current platform (aarch64-linux) is not included in the lockfile's platforms (arm64-darwin-21)")
+ expect(err).to include("bundle lock --add-platform aarch64-linux")
+ end
+ end
+
+ context "when adding a new gem that does not resolve under all locked platforms" do
+ before do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "arm-linux"
+ end
+
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.0-arm-linux)
+ nokogiri (1.14.0-x86_64-linux)
+
+ PLATFORMS
+ arm-linux
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+
+ bundle "lock", raise_on_error: false
+ end
+ end
+
+ it "raises a proper error" do
+ nice_error = <<~E.strip
+ Could not find gems matching 'sorbet-static' valid for all resolution platforms (arm-linux, x86_64-linux) in rubygems repository https://gem.repo4/ or installed locally.
+
+ The source contains the following gems matching 'sorbet-static':
+ * sorbet-static-0.5.10696-x86_64-linux
+ E
+ expect(err).to end_with(nice_error)
+ end
+ end
+
+ context "when locked generic variant supports current Ruby, but locked specific variant does not" do
+ let(:original_lockfile) do
+ <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.16.3)
+ nokogiri (1.16.3-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ before do
+ build_repo4 do
+ build_gem "nokogiri", "1.16.3"
+ build_gem "nokogiri", "1.16.3" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ lockfile original_lockfile
+ end
+
+ it "keeps both variants in the lockfile when installing, and uses the generic one since it's compatible" do
+ simulate_platform "x86_64-linux" do
+ bundle "install --verbose"
+
+ expect(lockfile).to eq(original_lockfile)
+ expect(the_bundle).to include_gems("nokogiri 1.16.3")
+ end
+ end
+
+ it "keeps both variants in the lockfile when updating, and uses the generic one since it's compatible" do
+ simulate_platform "x86_64-linux" do
+ bundle "update --verbose"
+
+ expect(lockfile).to eq(original_lockfile)
+ expect(the_bundle).to include_gems("nokogiri 1.16.3")
+ end
+ end
+ end
+
+ it "gives a meaningful error on ruby version mismatches between dependencies" do
+ build_repo4 do
+ build_gem "requires-old-ruby" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ build_lib("foo", path: bundled_app) do |s|
+ s.required_ruby_version = ">= #{Gem.ruby_version}"
+
+ s.add_dependency "requires-old-ruby"
+ end
+
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo4"
+ gemspec
G
- expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
- expect(the_bundle).to include_gems("rack 1.2")
+ expect(err).to end_with <<~E.strip
+ Could not find compatible versions
+
+ Because every version of foo depends on requires-old-ruby >= 0
+ and every version of requires-old-ruby depends on Ruby < #{Gem.ruby_version},
+ every version of foo requires Ruby < #{Gem.ruby_version}.
+ So, because Gemfile depends on foo >= 0
+ and current Ruby version is = #{Gem.ruby_version},
+ version solving has failed.
+ E
end
it "installs the older version under rate limiting conditions" do
build_repo4 do
- build_gem "rack", "9001.0.0" do |s|
+ build_gem "myrack", "9001.0.0" do |s|
s.required_ruby_version = "> 9000"
end
- build_gem "rack", "1.2"
+ build_gem "myrack", "1.2"
build_gem "foo1", "1.0"
end
- install_gemfile <<-G, :artifice => "compact_index_rate_limited", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
- ruby "#{RUBY_VERSION}"
- source "http://localgemserver.test/"
- gem 'rack'
+ install_gemfile <<-G, artifice: "compact_index_rate_limited"
+ ruby "#{Gem.ruby_version}"
+ source "https://gem.repo4"
+ gem 'myrack'
gem 'foo1'
G
- expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
- expect(the_bundle).to include_gems("rack 1.2")
+ expect(err).to_not include("myrack-9001.0.0 requires ruby version > 9000")
+ expect(the_bundle).to include_gems("myrack 1.2")
end
it "installs the older not platform specific version" do
build_repo4 do
- build_gem "rack", "9001.0.0" do |s|
+ build_gem "myrack", "9001.0.0" do |s|
s.required_ruby_version = "> 9000"
end
- build_gem "rack", "1.2" do |s|
- s.platform = mingw
+ build_gem "myrack", "1.2" do |s|
+ s.platform = "x86-mingw32"
s.required_ruby_version = "> 9000"
end
- build_gem "rack", "1.2"
+ build_gem "myrack", "1.2"
end
- simulate_platform mingw do
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
- ruby "#{RUBY_VERSION}"
- source "http://localgemserver.test/"
- gem 'rack'
+ simulate_platform "x86-mingw32" do
+ install_gemfile <<-G, artifice: "compact_index"
+ ruby "#{Gem.ruby_version}"
+ source "https://gem.repo4"
+ gem 'myrack'
G
end
- expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
- expect(out).to_not include("rack-1.2-#{Bundler.local_platform} requires ruby version > 9000")
- expect(the_bundle).to include_gems("rack 1.2")
+ expect(err).to_not include("myrack-9001.0.0 requires ruby version > 9000")
+ expect(err).to_not include("myrack-1.2-#{Bundler.local_platform} requires ruby version > 9000")
+ expect(the_bundle).to include_gems("myrack 1.2")
end
end
@@ -193,37 +645,47 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
- let(:ruby_requirement) { %("#{RUBY_VERSION}") }
- let(:error_message_requirement) { "~> #{RUBY_VERSION}.0" }
- let(:error_message_platform) do
- if Bundler.feature_flag.specific_platform?
- " #{Bundler.local_platform}"
- else
- ""
- end
+ let(:ruby_requirement) { %("#{Gem.ruby_version}") }
+ let(:error_message_requirement) { "= #{Gem.ruby_version}" }
+
+ it "raises a proper error that mentions the current Ruby version during resolution" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
+ gem 'require_ruby'
+ G
+
+ expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
+
+ nice_error = <<~E.strip
+ Could not find compatible versions
+
+ Because every version of require_ruby depends on Ruby > 9000
+ and Gemfile depends on require_ruby >= 0,
+ Ruby > 9000 is required.
+ So, because current Ruby version is #{error_message_requirement},
+ version solving has failed.
+ E
+ expect(err).to end_with(nice_error)
end
shared_examples_for "ruby version conflicts" do
it "raises an error during resolution" do
- skip "ruby requirement includes platform and it shouldn't" if Gem.win_platform?
-
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :raise_on_error => false
- source "http://localgemserver.test/"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
ruby #{ruby_requirement}
gem 'require_ruby'
G
expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
- nice_error = strip_whitespace(<<-E).strip
- Bundler found conflicting requirements for the Ruby\0 version:
- In Gemfile:
- Ruby\0 (#{error_message_requirement})#{error_message_platform}
-
- require_ruby#{error_message_platform} was resolved to 1.0, which depends on
- Ruby\0 (> 9000)
+ nice_error = <<~E.strip
+ Could not find compatible versions
- Ruby\0 (> 9000), which is required by gem 'require_ruby', is not available in the local ruby installation
+ Because every version of require_ruby depends on Ruby > 9000
+ and Gemfile depends on require_ruby >= 0,
+ Ruby > 9000 is required.
+ So, because current Ruby version is #{error_message_requirement},
+ version solving has failed.
E
expect(err).to end_with(nice_error)
end
@@ -233,7 +695,6 @@ RSpec.describe "bundle install with install-time dependencies" do
describe "with a < requirement" do
let(:ruby_requirement) { %("< 5000") }
- let(:error_message_requirement) { Gem::Requirement.new(["< 5000", "= #{Bundler::RubyVersion.system.to_gem_version_with_patchlevel}"]).to_s }
it_behaves_like "ruby version conflicts"
end
@@ -241,7 +702,6 @@ RSpec.describe "bundle install with install-time dependencies" do
describe "with a compound requirement" do
let(:reqs) { ["> 0.1", "< 5000"] }
let(:ruby_requirement) { reqs.map(&:dump).join(", ") }
- let(:error_message_requirement) { Gem::Requirement.new(reqs + ["= #{Bundler::RubyVersion.system.to_gem_version_with_patchlevel}"]).to_s }
it_behaves_like "ruby version conflicts"
end
@@ -256,13 +716,71 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo2)}"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2"
gem 'require_rubygems'
G
expect(err).to_not include("Gem::InstallError: require_rubygems requires RubyGems version > 9000")
- expect(err).to include("require_rubygems-1.0 requires rubygems version > 9000, which is incompatible with the current version, #{Gem::VERSION}")
+ nice_error = <<~E.strip
+ Because every version of require_rubygems depends on RubyGems > 9000
+ and Gemfile depends on require_rubygems >= 0,
+ RubyGems > 9000 is required.
+ So, because current RubyGems version is = #{Gem::VERSION},
+ version solving has failed.
+ E
+ expect(err).to end_with(nice_error)
+ end
+ end
+
+ context "when non platform specific gems bring more dependencies", :truffleruby_only do
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0" do |s|
+ s.add_dependency "bar"
+ end
+
+ build_gem "foo", "2.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem "bar"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "foo"
+ G
+ end
+
+ it "locks both ruby and current platform, and resolve to ruby variants that install on truffleruby" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "foo", "1.0"
+ c.checksum gem_repo4, "bar", "1.0"
+ end
+
+ simulate_platform "x86_64-linux" do
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ bar (1.0)
+ foo (1.0)
+ bar
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ foo
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
end
end
end
diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb
index 503cc4a4f0..96a305bb76 100644
--- a/spec/bundler/install/gems/standalone_spec.rb
+++ b/spec/bundler/install/gems/standalone_spec.rb
@@ -1,12 +1,18 @@
# frozen_string_literal: true
-RSpec.shared_examples "bundle install --standalone" do
+RSpec.describe "bundle install --standalone" do
shared_examples "common functionality" do
it "still makes the gems available to normal bundler" do
args = expected_gems.map {|k, v| "#{k} #{v}" }
expect(the_bundle).to include_gems(*args)
end
+ it "still makes system gems unavailable to normal bundler" do
+ system_gems "myrack-1.0.0"
+
+ expect(the_bundle).to_not include_gems("myrack")
+ end
+
it "generates a bundle/bundler/setup.rb" do
expect(bundled_app("bundle/bundler/setup.rb")).to exist
end
@@ -26,6 +32,54 @@ RSpec.shared_examples "bundle install --standalone" do
expect(out).to eq(expected_gems.values.join("\n"))
end
+ it "makes the gems available without bundler nor rubygems" do
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ RUBY
+ expected_gems.each do |k, _|
+ testrb << "\nrequire \"#{k}\""
+ testrb << "\nputs #{k.upcase}"
+ end
+ in_bundled_app %(#{Gem.ruby} --disable-gems -w -e #{testrb.shellescape})
+
+ expect(out).to eq(expected_gems.values.join("\n"))
+ end
+
+ it "makes the gems available without bundler via Kernel.require" do
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ RUBY
+ expected_gems.each do |k, _|
+ testrb << "\nKernel.require \"#{k}\""
+ testrb << "\nputs #{k.upcase}"
+ end
+ ruby testrb
+
+ expect(out).to eq(expected_gems.values.join("\n"))
+ end
+
+ it "makes system gems unavailable without bundler" do
+ system_gems "myrack-1.0.0"
+
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ begin
+ require "myrack"
+ rescue LoadError
+ puts "LoadError"
+ end
+ RUBY
+ ruby testrb
+
+ expect(out).to eq("LoadError")
+ end
+
it "works on a different system" do
begin
FileUtils.mv(bundled_app, "#{bundled_app}2")
@@ -43,7 +97,23 @@ RSpec.shared_examples "bundle install --standalone" do
testrb << "\nrequire \"#{k}\""
testrb << "\nputs #{k.upcase}"
end
- ruby testrb, :dir => "#{bundled_app}2"
+ ruby testrb, dir: "#{bundled_app}2"
+
+ expect(out).to eq(expected_gems.values.join("\n"))
+ end
+
+ it "skips activating gems" do
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ gem "do_not_activate_me"
+ RUBY
+ expected_gems.each do |k, _|
+ testrb << "\nrequire \"#{k}\""
+ testrb << "\nputs #{k.upcase}"
+ end
+ in_bundled_app %(#{Gem.ruby} -w -e #{testrb.shellescape})
expect(out).to eq(expected_gems.values.join("\n"))
end
@@ -52,11 +122,11 @@ RSpec.shared_examples "bundle install --standalone" do
describe "with simple gems" do
before do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
- bundle "config --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :dir => cwd
+ bundle_config "path #{bundled_app("bundle")}"
+ bundle :install, standalone: true, dir: cwd
end
let(:expected_gems) do
@@ -69,29 +139,162 @@ RSpec.shared_examples "bundle install --standalone" do
include_examples "common functionality"
end
- describe "with gems with native extension", :ruby_repo do
+ describe "with default gems and a lockfile", :ruby_repo do
+ it "works and points to the vendored copies, not to the default copies" do
+ base_system_gems "stringio", "psych", "etc", 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
+
+ bundle_config "path #{bundled_app("bundle")}"
+
+ bundle :install, standalone: true, dir: cwd, env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
+
+ load_path_lines = bundled_app("bundle/bundler/setup.rb").read.split("\n").select {|line| line.start_with?("$:.unshift") }
+
+ expect(load_path_lines).to eq [
+ '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/bar-1.0.0/lib")',
+ '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/foo-1.0.0/lib")',
+ ]
+ end
+
+ it "works for gems with extensions and points to the vendored copies, not to the default copies" do
+ simulate_platform "arm64-darwin-23" do
+ base_system_gems "stringio", "psych", "etc", "shellwords", "open3", path: scoped_gem_path(bundled_app("bundle"))
+
+ build_gem "baz", "1.0.0", to_system: true, default: true, &:add_c_extension
+
+ build_repo4 do
+ build_gem "baz", "1.0.0", &:add_c_extension
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "baz"
+ G
+
+ bundle_config "path #{bundled_app("bundle")}"
+
+ bundle "lock", dir: cwd
+
+ bundle :install, standalone: true, dir: cwd, env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
+ end
+
+ load_path_lines = bundled_app("bundle/bundler/setup.rb").read.split("\n").select {|line| line.start_with?("$:.unshift") }
+
+ expect(load_path_lines).to eq [
+ '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/arm64-darwin-23/#{Gem.extension_api_version}/baz-1.0.0")',
+ '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/baz-1.0.0/lib")',
+ ]
+ end
+ end
+
+ describe "with Gemfiles using absolute 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 "https://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
+
+ let(:cwd) { bundled_app }
+
+ describe "with Gemfiles using relative path sources and app moved to a different root" do
before do
- bundle "config --local path #{bundled_app("bundle")}"
- install_gemfile <<-G, :standalone => true, :dir => cwd
- source "#{file_uri_for(gem_repo1)}"
+ FileUtils.mkdir_p bundled_app("app/vendor")
+
+ build_lib "minitest", "1.0.0", path: bundled_app("app/vendor/minitest")
+
+ gemfile bundled_app("app/Gemfile"), <<-G
+ source "https://gem.repo1"
+ gem "minitest", :path => "vendor/minitest"
+ G
+
+ bundle "install", standalone: true, dir: bundled_app("app")
+
+ FileUtils.mv(bundled_app("app"), bundled_app2("app"))
+ end
+
+ it "also works" do
+ ruby <<-RUBY, dir: bundled_app2("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" do
+ before do
+ bundle_config "path #{bundled_app("bundle")}"
+ install_gemfile <<-G, standalone: true, dir: cwd
+ source "https://gem.repo1"
gem "very_simple_binary"
G
end
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 "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/'
- expect(extension_line).to end_with '/very_simple_binary-1.0"'
+ script_content = File.read(expected_path)
+ expect(script_content).to include("def self.ruby_api_version")
+ expect(script_content).to include("def self.extension_api_version")
+ extension_line = script_content.each_line.find {|line| line.include? "/extensions/" }.strip
+ platform = Gem::Platform.local
+ expect(extension_line).to start_with '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/'
+ expect(extension_line).to end_with platform.to_s + '/#{Gem.extension_api_version}/very_simple_binary-1.0")'
end
end
describe "with gem that has an invalid gemspec" do
before do
- build_git "bar", :gemspec => false do |s|
+ build_git "bar", gemspec: false do |s|
s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
s.write "bar.gemspec", <<-G
- lib = File.expand_path('../lib/', __FILE__)
+ lib = File.expand_path('lib/', __dir__)
$:.unshift lib unless $:.include?(lib)
require 'bar/version'
@@ -105,15 +308,16 @@ RSpec.shared_examples "bundle install --standalone" do
end
G
end
- bundle "config --local path #{bundled_app("bundle")}"
- install_gemfile <<-G, :standalone => true, :dir => cwd, :raise_on_error => false
+ bundle_config "path #{bundled_app("bundle")}"
+ install_gemfile <<-G, standalone: true, dir: cwd, raise_on_error: false
+ source "https://gem.repo1"
gem "bar", :git => "#{lib_path("bar-1.0")}"
G
end
it "outputs a helpful error message" do
expect(err).to include("You have one or more invalid gemspecs that need to be fixed.")
- expect(err).to include("bar 1.0 has an invalid gemspec")
+ expect(err).to include("bar.gemspec is not valid")
end
end
@@ -122,12 +326,12 @@ RSpec.shared_examples "bundle install --standalone" do
build_git "devise", "1.0"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
gem "devise", :git => "#{lib_path("devise-1.0")}"
G
- bundle "config --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :dir => cwd
+ bundle_config "path #{bundled_app("bundle")}"
+ bundle :install, standalone: true, dir: cwd
end
let(:expected_gems) do
@@ -146,16 +350,16 @@ RSpec.shared_examples "bundle install --standalone" do
build_git "devise", "1.0"
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
group :test do
gem "rspec"
- gem "rack-test"
+ gem "myrack-test"
end
G
- bundle "config --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :dir => cwd
+ bundle_config "path #{bundled_app("bundle")}"
+ bundle :install, standalone: true, dir: cwd
end
let(:expected_gems) do
@@ -168,8 +372,8 @@ 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 :install, :standalone => "default", :dir => cwd
+ bundle_config "path #{bundled_app("bundle")}"
+ bundle :install, standalone: "default", dir: cwd
load_error_ruby <<-RUBY, "spec"
$:.unshift File.expand_path("bundle")
@@ -181,13 +385,13 @@ RSpec.shared_examples "bundle install --standalone" do
RUBY
expect(out).to eq("2.3.2")
- expect(err).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- spec/)
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 :install, :standalone => true, :dir => cwd
+ bundle_config "path #{bundled_app("bundle")}"
+ bundle_config "without test"
+ bundle :install, standalone: true, dir: cwd
load_error_ruby <<-RUBY, "spec"
$:.unshift File.expand_path("bundle")
@@ -199,12 +403,12 @@ RSpec.shared_examples "bundle install --standalone" do
RUBY
expect(out).to eq("2.3.2")
- expect(err).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- spec/)
end
it "allows `path` configuration to change the location of the standalone bundle" do
- bundle "config --local path path/to/bundle"
- bundle "install", :standalone => true, :dir => cwd
+ bundle_config "path path/to/bundle"
+ bundle "install", standalone: true, dir: cwd
ruby <<-RUBY
$:.unshift File.expand_path("path/to/bundle")
@@ -218,10 +422,10 @@ 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 :install, :dir => cwd
- bundle "config --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :dir => cwd
+ bundle_config "without test"
+ bundle :install, dir: cwd
+ bundle_config "path #{bundled_app("bundle")}"
+ bundle :install, standalone: true, dir: cwd
load_error_ruby <<-RUBY, "spec"
$:.unshift File.expand_path("bundle")
@@ -233,7 +437,7 @@ RSpec.shared_examples "bundle install --standalone" do
RUBY
expect(out).to eq("2.3.2")
- expect(err).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- spec/)
end
end
@@ -246,8 +450,8 @@ RSpec.shared_examples "bundle install --standalone" do
source "#{source_uri}"
gem "rails"
G
- bundle "config --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :artifice => "endpoint", :dir => cwd
+ bundle_config "path #{bundled_app("bundle")}"
+ bundle :install, standalone: true, artifice: "endpoint", dir: cwd
end
let(:expected_gems) do
@@ -260,63 +464,61 @@ RSpec.shared_examples "bundle install --standalone" do
include_examples "common functionality"
end
end
+end
- describe "with --binstubs", :bundler => "< 3" do
- before do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
- G
- bundle "config --local path #{bundled_app("bundle")}"
- bundle :install, :standalone => true, :binstubs => true, :dir => cwd
- end
+RSpec.describe "bundle install --standalone run in a subdirectory" do
+ let(:cwd) { bundled_app("bob").tap(&:mkpath) }
- let(:expected_gems) do
- {
- "actionpack" => "2.3.2",
- "rails" => "2.3.2",
- }
- end
+ before do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails"
+ G
+ end
- include_examples "common functionality"
+ it "generates the script in the proper place" do
+ bundle :install, standalone: true, dir: cwd
- it "creates stubs that use the standalone load path" do
- expect(sys_exec("bin/rails -v").chomp).to eql "2.3.2"
- end
+ expect(bundled_app("bundle/bundler/setup.rb")).to exist
+ end
- it "creates stubs that can be executed from anywhere" do
- require "tmpdir"
- sys_exec(%(#{bundled_app("bin/rails")} -v), :dir => Dir.tmpdir)
- expect(out).to eq("2.3.2")
+ context "when path set to a relative path" do
+ before do
+ bundle_config "path bundle"
end
- it "creates stubs that can be symlinked" do
- skip "symlinks unsupported" if Gem.win_platform?
+ it "generates the script in the proper place" do
+ bundle :install, standalone: true, dir: cwd
- symlink_dir = tmp("symlink")
- FileUtils.mkdir_p(symlink_dir)
- symlink = File.join(symlink_dir, "rails")
-
- File.symlink(bundled_app("bin/rails"), symlink)
- sys_exec("#{symlink} -v")
- expect(out).to eq("2.3.2")
- end
-
- it "creates stubs with the correct load path" do
- extension_line = File.read(bundled_app("bin/rails")).each_line.find {|line| line.include? "$:.unshift" }.strip
- expect(extension_line).to eq %($:.unshift File.expand_path "../../bundle", path.realpath)
+ expect(bundled_app("bundle/bundler/setup.rb")).to exist
end
end
end
-RSpec.describe "bundle install --standalone" do
- let(:cwd) { bundled_app }
+RSpec.describe "bundle install --standalone --local" do
+ before do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
- include_examples("bundle install --standalone")
-end
+ system_gems "myrack-1.0.0", path: default_bundle_path
+ end
-RSpec.describe "bundle install --standalone run in a subdirectory" do
- let(:cwd) { bundled_app("bob").tap(&:mkpath) }
+ it "generates script pointing to system gems" do
+ bundle "install --standalone --local --verbose"
- include_examples("bundle install --standalone")
+ expect(out).to include("Using myrack 1.0.0")
+
+ load_error_ruby <<-RUBY, "spec"
+ require "./bundler/setup"
+
+ require "myrack"
+ puts MYRACK
+ require "spec"
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err_without_deprecations).to match(/cannot load such file -- spec/)
+ end
end
diff --git a/spec/bundler/install/gems/sudo_spec.rb b/spec/bundler/install/gems/sudo_spec.rb
deleted file mode 100644
index ff73b4a1fa..0000000000
--- a/spec/bundler/install/gems/sudo_spec.rb
+++ /dev/null
@@ -1,190 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "when using sudo", :sudo => true do
- describe "and BUNDLE_PATH is writable" do
- context "but BUNDLE_PATH/build_info is not writable" do
- let(:subdir) do
- system_gem_path("cache")
- end
-
- before do
- bundle "config set path.system true"
- subdir.mkpath
- sudo "chmod u-w #{subdir}"
- end
-
- after do
- sudo "chmod u+w #{subdir}"
- end
-
- it "installs" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- expect(out).to_not match(/an error occurred/i)
- expect(system_gem_path("cache/rack-1.0.0.gem")).to exist
- expect(the_bundle).to include_gems "rack 1.0"
- end
- end
- end
-
- describe "and GEM_HOME is owned by root" do
- before :each do
- bundle "config set path.system true"
- chown_system_gems_to_root
- end
-
- it "installs" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- gem "thin"
- G
-
- expect(system_gem_path("gems/rack-1.0.0")).to exist
- expect(system_gem_path("gems/rack-1.0.0").stat.uid).to eq(0)
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "installs rake and a gem dependent on rake in the same session" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rake"
- gem "another_implicit_rake_dep"
- G
- bundle "install"
- expect(system_gem_path("gems/another_implicit_rake_dep-1.0")).to exist
- end
-
- it "installs when BUNDLE_PATH is owned by root" do
- bundle_path = tmp("owned_by_root")
- FileUtils.mkdir_p bundle_path
- sudo "chown -R root #{bundle_path}"
-
- ENV["BUNDLE_PATH"] = bundle_path.to_s
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0")).to exist
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0").stat.uid).to eq(0)
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "installs when BUNDLE_PATH does not exist" do
- root_path = tmp("owned_by_root")
- FileUtils.mkdir_p root_path
- sudo "chown -R root #{root_path}"
- bundle_path = root_path.join("does_not_exist")
-
- ENV["BUNDLE_PATH"] = bundle_path.to_s
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0")).to exist
- expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0").stat.uid).to eq(0)
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "installs extensions/" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "very_simple_binary"
- G
-
- expect(system_gem_path("gems/very_simple_binary-1.0")).to exist
- binary_glob = system_gem_path("extensions/*/*/very_simple_binary-1.0")
- expect(Dir.glob(binary_glob).first).to be
- end
- end
-
- describe "and BUNDLE_PATH is not writable" do
- before do
- bundle "config set --local path .bundle"
- sudo "chmod ugo-w .bundle"
- end
-
- after do
- sudo "chmod ugo+w .bundle"
- end
-
- it "installs" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- expect(local_gem_path("gems/rack-1.0.0")).to exist
- expect(the_bundle).to include_gems "rack 1.0"
- end
-
- it "cleans up the tmpdirs generated" do
- require "tmpdir"
- Dir.glob("#{Dir.tmpdir}/bundler*").each do |tmpdir|
- FileUtils.remove_entry_secure(tmpdir)
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
- tmpdirs = Dir.glob("#{Dir.tmpdir}/bundler*")
-
- expect(tmpdirs).to be_empty
- end
- end
-
- describe "and GEM_HOME is not writable" do
- it "installs" do
- bundle "config set path.system true"
- gem_home = tmp("sudo_gem_home")
- sudo "mkdir -p #{gem_home}"
- sudo "chmod ugo-w #{gem_home}"
-
- system_gems :bundler, :path => gem_home
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", '1.0'
- G
-
- bundle :install, :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil }
- expect(gem_home.join("bin/rackup")).to exist
- expect(the_bundle).to include_gems "rack 1.0", :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil }
-
- sudo "rm -rf #{tmp("sudo_gem_home")}"
- end
- end
-
- describe "and root runs install" do
- let(:warning) { "Don't run Bundler as root." }
-
- before do
- gemfile %(source "#{file_uri_for(gem_repo1)}")
- end
-
- it "warns against that" do
- bundle :install, :sudo => :preserve_env
- expect(err).to include(warning)
- end
-
- context "when ENV['BUNDLE_SILENCE_ROOT_WARNING'] is set" do
- it "skips the warning" do
- bundle :install, :sudo => :preserve_env, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => "true" }
- expect(err).to_not include(warning)
- end
- end
-
- context "when silence_root_warning = false" do
- it "warns against that" do
- bundle :install, :sudo => :preserve_env, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => "false" }
- expect(err).to include(warning)
- end
- end
- end
-end
diff --git a/spec/bundler/install/gems/win32_spec.rb b/spec/bundler/install/gems/win32_spec.rb
index 419b14ff0f..be37673aa1 100644
--- a/spec/bundler/install/gems/win32_spec.rb
+++ b/spec/bundler/install/gems/win32_spec.rb
@@ -4,22 +4,22 @@ RSpec.describe "bundle install with win32-generated lockfile" do
it "should read lockfile" do
File.open(bundled_app_lock, "wb") do |f|
f << "GEM\r\n"
- f << " remote: #{file_uri_for(gem_repo1)}/\r\n"
+ f << " remote: https://gem.repo1/\r\n"
f << " specs:\r\n"
f << "\r\n"
- f << " rack (1.0.0)\r\n"
+ f << " myrack (1.0.0)\r\n"
f << "\r\n"
f << "PLATFORMS\r\n"
f << " ruby\r\n"
f << "\r\n"
f << "DEPENDENCIES\r\n"
- f << " rack\r\n"
+ f << " myrack\r\n"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
- gem "rack"
+ gem "myrack"
G
end
end
diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb
index 0db1f1985b..fb2271c830 100644
--- a/spec/bundler/install/gemspecs_spec.rb
+++ b/spec/bundler/install/gemspecs_spec.rb
@@ -4,13 +4,13 @@ RSpec.describe "bundle install" do
describe "when a gem has a YAML gemspec" do
before :each do
build_repo2 do
- build_gem "yaml_spec", :gemspec => :yaml
+ build_gem "yaml_spec", gemspec: :yaml
end
end
it "still installs correctly" do
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "yaml_spec"
G
bundle :install
@@ -18,9 +18,10 @@ RSpec.describe "bundle install" do
end
it "still installs correctly when using path" do
- build_lib "yaml_spec", :gemspec => :yaml
+ build_lib "yaml_spec", gemspec: :yaml
install_gemfile <<-G
+ source "https://gem.repo1"
gem 'yaml_spec', :path => "#{lib_path("yaml_spec-1.0")}"
G
expect(err).to be_empty
@@ -28,24 +29,24 @@ 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'
+ source "http://localgemserver.test"
+ gem 'myrack'
G
+ system_gems "myrack-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|
+ File.open("#{default_bundle_path}/specifications/myrack-1.0.0.gemspec", "w+") do |f|
spec = Gem::Specification.new do |s|
- s.name = "rack"
+ s.name = "myrack"
s.version = "1.0.0"
- s.add_runtime_dependency "activesupport", "2.3.2"
+ s.add_dependency "activesupport", "2.3.2"
end
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"
+ bundle :install, artifice: "endpoint_marshal_fail" # force gemspec load
+ expect(the_bundle).to include_gems "myrack 1.0.0", "activesupport 2.3.2"
end
it "does not hang when gemspec has incompatible encoding" do
@@ -58,7 +59,8 @@ RSpec.describe "bundle install" do
end
G
- install_gemfile <<-G, :env => { "LANG" => "C" }
+ install_gemfile <<-G, env: { "LANG" => "C" }
+ source "https://gem.repo1"
gemspec
G
@@ -84,6 +86,7 @@ RSpec.describe "bundle install" do
G
install_gemfile <<-G
+ source "https://gem.repo1"
gemspec
G
@@ -91,57 +94,60 @@ RSpec.describe "bundle install" do
end
context "when ruby version is specified in gemspec and gemfile" do
- it "installs when patch level is not specified and the version matches" do
- build_lib("foo", :path => bundled_app) do |s|
+ it "installs when patch level is not specified and the version matches",
+ if: RUBY_PATCHLEVEL >= 0 do
+ build_lib("foo", path: bundled_app) do |s|
s.required_ruby_version = "~> #{RUBY_VERSION}.0"
end
install_gemfile <<-G
ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby'
+ source "https://gem.repo1"
gemspec
G
expect(the_bundle).to include_gems "foo 1.0"
end
it "installs when patch level is specified and the version still matches the current version",
- :if => RUBY_PATCHLEVEL >= 0 do
- build_lib("foo", :path => bundled_app) do |s|
+ if: RUBY_PATCHLEVEL >= 0 do
+ build_lib("foo", path: bundled_app) do |s|
s.required_ruby_version = "#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{RUBY_PATCHLEVEL}'
+ source "https://gem.repo1"
gemspec
G
expect(the_bundle).to include_gems "foo 1.0"
end
- it "fails and complains about patchlevel on patchlevel mismatch",
- :if => RUBY_PATCHLEVEL >= 0 do
+ it "installs gems ignoring the mismatch even when patchlevel is mismatch",
+ if: RUBY_PATCHLEVEL >= 0 do
patchlevel = RUBY_PATCHLEVEL.to_i + 1
- build_lib("foo", :path => bundled_app) do |s|
+ build_lib("foo", path: bundled_app) do |s|
s.required_ruby_version = "#{RUBY_VERSION}.#{patchlevel}"
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{patchlevel}'
+ source "https://gem.repo1"
gemspec
G
- expect(err).to include("Ruby patchlevel")
- expect(err).to include("but your Gemfile specified")
- expect(exitstatus).to eq(18)
+ expect(the_bundle).to include_gems "foo 1.0"
end
it "fails and complains about version on version mismatch" do
version = Gem::Requirement.create(RUBY_VERSION).requirements.first.last.bump.version
- build_lib("foo", :path => bundled_app) do |s|
+ build_lib("foo", path: bundled_app) do |s|
s.required_ruby_version = version
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
ruby '#{version}', :engine_version => '#{version}', :engine => 'ruby'
+ source "https://gem.repo1"
gemspec
G
@@ -149,5 +155,25 @@ RSpec.describe "bundle install" do
expect(err).to include("but your Gemfile specified")
expect(exitstatus).to eq(18)
end
+
+ it "validates gemspecs just once when everything installed and lockfile up to date" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gemspec path: "#{lib_path("foo-1.0")}"
+
+ module Monkey
+ def validate(spec)
+ puts "Validate called on \#{spec.full_name}"
+ end
+ end
+ Bundler.rubygems.extend(Monkey)
+ G
+
+ bundle "install"
+
+ expect(out).to include("Validate called on foo-1.0").once
+ end
end
end
diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb
index 62658137e0..1172d661ae 100644
--- a/spec/bundler/install/git_spec.rb
+++ b/spec/bundler/install/git_spec.rb
@@ -2,50 +2,78 @@
RSpec.describe "bundle install" do
context "git sources" do
- it "displays the revision hash of the gem repository", :bundler => "< 3" do
- build_git "foo", "1.0", :path => lib_path("foo")
+ it "displays the revision hash of the gem repository" do
+ build_git "foo", "1.0", path: lib_path("foo")
- install_gemfile <<-G
- gem "foo", :git => "#{file_uri_for(lib_path("foo"))}"
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at main@#{revision_for(lib_path("foo"))[0..6]})")
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+ end
+
+ it "displays the revision hash of the gem repository when passed a relative local path" do
+ build_git "foo", "1.0", path: lib_path("foo")
+
+ relative_path = lib_path("foo").relative_path_from(bundled_app)
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo1"
+ gem "foo", :git => "#{relative_path}"
+ G
+
+ expect(out).to include("Using foo 1.0 from #{relative_path} (at main@#{revision_for(lib_path("foo"))[0..6]})")
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+ end
+
+ it "displays the correct default branch", git: ">= 2.28.0" do
+ build_git "foo", "1.0", path: lib_path("foo"), default_branch: "non-standard"
+
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo")}"
G
- expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at master@#{revision_for(lib_path("foo"))[0..6]})")
- expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at non-standard@#{revision_for(lib_path("foo"))[0..6]})")
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
end
- it "displays the ref of the gem repository when using branch~num as a ref", :bundler => "< 3" do
+ it "displays the ref of the gem repository when using branch~num as a ref" do
skip "maybe branch~num notation doesn't work on Windows' git" if Gem.win_platform?
- build_git "foo", "1.0", :path => lib_path("foo")
+ build_git "foo", "1.0", path: lib_path("foo")
rev = revision_for(lib_path("foo"))[0..6]
- update_git "foo", "2.0", :path => lib_path("foo"), :gemspec => true
+ update_git "foo", "2.0", path: lib_path("foo"), gemspec: true
rev2 = revision_for(lib_path("foo"))[0..6]
- update_git "foo", "3.0", :path => lib_path("foo"), :gemspec => true
+ update_git "foo", "3.0", path: lib_path("foo"), gemspec: true
- install_gemfile <<-G
- gem "foo", :git => "#{file_uri_for(lib_path("foo"))}", :ref => "master~2"
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo")}", :ref => "main~2"
G
- expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at master~2@#{rev})")
- expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at main~2@#{rev})")
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
- update_git "foo", "4.0", :path => lib_path("foo"), :gemspec => true
+ update_git "foo", "4.0", path: lib_path("foo"), gemspec: true
- bundle :update, :all => true
- expect(out).to include("Using foo 2.0 (was 1.0) from #{file_uri_for(lib_path("foo"))} (at master~2@#{rev2})")
- expect(the_bundle).to include_gems "foo 2.0", :source => "git@#{lib_path("foo")}"
+ bundle :update, all: true, verbose: true
+ expect(out).to include("Using foo 2.0 (was 1.0) from #{lib_path("foo")} (at main~2@#{rev2})")
+ expect(the_bundle).to include_gems "foo 2.0", source: "git@#{lib_path("foo")}"
end
- it "should allows git repos that are missing but not being installed" do
+ it "allows git repos that are missing but not being installed" do
revision = build_git("foo").ref_for("HEAD")
gemfile <<-G
- gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :group => :development
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :group => :development
G
lockfile <<-L
GIT
- remote: #{file_uri_for(lib_path("foo-1.0"))}
+ remote: #{lib_path("foo-1.0")}
revision: #{revision}
specs:
foo (1.0)
@@ -57,8 +85,8 @@ RSpec.describe "bundle install" do
foo!
L
- bundle "config --local path vendor/bundle"
- bundle "config --local without development"
+ bundle_config "path vendor/bundle"
+ bundle_config "without development"
bundle :install
expect(out).to include("Bundle complete!")
@@ -66,15 +94,15 @@ RSpec.describe "bundle install" do
it "allows multiple gems from the same git source" do
build_repo2 do
- build_lib "foo", "1.0", :path => lib_path("gems/foo")
- build_lib "zebra", "2.0", :path => lib_path("gems/zebra")
- build_git "gems", :path => lib_path("gems"), :gemspec => false
+ build_lib "foo", "1.0", path: lib_path("gems/foo")
+ build_lib "zebra", "2.0", path: lib_path("gems/zebra")
+ build_git "gems", path: lib_path("gems"), gemspec: false
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "foo", :git => "#{file_uri_for(lib_path("gems"))}", :glob => "foo/*.gemspec"
- gem "zebra", :git => "#{file_uri_for(lib_path("gems"))}", :glob => "zebra/*.gemspec"
+ source "https://gem.repo2"
+ gem "foo", :git => "#{lib_path("gems")}", :glob => "foo/*.gemspec"
+ gem "zebra", :git => "#{lib_path("gems")}", :glob => "zebra/*.gemspec"
G
bundle "info foo"
@@ -83,5 +111,259 @@ RSpec.describe "bundle install" do
bundle "info zebra"
expect(out).to include("* zebra (2.0 #{revision_for(lib_path("gems"))[0..6]})")
end
+
+ it "should always sort dependencies in the same order" do
+ # This Gemfile + lockfile had a problem where the first
+ # `bundle install` would change the order, but the second would
+ # change it back.
+
+ # NOTE: both gems MUST have the same path! It has to be two gems in one repo.
+
+ test = build_git "test", "1.0.0", path: lib_path("test-and-other")
+ other = build_git "other", "1.0.0", path: lib_path("test-and-other")
+ test_ref = test.ref_for("HEAD")
+ other_ref = other.ref_for("HEAD")
+
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "test", git: #{test.path.to_s.inspect}
+ gem "other", ref: #{other_ref.inspect}, git: #{other.path.to_s.inspect}
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{test.path}
+ revision: #{test_ref}
+ specs:
+ test (1.0.0)
+
+ GIT
+ remote: #{other.path}
+ revision: #{other_ref}
+ ref: #{other_ref}
+ specs:
+ other (1.0.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ other!
+ test!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ # If GH#6743 is present, the first `bundle install` will change the
+ # lockfile, by flipping the order (`other` would be moved to the top).
+ #
+ # The second `bundle install` would then change the lockfile back
+ # to the original.
+ #
+ # The fix makes it so it may change it once, but it will not change
+ # it a second time.
+ #
+ # So, we run `bundle install` once, and store the value of the
+ # modified lockfile.
+ bundle :install
+ modified_lockfile = lockfile
+
+ # If GH#6743 is present, the second `bundle install` would change the
+ # lockfile back to what it was originally.
+ #
+ # This `expect` makes sure it doesn't change a second time.
+ bundle :install
+ expect(lockfile).to eq(modified_lockfile)
+
+ expect(out).to include("Bundle complete!")
+ end
+
+ it "allows older revisions of git source when clean true" do
+ build_git "foo", "1.0", path: lib_path("foo")
+ rev = revision_for(lib_path("foo"))
+
+ bundle_config "path vendor/bundle"
+ bundle_config "clean true"
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at main@#{rev[0..6]})")
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+
+ old_lockfile = lockfile
+
+ update_git "foo", "2.0", path: lib_path("foo"), gemspec: true
+ rev2 = revision_for(lib_path("foo"))
+
+ bundle :update, all: true, verbose: true
+ expect(out).to include("Using foo 2.0 (was 1.0) from #{lib_path("foo")} (at main@#{rev2[0..6]})")
+ expect(out).to include("Removing foo (#{rev[0..11]})")
+ expect(the_bundle).to include_gems "foo 2.0", source: "git@#{lib_path("foo")}"
+
+ lockfile(old_lockfile)
+
+ bundle :install, verbose: true
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at main@#{rev[0..6]})")
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+ end
+
+ context "when install directory exists" do
+ let(:checkout_confirmation_log_message) { "Checking out revision" }
+ let(:using_foo_confirmation_log_message) { "Using foo 1.0 from #{lib_path("foo")} (at main@#{revision_for(lib_path("foo"))[0..6]})" }
+
+ context "and no contents besides .git directory are present" do
+ it "reinstalls gem" do
+ build_git "foo", "1.0", path: lib_path("foo")
+
+ gemfile = <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ install_gemfile gemfile, verbose: true
+
+ expect(out).to include(checkout_confirmation_log_message)
+ expect(out).to include(using_foo_confirmation_log_message)
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+
+ # validate that the installed directory exists and has some expected contents
+ install_directory = default_bundle_path("bundler/gems/foo-#{revision_for(lib_path("foo"))[0..11]}")
+ dot_git_directory = install_directory.join(".git")
+ lib_directory = install_directory.join("lib")
+ gemspec = install_directory.join("foo.gemspec")
+ expect([install_directory, dot_git_directory, lib_directory, gemspec]).to all exist
+
+ # remove all elements in the install directory except .git directory
+ FileUtils.rm_r(lib_directory)
+ gemspec.delete
+
+ expect(dot_git_directory).to exist
+ expect(lib_directory).not_to exist
+ expect(gemspec).not_to exist
+
+ # rerun bundle install
+ install_gemfile gemfile, verbose: true
+
+ expect(out).to include(checkout_confirmation_log_message)
+ expect(out).to include(using_foo_confirmation_log_message)
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+
+ # validate that it reinstalls all components
+ expect([install_directory, dot_git_directory, lib_directory, gemspec]).to all exist
+ end
+ end
+
+ context "and contents besides .git directory are present" do
+ # we want to confirm that the change to try to detect partial installs and reinstall does not
+ # result in repeatedly reinstalling the gem when it is fully installed
+ it "does not reinstall gem" do
+ build_git "foo", "1.0", path: lib_path("foo")
+
+ gemfile = <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ install_gemfile gemfile, verbose: true
+
+ expect(out).to include(checkout_confirmation_log_message)
+ expect(out).to include(using_foo_confirmation_log_message)
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+
+ # rerun bundle install
+ install_gemfile gemfile, verbose: true
+
+ # it isn't altogether straight-forward to validate that bundle didn't do soething on the second run, however,
+ # the presence of the 2nd log message confirms install got past the point that it would have logged the above if
+ # it was going to
+ expect(out).not_to include(checkout_confirmation_log_message)
+ expect(out).to include(using_foo_confirmation_log_message)
+ end
+ end
+ end
+ end
+
+ describe "with excluded groups" do
+ it "works if you exclude a group with a git gem", ruby: ">= 3.3" do
+ build_git "production_gem", "1.0"
+ build_git "development_gem", "1.0"
+
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "production_gem", :git => "#{lib_path("production_gem-1.0")}"
+
+ group :development do
+ gem "development_gem", :git => "#{lib_path("development_gem-1.0")}"
+ end
+ G
+
+ # First install all groups to create lockfile
+ bundle :install
+
+ # Set without and reinstall
+ bundle_config "without development"
+ bundle :install
+
+ # Verify only production gem is available
+ expect(the_bundle).to include_gems("production_gem 1.0")
+ expect(the_bundle).not_to include_gems("development_gem 1.0")
+ end
+
+ it "resolves indirect dependencies from a git source not in the requested groups" do
+ build_lib "activesupport", "1.0", path: lib_path("rails/activesupport")
+ build_git "activerecord", "1.0", path: lib_path("rails") do |s|
+ s.add_dependency "activesupport", "= 1.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "activerecord", :git => "#{lib_path("rails")}"
+
+ group :ci do
+ gem "myrack"
+ end
+ G
+
+ bundle_config "only ci"
+ bundle :install
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ expect(the_bundle).not_to include_gems("activerecord 1.0")
+ end
+
+ it "resolves indirect dependencies from a git source not in the requested groups (without compact_index dependency API)" do
+ build_lib "activesupport", "1.0", path: lib_path("rails/activesupport")
+ build_git "activerecord", "1.0", path: lib_path("rails") do |s|
+ s.add_dependency "activesupport", "= 1.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "activerecord", :git => "#{lib_path("rails")}"
+
+ group :ci do
+ gem "myrack"
+ end
+ G
+
+ # Force the RubygemsAggregate code path in find_source_requirements by
+ # making the dependency API unavailable.
+ bundle_config "only ci"
+ bundle :install, artifice: "endpoint_api_forbidden"
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ expect(the_bundle).not_to include_gems("activerecord 1.0")
+ end
end
end
diff --git a/spec/bundler/install/global_cache_spec.rb b/spec/bundler/install/global_cache_spec.rb
index f3609715fb..4cffa65b2a 100644
--- a/spec/bundler/install/global_cache_spec.rb
+++ b/spec/bundler/install/global_cache_spec.rb
@@ -1,164 +1,232 @@
# frozen_string_literal: true
RSpec.describe "global gem caching" do
+ # Uses subprocess because this setting must apply across multiple app directories (bundled_app and bundled_app2)
before { bundle "config set global_gem_cache true" }
describe "using the cross-application user cache" do
let(:source) { "http://localgemserver.test" }
let(:source2) { "http://gemserver.example.org" }
+ def cache_base
+ # Use the unified global gem cache path if available (from RubyGems),
+ # otherwise fall back to the Bundler-specific cache location
+ if Gem.respond_to?(:global_gem_cache_path)
+ Pathname.new(Gem.global_gem_cache_path)
+ else
+ home(".bundle", "cache", "gems")
+ end
+ end
+
def source_global_cache(*segments)
- home(".bundle", "cache", "gems", "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", *segments)
+ cache_base.join("localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", *segments)
end
def source2_global_cache(*segments)
- home(".bundle", "cache", "gems", "gemserver.example.org.80.1ae1663619ffe0a3c9d97712f44c705b", *segments)
+ cache_base.join("gemserver.example.org.80.1ae1663619ffe0a3c9d97712f44c705b", *segments)
end
it "caches gems into the global cache on download" do
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "#{source}"
- gem "rack"
+ gem "myrack"
G
- expect(the_bundle).to include_gems "rack 1.0.0"
- expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ expect(source_global_cache("myrack-1.0.0.gem")).to exist
end
it "uses globally cached gems if they exist" do
source_global_cache.mkpath
- FileUtils.cp(gem_repo1("gems/rack-1.0.0.gem"), source_global_cache("rack-1.0.0.gem"))
+ FileUtils.cp(gem_repo1("gems/myrack-1.0.0.gem"), source_global_cache("myrack-1.0.0.gem"))
+
+ install_gemfile <<-G, artifice: "compact_index_no_gem"
+ source "#{source}"
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "shows a proper error message if a cached gem is corrupted" do
+ skip "This example is not working on ruby/ruby repo" if ruby_core?
+
+ source_global_cache.mkpath
+ FileUtils.touch(source_global_cache("myrack-1.0.0.gem"))
- install_gemfile <<-G, :artifice => "compact_index_no_gem"
+ install_gemfile <<-G, artifice: "compact_index_no_gem", raise_on_error: false
source "#{source}"
- gem "rack"
+ gem "myrack"
G
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(err).to include("Gem::Package::FormatError: package metadata is missing in #{source_global_cache("myrack-1.0.0.gem")}")
+ end
+
+ it "uses a shorter path for the cache to not hit filesystem limits" do
+ install_gemfile <<-G, artifice: "compact_index", verbose: true
+ source "http://#{"a" * 255}.test"
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ source_segment = "a" * 222 + ".a3cb26de2edfce9f509a65c611d99c4b"
+ source_cache = cache_base.join(source_segment)
+ cached_gem = source_cache.join("myrack-1.0.0.gem")
+ expect(cached_gem).to exist
+ ensure
+ # We cleanup dummy files created by this spec manually because due to a
+ # Ruby on Windows bug, `FileUtils.rm_rf` (run in our global after hook)
+ # cannot traverse directories with such long names. So we delete
+ # everything explicitly to workaround the bug. An alternative workaround
+ # would be to shell out to `rm -rf`. That also works fine, but I went with
+ # the more verbose and explicit approach. This whole ensure block can be
+ # removed once/if https://bugs.ruby-lang.org/issues/21177 is fixed, and
+ # once the fix propagates to all supported rubies.
+ File.delete cached_gem
+ Dir.rmdir source_cache
+
+ File.delete compact_index_cache_path.join(source_segment, "info", "myrack")
+ Dir.rmdir compact_index_cache_path.join(source_segment, "info")
+ File.delete compact_index_cache_path.join(source_segment, "info-etags", "myrack-92f3313ce5721296f14445c3a6b9c073")
+ Dir.rmdir compact_index_cache_path.join(source_segment, "info-etags")
+ Dir.rmdir compact_index_cache_path.join(source_segment, "info-special-characters")
+ File.delete compact_index_cache_path.join(source_segment, "versions")
+ File.delete compact_index_cache_path.join(source_segment, "versions.etag")
+ Dir.rmdir compact_index_cache_path.join(source_segment)
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"
+ bundle_config "path.system true"
+
+ install_gemfile <<-G, artifice: "compact_index"
source "#{source}"
- gem "rack"
+ gem "myrack"
G
- simulate_new_machine
- expect(the_bundle).not_to include_gems "rack 1.0.0"
- expect(source_global_cache("rack-1.0.0.gem")).to exist
- # rack 1.0.0 is not installed and it is in the global cache
+ pristine_system_gems
+ expect(the_bundle).not_to include_gems "myrack 1.0.0"
+ expect(source_global_cache("myrack-1.0.0.gem")).to exist
+ # myrack 1.0.0 is not installed and it is in the global cache
- install_gemfile <<-G, :artifice => "compact_index"
+ install_gemfile <<-G, artifice: "compact_index"
source "#{source2}"
- gem "rack", "0.9.1"
+ gem "myrack", "0.9.1"
G
- simulate_new_machine
- expect(the_bundle).not_to include_gems "rack 0.9.1"
- expect(source2_global_cache("rack-0.9.1.gem")).to exist
- # rack 0.9.1 is not installed and it is in the global cache
+ pristine_system_gems
+ expect(the_bundle).not_to include_gems "myrack 0.9.1"
+ expect(source2_global_cache("myrack-0.9.1.gem")).to exist
+ # myrack 0.9.1 is not installed and it is in the global cache
gemfile <<-G
source "#{source}"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
G
- bundle :install, :artifice => "compact_index_no_gem"
- # rack 1.0.0 is installed and rack 0.9.1 is not
- expect(the_bundle).to include_gems "rack 1.0.0"
- expect(the_bundle).not_to include_gems "rack 0.9.1"
- simulate_new_machine
+ bundle :install, artifice: "compact_index_no_gem"
+ # myrack 1.0.0 is installed and myrack 0.9.1 is not
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ expect(the_bundle).not_to include_gems "myrack 0.9.1"
+ pristine_system_gems
gemfile <<-G
source "#{source2}"
- gem "rack", "0.9.1"
+ gem "myrack", "0.9.1"
G
- bundle :install, :artifice => "compact_index_no_gem"
- # rack 0.9.1 is installed and rack 1.0.0 is not
- expect(the_bundle).to include_gems "rack 0.9.1"
- expect(the_bundle).not_to include_gems "rack 1.0.0"
+ bundle :install, artifice: "compact_index_no_gem"
+ # myrack 0.9.1 is installed and myrack 1.0.0 is not
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ expect(the_bundle).not_to include_gems "myrack 1.0.0"
end
it "should not install if the wrong source is provided" do
+ bundle_config "path.system true"
+
gemfile <<-G
source "#{source}"
- gem "rack"
+ gem "myrack"
G
- bundle :install, :artifice => "compact_index"
- simulate_new_machine
- expect(the_bundle).not_to include_gems "rack 1.0.0"
- expect(source_global_cache("rack-1.0.0.gem")).to exist
- # rack 1.0.0 is not installed and it is in the global cache
+ bundle :install, artifice: "compact_index"
+ pristine_system_gems
+ expect(the_bundle).not_to include_gems "myrack 1.0.0"
+ expect(source_global_cache("myrack-1.0.0.gem")).to exist
+ # myrack 1.0.0 is not installed and it is in the global cache
gemfile <<-G
source "#{source2}"
- gem "rack", "0.9.1"
+ gem "myrack", "0.9.1"
G
- bundle :install, :artifice => "compact_index"
- simulate_new_machine
- expect(the_bundle).not_to include_gems "rack 0.9.1"
- expect(source2_global_cache("rack-0.9.1.gem")).to exist
- # rack 0.9.1 is not installed and it is in the global cache
+ bundle :install, artifice: "compact_index"
+ pristine_system_gems
+ expect(the_bundle).not_to include_gems "myrack 0.9.1"
+ expect(source2_global_cache("myrack-0.9.1.gem")).to exist
+ # myrack 0.9.1 is not installed and it is in the global cache
gemfile <<-G
source "#{source2}"
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
G
- expect(source_global_cache("rack-1.0.0.gem")).to exist
- expect(source2_global_cache("rack-0.9.1.gem")).to exist
- bundle :install, :artifice => "compact_index_no_gem", :raise_on_error => false
+ expect(source_global_cache("myrack-1.0.0.gem")).to exist
+ expect(source2_global_cache("myrack-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")
- # 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"
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+
+ # myrack 1.0.0 is not installed and myrack 0.9.1 is not
+ expect(the_bundle).not_to include_gems "myrack 1.0.0"
+ expect(the_bundle).not_to include_gems "myrack 0.9.1"
gemfile <<-G
source "#{source}"
- gem "rack", "0.9.1"
+ gem "myrack", "0.9.1"
G
- expect(source_global_cache("rack-1.0.0.gem")).to exist
- expect(source2_global_cache("rack-0.9.1.gem")).to exist
- bundle :install, :artifice => "compact_index_no_gem", :raise_on_error => false
+ expect(source_global_cache("myrack-1.0.0.gem")).to exist
+ expect(source2_global_cache("myrack-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")
- # 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"
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
+
+ # myrack 0.9.1 is not installed and myrack 1.0.0 is not
+ expect(the_bundle).not_to include_gems "myrack 0.9.1"
+ expect(the_bundle).not_to include_gems "myrack 1.0.0"
end
end
describe "when installing gems from a different directory" do
it "uses the global cache as a source" do
- install_gemfile <<-G, :artifice => "compact_index"
+ bundle_config "path.system true"
+
+ install_gemfile <<-G, artifice: "compact_index"
source "#{source}"
- gem "rack"
+ gem "myrack"
gem "activesupport"
G
# Both gems are installed and in the global cache
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
expect(the_bundle).to include_gems "activesupport 2.3.5"
- expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source_global_cache("myrack-1.0.0.gem")).to exist
expect(source_global_cache("activesupport-2.3.5.gem")).to exist
- simulate_new_machine
+ pristine_system_gems
# Both gems are now only in the global cache
- expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "myrack 1.0.0"
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
- install_gemfile <<-G, :artifice => "compact_index_no_gem"
+ install_gemfile <<-G, artifice: "compact_index_no_gem"
source "#{source}"
- gem "rack"
+ gem "myrack"
G
- # rack is installed and both are in the global cache
- expect(the_bundle).to include_gems "rack 1.0.0"
+ # myrack is installed and both are in the global cache
+ expect(the_bundle).to include_gems "myrack 1.0.0"
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
- expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(source_global_cache("myrack-1.0.0.gem")).to exist
expect(source_global_cache("activesupport-2.3.5.gem")).to exist
create_file bundled_app2("gems.rb"), <<-G
@@ -167,43 +235,45 @@ RSpec.describe "global gem caching" do
G
# Neither gem 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).not_to include_gems "activesupport 2.3.5", :dir => bundled_app2
- expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(the_bundle).not_to include_gems "myrack 1.0.0", dir: bundled_app2
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", dir: bundled_app2
+ expect(source_global_cache("myrack-1.0.0.gem")).to exist
expect(source_global_cache("activesupport-2.3.5.gem")).to exist
# Install using the global cache instead of by downloading the .gem
# from the server
- bundle :install, :artifice => "compact_index_no_gem", :dir => bundled_app2
+ 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
- expect(source_global_cache("rack-1.0.0.gem")).to exist
+ expect(the_bundle).not_to include_gems "myrack 1.0.0", dir: bundled_app2
+ expect(the_bundle).to include_gems "activesupport 2.3.5", dir: bundled_app2
+
+ expect(source_global_cache("myrack-1.0.0.gem")).to exist
expect(source_global_cache("activesupport-2.3.5.gem")).to exist
end
end
end
describe "extension caching" do
- it "works", :ruby_repo do
+ it "works" do
skip "gets incorrect ref in path" if Gem.win_platform?
+ skip "fails for unknown reason when run by ruby-core" if ruby_core?
build_git "very_simple_git_binary", &:add_c_extension
build_lib "very_simple_path_binary", &:add_c_extension
revision = revision_for(lib_path("very_simple_git_binary-1.0"))[0, 12]
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "very_simple_binary"
gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}"
gem "very_simple_path_binary", :path => "#{lib_path("very_simple_path_binary-1.0")}"
G
- gem_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope,
- Digest(:MD5).hexdigest("#{gem_repo1}/"), "very_simple_binary-1.0")
- git_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope,
+ gem_binary_cache = home(".bundle", "cache", "extensions", local_platform.to_s, Bundler.ruby_scope,
+ "gem.repo1.443.#{Digest(:MD5).hexdigest("gem.repo1.443./")}", "very_simple_binary-1.0")
+ git_binary_cache = home(".bundle", "cache", "extensions", local_platform.to_s, Bundler.ruby_scope,
"very_simple_git_binary-1.0-#{revision}", "very_simple_git_binary-1.0")
cached_extensions = Pathname.glob(home(".bundle", "cache", "extensions", "*", "*", "*", "*", "*")).sort
@@ -215,12 +285,12 @@ RSpec.describe "global gem caching" do
R
expect(out).to eq "VERY_SIMPLE_BINARY_IN_C\nVERY_SIMPLE_GIT_BINARY_IN_C"
- FileUtils.rm Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]
+ FileUtils.rm_r Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]
gem_binary_cache.join("very_simple_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" }
git_binary_cache.join("very_simple_git_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" }
- bundle "config set --local path different_path"
+ bundle_config "path different_path"
bundle :install
expect(Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]).to all(end_with(".rb"))
diff --git a/spec/bundler/install/path_spec.rb b/spec/bundler/install/path_spec.rb
index 2239706020..49360e511e 100644
--- a/spec/bundler/install/path_spec.rb
+++ b/spec/bundler/install/path_spec.rb
@@ -1,91 +1,87 @@
# frozen_string_literal: true
RSpec.describe "bundle install" do
- describe "with --path" do
+ describe "with path configured" do
before :each do
- build_gem "rack", "1.0.0", :to_system => true do |s|
- s.write "lib/rack.rb", "puts 'FAIL'"
+ build_gem "myrack", "1.0.0", to_system: true do |s|
+ s.write "lib/myrack.rb", "puts 'FAIL'"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
- it "does not use available system gems with bundle --path vendor/bundle", :bundler => "< 3" do
- bundle "config --local path vendor/bundle"
+ it "does not use available system gems with `vendor/bundle" do
+ bundle_config "path vendor/bundle"
bundle :install
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "uses system gems with `path.system` configured with more priority than `path`" do
+ bundle_config "path.system true"
+ bundle_config_global "path vendor/bundle"
+ bundle :install
+ run "require 'myrack'", raise_on_error: false
+ expect(out).to include("FAIL")
end
it "handles paths with regex characters in them" do
dir = bundled_app("bun++dle")
dir.mkpath
- bundle "config --local path #{dir.join("vendor/bundle")}"
- bundle :install, :dir => dir
+ bundle_config "path #{dir.join("vendor/bundle")}"
+ bundle :install, dir: dir
expect(out).to include("installed into `./vendor/bundle`")
- dir.rmtree
+ FileUtils.rm_rf dir
end
- it "prints a warning to let the user know what has happened with bundle --path vendor/bundle" do
- bundle "config --local path vendor/bundle"
+ it "prints a message to let the user know where gems where installed" do
+ bundle_config "path vendor/bundle"
bundle :install
expect(out).to include("gems are installed into `./vendor/bundle`")
end
- it "disallows --path vendor/bundle --system", :bundler => "< 3" do
- bundle "install --path vendor/bundle --system", :raise_on_error => false
- expect(err).to include("Please choose only one option.")
- expect(exitstatus).to eq(15)
+ it "installs the bundle relatively to repository root, when Bundler run from the same directory" do
+ bundle "config set path vendor/bundle", dir: bundled_app.parent
+ bundle "install --gemfile='#{bundled_app}/Gemfile'", dir: bundled_app.parent
+ expect(out).to include("installed into `./bundled_app/vendor/bundle`")
+ expect(bundled_app("vendor/bundle")).to be_directory
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "remembers to disable system gems after the first time with bundle --path vendor/bundle", :bundler => "< 3" do
- bundle "install --path vendor/bundle"
- FileUtils.rm_rf bundled_app("vendor")
- bundle "install"
-
- expect(vendored_gems("gems/rack-1.0.0")).to be_directory
- expect(the_bundle).to include_gems "rack 1.0.0"
+ it "installs the bundle relatively to repository root, when Bundler run from a different directory" do
+ bundle "config set path vendor/bundle", dir: bundled_app
+ bundle "install --gemfile='#{bundled_app}/Gemfile'", dir: bundled_app.parent
+ expect(out).to include("installed into `./bundled_app/vendor/bundle`")
+ expect(bundled_app("vendor/bundle")).to be_directory
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- context "with path_relative_to_cwd set to true" do
- before { bundle "config set path_relative_to_cwd true" }
+ it "installs the standalone bundle relative to the cwd" do
+ bundle :install, gemfile: bundled_app_gemfile, standalone: true, dir: bundled_app.parent
+ expect(out).to include("installed into `./bundled_app/bundle`")
+ expect(bundled_app("bundle")).to be_directory
+ expect(bundled_app("bundle/ruby")).to be_directory
- it "installs the bundle relatively to current working directory", :bundler => "< 3" do
- bundle "install --gemfile='#{bundled_app}/Gemfile' --path vendor/bundle", :dir => bundled_app.parent
- expect(out).to include("installed into `./vendor/bundle`")
- expect(bundled_app("../vendor/bundle")).to be_directory
- expect(the_bundle).to include_gems "rack 1.0.0"
- end
-
- it "installs the standalone bundle relative to the cwd" do
- bundle :install, :gemfile => bundled_app_gemfile, :standalone => true, :dir => bundled_app.parent
- expect(out).to include("installed into `./bundled_app/bundle`")
- expect(bundled_app("bundle")).to be_directory
- expect(bundled_app("bundle/ruby")).to be_directory
-
- bundle "config unset path"
-
- bundle :install, :gemfile => bundled_app_gemfile, :standalone => true, :dir => bundled_app("subdir").tap(&:mkpath)
- expect(out).to include("installed into `../bundle`")
- expect(bundled_app("bundle")).to be_directory
- expect(bundled_app("bundle/ruby")).to be_directory
- end
+ bundle :install, gemfile: bundled_app_gemfile, standalone: true, dir: bundled_app("subdir").tap(&:mkpath)
+ expect(out).to include("installed into `../bundle`")
+ expect(bundled_app("bundle")).to be_directory
+ expect(bundled_app("bundle/ruby")).to be_directory
end
end
describe "when BUNDLE_PATH or the global path config is set" do
before :each do
- build_lib "rack", "1.0.0", :to_system => true do |s|
- s.write "lib/rack.rb", "raise 'FAIL'"
+ build_lib "myrack", "1.0.0", to_system: true do |s|
+ s.write "lib/myrack.rb", "raise 'FAIL'"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
@@ -101,23 +97,23 @@ 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 "path vendor/bundle"
bundle :install
- expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(vendored_gems("gems/myrack-1.0.0")).to be_directory
expect(bundled_app("vendor2")).not_to be_directory
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "installs gems to ." do
set_bundle_path(type, ".")
- bundle "config set --global disable_shared_gems true"
+ bundle_config_global "disable_shared_gems true"
bundle :install
- paths_to_exist = %w[cache/rack-1.0.0.gem gems/rack-1.0.0 specifications/rack-1.0.0.gemspec].map {|path| bundled_app(Bundler.ruby_scope, path) }
+ paths_to_exist = %w[cache/myrack-1.0.0.gem gems/myrack-1.0.0 specifications/myrack-1.0.0.gemspec].map {|path| bundled_app(Bundler.ruby_scope, path) }
expect(paths_to_exist).to all exist
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "installs gems to the path" do
@@ -125,77 +121,77 @@ RSpec.describe "bundle install" do
bundle :install
- expect(bundled_app("vendor", Bundler.ruby_scope, "gems/rack-1.0.0")).to be_directory
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(bundled_app("vendor", Bundler.ruby_scope, "gems/myrack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "installs gems to the path relative to root when relative" do
set_bundle_path(type, "vendor")
FileUtils.mkdir_p bundled_app("lol")
- bundle :install, :dir => bundled_app("lol")
+ bundle :install, dir: bundled_app("lol")
- expect(bundled_app("vendor", Bundler.ruby_scope, "gems/rack-1.0.0")).to be_directory
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(bundled_app("vendor", Bundler.ruby_scope, "gems/myrack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
end
it "installs gems to BUNDLE_PATH from .bundle/config" do
- config "BUNDLE_PATH" => bundled_app("vendor/bundle").to_s
+ bundle_config "BUNDLE_PATH" => bundled_app("vendor/bundle").to_s
bundle :install
- expect(vendored_gems("gems/rack-1.0.0")).to be_directory
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(vendored_gems("gems/myrack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "sets BUNDLE_PATH as the first argument to bundle install" do
- bundle "config --local path ./vendor/bundle"
+ bundle_config "path ./vendor/bundle"
bundle :install
- expect(vendored_gems("gems/rack-1.0.0")).to be_directory
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(vendored_gems("gems/myrack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
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"
+ build_gem "myrack", "1.1.0", to_system: true
+ bundle_config "path ./vendor/bundle"
bundle :install
- expect(vendored_gems("gems/rack-1.0.0")).to be_directory
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(vendored_gems("gems/myrack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "re-installs gems whose extensions have been deleted", :ruby_repo do
- build_lib "very_simple_binary", "1.0.0", :to_system => true do |s|
+ it "re-installs gems whose extensions have been deleted" do
+ build_lib "very_simple_binary", "1.0.0", to_system: true do |s|
s.write "lib/very_simple_binary.rb", "raise 'FAIL'"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "very_simple_binary"
G
- bundle "config --local path ./vendor/bundle"
+ bundle_config "path ./vendor/bundle"
bundle :install
expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
expect(vendored_gems("extensions")).to be_directory
- expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1"
+ expect(the_bundle).to include_gems "very_simple_binary 1.0", source: "remote1"
- vendored_gems("extensions").rmtree
+ FileUtils.rm_rf vendored_gems("extensions")
- run "require 'very_simple_binary_c'", :raise_on_error => false
+ run "require 'very_simple_binary_c'", raise_on_error: false
expect(err).to include("Bundler::GemNotFound")
- bundle "config --local path ./vendor/bundle"
+ bundle_config "path ./vendor/bundle"
bundle :install
expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
expect(vendored_gems("extensions")).to be_directory
- expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1"
+ expect(the_bundle).to include_gems "very_simple_binary 1.0", source: "remote1"
end
end
@@ -206,12 +202,12 @@ RSpec.describe "bundle install" do
it "reports the file exists" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "config --local path bundle"
- bundle :install, :raise_on_error => false
+ bundle_config "path bundle"
+ bundle :install, raise_on_error: false
expect(err).to include("file already exists")
end
end
diff --git a/spec/bundler/install/prereleases_spec.rb b/spec/bundler/install/prereleases_spec.rb
index fb01220ed7..9f764d127c 100644
--- a/spec/bundler/install/prereleases_spec.rb
+++ b/spec/bundler/install/prereleases_spec.rb
@@ -1,10 +1,19 @@
# frozen_string_literal: true
RSpec.describe "bundle install" do
+ before do
+ build_repo2 do
+ build_gem "not_released", "1.0.pre"
+
+ build_gem "has_prerelease", "1.0"
+ build_gem "has_prerelease", "1.1.pre"
+ end
+ end
+
describe "when prerelease gems are available" do
it "finds prereleases" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "not_released"
G
expect(the_bundle).to include_gems "not_released 1.0.pre"
@@ -12,7 +21,7 @@ RSpec.describe "bundle install" do
it "uses regular releases if available" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "has_prerelease"
G
expect(the_bundle).to include_gems "has_prerelease 1.0"
@@ -20,7 +29,7 @@ RSpec.describe "bundle install" do
it "uses prereleases if requested" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
gem "has_prerelease", "1.1.pre"
G
expect(the_bundle).to include_gems "has_prerelease 1.1.pre"
@@ -29,13 +38,17 @@ RSpec.describe "bundle install" do
describe "when prerelease gems are not available" do
it "still works" do
- build_repo3
+ build_repo3 do
+ build_gem "myrack"
+ end
+ FileUtils.rm_r Dir[gem_repo3("prerelease*")]
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo3)}"
- gem "rack"
+ source "https://gem.repo3"
+ gem "myrack"
G
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
end
diff --git a/spec/bundler/install/process_lock_spec.rb b/spec/bundler/install/process_lock_spec.rb
index dac0d34bc4..b096291d1a 100644
--- a/spec/bundler/install/process_lock_spec.rb
+++ b/spec/bundler/install/process_lock_spec.rb
@@ -8,28 +8,106 @@ RSpec.describe "process lock spec" do
thread = Thread.new do
Bundler::ProcessLock.lock(default_bundle_path) do
sleep 1 # ignore quality_spec
- expect(the_bundle).not_to include_gems "rack 1.0"
+ expect(the_bundle).not_to include_gems "myrack 1.0"
end
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
thread.join
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
context "when creating a lock raises Errno::ENOTSUP" do
before { allow(File).to receive(:open).and_raise(Errno::ENOTSUP) }
- it "skips creating the lock file and yields" do
+ it "skips creating the lockfile and yields" do
processed = false
Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
expect(processed).to eq true
end
end
+
+ context "when creating a lock raises Errno::EPERM" do
+ before { allow(File).to receive(:open).and_raise(Errno::EPERM) }
+
+ it "skips creating the lockfile and yields" do
+ processed = false
+ Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
+
+ expect(processed).to eq true
+ end
+ end
+
+ context "when creating a lock raises Errno::EROFS" do
+ before { allow(File).to receive(:open).and_raise(Errno::EROFS) }
+
+ it "skips creating the lockfile and yields" do
+ processed = false
+ Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
+
+ expect(processed).to eq true
+ end
+ end
+
+ it "refreshes gem specification cache after waiting for lock" do
+ build_repo2 do
+ build_gem "myrack", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+
+ # First, install the gem so it's available
+ bundle "install"
+ expect(out).to include("Installing myrack")
+
+ # Queue for thread-safe communication
+ lock_acquired = Queue.new
+ can_release_lock = Queue.new
+ install_output = Queue.new
+
+ # Thread holds lock (simulating another bundle process that just finished installing)
+ thread = Thread.new do
+ Bundler::ProcessLock.lock(default_bundle_path) do
+ # Signal that we have the lock
+ lock_acquired << true
+ # Wait until main thread signals we can release
+ can_release_lock.pop
+ end
+ end
+
+ # Wait for thread to acquire lock
+ lock_acquired.pop
+
+ # Start another install in a thread - it will wait for the lock
+ install_thread = Thread.new do
+ bundle "install", verbose: true
+ install_output << out
+ end
+
+ # Give subprocess time to start and begin waiting for lock
+ sleep 0.5
+
+ # Signal thread to release the lock
+ can_release_lock << true
+
+ # Wait for both threads to complete
+ thread.join
+ install_thread.join
+
+ second_install_out = install_output.pop
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ # The second install should have refreshed its cache after acquiring
+ # the lock and seen that myrack was already installed
+ expect(second_install_out).to include("Using myrack")
+ end
end
end
diff --git a/spec/bundler/install/redownload_spec.rb b/spec/bundler/install/redownload_spec.rb
deleted file mode 100644
index 0eae615c59..0000000000
--- a/spec/bundler/install/redownload_spec.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "bundle install" do
- before :each do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
- end
-
- shared_examples_for "an option to force redownloading gems" do
- it "re-installs installed gems" do
- rack_lib = default_bundle_path("gems/rack-1.0.0/lib/rack.rb")
-
- bundle :install
- rack_lib.open("w") {|f| f.write("blah blah blah") }
- bundle :install, flag => true
-
- expect(out).to include "Installing rack 1.0.0"
- expect(rack_lib.open(&:read)).to eq("RACK = '1.0.0'\n")
- expect(the_bundle).to include_gems "rack 1.0.0"
- end
-
- it "works on first bundle install" do
- bundle :install, flag => true
-
- expect(out).to include "Installing rack 1.0.0"
- expect(the_bundle).to include_gems "rack 1.0.0"
- end
-
- context "with a git gem" do
- let!(:ref) { build_git("foo", "1.0").ref_for("HEAD", 11) }
-
- before do
- gemfile <<-G
- gem "foo", :git => "#{lib_path("foo-1.0")}"
- G
- end
-
- it "re-installs installed gems" do
- foo_lib = default_bundle_path("bundler/gems/foo-1.0-#{ref}/lib/foo.rb")
-
- bundle :install
- foo_lib.open("w") {|f| f.write("blah blah blah") }
- bundle :install, flag => true
-
- expect(foo_lib.open(&:read)).to eq("FOO = '1.0'\n")
- expect(the_bundle).to include_gems "foo 1.0"
- end
-
- it "works on first bundle install" do
- bundle :install, flag => true
-
- expect(the_bundle).to include_gems "foo 1.0"
- end
- end
- end
-
- describe "with --force", :bundler => 2 do
- it_behaves_like "an option to force redownloading gems" do
- let(:flag) { "force" }
- end
-
- it "shows a deprecation when single flag passed" do
- bundle "install --force"
- expect(err).to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
-
- it "shows a deprecation when multiple flags passed" do
- bundle "install --no-color --force"
- expect(err).to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
- end
-
- describe "with --redownload" do
- it_behaves_like "an option to force redownloading gems" do
- let(:flag) { "redownload" }
- end
-
- it "does not show a deprecation when single flag passed" do
- bundle "install --redownload"
- expect(err).not_to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
-
- it "does not show a deprecation when single multiple flags passed" do
- bundle "install --no-color --redownload"
- expect(err).not_to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
- end
-end
diff --git a/spec/bundler/install/security_policy_spec.rb b/spec/bundler/install/security_policy_spec.rb
index 43c3069c4e..e7f64dc227 100644
--- a/spec/bundler/install/security_policy_spec.rb
+++ b/spec/bundler/install/security_policy_spec.rb
@@ -9,30 +9,30 @@ RSpec.describe "policies with unsigned gems" do
before do
build_security_repo
gemfile <<-G
- source "#{file_uri_for(security_repo)}"
- gem "rack"
+ source "https://gems.security"
+ gem "myrack"
gem "signed_gem"
G
end
it "will work after you try to deploy without a lock" do
- bundle "install --deployment", :raise_on_error => false
+ bundle "install --deployment", raise_on_error: false
bundle :install
- expect(the_bundle).to include_gems "rack 1.0", "signed_gem 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0", "signed_gem 1.0"
end
it "will fail when given invalid security policy" do
- bundle "install --trust-policy=InvalidPolicyName", :raise_on_error => false
+ bundle "install --trust-policy=InvalidPolicyName", raise_on_error: false
expect(err).to include("RubyGems doesn't know about trust policy")
end
it "will fail with High Security setting due to presence of unsigned gem" do
- bundle "install --trust-policy=HighSecurity", :raise_on_error => false
+ bundle "install --trust-policy=HighSecurity", raise_on_error: false
expect(err).to include("security policy didn't allow")
end
it "will fail with Medium Security setting due to presence of unsigned gem" do
- bundle "install --trust-policy=MediumSecurity", :raise_on_error => false
+ bundle "install --trust-policy=MediumSecurity", raise_on_error: false
expect(err).to include("security policy didn't allow")
end
@@ -45,18 +45,18 @@ RSpec.describe "policies with signed gems and no CA" do
before do
build_security_repo
gemfile <<-G
- source "#{file_uri_for(security_repo)}"
+ source "https://gems.security"
gem "signed_gem"
G
end
it "will fail with High Security setting, gem is self-signed" do
- bundle "install --trust-policy=HighSecurity", :raise_on_error => false
+ bundle "install --trust-policy=HighSecurity", raise_on_error: false
expect(err).to include("security policy didn't allow")
end
it "will fail with Medium Security setting, gem is self-signed" do
- bundle "install --trust-policy=MediumSecurity", :raise_on_error => false
+ bundle "install --trust-policy=MediumSecurity", raise_on_error: false
expect(err).to include("security policy didn't allow")
end
diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb
index 6d3065a836..c92af7bfb0 100644
--- a/spec/bundler/install/yanked_spec.rb
+++ b/spec/bundler/install/yanked_spec.rb
@@ -1,40 +1,127 @@
# frozen_string_literal: true
RSpec.context "when installing a bundle that includes yanked gems" do
- before(:each) do
+ it "throws an error when the original gem version is yanked" do
build_repo4 do
build_gem "foo", "9.0.0"
end
- end
- it "throws an error when the original gem version is yanked" do
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo4)}
+ remote: https://gem.repo4
specs:
foo (10.0.0)
PLATFORMS
- ruby
+ #{lockfile_platforms}
DEPENDENCIES
foo (= 10.0.0)
L
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo4)}"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo4"
gem "foo", "10.0.0"
G
expect(err).to include("Your bundle is locked to foo (10.0.0)")
end
+ context "when a platform specific yanked version is included in the lockfile, and a generic variant is available remotely" do
+ let(:original_lockfile) do
+ <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ actiontext (6.1.6)
+ nokogiri (>= 1.8)
+ foo (1.0.0)
+ nokogiri (1.13.8-#{Bundler.local_platform})
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ actiontext (= 6.1.6)
+ foo (= 1.0.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ before do
+ skip "Materialization on Windows is not yet strict, so the example does not detect the gem has been yanked" if Gem.win_platform?
+
+ build_repo4 do
+ build_gem "foo", "1.0.0"
+ build_gem "foo", "1.0.1"
+ build_gem "actiontext", "6.1.7" do |s|
+ s.add_dependency "nokogiri", ">= 1.8"
+ end
+ build_gem "actiontext", "6.1.6" do |s|
+ s.add_dependency "nokogiri", ">= 1.8"
+ end
+ build_gem "actiontext", "6.1.7" do |s|
+ s.add_dependency "nokogiri", ">= 1.8"
+ end
+ build_gem "nokogiri", "1.13.8"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "foo", "1.0.0"
+ gem "actiontext", "6.1.6"
+ G
+
+ lockfile original_lockfile
+ end
+
+ context "and a re-resolve is necessary" do
+ before do
+ gemfile gemfile.sub('"foo", "1.0.0"', '"foo", "1.0.1"')
+ end
+
+ it "reresolves, and replaces the yanked gem with the generic version, printing a warning, when the old index is used" do
+ bundle "install", artifice: "endpoint", verbose: true
+
+ expect(out).to include("Installing nokogiri 1.13.8").and include("Installing foo 1.0.1")
+ expect(lockfile).to eq(original_lockfile.sub("nokogiri (1.13.8-#{Bundler.local_platform})", "nokogiri (1.13.8)").gsub("1.0.0", "1.0.1"))
+ expect(err).to include("Some locked specs have possibly been yanked (nokogiri-1.13.8-#{Bundler.local_platform}). Ignoring them...")
+ end
+
+ it "reresolves, and replaces the yanked gem with the generic version, printing a warning, when the compact index API is used" do
+ bundle "install", artifice: "compact_index", verbose: true
+
+ expect(out).to include("Installing nokogiri 1.13.8").and include("Installing foo 1.0.1")
+ expect(lockfile).to eq(original_lockfile.sub("nokogiri (1.13.8-#{Bundler.local_platform})", "nokogiri (1.13.8)").gsub("1.0.0", "1.0.1"))
+ expect(err).to include("Some locked specs have possibly been yanked (nokogiri-1.13.8-#{Bundler.local_platform}). Ignoring them...")
+ end
+ end
+
+ it "reports the yanked gem properly when the old index is used" do
+ bundle "install", artifice: "endpoint", raise_on_error: false
+
+ expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})")
+ end
+
+ it "reports the yanked gem properly when the compact index API is used" do
+ bundle "install", artifice: "compact_index", raise_on_error: false
+
+ expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})")
+ end
+ end
+
it "throws the original error when only the Gemfile specifies a gem version that doesn't exist" do
- bundle "config set force_ruby_platform true"
+ build_repo4 do
+ build_gem "foo", "9.0.0"
+ end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo4)}"
+ bundle_config "force_ruby_platform true"
+
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo4"
gem "foo", "10.0.0"
G
@@ -43,31 +130,125 @@ RSpec.context "when installing a bundle that includes yanked gems" do
end
end
+RSpec.context "when resolving a bundle that includes yanked gems, but unlocking an unrelated gem" do
+ before(:each) do
+ build_repo4 do
+ build_gem "foo", "10.0.0"
+
+ build_gem "bar", "1.0.0"
+ build_gem "bar", "2.0.0"
+ end
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4
+ specs:
+ foo (9.0.0)
+ bar (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ bar
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "foo"
+ gem "bar"
+ G
+ end
+
+ it "does not update the yanked gem" do
+ bundle "lock --update bar"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ bar (2.0.0)
+ foo (9.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ bar
+ foo
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+end
+
RSpec.context "when using gem before installing" do
it "does not suggest the author has yanked the gem" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "0.9.1"
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1
+ specs:
+ myrack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack (= 0.9.1)
+ L
+
+ bundle :list, raise_on_error: false
+
+ expect(err).to include("Could not find myrack-0.9.1 in locally installed gems")
+ expect(err).to_not include("Your bundle is locked to myrack (0.9.1) from")
+ expect(err).to_not include("If you haven't changed sources, that means the author of myrack (0.9.1) has removed it.")
+ expect(err).to_not include("You'll need to update your bundle to a different version of myrack (0.9.1) that hasn't been removed in order to install.")
+
+ # Check error message is still correct when multiple platforms are locked
+ lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
+
+ bundle :list, raise_on_error: false
+ expect(err).to include("Could not find myrack-0.9.1 in locally installed gems")
+ 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 "https://gem.repo1"
+ gem "myrack", "0.9.1"
+ gem "myrack_middleware", "1.0"
G
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo1)}
+ remote: https://gem.repo1
specs:
- rack (0.9.1)
+ myrack (0.9.1)
+ myrack_middleware (1.0)
PLATFORMS
- ruby
+ #{lockfile_platforms}
DEPENDENCIES
- rack (= 0.9.1)
+ myrack (= 0.9.1)
+ myrack_middleware (1.0)
L
- bundle :list, :raise_on_error => false
+ bundle :list, raise_on_error: false
- expect(err).to include("Could not find rack-0.9.1 in any of the sources")
- 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.")
+ expect(err).to include("Could not find myrack-0.9.1, myrack_middleware-1.0 in locally installed gems")
+ expect(err).to include("Install missing gems with `bundle install`.")
+ expect(err).to_not include("Your bundle is locked to myrack (0.9.1) from")
+ expect(err).to_not include("If you haven't changed sources, that means the author of myrack (0.9.1) has removed it.")
+ expect(err).to_not include("You'll need to update your bundle to a different version of myrack (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..c9f76115dc 100644
--- a/spec/bundler/lock/git_spec.rb
+++ b/spec/bundler/lock/git_spec.rb
@@ -1,19 +1,76 @@
# frozen_string_literal: true
RSpec.describe "bundle lock with git gems" do
- before :each do
+ let(:install_gemfile_with_foo_as_a_git_dependency) do
build_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem 'foo', :git => "#{lib_path("foo-1.0")}"
G
end
it "doesn't break right after running lock" do
+ install_gemfile_with_foo_as_a_git_dependency
+
expect(the_bundle).to include_gems "foo 1.0.0"
end
+ it "doesn't print errors even if running lock after removing the cache" do
+ install_gemfile_with_foo_as_a_git_dependency
+
+ FileUtils.rm_r(Dir[default_cache_path("git/foo-1.0-*")].first)
+
+ bundle "lock --verbose"
+
+ expect(err).to be_empty
+ end
+
+ it "prints a proper error when changing a locked Gemfile to point to a bad branch" do
+ install_gemfile_with_foo_as_a_git_dependency
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}", :branch => "bad"
+ G
+
+ bundle "lock --update foo", env: { "LANG" => "en" }, raise_on_error: false
+
+ expect(err).to include("Revision bad does not exist in the repository")
+ end
+
+ it "prints a proper error when installing a Gemfile with a locked ref that does not exist" do
+ install_gemfile_with_foo_as_a_git_dependency
+
+ lockfile <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{"a" * 40}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install", raise_on_error: false
+
+ expect(err).to include("Revision #{"a" * 40} does not exist in the repository")
+ end
+
it "locks a git source to the current ref" do
+ install_gemfile_with_foo_as_a_git_dependency
+
update_git "foo"
bundle :install
@@ -25,10 +82,177 @@ RSpec.describe "bundle lock with git gems" do
expect(out).to eq("WIN")
end
+ it "properly clones a git source locked to an out of date ref" do
+ install_gemfile_with_foo_as_a_git_dependency
+
+ update_git "foo"
+
+ bundle :install, env: { "BUNDLE_PATH" => "foo" }
+ expect(err).to be_empty
+ end
+
+ it "properly fetches a git source locked to an unreachable ref" do
+ install_gemfile_with_foo_as_a_git_dependency
+
+ # Create a commit and make it unreachable
+ git "checkout -b foo ", lib_path("foo-1.0")
+ unreachable_sha = update_git("foo").ref_for("HEAD")
+ git "checkout main ", lib_path("foo-1.0")
+ git "branch -D foo ", lib_path("foo-1.0")
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{unreachable_sha}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(err).to be_empty
+ end
+
+ it "properly fetches a git source locked to an annotated tag" do
+ install_gemfile_with_foo_as_a_git_dependency
+
+ # Create an annotated tag
+ git("tag -a v1.0 -m 'Annotated v1.0'", lib_path("foo-1.0"))
+ annotated_tag = git("rev-parse v1.0", lib_path("foo-1.0"))
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem 'foo', :git => "#{lib_path("foo-1.0")}"
+ G
+
+ lockfile <<-L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{annotated_tag}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(err).to be_empty
+ end
+
it "provides correct #full_gem_path" do
+ install_gemfile_with_foo_as_a_git_dependency
+
run <<-RUBY
puts Bundler.rubygems.find_name('foo').first.full_gem_path
RUBY
expect(out).to eq(bundle("info foo --path"))
end
+
+ it "does not lock versions that don't exist in the repository when changing a GEM transitive dep to a GIT direct dep" do
+ build_repo4 do
+ build_gem "activesupport", "8.0.0" do |s|
+ s.add_dependency "securerandom"
+ end
+
+ build_gem "securerandom", "0.3.1"
+ end
+
+ path = lib_path("securerandom")
+
+ build_git "securerandom", "0.3.2", path: path
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ activesupport (8.0.0)
+ securerandom
+ securerandom (0.3.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ activesupport
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "activesupport"
+ gem "securerandom", git: "#{path}"
+ G
+
+ bundle "lock"
+
+ expect(lockfile).to include("securerandom (0.3.2)")
+ end
+
+ it "does not lock versions that don't exist in the repository when changing a GIT direct dep to a GEM direct dep" do
+ build_repo4 do
+ build_gem "ruby-lsp", "0.16.1"
+ end
+
+ path = lib_path("ruby-lsp")
+ revision = build_git("ruby-lsp", "0.16.2", path: path).ref_for("HEAD")
+
+ lockfile <<~L
+ GIT
+ remote: #{path}
+ revision: #{revision}
+ specs:
+ ruby-lsp (0.16.2)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ruby-lsp!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "ruby-lsp"
+ G
+
+ bundle "lock"
+
+ expect(lockfile).to include("ruby-lsp (0.16.1)")
+ end
end
diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb
index 6eaa6f05cf..654ac02aa7 100644
--- a/spec/bundler/lock/lockfile_spec.rb
+++ b/spec/bundler/lock/lockfile_spec.rb
@@ -1,33 +1,39 @@
# frozen_string_literal: true
RSpec.describe "the lockfile format" do
- include Bundler::GemHelpers
+ before do
+ build_repo2
+ end
it "generates a simple lockfile for a single source, gem" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo2, "myrack", "1.0.0")
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
- gem "rack"
+ gem "myrack"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
-
+ myrack
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "updates the lockfile's bundler version if current ver. is newer" do
+ it "updates the lockfile's bundler version if current ver. is newer, and version was forced through BUNDLER_VERSION" do
system_gems "bundler-1.8.2"
lockfile <<-L
@@ -37,325 +43,373 @@ RSpec.describe "the lockfile format" do
specs:
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
omg!
- rack
+ myrack
BUNDLED WITH
1.8.2
L
- install_gemfile <<-G, :env => { "BUNDLER_VERSION" => Bundler::VERSION }
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, verbose: true, env: { "BUNDLER_VERSION" => Bundler::VERSION }
+ source "https://gem.repo2"
- gem "rack"
+ gem "myrack"
G
- lockfile_should_be <<-G
+ expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with 1.8.2.")
+ expect(out).to include("Using bundler #{Bundler::VERSION}")
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "does not update the lockfile's bundler version if nothing changed during bundle install" do
- version = "#{Bundler::VERSION.split(".").first}.0.0.a"
+ it "does not update the lockfile's bundler version if nothing changed during bundle install, but uses the locked version" do
+ version = "2.3.0"
+
+ build_repo4 do
+ build_gem "myrack", "1.0.0"
+
+ build_bundler version
+ end
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo4/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack
BUNDLED WITH
#{version}
L
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo4"
- gem "rack"
+ gem "myrack"
G
- lockfile_should_be <<-G
+ expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{version}.")
+ expect(out).to include("Using bundler #{version}")
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo4/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack
BUNDLED WITH
#{version}
G
end
- it "updates the lockfile's bundler version if not present" do
+ it "adds the BUNDLED WITH section if not present" do
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack
L
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo2"
- gem "rack", "> 0"
+ gem "myrack", "> 0"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack (> 0)
+ myrack (> 0)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "warns if the current is older than lockfile's bundler version" do
+ it "update the bundler major version just fine" do
current_version = Bundler::VERSION
- newer_minor = bump_minor(current_version)
+ older_major = previous_major(current_version)
+
+ system_gems "bundler-#{older_major}"
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack
BUNDLED WITH
- #{newer_minor}
+ #{older_major}
L
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, env: { "BUNDLER_VERSION" => Bundler::VERSION }
+ source "https://gem.repo2/"
- gem "rack"
+ gem "myrack"
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}`."
- expect(err).to include warning_message
+ expect(err).to be_empty
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack
BUNDLED WITH
- #{newer_minor}
+ #{current_version}
G
end
- it "warns when updating bundler major version" do
- current_version = Bundler::VERSION
- older_major = previous_major(current_version)
+ it "generates a simple lockfile for a single source, gem with dependencies" do
+ install_gemfile <<-G
+ source "https://gem.repo2/"
- system_gems "bundler-#{older_major}"
+ gem "myrack-obama"
+ G
- lockfile <<-L
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "myrack", "1.0.0"
+ c.checksum gem_repo2, "myrack-obama", "1.0"
+ end
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
-
+ myrack-obama
+ #{checksums}
BUNDLED WITH
- #{older_major}
- L
+ #{Bundler::VERSION}
+ G
+ end
- install_gemfile <<-G, :env => { "BUNDLER_VERSION" => Bundler::VERSION }
- source "#{file_uri_for(gem_repo1)}/"
+ it "generates a simple lockfile for a single source, gem with a version requirement" do
+ install_gemfile <<-G
+ source "https://gem.repo2/"
- gem "rack"
+ gem "myrack-obama", ">= 1.0"
G
- expect(err).to include(
- "Warning: the lockfile is being updated to Bundler " \
- "#{current_version.split(".").first}, after which you will be unable to return to Bundler #{older_major.split(".").first}."
- )
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "myrack", "1.0.0"
+ c.checksum gem_repo2, "myrack-obama", "1.0"
+ end
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
-
+ myrack-obama (>= 1.0)
+ #{checksums}
BUNDLED WITH
- #{current_version}
+ #{Bundler::VERSION}
G
end
- it "generates a simple lockfile for a single source, gem with dependencies" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
-
- gem "rack-obama"
- G
+ it "generates a lockfile without credentials" do
+ bundle "config set https://localgemserver.test/ user:pass"
- lockfile_should_be <<-G
- GEM
- remote: #{file_uri_for(gem_repo1)}/
- specs:
- rack (1.0.0)
- rack-obama (1.0)
- rack
+ install_gemfile(<<-G, artifice: "endpoint_strict_basic_authentication", quiet: true)
+ source "https://gem.repo1"
- PLATFORMS
- #{lockfile_platforms}
+ source "https://localgemserver.test/" do
- DEPENDENCIES
- rack-obama
+ end
- BUNDLED WITH
- #{Bundler::VERSION}
+ source "https://user:pass@othergemserver.test/" do
+ gem "myrack-obama", ">= 1.0"
+ end
G
- end
- it "generates a simple lockfile for a single source, gem with a version requirement" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "myrack", "1.0.0"
+ c.checksum gem_repo2, "myrack-obama", "1.0"
+ end
- gem "rack-obama", ">= 1.0"
- G
+ expect(lockfile).to eq <<~G
+ GEM
+ remote: https://gem.repo1/
+ specs:
- lockfile_should_be <<-G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://localgemserver.test/
specs:
- rack (1.0.0)
- rack-obama (1.0)
- rack
+
+ GEM
+ remote: https://othergemserver.test/
+ specs:
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack-obama (>= 1.0)
-
+ myrack-obama (>= 1.0)!
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "generates a lockfile without credentials for a configured source", :bundler => "< 3" do
+ it "does not add credentials to lockfile when it does not have them already" do
bundle "config set http://localgemserver.test/ user:pass"
- install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ gemfile <<~G
+ source "https://gem.repo1"
+
source "http://localgemserver.test/" do
end
source "http://user:pass@othergemserver.test/" do
- gem "rack-obama", ">= 1.0"
+ gem "myrack-obama", ">= 1.0"
end
G
- lockfile_should_be <<-G
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "myrack", "1.0.0"
+ c.checksum gem_repo2, "myrack-obama", "1.0"
+ end
+
+ lockfile_without_credentials = <<~L
GEM
remote: http://localgemserver.test/
- remote: http://user:pass@othergemserver.test/
specs:
- rack (1.0.0)
- rack-obama (1.0)
- rack
+
+ GEM
+ remote: http://othergemserver.test/
+ specs:
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack-obama (>= 1.0)!
-
+ myrack-obama (>= 1.0)!
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
- G
+ #{Bundler::VERSION}
+ L
+
+ lockfile lockfile_without_credentials
+
+ # when not re-resolving
+ bundle "install", artifice: "endpoint_strict_basic_authentication", quiet: true
+ expect(lockfile).to eq lockfile_without_credentials
+
+ # when re-resolving with full unlock
+ bundle "update", artifice: "endpoint_strict_basic_authentication"
+ expect(lockfile).to eq lockfile_without_credentials
+
+ # when re-resolving without ful unlocking
+ bundle "update myrack-obama", artifice: "endpoint_strict_basic_authentication"
+ expect(lockfile).to eq lockfile_without_credentials
end
- it "generates a lockfile without credentials for a configured source", :bundler => "3" do
+ it "keeps credentials in lockfile if already there" do
bundle "config set http://localgemserver.test/ user:pass"
- install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ gemfile <<~G
+ source "https://gem.repo1"
+
source "http://localgemserver.test/" do
end
source "http://user:pass@othergemserver.test/" do
- gem "rack-obama", ">= 1.0"
+ gem "myrack-obama", ">= 1.0"
end
G
- lockfile_should_be <<-G
- GEM
- specs:
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "myrack", "1.0.0"
+ c.checksum gem_repo2, "myrack-obama", "1.0"
+ end
+ lockfile_with_credentials = <<~L
GEM
remote: http://localgemserver.test/
specs:
@@ -363,30 +417,45 @@ RSpec.describe "the lockfile format" do
GEM
remote: http://user:pass@othergemserver.test/
specs:
- rack (1.0.0)
- rack-obama (1.0)
- rack
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack-obama (>= 1.0)!
-
+ myrack-obama (>= 1.0)!
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
- G
+ #{Bundler::VERSION}
+ L
+
+ lockfile lockfile_with_credentials
+
+ bundle "install", artifice: "endpoint_strict_basic_authentication", quiet: true
+
+ expect(lockfile).to eq lockfile_with_credentials
end
it "generates lockfiles with multiple requirements" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ source "https://gem.repo2/"
gem "net-sftp"
G
- lockfile_should_be <<-G
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "net-sftp", "1.1.1"
+ c.checksum gem_repo2, "net-ssh", "1.0"
+ end
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
net-sftp (1.1.1)
net-ssh (>= 1.0.0, < 1.99.0)
@@ -397,57 +466,35 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
net-sftp
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
expect(the_bundle).to include_gems "net-sftp 1.1.1", "net-ssh 1.0.0"
end
- it "generates a simple lockfile for a single pinned source, gem with a version requirement", :bundler => "< 3" do
- git = build_git "foo"
-
- install_gemfile <<-G
- gem "foo", :git => "#{lib_path("foo-1.0")}"
- G
-
- lockfile_should_be <<-G
- GIT
- remote: #{lib_path("foo-1.0")}
- revision: #{git.ref_for("master")}
- specs:
- foo (1.0)
-
- GEM
- specs:
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- foo!
-
- BUNDLED WITH
- #{Bundler::VERSION}
- G
- end
-
it "generates a simple lockfile for a single pinned source, gem with a version requirement" do
git = build_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- lockfile_should_be <<-G
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
+ expect(lockfile).to eq <<~G
GIT
remote: #{lib_path("foo-1.0")}
- revision: #{git.ref_for("master")}
+ revision: #{git.ref_for("main")}
specs:
foo (1.0)
GEM
+ remote: https://gem.repo1/
specs:
PLATFORMS
@@ -455,23 +502,23 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "does not asplode when a platform specific dependency is present and the Gemfile has not been resolved on that platform" do
- build_lib "omg", :path => lib_path("omg")
+ build_lib "omg", path: lib_path("omg")
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ source "https://gem.repo2/"
platforms :#{not_local_tag} do
gem "omg", :path => "#{lib_path("omg")}"
end
- gem "rack"
+ gem "myrack"
G
lockfile <<-L
@@ -481,42 +528,48 @@ RSpec.describe "the lockfile format" do
specs:
GEM
- remote: #{file_uri_for(gem_repo1)}//
+ remote: https://gem.repo2//
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{not_local}
DEPENDENCIES
omg!
- rack
+ myrack
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "serializes global git sources" do
git = build_git "foo"
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
install_gemfile <<-G
+ source "https://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")}
+ revision: #{git.ref_for("main")}
specs:
foo (1.0)
GEM
+ remote: https://gem.repo1/
specs:
PLATFORMS
@@ -524,21 +577,26 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "generates a lockfile with a ref for a single pinned source, git gem with a branch requirement" do
git = build_git "foo"
- update_git "foo", :branch => "omg"
+ update_git "foo", branch: "omg"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
+ source "https://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")}
@@ -547,6 +605,7 @@ RSpec.describe "the lockfile format" do
foo (1.0)
GEM
+ remote: https://gem.repo1/
specs:
PLATFORMS
@@ -554,21 +613,26 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "generates a lockfile with a ref for a single pinned source, git gem with a tag requirement" do
git = build_git "foo"
- update_git "foo", :tag => "omg"
+ update_git "foo", tag: "omg"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
+ source "https://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")}
@@ -577,6 +641,7 @@ RSpec.describe "the lockfile format" do
foo (1.0)
GEM
+ remote: https://gem.repo1/
specs:
PLATFORMS
@@ -584,26 +649,118 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
+ it "is conservative with dependencies of git gems" do
+ build_repo4 do
+ build_gem "orm_adapter", "0.4.1"
+ build_gem "orm_adapter", "0.5.0"
+ end
+
+ FileUtils.mkdir_p lib_path("ckeditor/lib")
+
+ @remote = build_git("ckeditor_remote", bare: true)
+
+ build_git "ckeditor", path: lib_path("ckeditor") do |s|
+ s.write "lib/ckeditor.rb", "CKEDITOR = '4.0.7'"
+ s.version = "4.0.7"
+ s.add_dependency "orm_adapter"
+ end
+
+ update_git "ckeditor", path: lib_path("ckeditor"), remote: @remote.path
+ update_git "ckeditor", path: lib_path("ckeditor"), tag: "v4.0.7"
+ old_git = update_git "ckeditor", path: lib_path("ckeditor"), push: "v4.0.7"
+
+ update_git "ckeditor", path: lib_path("ckeditor"), gemspec: true do |s|
+ s.write "lib/ckeditor.rb", "CKEDITOR = '4.0.8'"
+ s.version = "4.0.8"
+ s.add_dependency "orm_adapter"
+ end
+ update_git "ckeditor", path: lib_path("ckeditor"), tag: "v4.0.8"
+
+ new_git = update_git "ckeditor", path: lib_path("ckeditor"), push: "v4.0.8"
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "ckeditor", :git => "#{@remote.path}", :tag => "v4.0.8"
+ G
+
+ lockfile <<~L
+ GIT
+ remote: #{@remote.path}
+ revision: #{old_git.ref_for("v4.0.7")}
+ tag: v4.0.7
+ specs:
+ ckeditor (4.0.7)
+ orm_adapter
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ orm_adapter (0.4.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ckeditor!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ # Bumps the git gem, but keeps its dependency locked
+ expect(lockfile).to eq <<~L
+ GIT
+ remote: #{@remote.path}
+ revision: #{new_git.ref_for("v4.0.8")}
+ tag: v4.0.8
+ specs:
+ ckeditor (4.0.8)
+ orm_adapter
+
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ orm_adapter (0.4.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ckeditor!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
it "serializes pinned path sources to the lockfile" do
build_lib "foo"
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
install_gemfile <<-G
+ source "https://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: https://gem.repo1/
specs:
PLATFORMS
@@ -611,9 +768,9 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -621,20 +778,25 @@ RSpec.describe "the lockfile format" do
build_lib "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
- bundle "config set cache_all true"
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
bundle :cache
- bundle :install, :local => true
+ 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: https://gem.repo1/
specs:
PLATFORMS
@@ -642,9 +804,9 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -652,18 +814,24 @@ RSpec.describe "the lockfile format" do
build_lib "foo"
bar = build_git "bar"
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ c.no_checksum "bar", "1.0"
+ c.checksum gem_repo2, "myrack", "1.0.0"
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ source "https://gem.repo2/"
- gem "rack"
+ gem "myrack"
gem "foo", :path => "#{lib_path("foo-1.0")}"
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")}
+ revision: #{bar.ref_for("main")}
specs:
bar (1.0)
@@ -673,9 +841,9 @@ RSpec.describe "the lockfile format" do
foo (1.0)
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
@@ -683,58 +851,104 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
bar!
foo!
- rack
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "removes redundant sources" do
+ install_gemfile <<-G
+ source "https://gem.repo2/"
+
+ gem "myrack", :source => "https://gem.repo2/"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "myrack", "1.0.0"
+ end
+
+ expect(lockfile).to eq <<~G
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack (1.0.0)
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack!
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "lists gems alphabetically" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ source "https://gem.repo2/"
gem "thin"
gem "actionpack"
- gem "rack-obama"
+ gem "myrack-obama"
G
- lockfile_should_be <<-G
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "actionpack", "2.3.2"
+ c.checksum gem_repo2, "activesupport", "2.3.2"
+ c.checksum gem_repo2, "myrack", "1.0.0"
+ c.checksum gem_repo2, "myrack-obama", "1.0"
+ c.checksum gem_repo2, "thin", "1.0"
+ end
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
actionpack (2.3.2)
activesupport (= 2.3.2)
activesupport (2.3.2)
- rack (1.0.0)
- rack-obama (1.0)
- rack
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
thin (1.0)
- rack
+ myrack
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
actionpack
- rack-obama
+ myrack-obama
thin
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "orders dependencies' dependencies in alphabetical order" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ source "https://gem.repo2/"
gem "rails"
G
- lockfile_should_be <<-G
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "actionmailer", "2.3.2"
+ c.checksum gem_repo2, "actionpack", "2.3.2"
+ c.checksum gem_repo2, "activerecord", "2.3.2"
+ c.checksum gem_repo2, "activeresource", "2.3.2"
+ c.checksum gem_repo2, "activesupport", "2.3.2"
+ c.checksum gem_repo2, "rails", "2.3.2"
+ c.checksum gem_repo2, "rake", rake_version
+ end
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
actionmailer (2.3.2)
activesupport (= 2.3.2)
@@ -750,29 +964,44 @@ RSpec.describe "the lockfile format" do
actionpack (= 2.3.2)
activerecord (= 2.3.2)
activeresource (= 2.3.2)
- rake (= 13.0.1)
- rake (13.0.1)
+ rake (= #{rake_version})
+ rake (#{rake_version})
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
rails
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "orders dependencies by version" do
+ update_repo2 do
+ # Capistrano did this (at least until version 2.5.10)
+ # RubyGems 2.2 doesn't allow the specifying of a dependency twice
+ # See https://github.com/ruby/rubygems/commit/03dbac93a3396a80db258d9bc63500333c25bd2f
+ build_gem "double_deps", "1.0", skip_validation: true do |s|
+ s.add_dependency "net-ssh", ">= 1.0.0"
+ s.add_dependency "net-ssh"
+ end
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ source "https://gem.repo2"
gem 'double_deps'
G
- lockfile_should_be <<-G
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "double_deps", "1.0"
+ c.checksum gem_repo2, "net-ssh", "1.0"
+ end
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
double_deps (1.0)
net-ssh
@@ -784,80 +1013,96 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
double_deps
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "does not add the :require option to the lockfile" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ source "https://gem.repo2/"
- gem "rack-obama", ">= 1.0", :require => "rack/obama"
+ gem "myrack-obama", ">= 1.0", :require => "myrack/obama"
G
- lockfile_should_be <<-G
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "myrack", "1.0.0"
+ c.checksum gem_repo2, "myrack-obama", "1.0"
+ end
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
- rack-obama (1.0)
- rack
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack-obama (>= 1.0)
-
+ myrack-obama (>= 1.0)
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "does not add the :group option to the lockfile" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ source "https://gem.repo2/"
- gem "rack-obama", ">= 1.0", :group => :test
+ gem "myrack-obama", ">= 1.0", :group => :test
G
- lockfile_should_be <<-G
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "myrack", "1.0.0"
+ c.checksum gem_repo2, "myrack-obama", "1.0"
+ end
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
- rack-obama (1.0)
- rack
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack-obama (>= 1.0)
-
+ myrack-obama (>= 1.0)
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "stores relative paths when the path is provided in a relative fashion and in Gemfile dir" do
- build_lib "foo", :path => bundled_app("foo")
+ build_lib "foo", path: bundled_app("foo")
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
+ source "https://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: https://gem.repo1/
specs:
PLATFORMS
@@ -865,28 +1110,34 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "stores relative paths when the path is provided in a relative fashion and is above Gemfile dir" do
- build_lib "foo", :path => bundled_app(File.join("..", "foo"))
+ build_lib "foo", path: bundled_app(File.join("..", "foo"))
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
+ source "https://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: https://gem.repo1/
specs:
PLATFORMS
@@ -894,28 +1145,34 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "stores relative paths when the path is provided in an absolute fashion but is relative" do
- build_lib "foo", :path => bundled_app("foo")
+ build_lib "foo", path: bundled_app("foo")
install_gemfile <<-G
- path File.expand_path("../foo", __FILE__) do
+ source "https://gem.repo1"
+ path File.expand_path("foo", __dir__) do
gem "foo"
end
G
- lockfile_should_be <<-G
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
+
+ expect(lockfile).to eq <<~G
PATH
remote: foo
specs:
foo (1.0)
GEM
+ remote: https://gem.repo1/
specs:
PLATFORMS
@@ -923,26 +1180,32 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
it "stores relative paths when the path is provided for gemspec" do
- build_lib("foo", :path => tmp.join("foo"))
+ build_lib("foo", path: tmp("foo"))
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "1.0"
+ end
install_gemfile <<-G
+ source "https://gem.repo1"
gemspec :path => "../foo"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PATH
remote: ../foo
specs:
foo (1.0)
GEM
+ remote: https://gem.repo1/
specs:
PLATFORMS
@@ -950,432 +1213,1091 @@ RSpec.describe "the lockfile format" do
DEPENDENCIES
foo!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "keeps existing platforms in the lockfile", :bundler => "< 3" do
+ it "keeps existing platforms in the lockfile" do
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "myrack", "1.0.0"
+ end
+
lockfile <<-G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
java
DEPENDENCIES
- rack
-
+ myrack
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ source "https://gem.repo2/"
- gem "rack"
+ gem "myrack"
G
- lockfile_should_be <<-G
+ checksums.checksum(gem_repo2, "myrack", "1.0.0")
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
- java
- #{generic_local_platform}
+ #{lockfile_platforms("java", local_platform, defaults: [])}
DEPENDENCIES
- rack
-
+ myrack
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "keeps existing platforms in the lockfile", :bundler => "3" do
- lockfile <<-G
+ it "adds compatible platform specific variants to the lockfile, even if resolution fallback to ruby due to some other incompatible platform specific variant" do
+ simulate_platform "arm64-darwin-23" do
+ build_repo4 do
+ build_gem "google-protobuf", "3.25.1"
+ build_gem "google-protobuf", "3.25.1" do |s|
+ s.platform = "arm64-darwin-23"
+ end
+ build_gem "google-protobuf", "3.25.1" do |s|
+ s.platform = "x64-mingw-ucrt"
+ s.required_ruby_version = "> #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "google-protobuf"
+ G
+ bundle "lock --add-platform x64-mingw-ucrt"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "google-protobuf", "3.25.1"
+ c.checksum gem_repo4, "google-protobuf", "3.25.1", "arm64-darwin-23"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ google-protobuf (3.25.1)
+ google-protobuf (3.25.1-arm64-darwin-23)
+
+ PLATFORMS
+ arm64-darwin-23
+ ruby
+ x64-mingw-ucrt
+
+ DEPENDENCIES
+ google-protobuf
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ it "persists the spec's specific platform to the lockfile" do
+ build_repo2 do
+ build_gem "platform_specific", "1.0" do |s|
+ s.platform = Gem::Platform.new("universal-java-16")
+ end
+ end
+
+ simulate_platform "universal-java-16" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "platform_specific"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo2, "platform_specific", "1.0", "universal-java-16"
+ end
+
+ expect(lockfile).to eq <<~G
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ platform_specific (1.0-universal-java-16)
+
+ PLATFORMS
+ universal-java-16
+
+ DEPENDENCIES
+ platform_specific
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+ end
+
+ it "does not add duplicate gems" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo2, "activesupport", "2.3.5")
+ c.checksum(gem_repo2, "myrack", "1.0.0")
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2/"
+ gem "myrack"
+ G
+
+ install_gemfile <<-G
+ source "https://gem.repo2/"
+ gem "myrack"
+ gem "activesupport"
+ G
+
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ activesupport (2.3.5)
+ myrack (1.0.0)
PLATFORMS
- java
+ #{lockfile_platforms}
DEPENDENCIES
- rack
-
+ activesupport
+ myrack
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
+ end
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
+ it "does not add duplicate dependencies" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo2, "myrack", "1.0.0")
+ end
- gem "rack"
+ install_gemfile <<-G
+ source "https://gem.repo2/"
+ gem "myrack"
+ gem "myrack"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
- java
- #{generic_local_platform}
- #{specific_local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
- rack
-
+ myrack
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "persists the spec's platform to the lockfile", :bundler => "< 3" do
- build_repo2 do
- build_gem "platform_specific", "1.0" do |s|
- s.platform = Gem::Platform.new("universal-java-16")
- end
+ it "does not add duplicate dependencies with versions" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo2, "myrack", "1.0.0")
end
- simulate_platform "universal-java-16"
-
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "platform_specific"
+ source "https://gem.repo2/"
+ gem "myrack", "1.0"
+ gem "myrack", "1.0"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
- platform_specific (1.0-java)
+ myrack (1.0.0)
PLATFORMS
- java
+ #{lockfile_platforms}
DEPENDENCIES
- platform_specific
-
+ myrack (= 1.0)
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "persists the spec's platform and specific platform to the lockfile", :bundler => "3" do
- build_repo2 do
- build_gem "platform_specific", "1.0" do |s|
- s.platform = Gem::Platform.new("universal-java-16")
- end
+ it "does not add duplicate dependencies in different groups" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo2, "myrack", "1.0.0")
end
- simulate_platform "universal-java-16"
-
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "platform_specific"
+ source "https://gem.repo2/"
+ gem "myrack", "1.0", :group => :one
+ gem "myrack", "1.0", :group => :two
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
- platform_specific (1.0-java)
- platform_specific (1.0-universal-java-16)
+ myrack (1.0.0)
PLATFORMS
- java
- universal-java-16
+ #{lockfile_platforms}
DEPENDENCIES
- platform_specific
-
+ myrack (= 1.0)
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "does not add duplicate gems" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
- gem "rack"
+ it "raises if two different versions are used" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2/"
+ gem "myrack", "1.0"
+ gem "myrack", "1.1"
G
+ expect(bundled_app_lock).not_to exist
+ expect(err).to include "myrack (= 1.0) and myrack (= 1.1)"
+ end
+
+ it "raises if two different sources are used" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2/"
+ gem "myrack"
+ gem "myrack", :git => "git://hubz.com"
+ G
+
+ expect(bundled_app_lock).not_to exist
+ expect(err).to include "myrack (>= 0) should come from an unspecified source and git://hubz.com"
+ end
+
+ it "works correctly with multiple version dependencies" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo2, "myrack", "0.9.1")
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
- gem "rack"
- gem "activesupport"
+ source "https://gem.repo2/"
+ gem "myrack", "> 0.9", "< 1.0"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- activesupport (2.3.5)
- rack (1.0.0)
+ myrack (0.9.1)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- activesupport
- rack
-
+ myrack (> 0.9, < 1.0)
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "does not add duplicate dependencies" do
+ it "captures the Ruby version in the lockfile" do
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum(gem_repo2, "myrack", "0.9.1")
+ end
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
- gem "rack"
- gem "rack"
+ source "https://gem.repo2/"
+ ruby '#{Gem.ruby_version}'
+ gem "myrack", "> 0.9", "< 1.0"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (0.9.1)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack (> 0.9, < 1.0)
+ #{checksums}
+ RUBY VERSION
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
- it "does not add duplicate dependencies with versions" do
+ it "automatically fixes the lockfile when it's missing deps" do
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack_middleware (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
- gem "rack", "1.0"
- gem "rack", "1.0"
+ source "https://gem.repo2"
+ gem "myrack_middleware"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (0.9.1)
+ myrack_middleware (1.0)
+ myrack (= 0.9.1)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack (= 1.0)
+ myrack_middleware
BUNDLED WITH
- #{Bundler::VERSION}
- G
+ #{Bundler::VERSION}
+ L
end
- it "does not add duplicate dependencies in different groups" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
- gem "rack", "1.0", :group => :one
- gem "rack", "1.0", :group => :two
+ it "raises a clear error when frozen mode is set and lockfile is missing deps, and does not install any gems" do
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack_middleware (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ source "https://gem.repo2"
+ gem "myrack_middleware"
G
- lockfile_should_be <<-G
+ expect(err).to include("Bundler found incorrect dependencies in the lockfile for myrack_middleware-1.0")
+ expect(err).to include("myrack: gemspec specifies = 0.9.1, not in lockfile")
+ expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
+ end
+
+ it "raises a clear error when frozen mode is set and lockfile is missing entries in CHECKSUMS section, and does not install any gems" do
+ lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack_middleware (1.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack (= 1.0)
+ myrack_middleware
+
+ CHECKSUMS
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ source "https://gem.repo2"
+ gem "myrack_middleware"
G
+
+ expect(err).to eq <<~L.strip
+ Your lockfile is missing a CHECKSUMS entry for \"myrack_middleware\", but can't be updated because frozen mode is set
+
+ Run `bundle install` elsewhere and add the updated Gemfile.lock to version control.
+ L
+
+ expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
end
- it "raises if two different versions are used" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}/"
- gem "rack", "1.0"
- gem "rack", "1.1"
+ it "raises a clear error when frozen mode is set and lockfile has empty checksums in CHECKSUMS section, and does not install any gems" do
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+
+ CHECKSUMS
+ myrack (0.9.1)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ source "https://gem.repo2"
+ gem "myrack"
G
- expect(bundled_app_lock).not_to exist
- expect(err).to include "rack (= 1.0) and rack (= 1.1)"
+ expect(err).to eq <<~L.strip
+ Your lockfile has an empty CHECKSUMS entry for \"myrack\", but can't be updated because frozen mode is set
+
+ Run `bundle install` elsewhere and add the updated Gemfile.lock to version control.
+ L
+
+ expect(the_bundle).not_to include_gems "myrack 0.9.1"
end
- it "raises if two different sources are used" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}/"
- gem "rack"
- gem "rack", :git => "git://hubz.com"
+ it "automatically fixes the lockfile when it's missing deps, they conflict with other locked deps, but conflicts are fixable" do
+ build_repo4 do
+ build_gem "other_dep", "0.9"
+ build_gem "other_dep", "1.0"
+
+ build_gem "myrack", "0.9.1"
+
+ build_gem "myrack_middleware", "1.0" do |s|
+ s.add_dependency "myrack", "= 0.9.1"
+ s.add_dependency "other_dep", "= 0.9"
+ end
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack_middleware (1.0)
+ other_dep (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
+ other_dep
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack_middleware"
+ gem "other_dep"
G
- expect(bundled_app_lock).not_to exist
- expect(err).to include "rack (>= 0) should come from an unspecified source and git://hubz.com (at master)"
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (0.9.1)
+ myrack_middleware (1.0)
+ myrack (= 0.9.1)
+ other_dep (= 0.9)
+ other_dep (0.9)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
+ other_dep
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
- it "works correctly with multiple version dependencies" do
+ it "automatically fixes the lockfile when it's missing multiple deps, they conflict with other locked deps, but conflicts are fixable" do
+ build_repo4 do
+ build_gem "other_dep", "0.9"
+ build_gem "other_dep", "1.0"
+
+ build_gem "myrack", "0.9.1"
+
+ build_gem "myrack_middleware", "1.0" do |s|
+ s.add_dependency "myrack", "= 0.9.1"
+ end
+
+ build_gem "another_dep_middleware", "1.0" do |s|
+ s.add_dependency "other_dep", "= 0.9"
+ end
+ end
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack_middleware (1.0)
+ another_dep_middleware (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
+ another_dep_middleware
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
- gem "rack", "> 0.9", "< 1.0"
+ source "https://gem.repo4"
+ gem "myrack_middleware"
+ gem "another_dep_middleware"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ another_dep_middleware (1.0)
+ other_dep (= 0.9)
+ myrack (0.9.1)
+ myrack_middleware (1.0)
+ myrack (= 0.9.1)
+ other_dep (0.9)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ another_dep_middleware
+ myrack_middleware
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a resolution error when lockfile is missing deps, they conflict with other locked deps, and conflicts are not fixable" do
+ build_repo4 do
+ build_gem "other_dep", "0.9"
+ build_gem "other_dep", "1.0"
+
+ build_gem "myrack", "0.9.1"
+
+ build_gem "myrack_middleware", "1.0" do |s|
+ s.add_dependency "myrack", "= 0.9.1"
+ s.add_dependency "other_dep", "= 0.9"
+ end
+ end
+
+ lockfile <<~L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo4/
specs:
- rack (0.9.1)
+ myrack_middleware (1.0)
+ other_dep (1.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack (> 0.9, < 1.0)
+ myrack_middleware
+ other_dep
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo4"
+ gem "myrack_middleware"
+ gem "other_dep", "1.0"
G
+
+ expect(err).to eq <<~ERROR.strip
+ Could not find compatible versions
+
+ Because every version of myrack_middleware depends on other_dep = 0.9
+ and Gemfile depends on myrack_middleware >= 0,
+ other_dep = 0.9 is required.
+ So, because Gemfile depends on other_dep = 1.0,
+ version solving has failed.
+ ERROR
end
- it "captures the Ruby version in the lockfile" do
+ it "regenerates a lockfile with no specs" do
+ build_repo4 do
+ build_gem "indirect_dependency", "1.2.3" do |s|
+ s.metadata["funding_uri"] = "https://example.com/donate"
+ end
+
+ build_gem "direct_dependency", "4.5.6" do |s|
+ s.add_dependency "indirect_dependency", ">= 0"
+ end
+ end
+
+ lockfile <<-G
+ GEM
+ remote: https://gem.repo4/
+ specs:
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ direct_dependency
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
- ruby '#{RUBY_VERSION}'
- gem "rack", "> 0.9", "< 1.0"
+ source "https://gem.repo4"
+
+ gem "direct_dependency"
G
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo4/
specs:
- rack (0.9.1)
+ direct_dependency (4.5.6)
+ indirect_dependency
+ indirect_dependency (1.2.3)
PLATFORMS
- #{lockfile_platforms}
+ #{lockfile_platforms(generic_default_locked_platform || local_platform, defaults: ["ruby"])}
DEPENDENCIES
- rack (> 0.9, < 1.0)
+ direct_dependency
- RUBY VERSION
- ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+ end
+
+ it "automatically fixes the lockfile when it's missing deps and the full index is in use" do
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack_middleware (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile <<-G
+ source "#{file_uri_for(gem_repo2)}"
+ gem "myrack_middleware"
G
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ myrack (0.9.1)
+ myrack_middleware (1.0)
+ myrack (= 0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
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"))
+ it "automatically fixes the lockfile when it includes a gem under the correct GIT section, but also under an incorrect GEM section, with a higher version, and with no explicit Gemfile requirement" do
+ git = build_git "foo"
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}/"
- gem "omg", :git => "#{lib_path("omg")}", :branch => 'master'
+ gemfile <<~G
+ source "https://gem.repo1/"
+ gem "foo", git: "#{lib_path("foo-1.0")}"
G
- bundle "config --local path vendor"
- bundle :install
- expect(the_bundle).to include_gems "omg 1.0"
+ # If the lockfile erroneously lists platform versions of the gem
+ # that don't match the locked version of the git repo we should remove them.
- # Create a Gemfile.lock that has duplicate GIT sections
- lockfile <<-L
+ lockfile <<~L
GIT
- remote: #{lib_path("omg")}
- revision: #{revision}
- branch: master
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("main")}
specs:
- omg (1.0)
+ foo (1.0)
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ foo (1.1-x86_64-linux-gnu)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
GIT
- remote: #{lib_path("omg")}
- revision: #{revision}
- branch: master
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("main")}
specs:
- omg (1.0)
+ foo (1.0)
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- omg!
+ foo!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile when it includes a gem under the correct GIT section, but also under an incorrect GEM section, with a higher version" do
+ git = build_git "foo"
+
+ gemfile <<~G
+ source "https://gem.repo1/"
+ gem "foo", "= 1.0", git: "#{lib_path("foo-1.0")}"
+ G
+
+ # If the lockfile erroneously lists platform versions of the gem
+ # that don't match the locked version of the git repo we should remove them.
+
+ lockfile <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("main")}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ foo (1.1-x86_64-linux-gnu)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo (= 1.0)!
+
+ 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)
+ expect(lockfile).to eq <<~L
GIT
- remote: #{lib_path("omg")}
- revision: #{revision}
- branch: master
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("main")}
specs:
- omg (1.0)
+ foo (1.0)
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- omg!
+ foo (= 1.0)!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
- it "raises a helpful error message when the lockfile is missing deps" do
- lockfile <<-L
+ it "automatically fixes the lockfile when it has incorrect deps, keeping the locked version" do
+ build_repo4 do
+ build_gem "net-smtp", "0.5.0" do |s|
+ s.add_dependency "net-protocol"
+ end
+
+ build_gem "net-smtp", "0.5.1" do |s|
+ s.add_dependency "net-protocol"
+ end
+
+ build_gem "net-protocol", "0.2.2"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "net-smtp"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ net-protocol (0.2.2)
+ net-smtp (0.5.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ net-smtp
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ net-protocol (0.2.2)
+ net-smtp (0.5.0)
+ net-protocol
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ net-smtp
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "successfully updates the lockfile when a new gem is added in the Gemfile includes a gem that shouldn't be included" do
+ build_repo4 do
+ build_gem "logger", "1.7.0"
+ build_gem "rack", "3.2.0"
+ build_gem "net-smtp", "0.5.0"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "logger"
+ gem "net-smtp"
+
+ install_if -> { false } do
+ gem 'rack', github: 'rack/rack'
+ end
+ G
+
+ lockfile <<~L
+ GIT
+ remote: https://github.com/rack/rack.git
+ revision: 2fface9ac09fc582a81386becd939c987ad33f99
+ specs:
+ rack (3.2.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ logger (1.7.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ logger
+ rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GIT
+ remote: https://github.com/rack/rack.git
+ revision: 2fface9ac09fc582a81386becd939c987ad33f99
+ specs:
+ rack (3.2.0)
+
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: #{file_uri_for(gem_repo4)}/
specs:
- rack_middleware (1.0)
+ logger (1.7.0)
+ net-smtp (0.5.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack_middleware
+ logger
+ net-smtp
+ rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
+ end
+
+ shared_examples_for "a lockfile missing dependent specs" do
+ it "auto-heals" do
+ build_repo4 do
+ build_gem "minitest-bisect", "1.6.0" do |s|
+ s.add_dependency "path_expander", "~> 1.1"
+ end
+
+ build_gem "path_expander", "1.1.1"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "minitest-bisect"
+ G
+
+ # Corrupt lockfile (completely missing path_expander)
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ minitest-bisect (1.6.0)
+
+ PLATFORMS
+ #{platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ cache_gems "minitest-bisect-1.6.0", "path_expander-1.1.1", gem_repo: gem_repo4
+ bundle :install
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ minitest-bisect (1.6.0)
+ path_expander (~> 1.1)
+ path_expander (1.1.1)
+
+ PLATFORMS
+ #{platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "rack_middleware"
+ context "with just specific platform" do
+ let(:platforms) { lockfile_platforms }
+
+ it_behaves_like "a lockfile missing dependent specs"
+ end
+
+ context "with both ruby and specific platform" do
+ let(:platforms) { lockfile_platforms("ruby") }
+
+ it_behaves_like "a lockfile missing dependent specs"
+ end
+
+ it "auto-heals when the lockfile is missing specs" do
+ build_repo4 do
+ build_gem "minitest-bisect", "1.6.0" do |s|
+ s.add_dependency "path_expander", "~> 1.1"
+ end
+
+ build_gem "path_expander", "1.1.1"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "minitest-bisect"
G
- expect(err).to include("Downloading rack_middleware-1.0 revealed dependencies not in the API or the lockfile (#{Gem::Dependency.new("rack", "= 0.9.1")}).").
- and include("Either installing with `--full-index` or running `bundle update rack_middleware` should fix the problem.")
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ minitest-bisect (1.6.0)
+ path_expander (~> 1.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --verbose"
+ expect(out).to include("re-resolving dependencies because your lockfile includes \"minitest-bisect\" but not some of its dependencies")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ minitest-bisect (1.6.0)
+ path_expander (~> 1.1)
+ path_expander (1.1.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ minitest-bisect
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
describe "a line ending" do
@@ -1387,37 +2309,47 @@ RSpec.describe "the lockfile format" do
build_repo2
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "rack"
+ source "https://gem.repo2"
+ gem "myrack"
G
set_lockfile_mtime_to_known_value
end
it "generates Gemfile.lock with \\n line endings" do
expect(File.read(bundled_app_lock)).not_to match("\r\n")
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
context "during updates" do
it "preserves Gemfile.lock \\n line endings" do
- update_repo2
+ update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+ end
- expect { bundle "update", :all => true }.to change { File.mtime(bundled_app_lock) }
+ expect { bundle "update", all: true }.to change { File.mtime(bundled_app_lock) }
expect(File.read(bundled_app_lock)).not_to match("\r\n")
- expect(the_bundle).to include_gems "rack 1.2"
+ expect(the_bundle).to include_gems "myrack 1.2"
end
it "preserves Gemfile.lock \\n\\r line endings" do
skip "needs to be adapted" if Gem.win_platform?
- update_repo2
+ update_repo2 do
+ build_gem "myrack", "1.2" do |s|
+ s.executables = "myrackup"
+ end
+ end
+
win_lock = File.read(bundled_app_lock).gsub(/\n/, "\r\n")
File.open(bundled_app_lock, "wb") {|f| f.puts(win_lock) }
set_lockfile_mtime_to_known_value
- expect { bundle "update", :all => true }.to change { File.mtime(bundled_app_lock) }
+ 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"
+
+ expect(the_bundle).to include_gems "myrack 1.2"
end
end
@@ -1438,7 +2370,7 @@ RSpec.describe "the lockfile format" do
expect do
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.setup
RUBY
end.not_to change { File.mtime(bundled_app_lock) }
@@ -1449,48 +2381,36 @@ RSpec.describe "the lockfile format" do
it "refuses to install if Gemfile.lock contains conflict markers" do
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo1)}//
+ remote: https://gem.repo2//
specs:
<<<<<<<
- rack (1.0.0)
+ myrack (1.0.0)
=======
- rack (1.0.1)
+ myrack (1.0.1)
>>>>>>>
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}/"
- gem "rack"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo2/"
+ gem "myrack"
G
expect(err).to match(/your Gemfile.lock contains merge conflicts/i)
expect(err).to match(/git checkout HEAD -- Gemfile.lock/i)
end
-private
-
- def prerelease?(version)
- Gem::Version.new(version).prerelease?
- end
+ private
def previous_major(version)
version.split(".").map.with_index {|v, i| i == 0 ? v.to_i - 1 : v }.join(".")
end
-
- def bump_minor(version)
- bump(version, 1)
- end
-
- def bump(version, segment)
- version.split(".").map.with_index {|v, i| i == segment ? v.to_i + 1 : v }.join(".")
- end
end
diff --git a/spec/bundler/other/cli_dispatch_spec.rb b/spec/bundler/other/cli_dispatch_spec.rb
index 2d6080296f..a2c745b070 100644
--- a/spec/bundler/other/cli_dispatch_spec.rb
+++ b/spec/bundler/other/cli_dispatch_spec.rb
@@ -2,19 +2,19 @@
RSpec.describe "bundle command names" do
it "work when given fully" do
- bundle "install", :raise_on_error => false
+ bundle "install", raise_on_error: false
expect(err).to eq("Could not locate Gemfile")
- expect(last_command.stdboth).not_to include("Ambiguous command")
+ expect(stdboth).not_to include("Ambiguous command")
end
it "work when not ambiguous" do
- bundle "ins", :raise_on_error => false
+ bundle "ins", raise_on_error: false
expect(err).to eq("Could not locate Gemfile")
- expect(last_command.stdboth).not_to include("Ambiguous command")
+ expect(stdboth).not_to include("Ambiguous command")
end
it "print a friendly error when ambiguous" do
- bundle "in", :raise_on_error => false
- expect(err).to eq("Ambiguous command in matches [info, init, inject, install]")
+ bundle "in", raise_on_error: false
+ expect(err).to eq("Ambiguous command in matches [info, init, install]")
end
end
diff --git a/spec/bundler/other/cli_man_pages_spec.rb b/spec/bundler/other/cli_man_pages_spec.rb
new file mode 100644
index 0000000000..4e8f155309
--- /dev/null
+++ b/spec/bundler/other/cli_man_pages_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle commands" do
+ it "expects all commands to have all options and subcommands documented" do
+ check_commands!(Bundler::CLI)
+
+ Bundler::CLI.subcommand_classes.each_value do |klass|
+ check_commands!(klass)
+ end
+ end
+
+ private
+
+ def check_commands!(command_class)
+ command_class.commands.each do |command_name, command|
+ if command.is_a?(Bundler::Thor::HiddenCommand)
+ man_page = man_page(command_name)
+ expect(man_page).not_to exist
+ expect(main_man_page.read).not_to include("bundle #{command_name}")
+ elsif command_class == Bundler::CLI
+ man_page = man_page(command_name)
+ expect(man_page).to exist
+
+ check_options!(command, man_page)
+ else
+ man_page = man_page(command.ancestor_name)
+ expect(man_page).to exist
+
+ check_options!(command, man_page)
+ check_subcommand!(command_name, man_page)
+ end
+ end
+ end
+
+ def check_options!(command, man_page)
+ command.options.each do |_, option|
+ check_option!(option, man_page)
+ end
+ end
+
+ def check_option!(option, man_page)
+ man_page_content = man_page.read
+
+ aliases = option.aliases
+ formatted_aliases = aliases.sort.map {|name| "`#{name}`" }.join(", ") if aliases
+
+ help = if option.type == :boolean
+ "* #{append_aliases("`#{option.switch_name}`", formatted_aliases)}:"
+ elsif option.enum
+ formatted_aliases = "`#{option.switch_name}`" if aliases.empty? && option.lazy_default
+ "* #{prepend_aliases(option.enum.sort.map {|enum| "`#{option.switch_name}=#{enum}`" }.join(", "), formatted_aliases)}:"
+ else
+ names = [option.switch_name, *aliases]
+ value =
+ case option.type
+ when :array then "<list>"
+ when :numeric then "<number>"
+ else option.name.upcase
+ end
+
+ value = option.type != :numeric && option.lazy_default ? "[=#{value}]" : "=#{value}"
+
+ "* #{names.map {|name| "`#{name}#{value}`" }.join(", ")}:"
+ end
+
+ if option.banner.include?("(removed)")
+ expect(man_page_content).not_to include(help)
+ else
+ expect(man_page_content).to include(help)
+ end
+ end
+
+ def check_subcommand!(name, man_page)
+ expect(man_page.read).to match(name)
+ end
+
+ def append_aliases(text, aliases)
+ return text if aliases.empty?
+
+ "#{text}, #{aliases}"
+ end
+
+ def prepend_aliases(text, aliases)
+ return text if aliases.empty?
+
+ "#{aliases}, #{text}"
+ end
+
+ def man_page_content(command_name)
+ man_page(command_name).read
+ end
+
+ def man_page(command_name)
+ source_root.join("lib/bundler/man/bundle-#{command_name}.1.ronn")
+ end
+
+ def main_man_page
+ source_root.join("lib/bundler/man/bundle.1.ronn")
+ end
+end
diff --git a/spec/bundler/other/ext_spec.rb b/spec/bundler/other/ext_spec.rb
index e13f62a856..a883eefe06 100644
--- a/spec/bundler/other/ext_spec.rb
+++ b/spec/bundler/other/ext_spec.rb
@@ -1,61 +1,50 @@
# frozen_string_literal: true
-RSpec.describe "Gem::Specification#match_platform" do
+RSpec.describe "Gem::Specification#installable_on_platform?" do
it "does not match platforms other than the gem platform" do
darwin = gem "lol", "1.0", "platform_specific-1.0-x86-darwin-10"
- expect(darwin.match_platform(pl("java"))).to eq(false)
+ expect(darwin.installable_on_platform?(pl("java"))).to eq(false)
end
context "when platform is a string" do
it "matches when platform is a string" do
lazy_spec = Bundler::LazySpecification.new("lol", "1.0", "universal-mingw32")
- expect(lazy_spec.match_platform(pl("x86-mingw32"))).to eq(true)
- expect(lazy_spec.match_platform(pl("x64-mingw32"))).to eq(true)
+ expect(lazy_spec.installable_on_platform?(pl("x86-mingw32"))).to eq(true)
+ expect(lazy_spec.installable_on_platform?(pl("x64-mingw32"))).to eq(true)
end
end
end
-RSpec.describe "Bundler::GemHelpers#generic" do
- include Bundler::GemHelpers
-
- it "converts non-windows platforms into ruby" do
- expect(generic(pl("x86-darwin-10"))).to eq(pl("ruby"))
- expect(generic(pl("ruby"))).to eq(pl("ruby"))
- end
-
- it "converts java platform variants into java" do
- expect(generic(pl("universal-java-17"))).to eq(pl("java"))
- expect(generic(pl("java"))).to eq(pl("java"))
- end
-
- it "converts mswin platform variants into x86-mswin32" do
- expect(generic(pl("mswin32"))).to eq(pl("x86-mswin32"))
- expect(generic(pl("i386-mswin32"))).to eq(pl("x86-mswin32"))
- expect(generic(pl("x86-mswin32"))).to eq(pl("x86-mswin32"))
- end
-
- it "converts 32-bit mingw platform variants into x86-mingw32" do
- expect(generic(pl("mingw32"))).to eq(pl("x86-mingw32"))
- expect(generic(pl("i386-mingw32"))).to eq(pl("x86-mingw32"))
- expect(generic(pl("x86-mingw32"))).to eq(pl("x86-mingw32"))
- end
-
- it "converts 64-bit mingw platform variants into x64-mingw32" do
- expect(generic(pl("x64-mingw32"))).to eq(pl("x64-mingw32"))
- expect(generic(pl("x86_64-mingw32"))).to eq(pl("x64-mingw32"))
- end
-end
-
RSpec.describe "Gem::SourceIndex#refresh!" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
it "does not explode when called" do
- run "Gem.source_index.refresh!", :raise_on_error => false
- run "Gem::SourceIndex.new([]).refresh!", :raise_on_error => false
+ run "Gem.source_index.refresh!", raise_on_error: false
+ run "Gem::SourceIndex.new([]).refresh!", raise_on_error: false
+ end
+end
+
+RSpec.describe "Gem::NameTuple" do
+ describe "#initialize" do
+ it "creates a Gem::NameTuple with equality regardless of platform type" do
+ gem_platform = Gem::NameTuple.new "a", v("1"), pl("x86_64-linux")
+ str_platform = Gem::NameTuple.new "a", v("1"), "x86_64-linux"
+ expect(gem_platform).to eq(str_platform)
+ expect(gem_platform.hash).to eq(str_platform.hash)
+ expect(gem_platform.to_a).to eq(str_platform.to_a)
+ end
+ end
+
+ describe "#lock_name" do
+ it "returns the lock name" do
+ expect(Gem::NameTuple.new("a", v("1.0.0"), pl("x86_64-linux")).lock_name).to eq("a (1.0.0-x86_64-linux)")
+ expect(Gem::NameTuple.new("a", v("1.0.0"), "ruby").lock_name).to eq("a (1.0.0)")
+ expect(Gem::NameTuple.new("a", v("1.0.0")).lock_name).to eq("a (1.0.0)")
+ end
end
end
diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb
index 2ca6717827..ab7589d698 100644
--- a/spec/bundler/other/major_deprecation_spec.rb
+++ b/spec/bundler/other/major_deprecation_spec.rb
@@ -6,95 +6,91 @@ RSpec.describe "major deprecations" do
describe "Bundler" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
describe ".clean_env" do
before do
source = "Bundler.clean_env"
- bundle "exec ruby -e #{source.dump}"
+ bundle "exec ruby -e #{source.dump}", raise_on_error: false
end
- it "is deprecated in favor of .unbundled_env", :bundler => "2" 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` " \
- "(called at -e:1)"
+ it "is removed in favor of .unbundled_env and shows a helpful error message about it" do
+ expect(err).to include \
+ "`Bundler.clean_env` has been removed in favor of `Bundler.unbundled_env`. " \
+ "If you instead want the environment before bundler was originally loaded, use `Bundler.original_env`" \
end
-
- pending "is removed and shows a helpful error message about it", :bundler => "3"
end
describe ".with_clean_env" do
before do
source = "Bundler.with_clean_env {}"
- bundle "exec ruby -e #{source.dump}"
+ bundle "exec ruby -e #{source.dump}", raise_on_error: false
end
- it "is deprecated in favor of .unbundled_env", :bundler => "2" 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` " \
- "(called at -e:1)"
+ it "is removed in favor of .unbundled_env and shows a helpful error message about it" do
+ expect(err).to include(
+ "`Bundler.with_clean_env` has been removed in favor of `Bundler.with_unbundled_env`. " \
+ "If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env`"
)
end
-
- pending "is removed and shows a helpful error message about it", :bundler => "3"
end
describe ".clean_system" do
before do
source = "Bundler.clean_system('ls')"
- bundle "exec ruby -e #{source.dump}"
+ bundle "exec ruby -e #{source.dump}", raise_on_error: false
end
- it "is deprecated in favor of .unbundled_system", :bundler => "2" 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` " \
- "(called at -e:1)"
+ it "is removed in favor of .unbundled_system and shows a helpful error message about it" do
+ expect(err).to include(
+ "`Bundler.clean_system` has been removed 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`" \
)
end
-
- pending "is removed and shows a helpful error message about it", :bundler => "3"
end
describe ".clean_exec" do
before do
source = "Bundler.clean_exec('ls')"
- bundle "exec ruby -e #{source.dump}"
+ bundle "exec ruby -e #{source.dump}", raise_on_error: false
end
- it "is deprecated in favor of .unbundled_exec", :bundler => "2" 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` " \
- "(called at -e:1)"
+ it "is removed in favor of .unbundled_exec and shows a helpful error message about it" do
+ expect(err).to include(
+ "`Bundler.clean_exec` has been removed 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`" \
)
end
-
- pending "is removed and shows a helpful error message about it", :bundler => "3"
end
describe ".environment" do
before do
source = "Bundler.environment"
- bundle "exec ruby -e #{source.dump}"
+ bundle "exec ruby -e #{source.dump}", raise_on_error: false
end
- it "is deprecated in favor of .load", :bundler => "2" do
- expect(deprecations).to include "Bundler.environment has been removed in favor of Bundler.load (called at -e:1)"
+ it "is removed in favor of .load and shows a helpful error message about it" do
+ expect(err).to include "Bundler.environment has been removed in favor of Bundler.load"
end
+ end
+ end
- pending "is removed and shows a helpful error message about it", :bundler => "3"
+ 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 removed and shows a helpful error message about it" do
+ expect(err).to include "The `--no-keep-file-descriptors` has been removed. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to"
end
end
describe "bundle update --quiet" do
it "does not print any deprecations" do
- bundle :update, :quiet => true, :raise_on_error => false
+ bundle :update, quiet: true, raise_on_error: false
expect(deprecations).to be_empty
end
end
@@ -102,45 +98,181 @@ RSpec.describe "major deprecations" do
context "bundle check --path" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "check --path vendor/bundle", :raise_on_error => false
+ bundle "check --path vendor/bundle", raise_on_error: false
end
- it "should print a deprecation warning", :bundler => "2" do
- expect(deprecations).to include(
- "The `--path` flag is deprecated because it relies on being " \
- "remembered across bundler invocations, which bundler will no " \
- "longer do in future versions. Instead please use `bundle config set " \
- "path 'vendor/bundle'`, and stop using this flag"
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--path` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set path 'vendor/bundle'`, " \
+ "and stop using this flag"
)
end
-
- pending "should fail with a helpful error", :bundler => "3"
end
context "bundle check --path=" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "check --path=vendor/bundle", raise_on_error: false
+ end
+
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--path` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set path 'vendor/bundle'`, " \
+ "and stop using this flag"
+ )
+ end
+ end
+
+ context "bundle binstubs --path=" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "binstubs myrack --path=binpath", raise_on_error: false
+ end
+
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--path` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set bin 'binpath'`, " \
+ "and stop using this flag"
+ )
+ end
+ end
+
+ context "bundle cache --all" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
G
- bundle "check --path=vendor/bundle", :raise_on_error => false
+ bundle "cache --all --verbose", raise_on_error: false
end
- it "should print a deprecation warning", :bundler => "2" do
- expect(deprecations).to include(
- "The `--path` flag is deprecated because it relies on being " \
- "remembered across bundler invocations, which bundler will no " \
- "longer do in future versions. Instead please use `bundle config set " \
- "path 'vendor/bundle'`, and stop using this flag"
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--all` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set cache_all true`, " \
+ "and stop using this flag"
)
end
+ end
+
+ context "bundle cache --no-all" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "cache --no-all", raise_on_error: false
+ end
- pending "should fail with a helpful error", :bundler => "3"
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--no-all` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set cache_all false`, " \
+ "and stop using this flag"
+ )
+ end
+ end
+
+ context "bundle cache --path" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "cache --path foo", raise_on_error: false
+ end
+
+ it "should print a removal error" do
+ expect(err).to include(
+ "The `--path` flag has been removed because its semantics were 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
+ end
+
+ context "bundle cache --path=" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "cache --path=foo", raise_on_error: false
+ end
+
+ it "should print a deprecation warning" do
+ expect(err).to include(
+ "The `--path` flag has been removed because its semantics were 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
+ end
+
+ context "bundle cache --frozen" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "cache --frozen", raise_on_error: false
+ end
+
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--frozen` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set frozen true`, " \
+ "and stop using this flag"
+ )
+ end
+ end
+
+ context "bundle cache --no-prune" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "cache --no-prune", raise_on_error: false
+ end
+
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--no-prune` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set no_prune true`, " \
+ "and stop using this flag"
+ )
+ end
end
describe "bundle config" do
@@ -149,23 +281,23 @@ RSpec.describe "major deprecations" do
bundle "config"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config list` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old get interface" do
before do
- bundle "config waka"
+ bundle "config waka", raise_on_error: false
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config get waka` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old set interface" do
@@ -173,11 +305,11 @@ RSpec.describe "major deprecations" do
bundle "config waka wakapun"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config set waka wakapun` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old set interface with --local" do
@@ -185,11 +317,11 @@ RSpec.describe "major deprecations" do
bundle "config --local waka wakapun"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config set --local waka wakapun` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old set interface with --global" do
@@ -197,11 +329,11 @@ RSpec.describe "major deprecations" do
bundle "config --global waka wakapun"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config set --global waka wakapun` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old unset interface" do
@@ -209,11 +341,11 @@ RSpec.describe "major deprecations" do
bundle "config --delete waka"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config unset waka` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old unset interface with --local" do
@@ -221,11 +353,11 @@ RSpec.describe "major deprecations" do
bundle "config --delete --local waka"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config unset --local waka` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old unset interface with --global" do
@@ -233,28 +365,28 @@ RSpec.describe "major deprecations" do
bundle "config --delete --global waka"
end
- it "warns", :bundler => "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config unset --global waka` instead.")
end
- pending "fails with a helpful error", :bundler => "3"
+ pending "fails with a helpful error", bundler: "5"
end
end
describe "bundle update" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
- it "warns when no options are given", :bundler => "3" do
+ it "warns when no options are given", bundler: "4" do
bundle "update"
expect(deprecations).to include("Pass --all to `bundle update` to update everything")
end
- pending "fails with a helpful error when no options are given", :bundler => "3"
+ pending "fails with a helpful error when no options are given", bundler: "5"
it "does not warn when --all is passed" do
bundle "update --all"
@@ -264,24 +396,22 @@ RSpec.describe "major deprecations" do
describe "bundle install --binstubs" do
before do
- install_gemfile <<-G, :binstubs => true
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ install_gemfile <<-G, binstubs: true, raise_on_error: false
+ source "https://gem.repo1"
+ gem "myrack"
G
end
- it "should output a deprecation warning", :bundler => "2" do
- expect(deprecations).to include("The --binstubs option will be removed in favor of `bundle binstubs`")
+ it "fails with a helpful error" do
+ expect(err).to include("The --binstubs option has been removed in favor of `bundle binstubs --all`")
end
-
- pending "fails with a helpful error", :bundler => "3"
end
context "bundle install with both gems.rb and Gemfile present" do
it "should not warn about gems.rb" do
- create_file "gems.rb", <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ gemfile "gems.rb", <<-G
+ source "https://gem.repo1"
+ gem "myrack"
G
bundle :install
@@ -289,97 +419,217 @@ RSpec.describe "major deprecations" do
end
it "should print a proper warning, and use gems.rb" do
- create_file "gems.rb"
+ gemfile "gems.rb", "source 'https://gem.repo1'"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
expect(warnings).to include(
- "Multiple gemfiles (gems.rb and Gemfile) detected. Make sure you remove Gemfile and Gemfile.lock since bundler is ignoring them in favor of gems.rb and gems.rb.locked."
+ "Multiple gemfiles (gems.rb and Gemfile) detected. Make sure you remove Gemfile and Gemfile.lock since bundler is ignoring them in favor of gems.rb and gems.locked."
)
- expect(the_bundle).not_to include_gem "rack 1.0"
+ expect(the_bundle).not_to include_gem "myrack 1.0"
end
end
context "bundle install with flags" do
before do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
{
- "clean" => ["clean", true],
- "deployment" => ["deployment", true],
- "frozen" => ["frozen", true],
- "no-deployment" => ["deployment", false],
- "no-prune" => ["no_prune", true],
- "path" => ["path", "vendor/bundle"],
- "shebang" => ["shebang", "ruby27"],
- "system" => ["system", true],
- "without" => ["without", "development"],
- "with" => ["with", "development"],
+ "clean" => ["clean", "true"],
+ "deployment" => ["deployment", "true"],
+ "frozen" => ["frozen", "true"],
+ "no-deployment" => ["deployment", "false"],
+ "no-prune" => ["no_prune", "true"],
+ "path" => ["path", "'vendor/bundle'"],
+ "shebang" => ["shebang", "'ruby27'"],
+ "system" => ["path.system", "true"],
+ "without" => ["without", "'development'"],
+ "with" => ["with", "'development'"],
}.each do |name, expectations|
option_name, value = *expectations
flag_name = "--#{name}"
+ args = %w[true false].include?(value) ? flag_name : "#{flag_name} #{value}"
context "with the #{flag_name} flag" do
before do
bundle "install" # to create a lockfile, which deployment or frozen need
- bundle "install #{flag_name} #{value}"
+
+ bundle "install #{args}", raise_on_error: false
end
- it "should print a deprecation warning", :bundler => "2" do
- expect(deprecations).to include(
- "The `#{flag_name}` flag is deprecated because it relies on " \
- "being remembered across bundler invocations, which bundler " \
- "will no longer do in future versions. Instead please use " \
- "`bundle config set #{option_name} '#{value}'`, and stop using this flag"
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `#{flag_name}` flag has been removed because it relied on " \
+ "being remembered across bundler invocations, which bundler no " \
+ "longer does. Instead please use `bundle config set " \
+ "#{option_name} #{value}`, and stop using this flag"
)
end
-
- pending "should fail with a helpful error", :bundler => "3"
end
end
end
context "bundle install with multiple sources" do
before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo3)}"
- source "#{file_uri_for(gem_repo1)}"
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo3"
+ source "https://gem.repo1"
G
end
- it "shows a deprecation", :bundler => "2" 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. " \
- "To upgrade this warning to an error, run `bundle config set " \
- "disable_multisource true`."
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "This Gemfile contains multiple global sources. " \
+ "Each source after the first must include a block to indicate which gems " \
+ "should come from that source"
)
end
- pending "should fail with a helpful error", :bundler => "3"
+ it "doesn't show lockfile deprecations if there's a lockfile" do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo3/
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ bundle "install", raise_on_error: false
+
+ expect(err).to include(
+ "This Gemfile contains multiple global sources. " \
+ "Each source after the first must include a block to indicate which gems " \
+ "should come from that source"
+ )
+ expect(err).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 "frozen true"
+ bundle "install", raise_on_error: false
+
+ expect(err).to include(
+ "This Gemfile contains multiple global sources. " \
+ "Each source after the first must include a block to indicate which gems " \
+ "should come from that source"
+ )
+ expect(err).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
+ end
+
+ context "bundle install with a lockfile with a single rubygems section with multiple remotes" do
+ before do
+ build_repo3 do
+ build_gem "myrack", "0.9.1"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ source "https://gem.repo3" do
+ gem 'myrack'
+ end
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo1/
+ remote: https://gem.repo3/
+ specs:
+ myrack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ myrack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "shows an error" do
+ bundle "install", raise_on_error: false
+
+ expect(err).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
+ end
+
+ context "bundle install with a lockfile including X64_MINGW_LEGACY platform" do
+ before do
+ gemfile <<~G
+ source "https://gem.repo1"
+ gem "rake"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (10.3.2)
+
+ PLATFORMS
+ ruby
+ x64-mingw32
+
+ DEPENDENCIES
+ rake
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "warns a helpful error" do
+ bundle "install", raise_on_error: false
+
+ expect(err).to include("Found x64-mingw32 in lockfile, which is deprecated and will be removed in the future.")
+ end
+ end
+
+ context "with a global path source" do
+ before do
+ build_lib "foo"
+
+ install_gemfile <<-G, raise_on_error: false
+ path "#{lib_path("foo-1.0")}"
+ gem 'foo'
+ G
+ end
+
+ it "shows an error" do
+ expect(err).to include("You can no longer specify a path source by itself")
+ end
end
context "when Bundler.setup is run in a ruby script" do
before do
- create_file "gems.rb"
+ create_file "gems.rb", "source 'https://gem.repo1'"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :group => :test
+ source "https://gem.repo1"
+ gem "myrack", :group => :test
G
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.setup
Bundler.setup
@@ -388,203 +638,161 @@ RSpec.describe "major deprecations" do
it "should print a single deprecation warning" do
expect(warnings).to include(
- "Multiple gemfiles (gems.rb and Gemfile) detected. Make sure you remove Gemfile and Gemfile.lock since bundler is ignoring them in favor of gems.rb and gems.rb.locked."
+ "Multiple gemfiles (gems.rb and Gemfile) detected. Make sure you remove Gemfile and Gemfile.lock since bundler is ignoring them in favor of gems.rb and gems.locked."
)
end
end
context "when `bundler/deployment` is required in a ruby script" do
before do
- ruby(<<-RUBY, :env => env_for_missing_prerelease_default_gem_activation)
+ ruby <<-RUBY, raise_on_error: false
require 'bundler/deployment'
RUBY
end
- it "should print a capistrano deprecation warning", :bundler => "2" do
- expect(deprecations).to include("Bundler no longer integrates " \
+ it "should print a capistrano deprecation warning" do
+ expect(err).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"
end
- describe Bundler::Dsl do
+ context "when `bundler/capistrano` is required in a ruby script" do
before do
- @rubygems = double("rubygems")
- allow(Bundler::Source::Rubygems).to receive(:new) { @rubygems }
- end
-
- context "with github gems" do
- it "does not warn about removal", :bundler => "2" do
- expect(Bundler.ui).not_to receive(:warn)
- subject.gem("sparks", :github => "indirect/sparks")
- github_uri = "https://github.com/indirect/sparks.git"
- expect(subject.dependencies.first.source.uri).to eq(github_uri)
- end
-
- it "warns about removal", :bundler => "3" do
- msg = <<-EOS
-The :github git source is deprecated, and will be removed in the future. Change any "reponame" :github sources to "username/reponame". Add this code to the top of your Gemfile to ensure it continues to work:
-
- git_source(:github) {|repo_name| "https://github.com/\#{repo_name}.git" }
-
- EOS
- expect(Bundler.ui).to receive(:warn).with("[DEPRECATED] #{msg}")
- subject.gem("sparks", :github => "indirect/sparks")
- github_uri = "https://github.com/indirect/sparks.git"
- expect(subject.dependencies.first.source.uri).to eq(github_uri)
- end
+ ruby <<-RUBY, raise_on_error: false
+ require 'bundler/capistrano'
+ RUBY
end
- context "with bitbucket gems" do
- it "does not warn about removal", :bundler => "2" do
- expect(Bundler.ui).not_to receive(:warn)
- subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails")
- end
-
- it "warns about removal", :bundler => "3" do
- msg = <<-EOS
-The :bitbucket git source is deprecated, and will be removed in the future. Add this code to the top of your Gemfile to ensure it continues to work:
-
- git_source(:bitbucket) do |repo_name|
- user_name, repo_name = repo_name.split("/")
- repo_name ||= user_name
- "https://\#{user_name}@bitbucket.org/\#{user_name}/\#{repo_name}.git"
+ it "fails with a helpful error" do
+ expect(err).to include("[REMOVED] The Bundler task for Capistrano. Please use https://github.com/capistrano/bundler")
end
+ end
- EOS
- expect(Bundler.ui).to receive(:warn).with("[DEPRECATED] #{msg}")
- subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails")
- end
+ context "when `bundler/vlad` is required in a ruby script" do
+ before do
+ ruby <<-RUBY, raise_on_error: false
+ require 'bundler/vlad'
+ RUBY
end
- context "with gist gems" do
- it "does not warn about removal", :bundler => "2" do
- expect(Bundler.ui).not_to receive(:warn)
- subject.gem("not-really-a-gem", :gist => "1234")
- end
-
- it "warns about removal", :bundler => "3" do
- msg = <<-EOS
-The :gist git source is deprecated, and will be removed in the future. Add this code to the top of your Gemfile to ensure it continues to work:
-
- git_source(:gist) {|repo_name| "https://gist.github.com/\#{repo_name}.git" }
-
- EOS
- expect(Bundler.ui).to receive(:warn).with("[DEPRECATED] #{msg}")
- subject.gem("not-really-a-gem", :gist => "1234")
- end
+ it "fails with a helpful error" do
+ expect(err).to include("[REMOVED] The Bundler task for Vlad")
end
end
context "bundle show" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
- context "without flags" do
+ context "with --outdated flag" do
before do
- bundle :show
+ bundle "show --outdated", raise_on_error: false
end
- it "prints a deprecation warning recommending `bundle list`", :bundler => "2" do
- expect(deprecations).to include("use `bundle list` instead of `bundle show`")
+ it "fails with a helpful message" do
+ expect(err).to include("the `--outdated` flag to `bundle show` has been removed in favor of `bundle show --verbose`")
end
+ end
+ end
- pending "fails with a helpful message", :bundler => "3"
+ context "bundle remove" do
+ before do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
end
- context "with --outdated flag" do
- before do
- bundle "show --outdated"
- end
+ context "with --install" do
+ it "fails with a helpful message" do
+ bundle "remove myrack --install", raise_on_error: false
- it "prints a deprecation warning informing about its removal", :bundler => "2" do
- expect(deprecations).to include("the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement")
+ expect(err).to include "The `--install` flag has been removed. `bundle install` is triggered by default."
end
+ end
+ end
- pending "fails with a helpful message", :bundler => "3"
+ context "bundle viz" do
+ before do
+ bundle "viz", raise_on_error: false
end
- context "with --verbose flag" do
- before do
- bundle "show --verbose"
- end
+ it "fails with a helpful message" do
+ expect(err).to include "The `viz` command has been renamed to `graph` and moved to a plugin. See https://github.com/rubygems/bundler-graph"
+ end
+ 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
+ context "bundle inject" do
+ before do
+ bundle "inject", raise_on_error: false
+ end
- pending "fails with a helpful message", :bundler => "3"
+ it "fails with a helpful message" do
+ expect(err).to include "The `inject` command has been replaced by the `add` command"
end
+ end
- context "with a gem argument" do
- before do
- bundle "show rack"
+ context "bundle plugin install --local_git" do
+ before do
+ build_git "foo" do |s|
+ s.write "plugins.rb"
end
+ 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
+ it "fails with a helpful message" do
+ bundle "plugin install foo --local_git #{lib_path("foo-1.0")}", raise_on_error: false
+
+ expect(err).to include "--local_git has been removed, use --git"
+ end
+ end
- pending "fails with a helpful message", :bundler => "3"
+ describe "removing rubocop" do
+ before do
+ bundle_config_global "gem.mit false"
+ bundle_config_global "gem.test false"
+ bundle_config_global "gem.coc false"
+ bundle_config_global "gem.ci false"
+ bundle_config_global "gem.changelog false"
end
- context "with the --paths option" do
+ context "bundle gem --rubocop" do
before do
- bundle "show --paths"
+ bundle "gem my_new_gem --rubocop", raise_on_error: false
end
- it "prints a deprecation warning recommending `bundle list`", :bundler => "2" do
- expect(deprecations).to include("use `bundle list` instead of `bundle show --paths`")
+ it "prints an error" do
+ expect(err).to include \
+ "--rubocop has been removed, use --linter=rubocop"
end
-
- pending "fails with a helpful message", :bundler => "3"
end
- context "with a gem argument and the --paths option" do
+ context "bundle gem --no-rubocop" do
before do
- bundle "show rack --paths"
+ bundle "gem my_new_gem --no-rubocop", raise_on_error: false
end
- 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`")
+ it "prints an error" do
+ expect(err).to include \
+ "--no-rubocop has been removed, use --no-linter"
end
-
- pending "fails with a helpful message", :bundler => "3"
end
end
- context "bundle console" do
- before do
- bundle "console", :raise_on_error => false
+ context " bundle gem --ext parameter with no value" do
+ it "prints error when used before gem name" do
+ bundle "gem --ext foo", raise_on_error: false
+ expect(err).to include "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been removed. Please select a language, e.g. `--ext=rust` to generate a Rust extension."
end
- it "prints a deprecation warning", :bundler => "2" do
- expect(deprecations).to include \
- "bundle console will be replaced by `bin/console` generated by `bundle gem <name>`"
+ it "prints error when used after gem name" do
+ bundle "gem foo --ext", raise_on_error: false
+ expect(err).to include "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been removed. Please select a language, e.g. `--ext=rust` to generate a Rust extension."
end
-
- pending "fails with a helpful message", :bundler => "3"
- end
-
- context "bundle viz" do
- 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"
- 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"
- end
-
- pending "fails with a helpful message", :bundler => "3"
end
end
diff --git a/spec/bundler/other/platform_spec.rb b/spec/bundler/other/platform_spec.rb
deleted file mode 100644
index a8fd4d51fb..0000000000
--- a/spec/bundler/other/platform_spec.rb
+++ /dev/null
@@ -1,1264 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "bundle platform" do
- context "without flags" do
- let(:bundle_platform_platforms_string) do
- local_platforms.reverse.map {|pl| "* #{pl}" }.join("\n")
- end
-
- it "returns all the output" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
-
- #{ruby_version_correct}
-
- gem "foo"
- G
-
- bundle "platform"
- expect(out).to eq(<<-G.chomp)
-Your platform is: #{RUBY_PLATFORM}
-
-Your app has gems that work on these platforms:
-#{bundle_platform_platforms_string}
-
-Your Gemfile specifies a Ruby version requirement:
-* ruby #{RUBY_VERSION}
-
-Your current platform satisfies the Ruby version requirement.
-G
- end
-
- it "returns all the output including the patchlevel" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
-
- #{ruby_version_correct_patchlevel}
-
- gem "foo"
- G
-
- bundle "platform"
- expect(out).to eq(<<-G.chomp)
-Your platform is: #{RUBY_PLATFORM}
-
-Your app has gems that work on these platforms:
-#{bundle_platform_platforms_string}
-
-Your Gemfile specifies a Ruby version requirement:
-* ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
-
-Your current platform satisfies the Ruby version requirement.
-G
- end
-
- it "doesn't print ruby version requirement if it isn't specified" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
-
- gem "foo"
- G
-
- bundle "platform"
- expect(out).to eq(<<-G.chomp)
-Your platform is: #{RUBY_PLATFORM}
-
-Your app has gems that work on these platforms:
-#{bundle_platform_platforms_string}
-
-Your Gemfile does not specify a Ruby version requirement.
-G
- end
-
- it "doesn't match the ruby version requirement" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
-
- #{ruby_version_incorrect}
-
- gem "foo"
- G
-
- bundle "platform"
- expect(out).to eq(<<-G.chomp)
-Your platform is: #{RUBY_PLATFORM}
-
-Your app has gems that work on these platforms:
-#{bundle_platform_platforms_string}
-
-Your Gemfile specifies a Ruby version requirement:
-* ruby #{not_local_ruby_version}
-
-Your Ruby version is #{RUBY_VERSION}, but your Gemfile specified #{not_local_ruby_version}
-G
- end
- end
-
- context "--ruby" do
- it "returns ruby version when explicit" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "1.9.3", :engine => 'ruby', :engine_version => '1.9.3'
-
- gem "foo"
- G
-
- bundle "platform --ruby"
-
- expect(out).to eq("ruby 1.9.3")
- end
-
- it "defaults to MRI" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "1.9.3"
-
- gem "foo"
- G
-
- bundle "platform --ruby"
-
- expect(out).to eq("ruby 1.9.3")
- end
-
- it "handles jruby" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "1.8.7", :engine => 'jruby', :engine_version => '1.6.5'
-
- gem "foo"
- G
-
- bundle "platform --ruby"
-
- expect(out).to eq("ruby 1.8.7 (jruby 1.6.5)")
- end
-
- it "handles rbx" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "1.8.7", :engine => 'rbx', :engine_version => '1.2.4'
-
- gem "foo"
- G
-
- bundle "platform --ruby"
-
- expect(out).to eq("ruby 1.8.7 (rbx 1.2.4)")
- end
-
- it "handles truffleruby" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "2.5.1", :engine => 'truffleruby', :engine_version => '1.0.0-rc6'
-
- gem "foo"
- G
-
- bundle "platform --ruby"
-
- expect(out).to eq("ruby 2.5.1 (truffleruby 1.0.0-rc6)")
- end
-
- it "raises an error if engine is used but engine version is not" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "1.8.7", :engine => 'rbx'
-
- gem "foo"
- G
-
- bundle "platform", :raise_on_error => false
-
- expect(exitstatus).not_to eq(0)
- end
-
- it "raises an error if engine_version is used but engine is not" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "1.8.7", :engine_version => '1.2.4'
-
- gem "foo"
- G
-
- bundle "platform", :raise_on_error => false
-
- expect(exitstatus).not_to eq(0)
- end
-
- it "raises an error if engine version doesn't match ruby version for MRI" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- ruby "1.8.7", :engine => 'ruby', :engine_version => '1.2.4'
-
- gem "foo"
- G
-
- bundle "platform", :raise_on_error => false
-
- expect(exitstatus).not_to eq(0)
- end
-
- it "should print if no ruby version is specified" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
-
- gem "foo"
- G
-
- bundle "platform --ruby"
-
- expect(out).to eq("No ruby version specified")
- end
-
- it "handles when there is a locked requirement" do
- gemfile <<-G
- ruby "< 1.8.7"
- G
-
- lockfile <<-L
- GEM
- specs:
-
- PLATFORMS
- ruby
-
- DEPENDENCIES
-
- RUBY VERSION
- ruby 1.0.0p127
-
- BUNDLED WITH
- #{Bundler::VERSION}
- L
-
- bundle "platform --ruby"
- expect(out).to eq("ruby 1.0.0p127")
- end
-
- it "handles when there is a requirement in the gemfile" do
- gemfile <<-G
- ruby ">= 1.8.7"
- G
-
- bundle "platform --ruby"
- expect(out).to eq("ruby 1.8.7")
- end
-
- it "handles when there are multiple requirements in the gemfile" do
- gemfile <<-G
- ruby ">= 1.8.7", "< 2.0.0"
- G
-
- bundle "platform --ruby"
- expect(out).to eq("ruby 1.8.7")
- end
- end
-
- let(:ruby_version_correct) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{local_engine_version}\"" }
- let(:ruby_version_correct_engineless) { "ruby \"#{RUBY_VERSION}\"" }
- let(:ruby_version_correct_patchlevel) { "#{ruby_version_correct}, :patchlevel => '#{RUBY_PATCHLEVEL}'" }
- let(:ruby_version_incorrect) { "ruby \"#{not_local_ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_ruby_version}\"" }
- let(:engine_incorrect) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{not_local_tag}\", :engine_version => \"#{RUBY_VERSION}\"" }
- let(:engine_version_incorrect) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_engine_version}\"" }
- let(:patchlevel_incorrect) { "#{ruby_version_correct}, :patchlevel => '#{not_local_patchlevel}'" }
- let(:patchlevel_fixnum) { "#{ruby_version_correct}, :patchlevel => #{RUBY_PATCHLEVEL}1" }
-
- def should_be_ruby_version_incorrect
- expect(exitstatus).to eq(18)
- expect(err).to be_include("Your Ruby version is #{RUBY_VERSION}, but your Gemfile specified #{not_local_ruby_version}")
- end
-
- def should_be_engine_incorrect
- expect(exitstatus).to eq(18)
- expect(err).to be_include("Your Ruby engine is #{local_ruby_engine}, but your Gemfile specified #{not_local_tag}")
- end
-
- def should_be_engine_version_incorrect
- expect(exitstatus).to eq(18)
- expect(err).to be_include("Your #{local_ruby_engine} version is #{local_engine_version}, but your Gemfile specified #{local_ruby_engine} #{not_local_engine_version}")
- end
-
- def should_be_patchlevel_incorrect
- expect(exitstatus).to eq(18)
- expect(err).to be_include("Your Ruby patchlevel is #{RUBY_PATCHLEVEL}, but your Gemfile specified #{not_local_patchlevel}")
- end
-
- def should_be_patchlevel_fixnum
- expect(exitstatus).to eq(18)
- expect(err).to be_include("The Ruby patchlevel in your Gemfile must be a string")
- end
-
- context "bundle install" do
- it "installs fine when the ruby version matches" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{ruby_version_correct}
- G
-
- expect(bundled_app_lock).to exist
- end
-
- it "installs fine with any engine", :jruby do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{ruby_version_correct_engineless}
- G
-
- expect(bundled_app_lock).to exist
- end
-
- it "installs fine when the patchlevel matches" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{ruby_version_correct_patchlevel}
- G
-
- expect(bundled_app_lock).to exist
- end
-
- it "doesn't install when the ruby version doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{ruby_version_incorrect}
- G
-
- expect(bundled_app_lock).not_to exist
- should_be_ruby_version_incorrect
- end
-
- it "doesn't install when engine doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{engine_incorrect}
- G
-
- expect(bundled_app_lock).not_to exist
- should_be_engine_incorrect
- end
-
- it "doesn't install when engine version doesn't match", :jruby do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{engine_version_incorrect}
- G
-
- expect(bundled_app_lock).not_to exist
- should_be_engine_version_incorrect
- end
-
- it "doesn't install when patchlevel doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{patchlevel_incorrect}
- G
-
- expect(bundled_app_lock).not_to exist
- should_be_patchlevel_incorrect
- end
- end
-
- context "bundle check" do
- it "checks fine when the ruby version matches" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{ruby_version_correct}
- G
-
- bundle :check
- expect(out).to match(/\AResolving dependencies\.\.\.\.*\nThe Gemfile's dependencies are satisfied\z/)
- end
-
- it "checks fine with any engine", :jruby do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{ruby_version_correct_engineless}
- G
-
- bundle :check
- expect(out).to match(/\AResolving dependencies\.\.\.\.*\nThe Gemfile's dependencies are satisfied\z/)
- end
-
- it "fails when ruby version doesn't match" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{ruby_version_incorrect}
- G
-
- bundle :check, :raise_on_error => false
- should_be_ruby_version_incorrect
- end
-
- it "fails when engine doesn't match" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{engine_incorrect}
- G
-
- bundle :check, :raise_on_error => false
- should_be_engine_incorrect
- end
-
- it "fails when engine version doesn't match", :jruby do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{engine_version_incorrect}
- G
-
- bundle :check, :raise_on_error => false
- should_be_engine_version_incorrect
- end
-
- it "fails when patchlevel doesn't match" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{patchlevel_incorrect}
- G
-
- bundle :check, :raise_on_error => false
- should_be_patchlevel_incorrect
- end
- end
-
- context "bundle update" do
- before do
- build_repo2
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport"
- gem "rack-obama"
- G
- end
-
- it "updates successfully when the ruby version matches" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport"
- gem "rack-obama"
-
- #{ruby_version_correct}
- G
- update_repo2 do
- build_gem "activesupport", "3.0"
- end
-
- bundle "update", :all => true
- expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
- end
-
- it "updates fine with any engine", :jruby do
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport"
- gem "rack-obama"
-
- #{ruby_version_correct_engineless}
- G
- update_repo2 do
- build_gem "activesupport", "3.0"
- end
-
- bundle "update", :all => true
- expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0"
- end
-
- it "fails when ruby version doesn't match" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport"
- gem "rack-obama"
-
- #{ruby_version_incorrect}
- G
- update_repo2 do
- build_gem "activesupport", "3.0"
- end
-
- bundle :update, :all => true, :raise_on_error => false
- should_be_ruby_version_incorrect
- end
-
- it "fails when ruby engine doesn't match", :jruby do
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport"
- gem "rack-obama"
-
- #{engine_incorrect}
- G
- update_repo2 do
- build_gem "activesupport", "3.0"
- end
-
- bundle :update, :all => true, :raise_on_error => false
- should_be_engine_incorrect
- end
-
- it "fails when ruby engine version doesn't match", :jruby do
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport"
- gem "rack-obama"
-
- #{engine_version_incorrect}
- G
- update_repo2 do
- build_gem "activesupport", "3.0"
- end
-
- bundle :update, :all => true, :raise_on_error => false
- should_be_engine_version_incorrect
- end
-
- it "fails when patchlevel doesn't match" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{patchlevel_incorrect}
- G
- update_repo2 do
- build_gem "activesupport", "3.0"
- end
-
- bundle :update, :all => true, :raise_on_error => false
- should_be_patchlevel_incorrect
- end
- end
-
- context "bundle info" do
- before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
- G
- end
-
- it "prints path if ruby version is correct" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
-
- #{ruby_version_correct}
- G
-
- bundle "info rails --path"
- expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
- end
-
- it "prints path if ruby version is correct for any engine", :jruby do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
-
- #{ruby_version_correct_engineless}
- G
-
- bundle "info rails --path"
- expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
- end
-
- it "fails if ruby version doesn't match", :bundler => "< 3" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
-
- #{ruby_version_incorrect}
- G
-
- bundle "show rails", :raise_on_error => false
- should_be_ruby_version_incorrect
- end
-
- it "fails if engine doesn't match", :bundler => "< 3" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
-
- #{engine_incorrect}
- G
-
- bundle "show rails", :raise_on_error => false
- should_be_engine_incorrect
- end
-
- it "fails if engine version doesn't match", :bundler => "< 3", :jruby => true do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rails"
-
- #{engine_version_incorrect}
- G
-
- bundle "show rails", :raise_on_error => false
- should_be_engine_version_incorrect
- end
-
- it "fails when patchlevel doesn't match", :bundler => "< 3" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{patchlevel_incorrect}
- G
- update_repo2 do
- build_gem "activesupport", "3.0"
- end
-
- bundle "show rails", :raise_on_error => false
- should_be_patchlevel_incorrect
- end
- end
-
- context "bundle cache" do
- before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
- G
- end
-
- it "copies the .gem file to vendor/cache when ruby version matches" do
- gemfile <<-G
- gem 'rack'
-
- #{ruby_version_correct}
- G
-
- bundle :cache
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
- end
-
- it "copies the .gem file to vendor/cache when ruby version matches for any engine", :jruby do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
-
- #{ruby_version_correct_engineless}
- G
-
- bundle :cache
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
- end
-
- it "fails if the ruby version doesn't match" do
- gemfile <<-G
- gem 'rack'
-
- #{ruby_version_incorrect}
- G
-
- bundle :cache, :raise_on_error => false
- should_be_ruby_version_incorrect
- end
-
- it "fails if the engine doesn't match" do
- gemfile <<-G
- gem 'rack'
-
- #{engine_incorrect}
- G
-
- bundle :cache, :raise_on_error => false
- should_be_engine_incorrect
- end
-
- it "fails if the engine version doesn't match", :jruby do
- gemfile <<-G
- gem 'rack'
-
- #{engine_version_incorrect}
- G
-
- bundle :cache, :raise_on_error => false
- should_be_engine_version_incorrect
- end
-
- it "fails when patchlevel doesn't match" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{patchlevel_incorrect}
- G
-
- bundle :cache, :raise_on_error => false
- should_be_patchlevel_incorrect
- end
- end
-
- context "bundle pack" do
- before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
- G
- end
-
- it "copies the .gem file to vendor/cache when ruby version matches" do
- gemfile <<-G
- gem 'rack'
-
- #{ruby_version_correct}
- G
-
- bundle :cache
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
- end
-
- it "copies the .gem file to vendor/cache when ruby version matches any engine", :jruby do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
-
- #{ruby_version_correct_engineless}
- G
-
- bundle :cache
- expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
- end
-
- it "fails if the ruby version doesn't match" do
- gemfile <<-G
- gem 'rack'
-
- #{ruby_version_incorrect}
- G
-
- bundle :cache, :raise_on_error => false
- should_be_ruby_version_incorrect
- end
-
- it "fails if the engine doesn't match" do
- gemfile <<-G
- gem 'rack'
-
- #{engine_incorrect}
- G
-
- bundle :cache, :raise_on_error => false
- should_be_engine_incorrect
- end
-
- it "fails if the engine version doesn't match", :jruby do
- gemfile <<-G
- gem 'rack'
-
- #{engine_version_incorrect}
- G
-
- bundle :cache, :raise_on_error => false
- should_be_engine_version_incorrect
- end
-
- it "fails when patchlevel doesn't match" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{patchlevel_incorrect}
- G
-
- bundle :cache, :raise_on_error => false
- should_be_patchlevel_incorrect
- end
- end
-
- context "bundle exec" do
- before do
- ENV["BUNDLER_FORCE_TTY"] = "true"
- system_gems "rack-1.0.0", "rack-0.9.1", :path => default_bundle_path
- end
-
- it "activates the correct gem when ruby version matches" do
- gemfile <<-G
- gem "rack", "0.9.1"
-
- #{ruby_version_correct}
- G
-
- bundle "exec rackup"
- expect(out).to include("0.9.1")
- end
-
- 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
- gem "rack", "0.9.1"
-
- #{ruby_version_correct_engineless}
- G
-
- bundle "exec rackup"
- expect(out).to include("0.9.1")
- end
-
- it "fails when the ruby version doesn't match" do
- gemfile <<-G
- gem "rack", "0.9.1"
-
- #{ruby_version_incorrect}
- G
-
- bundle "exec rackup", :raise_on_error => false
- should_be_ruby_version_incorrect
- end
-
- it "fails when the engine doesn't match" do
- gemfile <<-G
- gem "rack", "0.9.1"
-
- #{engine_incorrect}
- G
-
- bundle "exec rackup", :raise_on_error => false
- should_be_engine_incorrect
- end
-
- # it "fails when the engine version doesn't match", :jruby do
- # gemfile <<-G
- # gem "rack", "0.9.1"
- #
- # #{engine_version_incorrect}
- # G
- #
- # bundle "exec rackup"
- # should_be_engine_version_incorrect
- # end
-
- it "fails when patchlevel doesn't match" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
-
- #{patchlevel_incorrect}
- G
-
- bundle "exec rackup", :raise_on_error => false
- should_be_patchlevel_incorrect
- end
- end
-
- context "bundle console", :bundler => "< 3" do
- before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "activesupport", :group => :test
- gem "rack_middleware", :group => :development
- G
- end
-
- it "starts IRB with the default group loaded when ruby version matches", :readline do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "activesupport", :group => :test
- gem "rack_middleware", :group => :development
-
- #{ruby_version_correct}
- G
-
- bundle "console" do |input, _, _|
- input.puts("puts RACK")
- input.puts("exit")
- end
- expect(out).to include("0.9.1")
- end
-
- it "starts IRB with the default group loaded when ruby version matches", :readline, :jruby do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "activesupport", :group => :test
- gem "rack_middleware", :group => :development
-
- #{ruby_version_correct_engineless}
- G
-
- bundle "console" do |input, _, _|
- input.puts("puts RACK")
- input.puts("exit")
- end
- expect(out).to include("0.9.1")
- end
-
- it "fails when ruby version doesn't match" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "activesupport", :group => :test
- gem "rack_middleware", :group => :development
-
- #{ruby_version_incorrect}
- G
-
- bundle "console", :raise_on_error => false
- should_be_ruby_version_incorrect
- end
-
- it "fails when engine doesn't match" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "activesupport", :group => :test
- gem "rack_middleware", :group => :development
-
- #{engine_incorrect}
- G
-
- bundle "console", :raise_on_error => false
- should_be_engine_incorrect
- end
-
- it "fails when engine version doesn't match", :jruby do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "activesupport", :group => :test
- gem "rack_middleware", :group => :development
-
- #{engine_version_incorrect}
- G
-
- bundle "console", :raise_on_error => false
- should_be_engine_version_incorrect
- end
-
- it "fails when patchlevel doesn't match" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- gem "activesupport", :group => :test
- gem "rack_middleware", :group => :development
-
- #{patchlevel_incorrect}
- G
-
- bundle "console", :raise_on_error => false
- should_be_patchlevel_incorrect
- end
- end
-
- context "Bundler.setup" do
- before do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "yard"
- gem "rack", :group => :test
- G
-
- ENV["BUNDLER_FORCE_TTY"] = "true"
- end
-
- it "makes a Gemfile.lock if setup succeeds" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "yard"
- gem "rack"
-
- #{ruby_version_correct}
- G
-
- FileUtils.rm(bundled_app_lock)
-
- run "1"
- expect(bundled_app_lock).to exist
- end
-
- it "makes a Gemfile.lock if setup succeeds for any engine", :jruby do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "yard"
- gem "rack"
-
- #{ruby_version_correct_engineless}
- G
-
- FileUtils.rm(bundled_app_lock)
-
- run "1"
- expect(bundled_app_lock).to exist
- end
-
- it "fails when ruby version doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "yard"
- gem "rack"
-
- #{ruby_version_incorrect}
- G
-
- FileUtils.rm(bundled_app_lock)
-
- ruby "require 'bundler/setup'", :env => { "BUNDLER_VERSION" => Bundler::VERSION }, :raise_on_error => false
-
- expect(bundled_app_lock).not_to exist
- should_be_ruby_version_incorrect
- end
-
- it "fails when engine doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "yard"
- gem "rack"
-
- #{engine_incorrect}
- G
-
- FileUtils.rm(bundled_app_lock)
-
- ruby "require 'bundler/setup'", :env => { "BUNDLER_VERSION" => Bundler::VERSION }, :raise_on_error => false
-
- expect(bundled_app_lock).not_to exist
- should_be_engine_incorrect
- end
-
- it "fails when engine version doesn't match", :jruby do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "yard"
- gem "rack"
-
- #{engine_version_incorrect}
- G
-
- FileUtils.rm(bundled_app_lock)
-
- ruby "require 'bundler/setup'", :env => { "BUNDLER_VERSION" => Bundler::VERSION }, :raise_on_error => false
-
- expect(bundled_app_lock).not_to exist
- should_be_engine_version_incorrect
- end
-
- it "fails when patchlevel doesn't match" do
- install_gemfile <<-G, :raise_on_error => false
- source "#{file_uri_for(gem_repo1)}"
- gem "yard"
- gem "rack"
-
- #{patchlevel_incorrect}
- G
-
- FileUtils.rm(bundled_app_lock)
-
- ruby "require 'bundler/setup'", :env => { "BUNDLER_VERSION" => Bundler::VERSION }, :raise_on_error => false
-
- expect(bundled_app_lock).not_to exist
- should_be_patchlevel_incorrect
- end
- end
-
- context "bundle outdated" do
- before do
- build_repo2 do
- build_git "foo", :path => lib_path("foo")
- end
-
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport", "2.3.5"
- gem "foo", :git => "#{lib_path("foo")}"
- G
- end
-
- it "returns list of outdated gems when the ruby version matches" do
- update_repo2 do
- build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
- end
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport", "2.3.5"
- gem "foo", :git => "#{lib_path("foo")}"
-
- #{ruby_version_correct}
- G
-
- bundle "outdated", :raise_on_error => false
-
- expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
- Gem Current Latest Requested Groups
- activesupport 2.3.5 3.0 = 2.3.5 default
- foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default
- TABLE
-
- expect(out).to match(Regexp.new(expected_output))
- end
-
- it "returns list of outdated gems when the ruby version matches for any engine", :jruby do
- bundle :install
- update_repo2 do
- build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
- end
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport", "2.3.5"
- gem "foo", :git => "#{lib_path("foo")}"
-
- #{ruby_version_correct_engineless}
- G
-
- bundle "outdated", :raise_on_error => false
-
- expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
- Gem Current Latest Requested Groups
- activesupport 2.3.5 3.0 = 2.3.5 default
- foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default
- TABLE
-
- expect(out).to match(Regexp.new(expected_output))
- end
-
- it "fails when the ruby version doesn't match" do
- update_repo2 do
- build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
- end
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport", "2.3.5"
- gem "foo", :git => "#{lib_path("foo")}"
-
- #{ruby_version_incorrect}
- G
-
- bundle "outdated", :raise_on_error => false
- should_be_ruby_version_incorrect
- end
-
- it "fails when the engine doesn't match" do
- update_repo2 do
- build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
- end
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport", "2.3.5"
- gem "foo", :git => "#{lib_path("foo")}"
-
- #{engine_incorrect}
- G
-
- bundle "outdated", :raise_on_error => false
- should_be_engine_incorrect
- end
-
- it "fails when the engine version doesn't match", :jruby do
- update_repo2 do
- build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
- end
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport", "2.3.5"
- gem "foo", :git => "#{lib_path("foo")}"
-
- #{engine_version_incorrect}
- G
-
- bundle "outdated", :raise_on_error => false
- should_be_engine_version_incorrect
- end
-
- it "fails when the patchlevel doesn't match", :jruby do
- update_repo2 do
- build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
- end
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport", "2.3.5"
- gem "foo", :git => "#{lib_path("foo")}"
-
- #{patchlevel_incorrect}
- G
-
- bundle "outdated", :raise_on_error => false
- should_be_patchlevel_incorrect
- end
-
- it "fails when the patchlevel is a fixnum", :jruby do
- update_repo2 do
- build_gem "activesupport", "3.0"
- update_git "foo", :path => lib_path("foo")
- end
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- gem "activesupport", "2.3.5"
- gem "foo", :git => "#{lib_path("foo")}"
-
- #{patchlevel_fixnum}
- G
-
- bundle "outdated", :raise_on_error => false
- should_be_patchlevel_fixnum
- end
- end
-end
diff --git a/spec/bundler/plugins/command_spec.rb b/spec/bundler/plugins/command_spec.rb
index 4728f66f5f..05d535a70c 100644
--- a/spec/bundler/plugins/command_spec.rb
+++ b/spec/bundler/plugins/command_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe "command plugins" do
end
end
- bundle "plugin install command-mah --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install command-mah --source https://gem.repo2"
end
it "executes without arguments" do
@@ -29,7 +29,7 @@ RSpec.describe "command plugins" do
end
it "accepts the arguments" do
- build_repo2 do
+ update_repo2 do
build_plugin "the-echoer" do |s|
s.write "plugins.rb", <<-RUBY
module Resonance
@@ -46,15 +46,49 @@ RSpec.describe "command plugins" do
end
end
- bundle "plugin install the-echoer --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install the-echoer --source https://gem.repo2"
expect(out).to include("Installed plugin the-echoer")
bundle "echo tacos tofu lasange"
expect(out).to eq("You gave me tacos, tofu, lasange")
end
+ it "passes help flag to plugin" do
+ update_repo2 do
+ build_plugin "helpful" do |s|
+ s.write "plugins.rb", <<-RUBY
+ module Helpful
+ class Command
+ Bundler::Plugin::API.command "greet", self
+
+ def exec(command, args)
+ if args.include?("--help") || args.include?("-h")
+ puts "Usage: bundle greet [NAME]"
+ else
+ puts "Hello"
+ end
+ end
+ end
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install helpful --source https://gem.repo2"
+ expect(out).to include("Installed plugin helpful")
+
+ bundle "greet --help"
+ expect(out).to eq("Usage: bundle greet [NAME]")
+
+ bundle "greet -h"
+ expect(out).to eq("Usage: bundle greet [NAME]")
+
+ bundle "help greet"
+ expect(out).to eq("Usage: bundle greet [NAME]")
+ end
+
it "raises error on redeclaration of command" do
- build_repo2 do
+ update_repo2 do
build_plugin "copycat" do |s|
s.write "plugins.rb", <<-RUBY
module CopyCat
@@ -69,12 +103,10 @@ RSpec.describe "command plugins" do
end
end
- bundle "plugin install copycat --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install copycat --source https://gem.repo2", raise_on_error: false
expect(out).not_to include("Installed plugin copycat")
- expect(err).to include("Failed to install plugin")
-
- 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/hook_spec.rb b/spec/bundler/plugins/hook_spec.rb
index 72feb14d84..ad8a4daeff 100644
--- a/spec/bundler/plugins/hook_spec.rb
+++ b/spec/bundler/plugins/hook_spec.rb
@@ -13,17 +13,17 @@ RSpec.describe "hook plugins" do
end
end
- bundle "plugin install before-install-all-plugin --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install before-install-all-plugin --source https://gem.repo2"
end
it "runs before all rubygems are installed" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rake"
- gem "rack"
+ gem "myrack"
G
- expect(out).to include "gems to be installed rake, rack"
+ expect(out).to include "gems to be installed rake, myrack"
end
end
@@ -39,18 +39,18 @@ RSpec.describe "hook plugins" do
end
end
- bundle "plugin install before-install-plugin --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install before-install-plugin --source https://gem.repo2"
end
it "runs before each rubygem is installed" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rake"
- gem "rack"
+ gem "myrack"
G
expect(out).to include "installing gem rake"
- expect(out).to include "installing gem rack"
+ expect(out).to include "installing gem myrack"
end
end
@@ -66,17 +66,17 @@ RSpec.describe "hook plugins" do
end
end
- bundle "plugin install after-install-all-plugin --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install after-install-all-plugin --source https://gem.repo2"
end
- it "runs after each rubygem is installed" do
+ it "runs after each all rubygems are installed" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rake"
- gem "rack"
+ gem "myrack"
G
- expect(out).to include "installed gems rake, rack"
+ expect(out).to include "installed gems rake, myrack"
end
end
@@ -92,18 +92,240 @@ RSpec.describe "hook plugins" do
end
end
- bundle "plugin install after-install-plugin --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install after-install-plugin --source https://gem.repo2"
end
it "runs after each rubygem is installed" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rake"
- gem "rack"
+ gem "myrack"
G
expect(out).to include "installed gem rake : installed"
- expect(out).to include "installed gem rack : installed"
+ expect(out).to include "installed gem myrack : installed"
+ end
+ end
+
+ context "before-require-all hook" do
+ before do
+ build_repo2 do
+ build_plugin "before-require-all-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_REQUIRE_ALL do |deps|
+ puts "gems to be required \#{deps.map(&:name).join(", ")}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install before-require-all-plugin --source https://gem.repo2"
+ end
+
+ it "runs before all rubygems are required" do
+ install_gemfile_and_bundler_require
+ expect(out).to include "gems to be required rake, myrack"
+ end
+ end
+
+ context "before-require hook" do
+ before do
+ build_repo2 do
+ build_plugin "before-require-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_REQUIRE do |dep|
+ puts "requiring gem \#{dep.name}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install before-require-plugin --source https://gem.repo2"
+ end
+
+ it "runs before each rubygem is required" do
+ install_gemfile_and_bundler_require
+ expect(out).to include "requiring gem rake"
+ expect(out).to include "requiring gem myrack"
end
end
+
+ context "after-require-all hook" do
+ before do
+ build_repo2 do
+ build_plugin "after-require-all-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_REQUIRE_ALL do |deps|
+ puts "required gems \#{deps.map(&:name).join(", ")}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install after-require-all-plugin --source https://gem.repo2"
+ end
+
+ it "runs after all rubygems are required" do
+ install_gemfile_and_bundler_require
+ expect(out).to include "required gems rake, myrack"
+ end
+ end
+
+ context "after-require hook" do
+ before do
+ build_repo2 do
+ build_plugin "after-require-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_REQUIRE do |dep|
+ puts "required gem \#{dep.name}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install after-require-plugin --source https://gem.repo2"
+ end
+
+ it "runs after each rubygem is required" do
+ install_gemfile_and_bundler_require
+ expect(out).to include "required gem rake"
+ expect(out).to include "required gem myrack"
+ end
+ end
+
+ context "before-eval hook" do
+ before do
+ build_repo2 do
+ build_plugin "before-eval-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_EVAL do |gemfile, lockfile|
+ puts "hooked eval start of \#{File.basename(gemfile)} to \#{File.basename(lockfile)}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install before-eval-plugin --source https://gem.repo2"
+ end
+
+ it "runs before the Gemfile is evaluated" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rake"
+ G
+
+ expect(out).to include "hooked eval start of Gemfile to Gemfile.lock"
+ end
+ end
+
+ context "after-eval hook" do
+ before do
+ build_repo2 do
+ build_plugin "after-eval-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_EVAL do |defn|
+ puts "hooked eval after with gems \#{defn.dependencies.map(&:name).join(", ")}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install after-eval-plugin --source https://gem.repo2"
+ end
+
+ it "runs after the Gemfile is evaluated" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "rake"
+ G
+
+ expect(out).to include "hooked eval after with gems myrack, rake"
+ end
+ end
+
+ context "before-fetch and after-fetch hooks" do
+ before do
+ build_repo2 do
+ build_plugin "fetch-timing-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ @timing_start = nil
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_FETCH do |spec|
+ @timing_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ puts "gem \#{spec.name} started fetch at \#{@timing_start}"
+ end
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_FETCH do |spec|
+ timing_end = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ puts "gem \#{spec.name} took \#{timing_end - @timing_start} to fetch"
+ @timing_start = nil
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install fetch-timing-plugin --source https://gem.repo2"
+ end
+
+ it "runs around each gem download" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rake"
+ gem "myrack"
+ G
+
+ expect(out).to include "gem rake started fetch at"
+ expect(out).to match(/gem rake took \d+\.\d+ to fetch/)
+ expect(out).to include "gem myrack started fetch at"
+ expect(out).to match(/gem myrack took \d+\.\d+ to fetch/)
+ end
+ end
+
+ context "before-git-fetch and after-git-fetch hooks" do
+ before do
+ build_repo2 do
+ build_plugin "git-fetch-timing-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ @timing_start = nil
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GIT_BEFORE_FETCH do |source|
+ @timing_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ puts "git source \#{source.name} started fetch at \#{@timing_start}"
+ end
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GIT_AFTER_FETCH do |source|
+ timing_end = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ puts "git source \#{source.name} took \#{timing_end - @timing_start} to fetch"
+ @timing_start = nil
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install git-fetch-timing-plugin --source https://gem.repo2"
+ end
+
+ it "runs around each git source fetch" do
+ build_git "foo", "1.0", path: lib_path("foo")
+
+ relative_path = lib_path("foo").relative_path_from(bundled_app)
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo1"
+ gem "foo", :git => "#{relative_path}"
+ G
+
+ expect(out).to include "git source foo started fetch at"
+ expect(out).to match(/git source foo took \d+\.\d+ to fetch/)
+ end
+ end
+
+ def install_gemfile_and_bundler_require
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rake"
+ gem "myrack"
+ G
+
+ ruby <<-RUBY
+ require "bundler"
+ Bundler.require
+ RUBY
+ end
end
diff --git a/spec/bundler/plugins/install_spec.rb b/spec/bundler/plugins/install_spec.rb
index ac5a28891b..dcacf764be 100644
--- a/spec/bundler/plugins/install_spec.rb
+++ b/spec/bundler/plugins/install_spec.rb
@@ -9,33 +9,54 @@ RSpec.describe "bundler plugin install" do
end
it "shows proper message when gem in not found in the source" do
- bundle "plugin install no-foo --source #{file_uri_for(gem_repo1)}", :raise_on_error => false
+ bundle "plugin install no-foo --source https://gem.repo1", raise_on_error: false
expect(err).to include("Could not find")
plugin_should_not_be_installed("no-foo")
end
it "installs from rubygems source" do
- bundle "plugin install foo --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo --source https://gem.repo2"
expect(out).to include("Installed plugin foo")
plugin_should_be_installed("foo")
end
+ it "installs from rubygems source in frozen mode" do
+ bundle "plugin install foo --source https://gem.repo2", env: { "BUNDLE_DEPLOYMENT" => "true" }
+
+ expect(out).to include("Installed plugin foo")
+ plugin_should_be_installed("foo")
+ end
+
+ it "installs from sources configured as Gem.sources without any flags" do
+ bundle "plugin install foo", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_SOURCES" => "https://gem.repo2" }
+
+ 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"
+
+ # The help message defined in ../../lib/bundler/man/bundle-plugin.1.ronn will be output.
+ expect(out).to include("You can install, uninstall, and list plugin(s)")
+ end
+
context "plugin is already installed" do
before do
- bundle "plugin install foo --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo --source https://gem.repo2"
end
it "doesn't install plugin again" do
- bundle "plugin install foo --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo --source https://gem.repo2"
expect(out).not_to include("Installing plugin foo")
expect(out).not_to include("Installed plugin foo")
end
end
it "installs multiple plugins" do
- bundle "plugin install foo kung-foo --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo kung-foo --source https://gem.repo2"
expect(out).to include("Installed plugin foo")
expect(out).to include("Installed plugin kung-foo")
@@ -49,13 +70,50 @@ RSpec.describe "bundler plugin install" do
build_plugin "kung-foo", "1.1"
end
- bundle "plugin install foo kung-foo --version '1.0' --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo kung-foo --version '1.0' --source https://gem.repo2"
expect(out).to include("Installing foo 1.0")
expect(out).to include("Installing kung-foo 1.0")
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 https://gem.repo2 --verbose"
+ expect(out).to include("Installing foo 1.0")
+
+ bundle "plugin install foo --source https://gem.repo2 --verbose"
+ expect(out).to include("Installing foo 1.1")
+
+ bundle "plugin install foo --source https://gem.repo2 --verbose"
+ expect(out).to include("Using foo 1.1")
+ end
+
+ it "raises an error when when --branch specified" do
+ bundle "plugin install foo --branch main --source https://gem.repo2", raise_on_error: false
+
+ expect(out).not_to include("Installed plugin foo")
+
+ expect(err).to include("--branch can only be used with git sources")
+ end
+
+ it "raises an error when --ref specified" do
+ bundle "plugin install foo --ref v1.2.3 --source https://gem.repo2", raise_on_error: false
+
+ expect(err).to include("--ref can only be used with git sources")
+ end
+
+ it "raises error when both --branch and --ref options are specified" do
+ bundle "plugin install foo --source https://gem.repo2 --branch main --ref v1.2.3", raise_on_error: false
+
+ expect(out).not_to include("Installed plugin foo")
+
+ expect(err).to include("You cannot specify `--branch` and `--ref` at the same time.")
+ end
+
it "works with different load paths" do
build_repo2 do
build_plugin "testing" do |s|
@@ -73,7 +131,7 @@ RSpec.describe "bundler plugin install" do
s.write("src/fubar.rb")
end
end
- bundle "plugin install testing --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install testing --source https://gem.repo2"
bundle "check2", "no-color" => false
expect(out).to eq("mate")
@@ -86,19 +144,19 @@ RSpec.describe "bundler plugin install" do
build_plugin "kung-foo", "1.1"
end
- bundle "plugin install foo kung-foo --version '1.0' --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo kung-foo --version '1.0' --source https://gem.repo2"
expect(out).to include("Installing foo 1.0")
expect(out).to include("Installing kung-foo 1.0")
plugin_should_be_installed("foo", "kung-foo")
- build_repo2 do
+ update_repo2 do
build_gem "charlie"
end
- bundle "plugin install charlie --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install charlie --source https://gem.repo2", raise_on_error: false
- expect(err).to include("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
@@ -110,12 +168,12 @@ RSpec.describe "bundler plugin install" do
build_repo2 do
build_plugin "chaplin" do |s|
s.write "plugins.rb", <<-RUBY
- raise "I got you man"
+ raise RuntimeError, "threw exception on load"
RUBY
end
end
- bundle "plugin install chaplin --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install chaplin --source https://gem.repo2", raise_on_error: false
expect(global_plugin_gem("chaplin-1.0")).not_to be_directory
@@ -129,7 +187,7 @@ RSpec.describe "bundler plugin install" do
s.write "plugins.rb"
end
- bundle "plugin install foo --git #{file_uri_for(lib_path("foo-1.0"))}"
+ bundle "plugin install foo --git #{lib_path("foo-1.0")}"
expect(out).to include("Installed plugin foo")
plugin_should_be_installed("foo")
@@ -140,17 +198,48 @@ RSpec.describe "bundler plugin install" do
s.write "plugins.rb"
end
- bundle "plugin install foo --local_git #{lib_path("foo-1.0")}"
+ bundle "plugin install foo --git #{lib_path("foo-1.0")}"
expect(out).to include("Installed plugin foo")
plugin_should_be_installed("foo")
end
+ end
+
+ context "path plugins" do
+ it "installs from a path source" do
+ build_lib "path_plugin" do |s|
+ s.write "plugins.rb"
+ end
+ bundle "plugin install path_plugin --path #{lib_path("path_plugin-1.0")}"
+
+ expect(out).to include("Installed plugin path_plugin")
+ plugin_should_be_installed("path_plugin")
+ end
+
+ it "installs from a relative path source" do
+ build_lib "path_plugin" do |s|
+ s.write "plugins.rb"
+ end
+ path = lib_path("path_plugin-1.0").relative_path_from(bundled_app)
+ bundle "plugin install path_plugin --path #{path}"
+
+ expect(out).to include("Installed plugin path_plugin")
+ plugin_should_be_installed("path_plugin")
+ end
+
+ it "installs from a relative path source when inside an app" do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ gemfile ""
+
+ build_lib "ga-plugin" do |s|
+ s.write "plugins.rb"
+ end
- it "raises an error when both git and local git sources are specified" do
- bundle "plugin install foo --local_git /phony/path/project --git git@gitphony.com:/repo/project", :raise_on_error => false
+ path = lib_path("ga-plugin-1.0").relative_path_from(bundled_app)
+ bundle "plugin install ga-plugin --path #{path}"
- expect(exitstatus).not_to eq(0)
- expect(err).to eq("Remote and local plugin git sources can't be both specified")
+ plugin_should_be_installed("ga-plugin")
+ expect(local_plugin_gem("foo-1.0")).not_to be_directory
end
end
@@ -161,9 +250,9 @@ RSpec.describe "bundler plugin install" do
it "installs plugins listed in gemfile" do
gemfile <<-G
- source '#{file_uri_for(gem_repo2)}'
+ source 'https://gem.repo2'
plugin 'foo'
- gem 'rack', "1.0.0"
+ gem 'myrack', "1.0.0"
G
bundle "install"
@@ -172,17 +261,54 @@ RSpec.describe "bundler plugin install" do
expect(out).to include("Bundle complete!")
- expect(the_bundle).to include_gems("rack 1.0.0")
+ expect(the_bundle).to include_gems("myrack 1.0.0")
plugin_should_be_installed("foo")
end
+ it "overrides the index with the new plugin version" do
+ gemfile <<-G
+ source 'https://gem.repo2'
+ plugin 'foo', "1.0"
+ gem 'myrack', "1.0.0"
+ G
+
+ bundle "install"
+
+ update_repo2 do
+ build_plugin "foo", "2.0.0"
+ end
+
+ gemfile <<-G
+ source 'https://gem.repo2'
+ plugin 'foo', "2.0"
+ gem 'myrack', "1.0.0"
+ G
+
+ bundle "install"
+
+ expected = local_plugin_gem("foo-2.0.0", "lib").to_s
+ expect(Bundler::Plugin.index.load_paths("foo")).to eq([expected])
+ end
+
+ it "respects bundler groups" do
+ gemfile <<-G
+ source 'https://gem.repo2'
+ plugin 'foo'
+ gem 'myrack', "1.0.0"
+ G
+
+ bundle "install", env: { "BUNDLE_WITHOUT" => "default" }
+
+ expect(out).to include("Bundle complete! 1 Gemfile dependency, 0 gems now installed.")
+ end
+
it "accepts plugin version" do
update_repo2 do
build_plugin "foo", "1.1.0"
end
gemfile <<-G
- source '#{file_uri_for(gem_repo2)}'
+ source 'https://gem.repo2'
plugin 'foo', "1.0"
G
@@ -201,6 +327,7 @@ RSpec.describe "bundler plugin install" do
end
install_gemfile <<-G
+ source "https://gem.repo1"
plugin 'ga-plugin', :git => "#{lib_path("ga-plugin-1.0")}"
G
@@ -208,25 +335,54 @@ RSpec.describe "bundler plugin install" do
plugin_should_be_installed("ga-plugin")
end
+ it "accepts path sources" do
+ build_lib "ga-plugin" do |s|
+ s.write "plugins.rb"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ plugin 'ga-plugin', :path => "#{lib_path("ga-plugin-1.0")}"
+ G
+
+ expect(out).to include("Installed plugin ga-plugin")
+ plugin_should_be_installed("ga-plugin")
+ end
+
+ it "accepts relative path sources" do
+ build_lib "ga-plugin" do |s|
+ s.write "plugins.rb"
+ end
+
+ path = lib_path("ga-plugin-1.0").relative_path_from(bundled_app)
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ plugin 'ga-plugin', :path => "#{path}"
+ G
+
+ expect(out).to include("Installed plugin ga-plugin")
+ plugin_should_be_installed("ga-plugin")
+ end
+
context "in deployment mode" do
it "installs plugins" do
install_gemfile <<-G
- source '#{file_uri_for(gem_repo2)}'
- gem 'rack', "1.0.0"
+ source 'https://gem.repo2'
+ gem 'myrack', "1.0.0"
G
- bundle "config --local deployment true"
+ bundle_config "deployment true"
install_gemfile <<-G
- source '#{file_uri_for(gem_repo2)}'
+ source 'https://gem.repo2'
plugin 'foo'
- gem 'rack', "1.0.0"
+ gem 'myrack', "1.0.0"
G
expect(out).to include("Installed plugin foo")
expect(out).to include("Bundle complete!")
- expect(the_bundle).to include_gems("rack 1.0.0")
+ expect(the_bundle).to include_gems("myrack 1.0.0")
plugin_should_be_installed("foo")
end
end
@@ -238,12 +394,12 @@ RSpec.describe "bundler plugin install" do
require "bundler/inline"
gemfile do
- source '#{file_uri_for(gem_repo2)}'
+ source 'https://gem.repo2'
plugin 'foo'
end
RUBY
- ruby code, :env => { "BUNDLER_VERSION" => Bundler::VERSION }
+ ruby code, artifice: "compact_index", env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(local_plugin_gem("foo-1.0", "plugins.rb")).to exist
end
end
@@ -252,7 +408,7 @@ RSpec.describe "bundler plugin install" do
it "is installed when inside an app" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
gemfile ""
- bundle "plugin install foo --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo --source https://gem.repo2"
plugin_should_be_installed("foo")
expect(local_plugin_gem("foo-1.0")).to be_directory
@@ -275,7 +431,7 @@ RSpec.describe "bundler plugin install" do
end
# inside the app
- gemfile "source '#{file_uri_for(gem_repo2)}'\nplugin 'fubar'"
+ gemfile "source 'https://gem.repo2'\nplugin 'fubar'"
bundle "install"
update_repo2 do
@@ -293,7 +449,7 @@ RSpec.describe "bundler plugin install" do
end
# outside the app
- bundle "plugin install fubar --source #{file_uri_for(gem_repo2)}", :dir => tmp
+ bundle "plugin install fubar --source https://gem.repo2", dir: tmp
end
it "inside the app takes precedence over global plugin" do
@@ -302,7 +458,7 @@ RSpec.describe "bundler plugin install" do
end
it "outside the app global plugin is used" do
- bundle "shout", :dir => tmp
+ bundle "shout", dir: tmp
expect(out).to eq("global_one")
end
end
diff --git a/spec/bundler/plugins/list_spec.rb b/spec/bundler/plugins/list_spec.rb
index 4a686415ad..30e3f82467 100644
--- a/spec/bundler/plugins/list_spec.rb
+++ b/spec/bundler/plugins/list_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe "bundler plugin list" do
context "single plugin installed" do
it "shows plugin name with commands list" do
- bundle "plugin install foo --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo --source https://gem.repo2"
plugin_should_be_installed("foo")
bundle "plugin list"
@@ -49,7 +49,7 @@ RSpec.describe "bundler plugin list" do
context "multiple plugins installed" do
it "shows plugin names with commands list" do
- bundle "plugin install foo bar --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo bar --source https://gem.repo2"
plugin_should_be_installed("foo", "bar")
bundle "plugin list"
diff --git a/spec/bundler/plugins/source/example_spec.rb b/spec/bundler/plugins/source/example_spec.rb
index 60ed051ec3..4cd4a1a931 100644
--- a/spec/bundler/plugins/source/example_spec.rb
+++ b/spec/bundler/plugins/source/example_spec.rb
@@ -33,6 +33,7 @@ RSpec.describe "real source plugins" do
def install(spec, opts)
mkdir_p(install_path.parent)
+ require 'fileutils'
FileUtils.cp_r(path, install_path)
spec_path = install_path.join("\#{spec.full_name}.gemspec")
@@ -51,7 +52,7 @@ RSpec.describe "real source plugins" do
build_lib "a-path-gem"
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}" # plugin source
+ source "https://gem.repo2" # plugin source
source "#{lib_path("a-path-gem-1.0")}", :type => :mpath do
gem "a-path-gem"
end
@@ -66,35 +67,14 @@ RSpec.describe "real source plugins" do
expect(the_bundle).to include_gems("a-path-gem 1.0")
end
- it "writes to lock file", :bundler => "< 3" do
+ it "writes to lockfile" do
bundle "install"
- lockfile_should_be <<-G
- PLUGIN SOURCE
- remote: #{lib_path("a-path-gem-1.0")}
- type: mpath
- specs:
- a-path-gem (1.0)
-
- GEM
- remote: #{file_uri_for(gem_repo2)}/
- specs:
-
- PLATFORMS
- #{generic_local_platform}
-
- DEPENDENCIES
- a-path-gem!
-
- BUNDLED WITH
- #{Bundler::VERSION}
- G
- end
-
- it "writes to lock file", :bundler => "3" do
- bundle "install"
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "a-path-gem", "1.0"
+ end
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PLUGIN SOURCE
remote: #{lib_path("a-path-gem-1.0")}
type: mpath
@@ -102,7 +82,7 @@ RSpec.describe "real source plugins" do
a-path-gem (1.0)
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
PLATFORMS
@@ -110,9 +90,9 @@ RSpec.describe "real source plugins" do
DEPENDENCIES
a-path-gem!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -130,7 +110,7 @@ RSpec.describe "real source plugins" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}" # plugin source
+ source "https://gem.repo2" # plugin source
source "#{lib_path("gem_with_bin-1.0")}", :type => :mpath do
gem "gem_with_bin"
end
@@ -144,38 +124,35 @@ RSpec.describe "real source plugins" do
let(:uri_hash) { Digest(:SHA1).hexdigest(lib_path("a-path-gem-1.0").to_s) }
it "copies repository to vendor cache and uses it" do
bundle "install"
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}/.git")).not_to exist
expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}/.bundlecache")).to be_file
- FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ FileUtils.rm_r lib_path("a-path-gem-1.0")
expect(the_bundle).to include_gems("a-path-gem 1.0")
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 "path vendor/bundle"
bundle :install
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
- FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ FileUtils.rm_r lib_path("a-path-gem-1.0")
expect(the_bundle).to include_gems("a-path-gem 1.0")
end
it "bundler package copies repository to vendor cache" do
- bundle "config --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
- FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ FileUtils.rm_r lib_path("a-path-gem-1.0")
expect(the_bundle).to include_gems("a-path-gem 1.0")
end
end
@@ -190,7 +167,7 @@ RSpec.describe "real source plugins" do
a-path-gem (1.0)
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
PLATFORMS
@@ -200,7 +177,7 @@ RSpec.describe "real source plugins" do
a-path-gem!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -227,7 +204,7 @@ RSpec.describe "real source plugins" do
def initialize(opts)
super
- @ref = options["ref"] || options["branch"] || options["tag"] || "master"
+ @ref = options["ref"] || options["branch"] || options["tag"] || "main"
@unlocked = false
end
@@ -271,7 +248,7 @@ RSpec.describe "real source plugins" do
def options_to_lock
opts = {"revision" => revision}
- opts["ref"] = ref if ref != "master"
+ opts["ref"] = ref if ref != "main"
opts
end
@@ -328,13 +305,7 @@ RSpec.describe "real source plugins" do
@install_path ||= begin
git_scope = "\#{base_name}-\#{shortref_for_path(revision)}"
- path = gem_install_dir.join(git_scope)
-
- if !path.exist? && requires_sudo?
- user_bundle_path.join(ruby_scope).join(git_scope)
- else
- path
- end
+ gem_install_dir.join(git_scope)
end
end
@@ -349,8 +320,8 @@ RSpec.describe "real source plugins" do
build_git "ma-gitp-gem"
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}" # plugin source
- source "#{file_uri_for(lib_path("ma-gitp-gem-1.0"))}", :type => :gitp do
+ source "https://gem.repo2" # plugin source
+ source "#{lib_path("ma-gitp-gem-1.0")}", :type => :gitp do
gem "ma-gitp-gem"
end
G
@@ -362,47 +333,24 @@ RSpec.describe "real source plugins" do
expect(the_bundle).to include_gems("ma-gitp-gem 1.0")
end
- it "writes to lock file", :bundler => "< 3" do
+ it "writes to lockfile" do
revision = revision_for(lib_path("ma-gitp-gem-1.0"))
bundle "install"
- lockfile_should_be <<-G
- PLUGIN SOURCE
- remote: #{file_uri_for(lib_path("ma-gitp-gem-1.0"))}
- type: gitp
- revision: #{revision}
- specs:
- ma-gitp-gem (1.0)
-
- GEM
- remote: #{file_uri_for(gem_repo2)}/
- specs:
-
- PLATFORMS
- #{generic_local_platform}
-
- DEPENDENCIES
- ma-gitp-gem!
-
- BUNDLED WITH
- #{Bundler::VERSION}
- G
- end
-
- it "writes to lock file", :bundler => "3" do
- revision = revision_for(lib_path("ma-gitp-gem-1.0"))
- bundle "install"
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "ma-gitp-gem", "1.0"
+ end
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
PLUGIN SOURCE
- remote: #{file_uri_for(lib_path("ma-gitp-gem-1.0"))}
+ remote: #{lib_path("ma-gitp-gem-1.0")}
type: gitp
revision: #{revision}
specs:
ma-gitp-gem (1.0)
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
PLATFORMS
@@ -410,9 +358,9 @@ RSpec.describe "real source plugins" do
DEPENDENCIES
ma-gitp-gem!
-
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -421,14 +369,14 @@ RSpec.describe "real source plugins" do
revision = revision_for(lib_path("ma-gitp-gem-1.0"))
lockfile <<-G
PLUGIN SOURCE
- remote: #{file_uri_for(lib_path("ma-gitp-gem-1.0"))}
+ remote: #{lib_path("ma-gitp-gem-1.0")}
type: gitp
revision: #{revision}
specs:
ma-gitp-gem (1.0)
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
PLATFORMS
@@ -438,7 +386,7 @@ RSpec.describe "real source plugins" do
ma-gitp-gem!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -470,10 +418,10 @@ RSpec.describe "real source plugins" do
end
it "updates the deps on change in gemfile" do
- update_git "ma-gitp-gem", "1.1", :path => lib_path("ma-gitp-gem-1.0"), :gemspec => true
+ update_git "ma-gitp-gem", "1.1", path: lib_path("ma-gitp-gem-1.0"), gemspec: true
gemfile <<-G
- source "#{file_uri_for(gem_repo2)}" # plugin source
- source "#{file_uri_for(lib_path("ma-gitp-gem-1.0"))}", :type => :gitp do
+ source "https://gem.repo2" # plugin source
+ source "#{lib_path("ma-gitp-gem-1.0")}", :type => :gitp do
gem "ma-gitp-gem", "1.1"
end
G
@@ -486,22 +434,21 @@ RSpec.describe "real source plugins" do
describe "bundle cache with gitp" do
it "copies repository to vendor cache and uses it" do
git = build_git "foo"
- ref = git.ref_for("master", 11)
+ ref = git.ref_for("main", 11)
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}" # plugin source
+ source "https://gem.repo2" # plugin source
source '#{lib_path("foo-1.0")}', :type => :gitp do
gem "foo"
end
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.bundlecache")).to be_file
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
expect(the_bundle).to include_gems "foo 1.0"
end
end
diff --git a/spec/bundler/plugins/source_spec.rb b/spec/bundler/plugins/source_spec.rb
index 14643e5c81..995e50e653 100644
--- a/spec/bundler/plugins/source_spec.rb
+++ b/spec/bundler/plugins/source_spec.rb
@@ -16,8 +16,8 @@ RSpec.describe "bundler source plugin" do
it "installs bundler-source-* gem when no handler for source is present" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- source "#{file_uri_for(lib_path("gitp"))}", :type => :psource do
+ source "https://gem.repo2"
+ source "#{lib_path("gitp")}", :type => :psource do
end
G
@@ -38,8 +38,8 @@ RSpec.describe "bundler source plugin" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
- source "#{file_uri_for(lib_path("gitp"))}", :type => :psource do
+ source "https://gem.repo2"
+ source "#{lib_path("gitp")}", :type => :psource do
end
G
@@ -62,11 +62,11 @@ RSpec.describe "bundler source plugin" do
context "explicit presence in gemfile" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
plugin "another-psource"
- source "#{file_uri_for(lib_path("gitp"))}", :type => :psource do
+ source "#{lib_path("gitp")}", :type => :psource do
end
G
end
@@ -88,11 +88,11 @@ RSpec.describe "bundler source plugin" do
context "explicit default source" do
before do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
plugin "bundler-source-psource"
- source "#{file_uri_for(lib_path("gitp"))}", :type => :psource do
+ source "#{lib_path("gitp")}", :type => :psource do
end
G
end
diff --git a/spec/bundler/plugins/uninstall_spec.rb b/spec/bundler/plugins/uninstall_spec.rb
index 8180241911..dedcc9f37c 100644
--- a/spec/bundler/plugins/uninstall_spec.rb
+++ b/spec/bundler/plugins/uninstall_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe "bundler plugin uninstall" do
end
it "uninstalls specified plugins" do
- bundle "plugin install foo kung-foo --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo kung-foo --source https://gem.repo2"
plugin_should_be_installed("foo")
plugin_should_be_installed("kung-foo")
@@ -30,9 +30,34 @@ RSpec.describe "bundler plugin uninstall" do
plugin_should_not_be_installed("foo")
end
+ it "doesn't wipe out path plugins" do
+ build_lib "path_plugin" do |s|
+ s.write "plugins.rb"
+ end
+ path = lib_path("path_plugin-1.0")
+ expect(path).to be_a_directory
+
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+
+ install_gemfile <<-G
+ source 'https://gem.repo2'
+ plugin 'path_plugin', :path => "#{path}"
+ gem 'myrack', '1.0.0'
+ G
+
+ plugin_should_be_installed("path_plugin")
+ expect(Bundler::Plugin.index.plugin_path("path_plugin")).to eq path
+
+ bundle "plugin uninstall path_plugin"
+ expect(out).to include("Uninstalled plugin path_plugin")
+ plugin_should_not_be_installed("path_plugin")
+ # the actual gem still exists though
+ expect(path).to be_a_directory
+ end
+
describe "with --all" do
it "uninstalls all installed plugins" do
- bundle "plugin install foo kung-foo --source #{file_uri_for(gem_repo2)}"
+ bundle "plugin install foo kung-foo --source https://gem.repo2"
plugin_should_be_installed("foo")
plugin_should_be_installed("kung-foo")
diff --git a/spec/bundler/quality_es_spec.rb b/spec/bundler/quality_es_spec.rb
index 90968e6270..e68674c030 100644
--- a/spec/bundler/quality_es_spec.rb
+++ b/spec/bundler/quality_es_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe "La biblioteca si misma" do
]
pattern = /\b#{Regexp.union(useless_words)}\b/i
- File.readlines(filename).each_with_index do |line, number|
+ File.readlines(File.expand_path(filename, source_root)).each_with_index do |line, number|
next unless word_found = pattern.match(line)
failing_line_message << "#{filename}:#{number.succ} contiene '#{word_found}'. Esta palabra tiene un significado subjetivo y es mejor obviarla en textos técnicos."
end
@@ -29,7 +29,7 @@ RSpec.describe "La biblioteca si misma" do
failing_line_message = []
specific_pronouns = /\b(él|ella|ellos|ellas)\b/i
- File.readlines(filename).each_with_index do |line, number|
+ File.readlines(File.expand_path(filename, source_root)).each_with_index do |line, number|
next unless word_found = specific_pronouns.match(line)
failing_line_message << "#{filename}:#{number.succ} contiene '#{word_found}'. Use pronombres más genéricos en la documentación."
end
@@ -41,7 +41,7 @@ RSpec.describe "La biblioteca si misma" do
included = /ronn/
error_messages = []
man_tracked_files.each do |filename|
- next unless filename =~ included
+ next unless filename&.match?(included)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
@@ -52,7 +52,7 @@ RSpec.describe "La biblioteca si misma" do
error_messages = []
exempt = /vendor/
lib_tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb
index d57098be47..16b7f18788 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 = /
<<<<<<<|
@@ -31,7 +12,7 @@ RSpec.describe "The library itself" do
failing_lines = []
each_line(filename) do |line, number|
- failing_lines << number + 1 if line =~ merge_conflicts_regex
+ failing_lines << number + 1 if line&.match?(merge_conflicts_regex)
end
return if failing_lines.empty?
@@ -39,9 +20,12 @@ RSpec.describe "The library itself" do
end
def check_for_tab_characters(filename)
+ # Because Go uses hard tabs
+ return if filename.end_with?(".go.tt")
+
failing_lines = []
each_line(filename) do |line, number|
- failing_lines << number + 1 if line =~ /\t/
+ failing_lines << number + 1 if line.include?("\t")
end
return if failing_lines.empty?
@@ -51,24 +35,22 @@ RSpec.describe "The library itself" do
def check_for_extra_spaces(filename)
failing_lines = []
each_line(filename) do |line, number|
- next if line =~ /^\s+#.*\s+\n$/
- failing_lines << number + 1 if line =~ /\s+\n$/
+ next if /^\s+#.*\s+\n$/.match?(line)
+ failing_lines << number + 1 if /\s+\n$/.match?(line)
end
return if failing_lines.empty?
"#{filename} has spaces on the EOL on lines #{failing_lines.join(", ")}"
end
- def check_for_straneous_quotes(filename)
- return if File.expand_path(filename) == __FILE__
-
+ def check_for_extraneous_quotes(filename)
failing_lines = []
each_line(filename) do |line, number|
- failing_lines << number + 1 if line =~ /’/
+ failing_lines << number + 1 if /\u{2019}/.match?(line)
end
return if failing_lines.empty?
- "#{filename} has an straneous quote on lines #{failing_lines.join(", ")}"
+ "#{filename} has an extraneous quote on lines #{failing_lines.join(", ")}"
end
def check_for_expendable_words(filename)
@@ -105,51 +87,39 @@ RSpec.describe "The library itself" do
end
it "has no malformed whitespace" do
- exempt = /\.gitmodules|fixtures|vendor|LICENSE|vcr_cassettes|rbreadline\.diff|\.txt$/
+ exempt = /\.gitmodules|fixtures|vendor|LICENSE|vcr_cassettes|rbreadline\.diff|index\.txt$/
error_messages = []
tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_tab_characters(filename)
error_messages << check_for_extra_spaces(filename)
end
expect(error_messages.compact).to be_well_formed
end
- it "has no estraneous quotes" do
+ it "has no extraneous quotes" do
exempt = /vendor|vcr_cassettes|LICENSE|rbreadline\.diff/
error_messages = []
tracked_files.each do |filename|
- next if filename =~ exempt
- error_messages << check_for_straneous_quotes(filename)
- end
- 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|\.txt|\.5|\.1}
- error_messages = []
- tracked_files.each do |filename|
- next if filename =~ exempt
- error_messages << check_for_debugging_mechanisms(filename)
+ next if filename&.match?(exempt)
+ error_messages << check_for_extraneous_quotes(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}
+ exempt = %r{lock/lockfile_spec|quality_spec|vcr_cassettes|\.ronn|lockfile_parser}
tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_git_merge_conflicts(filename)
end
expect(error_messages.compact).to be_well_formed
end
it "maintains language quality of the documentation" do
- included = /ronn/
error_messages = []
man_tracked_files.each do |filename|
- next unless filename =~ included
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
@@ -160,7 +130,7 @@ RSpec.describe "The library itself" do
error_messages = []
exempt = /vendor|vcr_cassettes|CODE_OF_CONDUCT/
lib_tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
@@ -169,12 +139,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.bundle
+ gem.rubocop
+ gem.test
+ git.allow_insecure
inline
- use_gem_version_promoter_for_major_updates
+ trust-policy
]
all_settings = Hash.new {|h, k| h[k] = [] }
@@ -183,6 +158,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|
@@ -190,7 +166,8 @@ RSpec.describe "The library itself" do
line.scan(/Bundler\.settings\[:#{key_pattern}\]/).flatten.each {|s| all_settings[s] << "referenced at `#{filename}:#{number.succ}`" }
end
end
- documented_settings = File.read("man/bundle-config.ronn")[/LIST OF AVAILABLE KEYS.*/m].scan(/^\* `#{key_pattern}`/).flatten
+ settings_section = File.read(source_root.join("lib/bundler/man/bundle-config.1.ronn")).split(/^## /).find {|section| section.start_with?("LIST OF AVAILABLE KEYS") }
+ documented_settings = settings_section.scan(/^\* `#{key_pattern}`/).flatten
documented_settings.each do |s|
all_settings.delete(s)
@@ -210,17 +187,18 @@ RSpec.describe "The library itself" do
end
it "can still be built" do
- with_built_bundler do |_gem_path|
- expect(err).to be_empty, "bundler should build as a gem without warnings, but\n#{err}"
+ with_built_bundler do |gem_path|
+ expect(File.exist?(gem_path)).to be true
end
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 = tracked_files.reject {|f| f.start_with?("spec/") }
gem_list = loaded_gemspec.files
+ gem_list.map! {|f| f.sub(%r{\Aexe/}, "libexec/") } if ruby_core?
- expect(git_list.to_set).to eq(gem_list.to_set)
+ expect(git_list).to match_array(gem_list)
end
it "does not contain any warnings" do
@@ -229,7 +207,6 @@ RSpec.describe "The library itself" do
lib/bundler/deployment.rb
lib/bundler/gem_tasks.rb
lib/bundler/vlad.rb
- lib/bundler/templates/gems.rb
]
files_to_require = lib_tracked_files.grep(/\.rb$/) - exclusions
files_to_require.reject! {|f| f.start_with?("lib/bundler/vendor") }
@@ -241,7 +218,7 @@ RSpec.describe "The library itself" do
end
end
- warnings = last_command.stdboth.split("\n")
+ warnings = stdboth.split("\n")
# ignore warnings around deprecated Object#=~ method in RubyGems
warnings.reject! {|w| w =~ %r{rubygems\/version.rb.*deprecated\ Object#=~} }
@@ -249,10 +226,10 @@ RSpec.describe "The library itself" do
end
it "does not use require internally, but require_relative" do
- exempt = %r{templates/|vendor/}
+ exempt = %r{templates/|\.5|\.1|vendor/}
all_bad_requires = []
lib_tracked_files.each do |filename|
- next if filename =~ exempt
+ next if filename&.match?(exempt)
each_line(filename) do |line, number|
line.scan(/^ *require "bundler/).each { all_bad_requires << "#{filename}:#{number.succ}" }
end
@@ -261,9 +238,24 @@ RSpec.describe "The library itself" do
expect(all_bad_requires).to be_empty, "#{all_bad_requires.size} internal requires that should use `require_relative`: #{all_bad_requires}"
end
-private
+ # We don't want our artifice code to activate bundler, but it needs to use the
+ # namespaced implementation of `Net::HTTP`. So we duplicate the file in
+ # bundler that loads that.
+ it "keeps vendored_net_http spec code in sync with the lib implementation" do
+ lib_implementation_path = File.join(source_lib_dir, "bundler", "vendored_net_http.rb")
+ expect(File.exist?(lib_implementation_path)).to be_truthy
+ lib_code = File.read(lib_implementation_path)
+
+ spec_implementation_path = File.join(spec_dir, "support", "vendored_net_http.rb")
+ expect(File.exist?(spec_implementation_path)).to be_truthy
+ spec_code = File.read(spec_implementation_path)
+
+ expect(lib_code).to eq(spec_code)
+ end
+
+ private
def each_line(filename, &block)
- File.readlines(filename, :encoding => "UTF-8").each_with_index(&block)
+ File.readlines(File.expand_path(filename, source_root), encoding: "UTF-8").each_with_index(&block)
end
end
diff --git a/spec/bundler/realworld/dependency_api_spec.rb b/spec/bundler/realworld/dependency_api_spec.rb
deleted file mode 100644
index 08c6acf190..0000000000
--- a/spec/bundler/realworld/dependency_api_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "../support/silent_logger"
-
-RSpec.describe "gemcutter's dependency API", :realworld => true do
- context "when Gemcutter API takes too long to respond" do
- before do
- require_rack
-
- port = find_unused_port
- @server_uri = "http://127.0.0.1:#{port}"
-
- require_relative "../support/artifice/endpoint_timeout"
-
- @t = Thread.new do
- server = Rack::Server.start(:app => EndpointTimeout,
- :Host => "0.0.0.0",
- :Port => port,
- :server => "webrick",
- :AccessLog => [],
- :Logger => Spec::SilentLogger.new)
- server.start
- end
- @t.run
-
- wait_for_server("127.0.0.1", port)
- bundle "config set timeout 1"
- end
-
- after do
- Artifice.deactivate
- @t.kill
- @t.join
- end
-
- it "times out and falls back on the modern index" do
- install_gemfile <<-G, :artifice => nil
- source "#{@server_uri}"
- gem "rack"
- G
-
- expect(out).to include("Fetching source index from #{@server_uri}/")
- expect(the_bundle).to include_gems "rack 1.0.0"
- end
- end
-end
diff --git a/spec/bundler/realworld/double_check_spec.rb b/spec/bundler/realworld/double_check_spec.rb
index 0a8f5e4d9e..0741560395 100644
--- a/spec/bundler/realworld/double_check_spec.rb
+++ b/spec/bundler/realworld/double_check_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "double checking sources", :realworld => true, :sometimes => true do
+RSpec.describe "double checking sources", realworld: true do
it "finds already-installed gems" do
create_file("rails.gemspec", <<-RUBY)
Gem::Specification.new do |s|
@@ -25,9 +25,9 @@ RSpec.describe "double checking sources", :realworld => true, :sometimes => true
RUBY
cmd = <<-RUBY
- require "#{lib_dir}/bundler"
+ require "bundler"
require "#{spec_dir}/support/artifice/vcr"
- require "#{lib_dir}/bundler/inline"
+ require "bundler/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 e5600bb1c1..391aa0cef6 100644
--- a/spec/bundler/realworld/edgecases_spec.rb
+++ b/spec/bundler/realworld/edgecases_spec.rb
@@ -1,20 +1,22 @@
# frozen_string_literal: true
-RSpec.describe "real world edgecases", :realworld => true, :sometimes => true do
+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 "bundler"
+ require "bundler/source/rubygems/remote"
+ require "bundler/fetcher"
rubygem = Bundler.ui.silence do
- source = Bundler::Source::Rubygems::Remote.new(Bundler::URI("https://rubygems.org"))
- fetcher = Bundler::Fetcher.new(source)
- index = fetcher.specs([#{name.dump}], nil)
- index.search(Gem::Dependency.new(#{name.dump}, #{requirement.dump})).last
+ remote = Bundler::Source::Rubygems::Remote.new(Gem::URI("https://rubygems.org"))
+ source = Bundler::Source::Rubygems.new
+ fetcher = Bundler::Fetcher.new(remote)
+ index = fetcher.specs([#{name.dump}], source)
+ requirement = Gem::Requirement.create(#{requirement.dump})
+ index.search(#{name.dump}).select {|spec| requirement.satisfied_by?(spec.version) }.last
end
if rubygem.nil?
- raise "Could not find #{name} (#{requirement}) on rubygems.org!\n" \
+ raise ArgumentError, "Could not find #{name} (#{requirement}) on rubygems.org!\n" \
"Found specs:\n\#{index.send(:specs).inspect}"
end
puts "#{name} (\#{rubygem.version})"
@@ -61,288 +63,13 @@ RSpec.describe "real world edgecases", :realworld => true, :sometimes => true do
expect(lockfile).to include(rubygems_version("activesupport", "~> 3.0"))
end
- it "is able to update a top-level dependency when there is a conflict on a shared transitive child" do
- # from https://github.com/rubygems/bundler/issues/5031
-
- gemfile <<-G
- source "https://rubygems.org"
- gem 'rails', '~> 4.2.7.1'
- gem 'paperclip', '~> 5.1.0'
- G
-
- lockfile <<-L
- GEM
- remote: https://rubygems.org/
- specs:
- actionmailer (4.2.7.1)
- actionpack (= 4.2.7.1)
- actionview (= 4.2.7.1)
- activejob (= 4.2.7.1)
- mail (~> 2.5, >= 2.5.4)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- actionpack (4.2.7.1)
- actionview (= 4.2.7.1)
- activesupport (= 4.2.7.1)
- rack (~> 1.6)
- rack-test (~> 0.6.2)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (4.2.7.1)
- activesupport (= 4.2.7.1)
- builder (~> 3.1)
- erubis (~> 2.7.0)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
- activejob (4.2.7.1)
- activesupport (= 4.2.7.1)
- globalid (>= 0.3.0)
- activemodel (4.2.7.1)
- activesupport (= 4.2.7.1)
- builder (~> 3.1)
- activerecord (4.2.7.1)
- activemodel (= 4.2.7.1)
- activesupport (= 4.2.7.1)
- arel (~> 6.0)
- activesupport (4.2.7.1)
- i18n (~> 0.7)
- json (~> 1.7, >= 1.7.7)
- minitest (~> 5.1)
- thread_safe (~> 0.3, >= 0.3.4)
- tzinfo (~> 1.1)
- arel (6.0.3)
- builder (3.2.2)
- climate_control (0.0.3)
- activesupport (>= 3.0)
- cocaine (0.5.8)
- climate_control (>= 0.0.3, < 1.0)
- concurrent-ruby (1.0.2)
- erubis (2.7.0)
- globalid (0.3.7)
- activesupport (>= 4.1.0)
- i18n (0.7.0)
- json (1.8.3)
- loofah (2.0.3)
- nokogiri (>= 1.5.9)
- mail (2.6.4)
- mime-types (>= 1.16, < 4)
- mime-types (3.1)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2016.0521)
- mimemagic (0.3.2)
- mini_portile2 (2.1.0)
- minitest (5.9.1)
- nokogiri (1.6.8)
- mini_portile2 (~> 2.1.0)
- pkg-config (~> 1.1.7)
- paperclip (5.1.0)
- activemodel (>= 4.2.0)
- activesupport (>= 4.2.0)
- cocaine (~> 0.5.5)
- mime-types
- mimemagic (~> 0.3.0)
- pkg-config (1.1.7)
- rack (1.6.4)
- rack-test (0.6.3)
- rack (>= 1.0)
- rails (4.2.7.1)
- actionmailer (= 4.2.7.1)
- actionpack (= 4.2.7.1)
- actionview (= 4.2.7.1)
- activejob (= 4.2.7.1)
- activemodel (= 4.2.7.1)
- activerecord (= 4.2.7.1)
- activesupport (= 4.2.7.1)
- bundler (>= 1.3.0, < 3.0)
- railties (= 4.2.7.1)
- sprockets-rails
- rails-deprecated_sanitizer (1.0.3)
- activesupport (>= 4.2.0.alpha)
- rails-dom-testing (1.0.7)
- activesupport (>= 4.2.0.beta, < 5.0)
- nokogiri (~> 1.6.0)
- rails-deprecated_sanitizer (>= 1.0.1)
- rails-html-sanitizer (1.0.3)
- loofah (~> 2.0)
- railties (4.2.7.1)
- actionpack (= 4.2.7.1)
- activesupport (= 4.2.7.1)
- rake (>= 0.8.7)
- thor (>= 0.18.1, < 2.0)
- rake (11.3.0)
- sprockets (3.7.0)
- concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (3.2.0)
- actionpack (>= 4.0)
- activesupport (>= 4.0)
- sprockets (>= 3.0.0)
- thor (0.19.1)
- thread_safe (0.3.5)
- tzinfo (1.2.2)
- thread_safe (~> 0.1)
-
- PLATFORMS
- ruby
-
- DEPENDENCIES
- paperclip (~> 5.1.0)
- rails (~> 4.2.7.1)
- L
-
- bundle "lock --update paperclip"
-
- 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
+ it "outputs a helpful warning when gems have a gemspec with invalid `require_paths`" do
+ install_gemfile <<-G, standalone: true, 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")
+ expect(err).to include("resque-scheduler 2.2.0 includes a gemspec with `require_paths` set to an array of arrays. Newer versions of this gem might've already fixed this").once
end
end
diff --git a/spec/bundler/realworld/ffi_spec.rb b/spec/bundler/realworld/ffi_spec.rb
new file mode 100644
index 0000000000..bede372b41
--- /dev/null
+++ b/spec/bundler/realworld/ffi_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+RSpec.describe "loading dynamically 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
+
+ in_bundled_app "gcc -g -o libfoo.so -shared -fpic libfoo.c"
+
+ install_gemfile <<-G
+ source "https://rubygems.org"
+
+ gem 'ffi', force_ruby_platform: true
+ G
+
+ bundle "exec ruby foo.rb"
+
+ expect(out).to eq("OK")
+ end
+end
diff --git a/spec/bundler/realworld/fixtures/tapioca/Gemfile b/spec/bundler/realworld/fixtures/tapioca/Gemfile
new file mode 100644
index 0000000000..447d715706
--- /dev/null
+++ b/spec/bundler/realworld/fixtures/tapioca/Gemfile
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gem "tapioca"
diff --git a/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock b/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock
new file mode 100644
index 0000000000..c2df2f9229
--- /dev/null
+++ b/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock
@@ -0,0 +1,49 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ erubi (1.13.1)
+ netrc (0.11.0)
+ parallel (1.26.3)
+ prism (1.3.0)
+ rbi (0.2.2)
+ prism (~> 1.0)
+ sorbet-runtime (>= 0.5.9204)
+ sorbet (0.5.11725)
+ sorbet-static (= 0.5.11725)
+ sorbet-runtime (0.5.11725)
+ sorbet-static (0.5.11725-aarch64-linux)
+ sorbet-static (0.5.11725-universal-darwin)
+ sorbet-static (0.5.11725-x86_64-linux)
+ sorbet-static-and-runtime (0.5.11725)
+ sorbet (= 0.5.11725)
+ sorbet-runtime (= 0.5.11725)
+ spoom (1.5.0)
+ erubi (>= 1.10.0)
+ prism (>= 0.28.0)
+ sorbet-static-and-runtime (>= 0.5.10187)
+ thor (>= 0.19.2)
+ tapioca (0.16.6)
+ bundler (>= 2.2.25)
+ netrc (>= 0.11.0)
+ parallel (>= 1.21.0)
+ rbi (~> 0.2)
+ sorbet-static-and-runtime (>= 0.5.11087)
+ spoom (>= 1.2.0)
+ thor (>= 1.2.0)
+ yard-sorbet
+ thor (1.4.0)
+ yard (0.9.42)
+ yard-sorbet (0.9.0)
+ sorbet-runtime
+ yard
+
+PLATFORMS
+ aarch64-linux
+ universal-darwin
+ x86_64-linux
+
+DEPENDENCIES
+ tapioca
+
+BUNDLED WITH
+ 4.1.0.dev
diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile b/spec/bundler/realworld/fixtures/warbler/Gemfile
index 4fbf2d05a7..5687bbd975 100644
--- a/spec/bundler/realworld/fixtures/warbler/Gemfile
+++ b/spec/bundler/realworld/fixtures/warbler/Gemfile
@@ -2,6 +2,6 @@
source "https://rubygems.org"
-gem "demo", :path => "./demo"
-gem "jruby-jars", "~> 9.2"
-gem "warbler", "~> 2.0"
+gem "demo", path: "./demo"
+gem "jruby-jars", "~> 10.0"
+gem "warbler", "~> 2.1"
diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock
index 6363b8bbd8..05f3bc4e3f 100644
--- a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock
+++ b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock
@@ -6,24 +6,30 @@ PATH
GEM
remote: https://rubygems.org/
specs:
- jruby-jars (9.2.9.0)
- jruby-rack (1.1.21)
- rake (13.0.1)
- rubyzip (1.3.0)
- warbler (2.0.5)
- jruby-jars (>= 9.0.0.0)
- jruby-rack (>= 1.1.1, < 1.3)
- rake (>= 10.1.0)
- rubyzip (~> 1.0, < 1.4)
+ jruby-jars (10.0.0.1)
+ jruby-rack (1.2.7)
+ ostruct (0.6.3)
+ rake (13.3.0)
+ rexml (3.4.2)
+ rubyzip (3.3.0)
+ warbler (2.1.0)
+ jruby-jars (>= 9.4, < 10.1)
+ jruby-rack (>= 1.2.3, < 1.3)
+ ostruct (~> 0.6.2)
+ rake (~> 13.0, >= 13.0.3)
+ rexml (~> 3.0)
+ rubyzip (>= 3.0.0)
PLATFORMS
+ arm64-darwin
java
ruby
+ universal-java
DEPENDENCIES
demo!
- jruby-jars (~> 9.2)
- warbler (~> 2.0)
+ jruby-jars (~> 10.0)
+ warbler (~> 2.1)
BUNDLED WITH
- 2.2.0.dev
+ 4.1.0.dev
diff --git a/spec/bundler/realworld/gemfile_source_header_spec.rb b/spec/bundler/realworld/gemfile_source_header_spec.rb
deleted file mode 100644
index 3f507b056a..0000000000
--- a/spec/bundler/realworld/gemfile_source_header_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "../support/silent_logger"
-
-RSpec.describe "fetching dependencies with a mirrored source", :realworld => true do
- let(:mirror) { "https://server.example.org" }
- let(:original) { "http://127.0.0.1:#{@port}" }
-
- before do
- setup_server
- bundle "config set --local mirror.#{mirror} #{original}"
- end
-
- after do
- Artifice.deactivate
- @t.kill
- @t.join
- end
-
- it "sets the 'X-Gemfile-Source' header and bundles successfully" do
- gemfile <<-G
- source "#{mirror}"
- gem 'weakling'
- G
-
- bundle :install, :artifice => nil
-
- expect(out).to include("Installing weakling")
- expect(out).to include("Bundle complete")
- expect(the_bundle).to include_gems "weakling 0.0.3"
- end
-
-private
-
- def setup_server
- require_rack
- @port = find_unused_port
- @server_uri = "http://127.0.0.1:#{@port}"
-
- require_relative "../support/artifice/endpoint_mirror_source"
-
- @t = Thread.new do
- Rack::Server.start(:app => EndpointMirrorSource,
- :Host => "0.0.0.0",
- :Port => @port,
- :server => "webrick",
- :AccessLog => [],
- :Logger => Spec::SilentLogger.new)
- end.run
-
- wait_for_server("127.0.0.1", @port)
- end
-end
diff --git a/spec/bundler/realworld/git_spec.rb b/spec/bundler/realworld/git_spec.rb
new file mode 100644
index 0000000000..9eff74f1c9
--- /dev/null
+++ b/spec/bundler/realworld/git_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+RSpec.describe "github source", realworld: true do
+ it "properly fetches PRs" do
+ install_gemfile <<-G
+ source "https://rubygems.org"
+
+ gem "reline", github: "https://github.com/ruby/reline/pull/488"
+ G
+ end
+end
diff --git a/spec/bundler/realworld/mirror_probe_spec.rb b/spec/bundler/realworld/mirror_probe_spec.rb
deleted file mode 100644
index 626092c7eb..0000000000
--- a/spec/bundler/realworld/mirror_probe_spec.rb
+++ /dev/null
@@ -1,144 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "../support/silent_logger"
-
-RSpec.describe "fetching dependencies with a not available mirror", :realworld => true do
- let(:mirror) { @mirror_uri }
- let(:original) { @server_uri }
- let(:server_port) { @server_port }
- let(:host) { "127.0.0.1" }
-
- before do
- require_rack
- setup_server
- setup_mirror
- end
-
- after do
- Artifice.deactivate
- @server_thread.kill
- @server_thread.join
- end
-
- context "with a specific fallback timeout" do
- before do
- global_config("BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/__FALLBACK_TIMEOUT/" => "true",
- "BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/" => mirror)
- end
-
- it "install a gem using the original uri when the mirror is not responding" do
- gemfile <<-G
- source "#{original}"
- gem 'weakling'
- G
-
- bundle :install, :artifice => nil
-
- expect(out).to include("Installing weakling")
- expect(out).to include("Bundle complete")
- expect(the_bundle).to include_gems "weakling 0.0.3"
- end
- end
-
- context "with a global fallback timeout" do
- before do
- global_config("BUNDLE_MIRROR__ALL__FALLBACK_TIMEOUT/" => "1",
- "BUNDLE_MIRROR__ALL" => mirror)
- end
-
- it "install a gem using the original uri when the mirror is not responding" do
- gemfile <<-G
- source "#{original}"
- gem 'weakling'
- G
-
- bundle :install, :artifice => nil
-
- expect(out).to include("Installing weakling")
- expect(out).to include("Bundle complete")
- expect(the_bundle).to include_gems "weakling 0.0.3"
- end
- end
-
- context "with a specific mirror without a fallback timeout" do
- before do
- global_config("BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/" => mirror)
- end
-
- it "fails to install the gem with a timeout error" do
- gemfile <<-G
- source "#{original}"
- gem 'weakling'
- G
-
- 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}")
- expect(err).to include("Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
- expect(err).to include("Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
- expect(err).to include("Could not fetch specs from #{mirror}")
- end
-
- it "prints each error and warning on a new line" do
- gemfile <<-G
- source "#{original}"
- gem 'weakling'
- G
-
- 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}/
-Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}/
-Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}/
-Could not fetch specs from #{mirror}/
- EOS
- end
- end
-
- context "with a global mirror without a fallback timeout" do
- before do
- global_config("BUNDLE_MIRROR__ALL" => mirror)
- end
-
- it "fails to install the gem with a timeout error" do
- gemfile <<-G
- source "#{original}"
- gem 'weakling'
- G
-
- 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}")
- expect(err).to include("Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
- expect(err).to include("Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from #{mirror}")
- expect(err).to include("Could not fetch specs from #{mirror}")
- end
- end
-
- def setup_server
- @server_port = find_unused_port
- @server_uri = "http://#{host}:#{@server_port}"
-
- require_relative "../support/artifice/endpoint"
-
- @server_thread = Thread.new do
- Rack::Server.start(:app => Endpoint,
- :Host => host,
- :Port => @server_port,
- :server => "webrick",
- :AccessLog => [],
- :Logger => Spec::SilentLogger.new)
- end.run
-
- wait_for_server(host, @server_port)
- end
-
- def setup_mirror
- mirror_port = find_unused_port
- @mirror_uri = "http://#{host}:#{mirror_port}"
- end
-end
diff --git a/spec/bundler/realworld/parallel_spec.rb b/spec/bundler/realworld/parallel_spec.rb
index 2a5154ffbf..b57fdfd0ee 100644
--- a/spec/bundler/realworld/parallel_spec.rb
+++ b/spec/bundler/realworld/parallel_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "parallel", :realworld => true, :sometimes => true do
+RSpec.describe "parallel", realworld: true do
it "installs" do
gemfile <<-G
source "https://rubygems.org"
@@ -9,7 +9,7 @@ RSpec.describe "parallel", :realworld => true, :sometimes => true do
gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+
G
- bundle :install, :jobs => 4, :env => { "DEBUG" => "1" }
+ bundle :install, jobs: 4, env: { "DEBUG" => "1" }
expect(out).to match(/[1-3]: /)
@@ -34,7 +34,7 @@ RSpec.describe "parallel", :realworld => true, :sometimes => true do
gem 'i18n', '~> 0.6.0' # Because 0.7+ requires Ruby 1.9.3+
G
- bundle :update, :jobs => 4, :env => { "DEBUG" => "1" }, :all => true
+ bundle :update, jobs: 4, env: { "DEBUG" => "1" }, all: true
expect(out).to match(/[1-3]: /)
@@ -46,12 +46,12 @@ RSpec.describe "parallel", :realworld => true, :sometimes => true do
end
it "works with --standalone" do
- gemfile <<-G, :standalone => true
+ gemfile <<-G
source "https://rubygems.org"
gem "diff-lcs"
G
- bundle :install, :standalone => true, :jobs => 4
+ bundle :install, standalone: true, jobs: 4
ruby <<-RUBY
$:.unshift File.expand_path("bundle")
diff --git a/spec/bundler/realworld/slow_perf_spec.rb b/spec/bundler/realworld/slow_perf_spec.rb
new file mode 100644
index 0000000000..5d36ba7455
--- /dev/null
+++ b/spec/bundler/realworld/slow_perf_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe "bundle install with complex dependencies", realworld: true do
+ it "resolves quickly" do
+ gemfile <<-G
+ source 'https://rubygems.org'
+
+ gem "actionmailer"
+ gem "mongoid", ">= 0.10.2"
+ G
+
+ bundle "lock", env: { "DEBUG_RESOLVER" => "1" }
+ expect(out).to include("Solution found after 1 attempts")
+ end
+
+ it "resolves quickly (case 2)" do
+ gemfile <<-G
+ source "https://rubygems.org"
+
+ gem 'metasploit-erd'
+ gem 'rails-erd'
+ gem 'yard'
+
+ gem 'coveralls'
+ gem 'rails'
+ gem 'simplecov'
+ gem 'rspec-rails'
+ G
+
+ bundle "lock", env: { "DEBUG_RESOLVER" => "1" }
+ expect(out).to include("Solution found after 2 attempts")
+ end
+end
diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb
index 57897f89b4..185df1b1c7 100644
--- a/spec/bundler/resolver/basic_spec.rb
+++ b/spec/bundler/resolver/basic_spec.rb
@@ -6,15 +6,15 @@ RSpec.describe "Resolving" do
end
it "resolves a single gem" do
- dep "rack"
+ dep "myrack"
- should_resolve_as %w[rack-1.1]
+ should_resolve_as %w[myrack-1.1]
end
it "resolves a gem with dependencies" do
dep "actionpack"
- should_resolve_as %w[actionpack-2.3.5 activesupport-2.3.5 rack-1.0]
+ should_resolve_as %w[actionpack-2.3.5 activesupport-2.3.5 myrack-1.0]
end
it "resolves a conflicting index" do
@@ -84,7 +84,7 @@ RSpec.describe "Resolving" do
dep "activesupport", "= 3.0.0.beta"
dep "actionpack"
- should_resolve_as %w[activesupport-3.0.0.beta actionpack-3.0.0.beta rack-1.1 rack-mount-0.6]
+ should_resolve_as %w[activesupport-3.0.0.beta actionpack-3.0.0.beta myrack-1.1 myrack-mount-0.6]
end
it "prefers non-pre-releases when doing conservative updates" do
@@ -100,11 +100,20 @@ RSpec.describe "Resolving" do
end
it "raises an exception if a child dependency is not resolved" do
- @index = a_unresovable_child_index
+ @index = a_unresolvable_child_index
dep "chef_app_error"
expect do
resolve
- end.to raise_error(Bundler::VersionConflict)
+ end.to raise_error(Bundler::SolveFailure)
+ end
+
+ it "does not try to re-resolve including prereleases if gems involved don't have prereleases" do
+ @index = a_unresolvable_child_index
+ dep "chef_app_error"
+ expect(Bundler.ui).not_to receive(:debug).with("Retrying resolution...", any_args)
+ expect do
+ resolve
+ end.to raise_error(Bundler::SolveFailure)
end
it "raises an exception with the minimal set of conflicting dependencies" do
@@ -118,14 +127,15 @@ RSpec.describe "Resolving" do
dep "c"
expect do
resolve
- end.to raise_error(Bundler::VersionConflict, <<-E.strip)
-Bundler could not find compatible versions for gem "a":
- In Gemfile:
- b was resolved to 1.0, which depends on
- a (>= 2)
-
- c was resolved to 1.0, which depends on
- a (< 1)
+ end.to raise_error(Bundler::SolveFailure, <<~E.strip)
+ Could not find compatible versions
+
+ Because every version of c depends on a < 1
+ and every version of b depends on a >= 2,
+ every version of c is incompatible with b >= 0.
+ So, because Gemfile depends on b >= 0
+ and Gemfile depends on c >= 0,
+ version solving has failed.
E
end
@@ -134,7 +144,7 @@ Bundler could not find compatible versions for gem "a":
dep "circular_app"
expect do
- resolve
+ Bundler::SpecSet.new(resolve).sort
end.to raise_error(Bundler::CyclicDependencyError, /please remove either gem 'bar' or gem 'foo'/i)
end
@@ -174,12 +184,7 @@ Bundler could not find compatible versions for gem "a":
dep "foo"
dep "Ruby\0", "1.8.7"
- deps = []
- @deps.each do |d|
- deps << Bundler::DepProxy.new(d, "ruby")
- end
-
- should_resolve_and_include %w[foo-1.0.0 bar-1.0.0], [[]]
+ should_resolve_and_include %w[foo-1.0.0 bar-1.0.0]
end
context "conservative" do
@@ -206,12 +211,12 @@ Bundler could not find compatible versions for gem "a":
it "resolves all gems to latest patch" do
# strict is not set, so bar goes up a minor version due to dependency from foo 1.4.5
- should_conservative_resolve_and_include :patch, [], %w[foo-1.4.5 bar-2.1.1]
+ should_conservative_resolve_and_include :patch, true, %w[foo-1.4.5 bar-2.1.1]
end
it "resolves all gems to latest patch strict" do
# strict is set, so foo can only go up to 1.4.4 to avoid bar going up a minor version, and bar can go up to 2.0.5
- should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.4 bar-2.0.5]
+ should_conservative_resolve_and_include [:patch, :strict], true, %w[foo-1.4.4 bar-2.0.5]
end
it "resolves foo only to latest patch - same dependency case" do
@@ -233,7 +238,7 @@ Bundler could not find compatible versions for gem "a":
it "resolves foo only to latest patch - changing dependency declared case" do
# bar is locked AND a declared dependency in the Gemfile, so it will not move, and therefore
# foo can only move up to 1.4.4.
- @base << build_spec("bar", "2.0.3").first
+ @base = Bundler::SpecSet.new([Bundler::LazySpecification.new("bar", Gem::Version.new("2.0.3"), nil)])
should_conservative_resolve_and_include :patch, ["foo"], %w[foo-1.4.4 bar-2.0.3]
end
@@ -251,20 +256,20 @@ Bundler could not find compatible versions for gem "a":
it "resolves all gems to latest minor" do
# strict is not set, so bar goes up a major version due to dependency from foo 1.4.5
- should_conservative_resolve_and_include :minor, [], %w[foo-1.5.1 bar-3.0.0]
+ should_conservative_resolve_and_include :minor, true, %w[foo-1.5.1 bar-3.0.0]
end
it "resolves all gems to latest minor strict" do
# strict is set, so foo can only go up to 1.5.0 to avoid bar going up a major version
- should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.5.0 bar-2.1.1]
+ should_conservative_resolve_and_include [:minor, :strict], true, %w[foo-1.5.0 bar-2.1.1]
end
it "resolves all gems to latest major" do
- should_conservative_resolve_and_include :major, [], %w[foo-2.0.0 bar-3.0.0]
+ should_conservative_resolve_and_include :major, true, %w[foo-2.0.0 bar-3.0.0]
end
it "resolves all gems to latest major strict" do
- should_conservative_resolve_and_include [:major, :strict], [], %w[foo-2.0.0 bar-3.0.0]
+ should_conservative_resolve_and_include [:major, :strict], true, %w[foo-2.0.0 bar-3.0.0]
end
# Why would this happen in real life? If bar 2.2 has a bug that the author of foo wants to bypass
@@ -287,22 +292,131 @@ Bundler could not find compatible versions for gem "a":
end
it "could revert to a previous version level patch" do
- should_conservative_resolve_and_include :patch, [], %w[foo-1.4.4 bar-2.1.1]
+ should_conservative_resolve_and_include :patch, true, %w[foo-1.4.4 bar-2.1.1]
end
it "cannot revert to a previous version in strict mode level patch" do
# fall back to the locked resolution since strict means we can't regress either version
- should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.3 bar-2.2.3]
+ should_conservative_resolve_and_include [:patch, :strict], true, %w[foo-1.4.3 bar-2.2.3]
end
it "could revert to a previous version level minor" do
- should_conservative_resolve_and_include :minor, [], %w[foo-1.5.0 bar-2.0.5]
+ should_conservative_resolve_and_include :minor, true, %w[foo-1.5.0 bar-2.0.5]
end
it "cannot revert to a previous version in strict mode level minor" do
# fall back to the locked resolution since strict means we can't regress either version
- should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.4.3 bar-2.2.3]
+ should_conservative_resolve_and_include [:minor, :strict], true, %w[foo-1.4.3 bar-2.2.3]
+ end
+ end
+ end
+
+ it "handles versions that redundantly depend on themselves" do
+ @index = build_index do
+ gem "myrack", "3.0.0"
+
+ gem "standalone_migrations", "7.1.0" do
+ dep "myrack", "~> 2.0"
+ end
+
+ gem "standalone_migrations", "2.0.4" do
+ dep "standalone_migrations", ">= 0"
+ end
+
+ gem "standalone_migrations", "1.0.13" do
+ dep "myrack", ">= 0"
+ end
+ end
+
+ dep "myrack", "~> 3.0"
+ dep "standalone_migrations"
+
+ should_resolve_as %w[myrack-3.0.0 standalone_migrations-2.0.4]
+ end
+
+ it "ignores versions that incorrectly depend on themselves" do
+ @index = build_index do
+ gem "myrack", "3.0.0"
+
+ gem "standalone_migrations", "7.1.0" do
+ dep "myrack", "~> 2.0"
+ end
+
+ gem "standalone_migrations", "2.0.4" do
+ dep "standalone_migrations", ">= 2.0.5"
+ end
+
+ gem "standalone_migrations", "1.0.13" do
+ dep "myrack", ">= 0"
+ end
+ end
+
+ dep "myrack", "~> 3.0"
+ dep "standalone_migrations"
+
+ should_resolve_as %w[myrack-3.0.0 standalone_migrations-1.0.13]
+ end
+
+ it "does not ignore versions that incorrectly depend on themselves when dependency_api is not available" do
+ @index = build_index do
+ gem "myrack", "3.0.0"
+
+ gem "standalone_migrations", "7.1.0" do
+ dep "myrack", "~> 2.0"
+ end
+
+ gem "standalone_migrations", "2.0.4" do
+ dep "standalone_migrations", ">= 2.0.5"
+ end
+
+ gem "standalone_migrations", "1.0.13" do
+ dep "myrack", ">= 0"
+ end
+ end
+
+ dep "myrack", "~> 3.0"
+ dep "standalone_migrations"
+
+ should_resolve_without_dependency_api %w[myrack-3.0.0 standalone_migrations-2.0.4]
+ end
+
+ it "resolves fine cases that need joining unbounded disjoint ranges" do
+ @index = build_index do
+ gem "inspec", "5.22.3" do
+ dep "ruby", ">= 3.2.2"
+ dep "train-kubernetes", ">= 0.1.7"
+ end
+
+ gem "ruby", "3.2.2"
+
+ gem "train-kubernetes", "0.1.12" do
+ dep "k8s-ruby", ">= 0.14.0"
+ end
+
+ gem "train-kubernetes", "0.1.10" do
+ dep "k8s-ruby", "= 0.10.5"
+ end
+
+ gem "train-kubernetes", "0.1.7" do
+ dep "k8s-ruby", ">= 0.10.5"
+ end
+
+ gem "k8s-ruby", "0.10.5" do
+ dep "ruby","< 3.2.2"
+ end
+
+ gem "k8s-ruby", "0.11.0" do
+ dep "ruby", ">= 3.2.2"
+ end
+
+ gem "k8s-ruby", "0.14.0" do
+ dep "ruby", "< 3.2.2"
end
end
+
+ dep "inspec", "5.22.3"
+ dep "ruby", "3.2.2"
+
+ should_resolve_as %w[inspec-5.22.3 ruby-3.2.2 train-kubernetes-0.1.7 k8s-ruby-0.11.0]
end
end
diff --git a/spec/bundler/resolver/platform_spec.rb b/spec/bundler/resolver/platform_spec.rb
index 415c5458df..a1d095d024 100644
--- a/spec/bundler/resolver/platform_spec.rb
+++ b/spec/bundler/resolver/platform_spec.rb
@@ -28,14 +28,31 @@ 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"
- gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.0.0", "x64-mingw-ucrt"
gem "foo", "1.1.0"
end
dep "foo"
- platforms "x64-mingw32"
+ platforms "x64-mingw-ucrt"
should_resolve_as %w[foo-1.1.0]
end
@@ -44,12 +61,12 @@ RSpec.describe "Resolving platform craziness" do
@index = build_index do
gem "bar", "1.0.0"
gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32" do
+ gem "foo", "1.0.0", "x64-mingw-ucrt" do
dep "bar", "< 1"
end
end
dep "foo"
- platforms "x64-mingw32"
+ platforms "x64-mingw-ucrt"
should_resolve_as %w[foo-1.0.0]
end
@@ -57,46 +74,130 @@ RSpec.describe "Resolving platform craziness" do
it "prefers the platform specific gem to the ruby version" do
@index = build_index do
gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.0.0", "x64-mingw-ucrt"
end
dep "foo"
- platforms "x64-mingw32"
+ platforms "x64-mingw-ucrt"
- should_resolve_as %w[foo-1.0.0-x64-mingw32]
+ should_resolve_as %w[foo-1.0.0-x64-mingw-ucrt]
end
- it "takes the latest ruby gem if the platform specific gem doesn't match the required_ruby_version" do
- @index = build_index do
- gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
- gem "foo", "1.1.0"
- gem "foo", "1.1.0", "x64-mingw32" do |s|
- s.required_ruby_version = [">= 2.0", "< 2.4"]
+ describe "on a linux platform" do
+ # Ruby's platform is *-linux => platform's libc is glibc, so not musl
+ # Ruby's platform is *-linux-musl => platform's libc is musl, so not glibc
+ # Gem's platform is *-linux => gem is glibc + maybe musl compatible
+ # Gem's platform is *-linux-musl => gem is musl compatible but not glibc
+
+ it "favors the platform version-specific gem on a version-specifying linux platform" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux"
+ gem "foo", "1.0.0", "x86_64-linux-musl"
end
- gem "Ruby\0", "2.5.1"
+ dep "foo"
+ platforms "x86_64-linux-musl"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux-musl]
end
- dep "foo"
- dep "Ruby\0", "2.5.1"
- platforms "x64-mingw32"
- should_resolve_as %w[foo-1.1.0]
+ it "favors the version-less gem over the version-specific gem on a gnu linux platform" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux"
+ gem "foo", "1.0.0", "x86_64-linux-musl"
+ end
+ dep "foo"
+ platforms "x86_64-linux"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux]
+ end
+
+ it "ignores the platform version-specific gem on a gnu linux platform" do
+ @index = build_index do
+ gem "foo", "1.0.0", "x86_64-linux-musl"
+ end
+ dep "foo"
+ platforms "x86_64-linux"
+
+ should_not_resolve
+ end
+
+ it "falls back to the platform version-less gem on a linux platform with a version" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux"
+ end
+ dep "foo"
+ platforms "x86_64-linux-musl"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux]
+ end
+
+ it "falls back to the ruby platform gem on a gnu linux platform when only a version-specifying gem is available" do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x86_64-linux-musl"
+ end
+ dep "foo"
+ platforms "x86_64-linux"
+
+ should_resolve_as %w[foo-1.0.0]
+ end
+
+ it "falls back to the platform version-less gem on a version-specifying linux platform and no ruby platform gem is available" do
+ @index = build_index do
+ gem "foo", "1.0.0", "x86_64-linux"
+ end
+ dep "foo"
+ platforms "x86_64-linux-musl"
+
+ should_resolve_as %w[foo-1.0.0-x86_64-linux]
+ end
+ end
+
+ context "when the platform specific gem doesn't match the required_ruby_version" do
+ before do
+ @index = build_index do
+ gem "foo", "1.0.0"
+ gem "foo", "1.0.0", "x64-mingw-ucrt"
+ gem "foo", "1.1.0"
+ gem "foo", "1.1.0", "x64-mingw-ucrt" do |s|
+ s.required_ruby_version = [">= 2.0", "< 2.4"]
+ end
+ gem "Ruby\0", "2.5.1"
+ end
+ dep "Ruby\0", "2.5.1"
+ platforms "x64-mingw-ucrt"
+ end
+
+ it "takes the latest ruby gem" do
+ dep "foo"
+
+ should_resolve_as %w[foo-1.1.0]
+ end
+
+ it "takes the latest ruby gem, even if requirement does not match previous versions with the same ruby requirement" do
+ dep "foo", "1.1.0"
+
+ should_resolve_as %w[foo-1.1.0]
+ end
end
it "takes the latest ruby gem with required_ruby_version if the platform specific gem doesn't match the required_ruby_version" do
@index = build_index do
gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.0.0", "x64-mingw-ucrt"
gem "foo", "1.1.0" do |s|
s.required_ruby_version = [">= 2.0"]
end
- gem "foo", "1.1.0", "x64-mingw32" do |s|
+ gem "foo", "1.1.0", "x64-mingw-ucrt" do |s|
s.required_ruby_version = [">= 2.0", "< 2.4"]
end
gem "Ruby\0", "2.5.1"
end
dep "foo"
dep "Ruby\0", "2.5.1"
- platforms "x64-mingw32"
+ platforms "x64-mingw-ucrt"
should_resolve_as %w[foo-1.1.0]
end
@@ -104,26 +205,144 @@ RSpec.describe "Resolving platform craziness" do
it "takes the latest ruby gem if the platform specific gem doesn't match the required_ruby_version with multiple platforms" do
@index = build_index do
gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.0.0", "x64-mingw-ucrt"
gem "foo", "1.1.0" do |s|
s.required_ruby_version = [">= 2.0"]
end
- gem "foo", "1.1.0", "x64-mingw32" do |s|
+ gem "foo", "1.1.0", "x64-mingw-ucrt" do |s|
s.required_ruby_version = [">= 2.0", "< 2.4"]
end
gem "Ruby\0", "2.5.1"
end
dep "foo"
dep "Ruby\0", "2.5.1"
- platforms "x86_64-linux", "x64-mingw32"
+ platforms "x86_64-linux", "x64-mingw-ucrt"
should_resolve_as %w[foo-1.1.0]
end
+ it "includes gems needed for at least one platform" do
+ @index = build_index do
+ gem "empyrean", "0.1.0"
+ gem "coderay", "1.1.2"
+ gem "method_source", "0.9.0"
+
+ gem "spoon", "0.0.6" do
+ dep "ffi", ">= 0"
+ end
+
+ gem "pry", "0.11.3", "java" do
+ dep "coderay", "~> 1.1.0"
+ dep "method_source", "~> 0.9.0"
+ dep "spoon", "~> 0.0"
+ end
+
+ gem "pry", "0.11.3" do
+ dep "coderay", "~> 1.1.0"
+ dep "method_source", "~> 0.9.0"
+ end
+
+ gem "ffi", "1.9.23", "java"
+ gem "ffi", "1.9.23"
+
+ gem "extra", "1.0.0" do
+ dep "ffi", ">= 0"
+ end
+ end
+
+ dep "empyrean", "0.1.0"
+ dep "pry"
+ dep "extra"
+
+ platforms "ruby", "java"
+
+ should_resolve_as %w[coderay-1.1.2 empyrean-0.1.0 extra-1.0.0 ffi-1.9.23 ffi-1.9.23-java method_source-0.9.0 pry-0.11.3 pry-0.11.3-java spoon-0.0.6]
+ end
+
+ it "includes gems needed for at least one platform even when the platform specific requirement is processed earlier than the generic requirement" do
+ @index = build_index do
+ gem "empyrean", "0.1.0"
+ gem "coderay", "1.1.2"
+ gem "method_source", "0.9.0"
+
+ gem "spoon", "0.0.6" do
+ dep "ffi", ">= 0"
+ end
+
+ gem "pry", "0.11.3", "java" do
+ dep "coderay", "~> 1.1.0"
+ dep "method_source", "~> 0.9.0"
+ dep "spoon", "~> 0.0"
+ end
+
+ gem "pry", "0.11.3" do
+ dep "coderay", "~> 1.1.0"
+ dep "method_source", "~> 0.9.0"
+ end
+
+ gem "ffi", "1.9.23", "java"
+ gem "ffi", "1.9.23"
+
+ gem "extra", "1.0.0" do
+ dep "extra2", ">= 0"
+ end
+
+ gem "extra2", "1.0.0" do
+ dep "extra3", ">= 0"
+ end
+
+ gem "extra3", "1.0.0" do
+ dep "ffi", ">= 0"
+ end
+ end
+
+ dep "empyrean", "0.1.0"
+ dep "pry"
+ dep "extra"
+
+ platforms "ruby", "java"
+
+ should_resolve_as %w[coderay-1.1.2 empyrean-0.1.0 extra-1.0.0 extra2-1.0.0 extra3-1.0.0 ffi-1.9.23 ffi-1.9.23-java method_source-0.9.0 pry-0.11.3 pry-0.11.3-java spoon-0.0.6]
+ end
+
+ it "properly adds platforms when platform requirements come from different dependencies" do
+ @index = build_index do
+ gem "ffi", "1.9.14"
+ gem "ffi", "1.9.14", "universal-mingw32"
+
+ gem "gssapi", "0.1"
+ gem "gssapi", "0.2"
+ gem "gssapi", "0.3"
+ gem "gssapi", "1.2.0" do
+ dep "ffi", ">= 1.0.1"
+ end
+
+ gem "mixlib-shellout", "2.2.6"
+ gem "mixlib-shellout", "2.2.6", "universal-mingw32" do
+ dep "win32-process", "~> 0.8.2"
+ end
+
+ # we need all these versions to get the sorting the same as it would be
+ # pulling from rubygems.org
+ %w[0.8.3 0.8.2 0.8.1 0.8.0].each do |v|
+ gem "win32-process", v do
+ dep "ffi", ">= 1.0.0"
+ end
+ end
+ end
+
+ dep "mixlib-shellout"
+ dep "gssapi"
+
+ platforms "universal-mingw32", "ruby"
+
+ should_resolve_as %w[ffi-1.9.14 ffi-1.9.14-universal-mingw32 gssapi-1.2.0 mixlib-shellout-2.2.6 mixlib-shellout-2.2.6-universal-mingw32 win32-process-0.8.3]
+ end
+
describe "with mingw32" do
before :each do
@index = build_index do
- platforms "mingw32 mswin32 x64-mingw32" do |platform|
+ platforms "mingw32 mswin32 x64-mingw-ucrt" do |platform|
gem "thin", "1.2.7", platform
end
gem "win32-api", "1.5.1", "universal-mingw32"
@@ -144,10 +363,10 @@ RSpec.describe "Resolving platform craziness" do
should_resolve_as %w[thin-1.2.7-mingw32]
end
- it "finds x64-mingw gems" do
- platforms "x64-mingw32"
+ it "finds x64-mingw-ucrt gems" do
+ platforms "x64-mingw-ucrt"
dep "thin"
- should_resolve_as %w[thin-1.2.7-x64-mingw32]
+ should_resolve_as %w[thin-1.2.7-x64-mingw-ucrt]
end
it "finds universal-mingw gems on x86-mingw" do
@@ -157,7 +376,19 @@ RSpec.describe "Resolving platform craziness" do
end
it "finds universal-mingw gems on x64-mingw" do
- platform "x64-mingw32"
+ platform "x64-mingw-ucrt"
+ dep "win32-api"
+ should_resolve_as %w[win32-api-1.5.1-universal-mingw32]
+ end
+
+ it "finds x64-mingw-ucrt gems" do
+ platforms "x64-mingw-ucrt"
+ dep "thin"
+ should_resolve_as %w[thin-1.2.7-x64-mingw-ucrt]
+ end
+
+ it "finds universal-mingw gems on x64-mingw-ucrt" do
+ platform "x64-mingw-ucrt"
dep "win32-api"
should_resolve_as %w[win32-api-1.5.1-universal-mingw32]
end
diff --git a/spec/bundler/runtime/env_helpers_spec.rb b/spec/bundler/runtime/env_helpers_spec.rb
new file mode 100644
index 0000000000..c4ebdd1fd2
--- /dev/null
+++ b/spec/bundler/runtime/env_helpers_spec.rb
@@ -0,0 +1,236 @@
+# frozen_string_literal: true
+
+RSpec.describe "env helpers" do
+ def bundle_exec_ruby(args, options = {})
+ build_bundler_context options.dup
+ bundle "exec '#{Gem.ruby}' #{args}", options
+ end
+
+ def build_bundler_context(options = {})
+ bundle "config set path vendor/bundle", options.dup
+ gemfile "source 'https://gem.repo1'"
+ bundle "install", options
+ end
+
+ def run_bundler_script(env, script)
+ system(env, "ruby", "-I#{lib_dir}", "-rbundler", script.to_s)
+ end
+
+ describe "Bundler.original_env" do
+ it "should return the PATH present before bundle was activated" do
+ create_file("source.rb", <<-RUBY)
+ print Bundler.original_env["PATH"]
+ RUBY
+ path = `getconf PATH`.strip + "#{File::PATH_SEPARATOR}/foo"
+ with_path_as(path) do
+ bundle_exec_ruby(bundled_app("source.rb").to_s)
+ expect(stdboth).to eq(path)
+ end
+ end
+
+ it "should return the GEM_PATH present before bundle was activated" do
+ create_file("source.rb", <<-RUBY)
+ print Bundler.original_env['GEM_PATH']
+ RUBY
+ gem_path = ENV["GEM_PATH"] + "#{File::PATH_SEPARATOR}/foo"
+ with_gem_path_as(gem_path) do
+ bundle_exec_ruby(bundled_app("source.rb").to_s)
+ expect(stdboth).to eq(gem_path)
+ end
+ end
+
+ it "works with nested bundle exec invocations", :ruby_repo do
+ create_file("exe.rb", <<-'RUBY')
+ count = ARGV.first.to_i
+ exit if count < 0
+ STDERR.puts "#{count} #{ENV["PATH"].end_with?("#{File::PATH_SEPARATOR}/foo")}"
+ if count == 2
+ ENV["PATH"] = "#{ENV["PATH"]}#{File::PATH_SEPARATOR}/foo"
+ end
+ exec(Gem.ruby, __FILE__, (count - 1).to_s)
+ RUBY
+ path = `getconf PATH`.strip + File::PATH_SEPARATOR + File.dirname(Gem.ruby)
+ with_path_as(path) do
+ build_bundler_context
+ bundle_exec_ruby("#{bundled_app("exe.rb")} 2")
+ end
+ expect(err).to eq <<-EOS.strip
+2 false
+1 true
+0 true
+ EOS
+ end
+
+ it "removes variables that bundler added", :ruby_repo do
+ original = ruby('puts ENV.to_a.map {|e| e.join("=") }.sort.join("\n")', artifice: "fail")
+ create_file("source.rb", <<-RUBY)
+ puts Bundler.original_env.to_a.map {|e| e.join("=") }.sort.join("\n")
+ RUBY
+ bundle_exec_ruby bundled_app("source.rb"), artifice: "fail"
+ expect(out).to eq original
+ end
+ end
+
+ describe "Bundler.unbundled_env" do
+ it "should delete BUNDLE_PATH" do
+ create_file("source.rb", <<-RUBY)
+ print Bundler.unbundled_env.has_key?('BUNDLE_PATH')
+ RUBY
+ ENV["BUNDLE_PATH"] = "./foo"
+ bundle_exec_ruby bundled_app("source.rb")
+ expect(stdboth).to include "false"
+ end
+
+ it "should remove absolute path to 'bundler/setup' from RUBYOPT even if it was present in original env" do
+ create_file("source.rb", <<-RUBY)
+ print Bundler.unbundled_env['RUBYOPT']
+ RUBY
+ setup_require = "-r#{lib_dir}/bundler/setup"
+ ENV["BUNDLER_ORIG_RUBYOPT"] = "-W2 #{setup_require} #{ENV["RUBYOPT"]}"
+ bundle_exec_ruby bundled_app("source.rb")
+ expect(stdboth).not_to include(setup_require)
+ end
+
+ it "should remove relative path to 'bundler/setup' from RUBYOPT even if it was present in original env" do
+ create_file("source.rb", <<-RUBY)
+ print Bundler.unbundled_env['RUBYOPT']
+ RUBY
+ ENV["BUNDLER_ORIG_RUBYOPT"] = "-W2 -rbundler/setup #{ENV["RUBYOPT"]}"
+ bundle_exec_ruby bundled_app("source.rb")
+ expect(stdboth).not_to include("-rbundler/setup")
+ end
+
+ it "should delete BUNDLER_SETUP even if it was present in original env" do
+ create_file("source.rb", <<-RUBY)
+ print Bundler.unbundled_env.has_key?('BUNDLER_SETUP')
+ RUBY
+ ENV["BUNDLER_ORIG_BUNDLER_SETUP"] = system_gem_path("gems/bundler-#{Bundler::VERSION}/lib/bundler/setup").to_s
+ bundle_exec_ruby bundled_app("source.rb")
+ expect(stdboth).to include "false"
+ end
+
+ it "should restore RUBYLIB", :ruby_repo do
+ create_file("source.rb", <<-RUBY)
+ print Bundler.unbundled_env['RUBYLIB']
+ RUBY
+ ENV["RUBYLIB"] = lib_dir.to_s + File::PATH_SEPARATOR + "/foo"
+ ENV["BUNDLER_ORIG_RUBYLIB"] = lib_dir.to_s + File::PATH_SEPARATOR + "/foo-original"
+ bundle_exec_ruby bundled_app("source.rb")
+ expect(stdboth).to include("/foo-original")
+ end
+
+ it "should restore the original MANPATH" do
+ create_file("source.rb", <<-RUBY)
+ print Bundler.unbundled_env['MANPATH']
+ RUBY
+ ENV["MANPATH"] = "/foo"
+ ENV["BUNDLER_ORIG_MANPATH"] = "/foo-original"
+ bundle_exec_ruby bundled_app("source.rb")
+ expect(stdboth).to include("/foo-original")
+ end
+ end
+
+ describe "Bundler.with_original_env" do
+ it "should set ENV to original_env in the block" do
+ expected = Bundler.original_env
+ actual = Bundler.with_original_env { ENV.to_hash }
+ expect(actual).to eq(expected)
+ end
+
+ it "should restore the environment after execution" do
+ Bundler.with_original_env do
+ ENV["FOO"] = "hello"
+ end
+
+ expect(ENV).not_to have_key("FOO")
+ end
+ end
+
+ describe "Bundler.with_unbundled_env" do
+ it "should set ENV to unbundled_env in the block" do
+ expected = Bundler.unbundled_env
+ actual = Bundler.with_unbundled_env { ENV.to_hash }
+ expect(actual).to eq(expected)
+ end
+
+ it "should restore the environment after execution" do
+ Bundler.with_unbundled_env do
+ ENV["FOO"] = "hello"
+ end
+
+ expect(ENV).not_to have_key("FOO")
+ end
+ end
+
+ describe "Bundler.original_system" do
+ before do
+ create_file("source.rb", <<-'RUBY')
+ Bundler.original_system("ruby", "-e", "exit(42) if ENV['BUNDLE_FOO'] == 'bar'")
+
+ exit $?.exitstatus
+ RUBY
+ end
+
+ it "runs system inside with_original_env" do
+ run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
+ expect($?.exitstatus).to eq(42)
+ end
+ end
+
+ describe "Bundler.unbundled_system" do
+ before do
+ create_file("source.rb", <<-'RUBY')
+ Bundler.unbundled_system("ruby", "-e", "exit(42) unless ENV['BUNDLE_FOO'] == 'bar'")
+
+ exit $?.exitstatus
+ RUBY
+ end
+
+ it "runs system inside with_unbundled_env" do
+ run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
+ expect($?.exitstatus).to eq(42)
+ end
+ end
+
+ describe "Bundler.original_exec" do
+ before do
+ create_file("source.rb", <<-'RUBY')
+ Process.fork do
+ exit Bundler.original_exec(%(test "\$BUNDLE_FOO" = "bar"))
+ end
+
+ _, status = Process.wait2
+
+ exit(status.exitstatus)
+ RUBY
+ end
+
+ it "runs exec inside with_original_env" do
+ skip "Fork not implemented" if Gem.win_platform?
+
+ run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
+ expect($?.exitstatus).to eq(0)
+ end
+ end
+
+ describe "Bundler.unbundled_exec" do
+ before do
+ create_file("source.rb", <<-'RUBY')
+ Process.fork do
+ exit Bundler.unbundled_exec(%(test "\$BUNDLE_FOO" = "bar"))
+ end
+
+ _, status = Process.wait2
+
+ exit(status.exitstatus)
+ RUBY
+ end
+
+ it "runs exec inside with_unbundled_env" do
+ skip "Fork not implemented" if Gem.win_platform?
+
+ run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
+ expect($?.exitstatus).to eq(1)
+ end
+ end
+end
diff --git a/spec/bundler/runtime/executable_spec.rb b/spec/bundler/runtime/executable_spec.rb
index e39338e425..89cee21b00 100644
--- a/spec/bundler/runtime/executable_spec.rb
+++ b/spec/bundler/runtime/executable_spec.rb
@@ -3,139 +3,112 @@
RSpec.describe "Running bin/* commands" do
before :each do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
it "runs the bundled command when in the bundle" do
- bundle "binstubs rack"
+ bundle "binstubs myrack"
- build_gem "rack", "2.0", :to_system => true do |s|
- s.executables = "rackup"
+ build_gem "myrack", "2.0", to_system: true do |s|
+ s.executables = "myrackup"
end
- gembin "rackup"
+ gembin "myrackup"
expect(out).to eq("1.0.0")
end
- it "allows the location of the gem stubs to be specified" do
- bundle "binstubs rack", :path => "gbin"
+ it "allows the location of the gem stubs to be configured" do
+ bundle_config "bin gbin"
+ bundle "binstubs myrack"
expect(bundled_app("bin")).not_to exist
- expect(bundled_app("gbin/rackup")).to exist
+ expect(bundled_app("gbin/myrackup")).to exist
- gembin bundled_app("gbin/rackup")
+ gembin bundled_app("gbin/myrackup")
expect(out).to eq("1.0.0")
end
it "allows absolute paths as a specification of where to install bin stubs" do
- bundle "binstubs rack", :path => tmp("bin")
+ bundle_config "bin #{tmp("bin")}"
+ bundle "binstubs myrack"
- gembin tmp("bin/rackup")
+ gembin tmp("bin/myrackup")
expect(out).to eq("1.0.0")
end
it "uses the default ruby install name when shebang is not specified" do
- bundle "binstubs rack"
- expect(File.readlines(bundled_app("bin/rackup")).first).to eq("#!/usr/bin/env #{RbConfig::CONFIG["ruby_install_name"]}\n")
+ bundle "binstubs myrack"
+ expect(File.readlines(bundled_app("bin/myrackup")).first).to eq("#!/usr/bin/env #{RbConfig::CONFIG["ruby_install_name"]}\n")
end
it "allows the name of the shebang executable to be specified" do
- bundle "binstubs rack", :shebang => "ruby-foo"
- expect(File.readlines(bundled_app("bin/rackup")).first).to eq("#!/usr/bin/env ruby-foo\n")
+ bundle "binstubs myrack", shebang: "ruby-foo"
+ expect(File.readlines(bundled_app("bin/myrackup")).first).to eq("#!/usr/bin/env ruby-foo\n")
end
it "runs the bundled command when out of the bundle" do
- bundle "binstubs rack"
+ bundle "binstubs myrack"
- build_gem "rack", "2.0", :to_system => true do |s|
- s.executables = "rackup"
+ build_gem "myrack", "2.0", to_system: true do |s|
+ s.executables = "myrackup"
end
- gembin "rackup", :dir => tmp
+ gembin "myrackup", dir: tmp
expect(out).to eq("1.0.0")
end
it "works with gems in path" do
- build_lib "rack", :path => lib_path("rack") do |s|
- s.executables = "rackup"
+ build_lib "myrack", path: lib_path("myrack") do |s|
+ s.executables = "myrackup"
end
gemfile <<-G
- gem "rack", :path => "#{lib_path("rack")}"
+ source "https://gem.repo1"
+ gem "myrack", :path => "#{lib_path("myrack")}"
G
- bundle "binstubs rack"
+ bundle "binstubs myrack"
- build_gem "rack", "2.0", :to_system => true do |s|
- s.executables = "rackup"
+ build_gem "myrack", "2.0", to_system: true do |s|
+ s.executables = "myrackup"
end
- gembin "rackup"
+ gembin "myrackup"
expect(out).to eq("1.0")
end
- it "creates a bundle binstub" do
+ it "does not create a bundle binstub" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "bundler"
G
bundle "binstubs bundler"
- expect(bundled_app("bin/bundle")).to exist
- end
-
- it "does not generate bin stubs if the option was not specified" do
- bundle "install"
-
- expect(bundled_app("bin/rackup")).not_to exist
- end
-
- it "allows you to stop installing binstubs", :bundler => "< 3" do
- skip "delete permission error" if Gem.win_platform?
-
- bundle "install --binstubs bin/"
- bundled_app("bin/rackup").rmtree
- bundle "install --binstubs \"\""
-
- expect(bundled_app("bin/rackup")).not_to exist
+ expect(bundled_app("bin/bundle")).not_to exist
- bundle "config bin"
- expect(out).to include("You have not configured a value for `bin`")
+ expect(err).to include("Bundler itself does not use binstubs because its version is selected by RubyGems")
end
- it "remembers that the option was specified", :bundler => "< 3" do
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "activesupport"
- G
-
- bundle :install, :binstubs => "bin"
-
- gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "activesupport"
- gem "rack"
- G
-
+ it "does not generate bin stubs if the option was not specified" do
bundle "install"
- expect(bundled_app("bin/rackup")).to exist
+ expect(bundled_app("bin/myrackup")).not_to exist
end
- it "rewrites bins on binstubs (to maintain backwards compatibility)" do
+ it "rewrites bins on binstubs with --force option" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- create_file("bin/rackup", "OMG")
+ create_file("bin/myrackup", "OMG")
- bundle "binstubs rack"
+ bundle "binstubs myrack", { force: true }
- expect(bundled_app("bin/rackup").read).to_not eq("OMG")
+ expect(bundled_app("bin/myrackup").read.strip).to_not eq("OMG")
end
it "use BUNDLE_GEMFILE gemfile for binstub" do
@@ -147,8 +120,8 @@ RSpec.describe "Running bin/* commands" do
build_gem("bindir") {|s| s.executables = "foo" }
end
- create_file("OtherGemfile", <<-G)
- source "#{file_uri_for(gem_repo2)}"
+ gemfile("OtherGemfile", <<-G)
+ source "https://gem.repo2"
gem 'bindir'
G
diff --git a/spec/bundler/runtime/gem_tasks_spec.rb b/spec/bundler/runtime/gem_tasks_spec.rb
index b2f9ae725e..b855142e60 100644
--- a/spec/bundler/runtime/gem_tasks_spec.rb
+++ b/spec/bundler/runtime/gem_tasks_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.describe "require 'bundler/gem_tasks'" do
- before :each do
+ let(:define_local_gem_using_gem_tasks) do
bundled_app("foo.gemspec").open("w") do |f|
f.write <<-GEMSPEC
Gem::Specification.new do |s|
@@ -20,17 +20,54 @@ RSpec.describe "require 'bundler/gem_tasks'" do
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rake"
G
end
- it "includes the relevant tasks" do
- with_gem_path_as(base_system_gems.to_s) do
- sys_exec "#{rake} -T", :env => { "GEM_HOME" => system_gem_path.to_s }
+ let(:define_local_gem_with_extensions_using_gem_tasks_and_gemspec_dsl) do
+ bundled_app("foo.gemspec").open("w") do |f|
+ f.write <<-GEMSPEC
+ Gem::Specification.new do |s|
+ s.name = "foo"
+ s.version = "1.0"
+ s.summary = "dummy"
+ s.author = "Perry Mason"
+ s.extensions = "ext/extconf.rb"
+ end
+ GEMSPEC
+ end
+
+ bundled_app("Rakefile").open("w") do |f|
+ f.write <<-RAKEFILE
+ require "bundler/gem_tasks"
+ RAKEFILE
end
+ Dir.mkdir bundled_app("ext")
+
+ bundled_app("ext/extconf.rb").open("w") do |f|
+ f.write <<-EXTCONF
+ require "mkmf"
+ File.write("Makefile", dummy_makefile($srcdir).join)
+ EXTCONF
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+
+ gemspec
+
+ gem "rake"
+ G
+ end
+
+ it "includes the relevant tasks" do
+ define_local_gem_using_gem_tasks
+
+ in_bundled_app "rake -T"
+
expect(err).to be_empty
expected_tasks = [
"rake build",
@@ -44,9 +81,9 @@ RSpec.describe "require 'bundler/gem_tasks'" do
end
it "defines a working `rake install` task", :ruby_repo do
- with_gem_path_as(base_system_gems.to_s) do
- sys_exec "#{rake} install", :env => { "GEM_HOME" => system_gem_path.to_s }
- end
+ define_local_gem_using_gem_tasks
+
+ in_bundled_app "rake install"
expect(err).to be_empty
@@ -55,11 +92,35 @@ RSpec.describe "require 'bundler/gem_tasks'" do
expect(err).to be_empty
end
+ it "defines a working `rake install` task for local gems with extensions", :ruby_repo do
+ define_local_gem_with_extensions_using_gem_tasks_and_gemspec_dsl
+
+ bundle "exec rake install"
+
+ expect(err).to be_empty
+ end
+
context "rake build when path has spaces", :ruby_repo do
before do
- spaced_bundled_app = tmp.join("bundled app")
+ define_local_gem_using_gem_tasks
+
+ spaced_bundled_app = tmp("bundled app")
FileUtils.cp_r bundled_app, spaced_bundled_app
- bundle "exec rake build", :dir => spaced_bundled_app
+ bundle "exec rake build", dir: spaced_bundled_app
+ end
+
+ it "still runs successfully" do
+ expect(err).to be_empty
+ end
+ end
+
+ context "rake build when path has brackets", :ruby_repo do
+ before do
+ define_local_gem_using_gem_tasks
+
+ bracketed_bundled_app = tmp("bundled[app")
+ FileUtils.cp_r bundled_app, bracketed_bundled_app
+ bundle "exec rake build", dir: bracketed_bundled_app
end
it "still runs successfully" do
@@ -69,12 +130,14 @@ RSpec.describe "require 'bundler/gem_tasks'" do
context "bundle path configured locally" do
before do
- bundle "config set path vendor/bundle"
+ define_local_gem_using_gem_tasks
+
+ bundle_config "path vendor/bundle"
end
it "works", :ruby_repo do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rake"
G
@@ -86,9 +149,10 @@ RSpec.describe "require 'bundler/gem_tasks'" do
end
it "adds 'pkg' to rake/clean's CLOBBER" do
- with_gem_path_as(base_system_gems.to_s) do
- sys_exec %(#{rake} -e 'load "Rakefile"; puts CLOBBER.inspect'), :env => { "GEM_HOME" => system_gem_path.to_s }
- end
+ define_local_gem_using_gem_tasks
+
+ in_bundled_app %(rake -e 'load "Rakefile"; puts CLOBBER.inspect')
+
expect(out).to eq '["pkg"]'
end
end
diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb
index 86ac3f4bb6..c6f9bbdbd7 100644
--- a/spec/bundler/runtime/inline_spec.rb
+++ b/spec/bundler/runtime/inline_spec.rb
@@ -2,10 +2,9 @@
RSpec.describe "bundler/inline#gemfile" do
def script(code, options = {})
- requires = ["#{lib_dir}/bundler/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)
+ options[:artifice] ||= "compact_index"
+ options[:env] ||= { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ ruby("require 'bundler/inline'\n\n" + code, options)
end
before :each do
@@ -28,7 +27,7 @@ RSpec.describe "bundler/inline#gemfile" do
s.write "lib/four.rb", "puts 'four'"
end
- build_lib "five", "1.0.0", :no_default => true do |s|
+ build_lib "five", "1.0.0", no_default: true do |s|
s.write "lib/mofive.rb", "puts 'five'"
end
@@ -46,10 +45,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 "https://gem.repo1"
path "#{lib_path}" do
gem "two"
end
@@ -58,8 +56,9 @@ RSpec.describe "bundler/inline#gemfile" do
expect(out).to eq("two")
- script <<-RUBY, :raise_on_error => false
+ script <<-RUBY, raise_on_error: false
gemfile do
+ source "https://gem.repo1"
path "#{lib_path}" do
gem "eleven"
end
@@ -73,38 +72,38 @@ RSpec.describe "bundler/inline#gemfile" do
script <<-RUBY
gemfile(true) do
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
end
RUBY
- expect(out).to include("Rack's post install message")
+ expect(out).to include("Myrack's post install message")
- script <<-RUBY, :artifice => "endpoint"
+ script <<-RUBY, artifice: "endpoint"
gemfile(true) do
- source "https://notaserver.com"
+ source "https://notaserver.test"
gem "activesupport", :require => true
end
RUBY
expect(out).to include("Installing activesupport")
err_lines = err.split("\n")
- err_lines.reject!{|line| line =~ /\.rb:\d+: warning: / } unless RUBY_VERSION < "2.7"
+ err_lines.reject! {|line| line =~ /\.rb:\d+: warning: / }
expect(err_lines).to be_empty
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'
- class MyBundlerUI < Bundler::UI::Silent
+ script <<-RUBY, artifice: "endpoint"
+ require 'bundler'
+ class MyBundlerUI < Bundler::UI::Shell
def confirm(msg, newline = nil)
puts "CONFIRMED!"
end
end
- gemfile(true, :ui => MyBundlerUI.new) do
- source "https://notaserver.com"
+ my_ui = MyBundlerUI.new
+ my_ui.level = "confirm"
+ gemfile(true, :ui => my_ui) do
+ source "https://notaserver.test"
gem "activesupport", :require => true
end
RUBY
@@ -113,11 +112,11 @@ RSpec.describe "bundler/inline#gemfile" do
end
it "has an option for quiet installation" do
- script <<-RUBY, :artifice => "endpoint"
- require '#{lib_dir}/bundler/inline'
+ script <<-RUBY, artifice: "endpoint"
+ require 'bundler/inline'
gemfile(true, :quiet => true) do
- source "https://notaserver.com"
+ source "https://notaserver.test"
gem "activesupport", :require => true
end
RUBY
@@ -126,7 +125,7 @@ RSpec.describe "bundler/inline#gemfile" do
end
it "raises an exception if passed unknown arguments" do
- script <<-RUBY, :raise_on_error => false
+ script <<-RUBY, raise_on_error: false
gemfile(true, :arglebargle => true) do
path "#{lib_path}"
gem "two"
@@ -140,9 +139,10 @@ RSpec.describe "bundler/inline#gemfile" do
it "does not mutate the option argument" do
script <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
options = { :ui => Bundler::UI::Shell.new }
gemfile(false, options) do
+ source "https://gem.repo1"
path "#{lib_path}" do
gem "two"
end
@@ -156,11 +156,59 @@ RSpec.describe "bundler/inline#gemfile" do
it "installs quietly if necessary when the install option is not set" do
script <<-RUBY
gemfile do
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
+ end
+
+ puts MYRACK
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ end
+
+ it "installs subdependencies quietly if necessary when the install option is not set" do
+ build_repo4 do
+ build_gem "myrack" do |s|
+ s.add_dependency "myrackdep"
+ end
+
+ build_gem "myrackdep", "1.0.0"
+ end
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ gemfile do
+ source "https://gem.repo4"
+ gem "myrack"
+ end
+
+ require "myrackdep"
+ puts MYRACKDEP
+ RUBY
+
+ expect(out).to eq("1.0.0")
+ expect(err).to be_empty
+ end
+
+ it "installs subdependencies quietly if necessary when the install option is not set, and multiple sources used" do
+ build_repo4 do
+ build_gem "myrack" do |s|
+ s.add_dependency "myrackdep"
+ end
+
+ build_gem "myrackdep", "1.0.0"
+ end
+
+ script <<-RUBY, artifice: "compact_index_extra_api"
+ gemfile do
+ source "https://test.repo"
+ source "https://test.repo/extra" do
+ gem "myrack"
+ end
end
- puts RACK
+ require "myrackdep"
+ puts MYRACKDEP
RUBY
expect(out).to eq("1.0.0")
@@ -172,6 +220,7 @@ RSpec.describe "bundler/inline#gemfile" do
baz_ref = build_git("baz", "2.0.0").ref_for("HEAD")
script <<-RUBY
gemfile do
+ source "https://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 +237,14 @@ RSpec.describe "bundler/inline#gemfile" do
script <<-RUBY
gemfile do
path "#{lib_path}" do
+ source "https://gem.repo1"
gem "two"
end
end
gemfile do
path "#{lib_path}" do
+ source "https://gem.repo1"
gem "four"
end
end
@@ -203,9 +254,116 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to be_empty
end
+ it "doesn't reinstall already installed gems" do
+ system_gems "myrack-1.0.0"
+
+ script <<-RUBY
+ require 'bundler'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+
+ gemfile(true, ui: ui) do
+ source "https://gem.repo1"
+ gem "activesupport"
+ gem "myrack"
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ expect(out).not_to include("Installing myrack")
+ expect(err).to be_empty
+ end
+
+ it "installs gems in later gemfile calls" do
+ system_gems "myrack-1.0.0"
+
+ script <<-RUBY
+ require 'bundler'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+ gemfile(true, ui: ui) do
+ source "https://gem.repo1"
+ gem "myrack"
+ end
+
+ gemfile(true, ui: ui) do
+ source "https://gem.repo1"
+ gem "activesupport"
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ expect(out).not_to include("Installing myrack")
+ expect(err).to be_empty
+ end
+
+ it "doesn't reinstall already installed gems in later gemfile calls" do
+ system_gems "myrack-1.0.0"
+
+ script <<-RUBY
+ require 'bundler'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+ gemfile(true, ui: ui) do
+ source "https://gem.repo1"
+ gem "activesupport"
+ end
+
+ gemfile(true, ui: ui) do
+ source "https://gem.repo1"
+ gem "myrack"
+ end
+ RUBY
+
+ expect(out).to include("Installing activesupport")
+ expect(out).not_to include("Installing myrack")
+ expect(err).to be_empty
+ end
+
+ it "installs gems with native extensions in later gemfile calls" do
+ system_gems "myrack-1.0.0"
+
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("lib", __dir__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/foo.rb", "w") do |f|
+ f.puts "FOO = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ script <<-RUBY
+ require 'bundler'
+ ui = Bundler::UI::Shell.new
+ ui.level = "confirm"
+ gemfile(true, ui: ui) do
+ source "https://gem.repo1"
+ gem "myrack"
+ end
+
+ gemfile(true, ui: ui) do
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ end
+
+ require 'foo'
+ puts FOO
+ puts $:.grep(/ext/)
+ RUBY
+
+ expect(out).to include("YES")
+ expect(out).to include(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s)
+ expect(err).to be_empty
+ end
+
it "installs inline gems when a Gemfile.lock is present" do
gemfile <<-G
- source "https://notaserver.com"
+ source "https://notaserver.test"
gem "rake"
G
@@ -222,29 +380,76 @@ RSpec.describe "bundler/inline#gemfile" do
rake
BUNDLED WITH
- 1.13.6
+ #{Bundler::VERSION}
G
script <<-RUBY
gemfile do
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
end
- puts RACK
+ puts MYRACK
+ RUBY
+
+ expect(err).to be_empty
+ end
+
+ it "does not leak Gemfile.lock versions to the installation output" do
+ gemfile <<-G
+ source "https://notaserver.test"
+ gem "rake"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (11.3.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rake
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ G
+
+ script <<-RUBY
+ gemfile(true) do
+ source "https://gem.repo1"
+ gem "rake", "#{rake_version}"
+ end
RUBY
+ expect(out).to include("Installing rake #{rake_version}")
+ expect(out).not_to include("was 11.3.0")
expect(err).to be_empty
end
it "installs inline gems when frozen is set" do
- script <<-RUBY, :env => { "BUNDLE_FROZEN" => "true" }
+ script <<-RUBY, env: { "BUNDLE_FROZEN" => "true", "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ gemfile do
+ source "https://gem.repo1"
+ gem "myrack"
+ end
+
+ puts MYRACK
+ RUBY
+
+ expect(last_command.stderr).to be_empty
+ end
+
+ it "installs inline gems when deployment is set" do
+ script <<-RUBY, env: { "BUNDLE_DEPLOYMENT" => "true", "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
gemfile do
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
end
- puts RACK
+ puts MYRACK
RUBY
expect(last_command.stderr).to be_empty
@@ -255,11 +460,11 @@ RSpec.describe "bundler/inline#gemfile" do
script <<-RUBY
gemfile do
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
end
- puts RACK
+ puts MYRACK
RUBY
expect(err).to be_empty
@@ -270,11 +475,11 @@ RSpec.describe "bundler/inline#gemfile" do
script <<-RUBY
gemfile do
- source "#{file_uri_for(gem_repo1)}"
- gem "rack" # has the rackup executable
+ source "https://gem.repo1"
+ gem "myrack" # has the myrackup executable
end
- puts RACK
+ puts MYRACK
RUBY
expect(last_command).to be_success
expect(out).to eq "1.0.0"
@@ -282,36 +487,50 @@ RSpec.describe "bundler/inline#gemfile" do
context "when BUNDLE_PATH is set" do
it "installs inline gems to the system path regardless" do
- script <<-RUBY, :env => { "BUNDLE_PATH" => "./vendor/inline" }
+ script <<-RUBY, env: { "BUNDLE_PATH" => "./vendor/inline", "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
gemfile(true) do
- source "file://#{gem_repo1}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
end
RUBY
expect(last_command).to be_success
- expect(system_gem_path("gems/rack-1.0.0")).to exist
+ expect(system_gem_path("gems/myrack-1.0.0")).to exist
end
end
it "skips platform warnings" do
- simulate_platform "ruby"
+ bundle_config "force_ruby_platform true"
script <<-RUBY
gemfile(true) do
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", platform: :jruby
+ source "https://gem.repo1"
+ gem "myrack", platform: :jruby
end
RUBY
expect(err).to be_empty
end
+ it "still installs if the application has `bundle package` no_install config set" do
+ bundle_config "no_install true"
+
+ script <<-RUBY
+ gemfile do
+ source "https://gem.repo1"
+ gem "myrack"
+ end
+ RUBY
+
+ expect(last_command).to be_success
+ expect(system_gem_path("gems/myrack-1.0.0")).to exist
+ end
+
it "preserves previous BUNDLE_GEMFILE value" do
ENV["BUNDLE_GEMFILE"] = ""
script <<-RUBY
gemfile do
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
end
puts "BUNDLE_GEMFILE is empty" if ENV["BUNDLE_GEMFILE"].empty?
@@ -327,8 +546,8 @@ RSpec.describe "bundler/inline#gemfile" do
ENV["BUNDLE_GEMFILE"] = nil
script <<-RUBY
gemfile do
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
end
puts "BUNDLE_GEMFILE is empty" if ENV["BUNDLE_GEMFILE"].empty?
@@ -339,4 +558,194 @@ RSpec.describe "bundler/inline#gemfile" do
expect(last_command).to be_success
expect(out).to include("BUNDLE_GEMFILE is empty")
end
+
+ it "does not error out if library requires optional dependencies" do
+ Dir.mkdir tmp("path_without_gemfile")
+
+ foo_code = <<~RUBY
+ begin
+ gem "bar"
+ rescue LoadError
+ end
+
+ puts "WIN"
+ RUBY
+
+ build_lib "foo", "1.0.0" do |s|
+ s.write "lib/foo.rb", foo_code
+ end
+
+ script <<-RUBY, dir: tmp("path_without_gemfile"), env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ gemfile do
+ source "https://gem.repo2"
+ path "#{lib_path}" do
+ gem "foo", require: false
+ end
+ end
+
+ require "foo"
+ RUBY
+
+ expect(out).to eq("WIN")
+ expect(err).to be_empty
+ end
+
+ it "does not load default timeout", rubygems: ">= 3.5.0" do
+ default_timeout_version = ruby "gem 'timeout', '< 999999'; require 'timeout'; puts Timeout::VERSION", raise_on_error: false
+ skip "timeout isn't a default gem" if default_timeout_version.empty?
+
+ build_repo4 do
+ build_gem "timeout", "999"
+ end
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ require "bundler/inline"
+
+ gemfile(true) do
+ source "https://gem.repo4"
+
+ gem "timeout"
+ end
+ RUBY
+
+ expect(out).to include("Installing timeout 999")
+ end
+
+ it "does not upcase ENV" do
+ script <<-RUBY
+ require 'bundler/inline'
+
+ ENV['Test_Variable'] = 'value string'
+ puts("before: \#{ENV.each_key.select { |key| key.match?(/test_variable/i) }}")
+
+ gemfile do
+ source "https://gem.repo1"
+ end
+
+ puts("after: \#{ENV.each_key.select { |key| key.match?(/test_variable/i) }}")
+ RUBY
+
+ expect(out).to include("before: [\"Test_Variable\"]")
+ expect(out).to include("after: [\"Test_Variable\"]")
+ end
+
+ it "does not create a lockfile" do
+ script <<-RUBY
+ require 'bundler/inline'
+
+ gemfile do
+ source "https://gem.repo1"
+ end
+
+ puts Dir.glob("Gemfile.lock")
+ RUBY
+
+ expect(out).to be_empty
+ end
+
+ it "does not reset ENV" do
+ script <<-RUBY
+ require 'bundler/inline'
+
+ gemfile do
+ source "https://gem.repo1"
+
+ ENV['FOO'] = 'bar'
+ end
+
+ puts ENV['FOO']
+ RUBY
+
+ expect(out).to eq("bar")
+ end
+
+ it "does not load specified version of psych and stringio", :ruby_repo do
+ build_repo4 do
+ build_gem "psych", "999"
+ build_gem "stringio", "999"
+ end
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ require "bundler/inline"
+
+ gemfile(true) do
+ source "https://gem.repo4"
+
+ gem "psych"
+ gem "stringio"
+ end
+ RUBY
+
+ expect(out).to include("Installing psych 999")
+ expect(out).to include("Installing stringio 999")
+ if Gem.respond_to?(:use_psych?) && Gem.use_psych?
+ expect(out).to include("The psych gem was resolved to 999")
+ expect(out).to include("The stringio gem was resolved to 999")
+ end
+ end
+
+ it "installs a conflicting default gem and non-default gems together" do
+ build_repo4 do
+ build_gem "securerandom", "999"
+ build_gem "myrack", "1.0.0"
+ end
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ gemfile(true) do
+ source "https://gem.repo4"
+ gem "securerandom"
+ gem "myrack"
+ end
+
+ puts MYRACK
+ RUBY
+
+ expect(out).to include("Installing securerandom 999")
+ expect(out).to include("Installing myrack 1.0.0")
+ expect(out).to include("1.0.0")
+ expect(err).to be_empty
+ end
+
+ it "installs a conflicting default gem alongside git sources" do
+ build_repo4 do
+ build_gem "securerandom", "999"
+ end
+
+ build_git "foo", "1.0.0"
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ gemfile(true) do
+ source "https://gem.repo4"
+ gem "securerandom"
+ gem "foo", :git => #{lib_path("foo-1.0.0").to_s.dump}
+ end
+
+ puts FOO
+ RUBY
+
+ expect(out).to include("Installing securerandom 999")
+ expect(out).to include("1.0.0")
+ expect(err).to be_empty
+ end
+
+ it "leaves a lockfile in the same directory as the inline script alone" do
+ install_gemfile <<~G
+ source "https://gem.repo1"
+ gem "foo"
+ G
+
+ original_lockfile = lockfile
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
+ require "bundler/inline"
+
+ gemfile(true) do
+ source "https://gem.repo1"
+
+ gem "myrack"
+ end
+ RUBY
+
+ expect(lockfile).to eq(original_lockfile)
+ end
end
diff --git a/spec/bundler/runtime/load_spec.rb b/spec/bundler/runtime/load_spec.rb
index 0274ba18b8..472cde87c5 100644
--- a/spec/bundler/runtime/load_spec.rb
+++ b/spec/bundler/runtime/load_spec.rb
@@ -4,18 +4,18 @@ RSpec.describe "Bundler.load" do
describe "with a gemfile" do
before(:each) do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
allow(Bundler::SharedHelpers).to receive(:pwd).and_return(bundled_app)
end
it "provides a list of the env dependencies" do
- expect(Bundler.load.dependencies).to have_dep("rack", ">= 0")
+ expect(Bundler.load.dependencies).to have_dep("myrack", ">= 0")
end
it "provides a list of the resolved gems" do
- expect(Bundler.load.gems).to have_gem("rack-1.0.0", "bundler-#{Bundler::VERSION}")
+ expect(Bundler.load.gems).to have_gem("myrack-1.0.0", "bundler-#{Bundler::VERSION}")
end
it "ignores blank BUNDLE_GEMFILEs" do
@@ -28,20 +28,20 @@ RSpec.describe "Bundler.load" do
describe "with a gems.rb file" do
before(:each) do
- create_file "gems.rb", <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ gemfile "gems.rb", <<-G
+ source "https://gem.repo1"
+ gem "myrack"
G
bundle :install
allow(Bundler::SharedHelpers).to receive(:pwd).and_return(bundled_app)
end
it "provides a list of the env dependencies" do
- expect(Bundler.load.dependencies).to have_dep("rack", ">= 0")
+ expect(Bundler.load.dependencies).to have_dep("myrack", ">= 0")
end
it "provides a list of the resolved gems" do
- expect(Bundler.load.gems).to have_gem("rack-1.0.0", "bundler-#{Bundler::VERSION}")
+ expect(Bundler.load.gems).to have_gem("myrack-1.0.0", "bundler-#{Bundler::VERSION}")
end
end
@@ -68,7 +68,7 @@ RSpec.describe "Bundler.load" do
begin
expect { Bundler.load }.to raise_error(Bundler::GemfileNotFound)
ensure
- bundler_gemfile.rmtree if @remove_bundler_gemfile
+ FileUtils.rm_rf bundler_gemfile if @remove_bundler_gemfile
end
end
end
@@ -76,16 +76,16 @@ RSpec.describe "Bundler.load" do
describe "when called twice" do
it "doesn't try to load the runtime twice" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
gem "activesupport", :group => :test
G
ruby <<-RUBY
- require "#{lib_dir}/bundler"
+ require "bundler"
Bundler.setup :default
Bundler.require :default
- puts RACK
+ puts MYRACK
begin
require "activesupport"
rescue LoadError
@@ -100,7 +100,7 @@ RSpec.describe "Bundler.load" do
describe "not hurting brittle rubygems" do
it "does not inject #source into the generated YAML of the gem specs" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activerecord"
G
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb
index 215ba8628f..6d96758956 100644
--- a/spec/bundler/runtime/platform_spec.rb
+++ b/spec/bundler/runtime/platform_spec.rb
@@ -3,26 +3,26 @@
RSpec.describe "Bundler.setup with multi platform stuff" do
it "raises a friendly error when gems are missing locally" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
lockfile <<-G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (1.0)
+ myrack (1.0)
PLATFORMS
#{local_tag}
DEPENDENCIES
- rack
+ myrack
G
ruby <<-R
begin
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.ui.silence { Bundler.setup }
rescue Bundler::GemNotFound => e
puts "WIN"
@@ -35,7 +35,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
it "will resolve correctly on the current platform when the lockfile was targeted for a different one" do
lockfile <<-G
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
nokogiri (1.4.2-java)
weakling (= 0.0.3)
@@ -48,19 +48,147 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
nokogiri
G
- simulate_platform "x86-darwin-10"
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ simulate_platform "x86-darwin-10" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "nokogiri"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+ 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 "racca", "~> 1.5.2"
+ end
+
+ build_gem "nokogiri", "1.11.1" do |s|
+ s.platform = Bundler.local_platform
+ s.add_dependency "racca", "~> 1.4"
+ end
+
+ build_gem "mini_portile2", "2.5.0"
+ build_gem "racca", "1.5.2"
+ end
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo4, "mini_portile2", "2.5.0"
+ c.checksum gem_repo4, "nokogiri", "1.11.1"
+ c.checksum gem_repo4, "nokogiri", "1.11.1", Bundler.local_platform
+ c.checksum gem_repo4, "racca", "1.5.2"
+ end
+
+ good_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ mini_portile2 (2.5.0)
+ nokogiri (1.11.1)
+ mini_portile2 (~> 2.5.0)
+ racca (~> 1.5.2)
+ nokogiri (1.11.1-#{Bundler.local_platform})
+ racca (~> 1.4)
+ racca (1.5.2)
+
+ PLATFORMS
+ #{lockfile_platforms("ruby")}
+
+ DEPENDENCIES
+ nokogiri (~> 1.11)
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<-G
+ source "https://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://gem.repo4"
gem "nokogiri"
G
- expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.11.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ 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_only do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "nokogiri"
+ G
+
+ lockfile <<-G
+ GEM
+ remote: https://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
+ #{Bundler::VERSION}
+ 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
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
nokogiri (1.4.2-java)
weakling (= 0.0.3)
@@ -73,66 +201,207 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
nokogiri
G
- simulate_platform "x86-darwin-100"
+ simulate_platform "x86-darwin-100" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "nokogiri"
+ gem "platform_specific"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 x86-darwin-100"
+ end
+ end
+ it "allows specifying only-ruby-platform on jruby", :jruby_only do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "nokogiri"
gem "platform_specific"
G
- expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 x86-darwin-100"
+ bundle_config "force_ruby_platform true"
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 ruby"
end
it "allows specifying only-ruby-platform" do
- simulate_platform "java"
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "nokogiri"
+ gem "platform_specific"
+ G
+
+ bundle_config "force_ruby_platform true"
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 ruby"
+ end
+ it "allows specifying only-ruby-platform even if the lockfile is locked to a specific compatible platform" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "nokogiri"
gem "platform_specific"
G
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 ruby"
+ end
+
+ it "doesn't pull platform specific gems on truffleruby", :truffleruby_only do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 ruby"
+ end
+
+ it "doesn't pull platform specific gems on truffleruby (except when whitelisted) even if lockfile was generated with an older version that declared ruby as platform", :truffleruby_only do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ platform_specific (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 ruby"
+
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "libv8"
+
+ build_gem "libv8" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "libv8"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ libv8 (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ libv8
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "libv8 1.0 x86_64-linux"
+ end
+ end
+
+ it "doesn't pull platform specific gems on truffleruby, even if lockfile only includes those", :truffleruby_only do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ platform_specific (1.0-x86-darwin-100)
+
+ PLATFORMS
+ x86-darwin-100
+
+ DEPENDENCIES
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
bundle "install"
- expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY"
+ expect(the_bundle).to include_gems "platform_specific 1.0 ruby"
+ end
+
+ it "pulls platform specific gems correctly on musl" do
+ build_repo4 do
+ build_gem "nokogiri", "1.13.8" do |s|
+ s.platform = "aarch64-linux"
+ end
+ end
+
+ simulate_platform "aarch64-linux-musl" do
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo4"
+ gem "nokogiri"
+ G
+ end
+
+ expect(out).to include("Fetching nokogiri 1.13.8 (aarch64-linux)")
end
it "allows specifying only-ruby-platform on windows with dependency platforms" do
- simulate_windows do
+ simulate_platform "x86-mswin32" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "nokogiri", :platforms => [:mingw, :mswin, :x64_mingw, :jruby]
+ source "https://gem.repo1"
+ gem "nokogiri", :platforms => [:windows, :jruby]
gem "platform_specific"
G
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bundle "install"
- expect(the_bundle).to include_gems "platform_specific 1.0 RUBY"
+ expect(the_bundle).to include_gems "platform_specific 1.0 ruby"
expect(the_bundle).to not_include_gems "nokogiri"
end
end
it "allows specifying only-ruby-platform on windows with gemspec dependency" do
- build_lib("foo", "1.0", :path => bundled_app) do |s|
- s.add_dependency "rack"
+ build_lib("foo", "1.0", path: bundled_app) do |s|
+ s.add_dependency "myrack"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gemspec
G
bundle :lock
- simulate_windows do
- bundle "config set force_ruby_platform true"
+ simulate_platform "x86-mswin32" do
+ bundle_config "force_ruby_platform true"
bundle "install"
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
@@ -142,29 +411,58 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
s.add_dependency "platform_specific"
end
end
- simulate_windows x64_mingw do
+ simulate_platform "x64-mingw-ucrt" do
lockfile <<-L
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
platform_specific (1.0-x86-mingw32)
requires_platform_specific (1.0)
platform_specific
PLATFORMS
- x64-mingw32
+ x64-mingw-ucrt
x86-mingw32
DEPENDENCIES
requires_platform_specific
L
- install_gemfile <<-G, :verbose => true
- source "#{file_uri_for(gem_repo2)}"
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo2"
gem "requires_platform_specific"
G
- expect(the_bundle).to include_gem "platform_specific 1.0 x64-mingw32"
+ expect(out).to include("lockfile does not have all gems needed for the current platform")
+ expect(the_bundle).to include_gem "platform_specific 1.0 x64-mingw-ucrt"
+ end
+ end
+
+ %w[x86-mswin32 x64-mswin64 x86-mingw32 x64-mingw-ucrt aarch64-mingw-ucrt].each do |platform|
+ it "allows specifying platform windows on #{platform} platform" do
+ simulate_platform platform do
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ platform_specific (1.0-#{platform})
+ requires_platform_specific (1.0)
+ platform_specific
+
+ PLATFORMS
+ #{platform}
+
+ DEPENDENCIES
+ requires_platform_specific
+ L
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific", :platforms => [:windows]
+ G
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 #{platform}"
+ end
end
end
end
diff --git a/spec/bundler/runtime/require_spec.rb b/spec/bundler/runtime/require_spec.rb
index ad30529e86..46613286d2 100644
--- a/spec/bundler/runtime/require_spec.rb
+++ b/spec/bundler/runtime/require_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe "Bundler.require" do
s.write "lib/four.rb", "puts 'four'"
end
- build_lib "five", "1.0.0", :no_default => true do |s|
+ build_lib "five", "1.0.0", no_default: true do |s|
s.write "lib/mofive.rb", "puts 'five'"
end
@@ -46,6 +46,7 @@ RSpec.describe "Bundler.require" do
end
gemfile <<-G
+ source "https://gem.repo1"
path "#{lib_path}" do
gem "one", :group => :bar, :require => %w[baz qux]
gem "two"
@@ -112,16 +113,15 @@ RSpec.describe "Bundler.require" do
it "raises an exception if a require is specified but the file does not exist" do
gemfile <<-G
+ source "https://gem.repo1"
path "#{lib_path}" do
gem "two", :require => 'fail'
end
G
- load_error_run <<-R, "fail"
- Bundler.require
- R
+ run "Bundler.require", raise_on_error: false
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to include("cannot load such file -- fail")
end
it "displays a helpful message if the required gem throws an error" do
@@ -130,12 +130,13 @@ RSpec.describe "Bundler.require" do
end
gemfile <<-G
+ source "https://gem.repo1"
path "#{lib_path}" do
gem "faulty"
end
G
- run "Bundler.require", :raise_on_error => false
+ run "Bundler.require", raise_on_error: false
expect(err).to match("error while trying to load the gem 'faulty'")
expect(err).to match("Gem Internal Error Message")
end
@@ -146,21 +147,15 @@ RSpec.describe "Bundler.require" do
end
gemfile <<-G
+ source "https://gem.repo1"
path "#{lib_path}" do
gem "loadfuuu"
end
G
- cmd = <<-RUBY
- begin
- Bundler.require
- rescue LoadError => e
- warn "ZOMG LOAD ERROR: \#{e.message}"
- end
- RUBY
- run(cmd)
+ run "Bundler.require", raise_on_error: false
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR: cannot load such file -- load-bar")
+ expect(err_without_deprecations).to include("cannot load such file -- load-bar")
end
describe "with namespaced gems" do
@@ -172,6 +167,7 @@ RSpec.describe "Bundler.require" do
it "requires gem names that are namespaced" do
gemfile <<-G
+ source "https://gem.repo1"
path '#{lib_path}' do
gem 'jquery-rails'
end
@@ -182,17 +178,19 @@ RSpec.describe "Bundler.require" do
end
it "silently passes if the require fails" do
- build_lib "bcrypt-ruby", "1.0.0", :no_default => true do |s|
+ build_lib "bcrypt-ruby", "1.0.0", no_default: true do |s|
s.write "lib/brcrypt.rb", "BCrypt = '1.0.0'"
end
gemfile <<-G
+ source "https://gem.repo1"
+
path "#{lib_path}" do
gem "bcrypt-ruby"
end
G
cmd = <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.require
RUBY
ruby(cmd)
@@ -202,15 +200,15 @@ RSpec.describe "Bundler.require" do
it "does not mangle explicitly given requires" do
gemfile <<-G
+ source "https://gem.repo1"
path "#{lib_path}" do
gem 'jquery-rails', :require => 'jquery-rails'
end
G
- load_error_run <<-R, "jquery-rails"
- Bundler.require
- R
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ run "Bundler.require", raise_on_error: false
+
+ expect(err_without_deprecations).to include("cannot load such file -- jquery-rails")
end
it "handles the case where regex fails" do
@@ -219,21 +217,15 @@ RSpec.describe "Bundler.require" do
end
gemfile <<-G
+ source "https://gem.repo1"
path "#{lib_path}" do
gem "load-fuuu"
end
G
- cmd = <<-RUBY
- begin
- Bundler.require
- rescue LoadError => e
- warn "ZOMG LOAD ERROR" if e.message.include?("Could not open library 'libfuuu-1.0'")
- end
- RUBY
- run(cmd)
+ run "Bundler.require", raise_on_error: false
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to include("libfuuu-1.0").and include("cannot open shared object file")
end
it "doesn't swallow the error when the library has an unrelated error" do
@@ -242,21 +234,15 @@ RSpec.describe "Bundler.require" do
end
gemfile <<-G
+ source "https://gem.repo1"
path "#{lib_path}" do
gem "load-fuuu"
end
G
- cmd = <<-RUBY
- begin
- Bundler.require
- rescue LoadError => e
- warn "ZOMG LOAD ERROR: \#{e.message}"
- end
- RUBY
- run(cmd)
+ run "Bundler.require", raise_on_error: false
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR: cannot load such file -- load-bar")
+ expect(err_without_deprecations).to include("cannot load such file -- load-bar")
end
end
@@ -300,6 +286,7 @@ RSpec.describe "Bundler.require" do
it "works when the gems are in the Gemfile in the correct order" do
gemfile <<-G
+ source "https://gem.repo1"
path "#{lib_path}" do
gem "two"
gem "one"
@@ -312,12 +299,13 @@ RSpec.describe "Bundler.require" do
describe "a gem with different requires for different envs" do
before(:each) do
- build_gem "multi_gem", :to_bundle => true do |s|
+ build_gem "multi_gem", to_bundle: true do |s|
s.write "lib/one.rb", "puts 'ONE'"
s.write "lib/two.rb", "puts 'TWO'"
end
install_gemfile <<-G
+ source "https://gem.repo1"
gem "multi_gem", :require => "one", :group => :one
gem "multi_gem", :require => "two", :group => :two
G
@@ -341,6 +329,7 @@ RSpec.describe "Bundler.require" do
it "fails when the gems are in the Gemfile in the wrong order" do
gemfile <<-G
+ source "https://gem.repo1"
path "#{lib_path}" do
gem "one"
gem "two"
@@ -353,30 +342,30 @@ RSpec.describe "Bundler.require" do
describe "with busted gems" do
it "should be busted" do
- build_gem "busted_require", :to_bundle => true do |s|
+ build_gem "busted_require", to_bundle: true do |s|
s.write "lib/busted_require.rb", "require 'no_such_file_omg'"
end
install_gemfile <<-G
+ source "https://gem.repo1"
gem "busted_require"
G
- load_error_run <<-R, "no_such_file_omg"
- Bundler.require
- R
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ run "Bundler.require", raise_on_error: false
+
+ expect(err_without_deprecations).to include("cannot load such file -- no_such_file_omg")
end
end
end
it "does not load rubygems gemspecs that are used" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
run <<-R
- path = File.join(Gem.dir, "specifications", "rack-1.0.0.gemspec")
+ path = File.join(Gem.dir, "specifications", "myrack-1.0.0.gemspec")
contents = File.read(path)
contents = contents.lines.to_a.insert(-2, "\n raise 'broken gemspec'\n").join
File.open(path, "w") do |f|
@@ -396,6 +385,7 @@ RSpec.describe "Bundler.require" do
build_git "foo"
install_gemfile <<-G
+ source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -415,18 +405,58 @@ RSpec.describe "Bundler.require" do
expect(out).to eq("WIN")
end
+
+ it "does not load plugins" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ create_file "plugins/rubygems_plugin.rb", "puts 'FAIL'"
+
+ run <<~R, env: { "RUBYLIB" => rubylib.unshift(bundled_app("plugins").to_s).join(File::PATH_SEPARATOR) }
+ Bundler.require
+ puts "WIN"
+ R
+
+ expect(out).to eq("WIN")
+ end
+
+ it "does not extract gemspecs from application cache packages" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle :cache
+
+ path = cached_gem("myrack-1.0.0")
+
+ run <<-R
+ File.open("#{path}", "w") do |f|
+ f.write "broken package"
+ end
+ R
+
+ run <<-R
+ Bundler.require
+ puts "WIN"
+ R
+
+ expect(out).to eq("WIN")
+ end
end
RSpec.describe "Bundler.require with platform specific dependencies" do
it "does not require the gems that are pinned to other platforms" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
platforms :#{not_local_tag} do
gem "platform_specific", :require => "omgomg"
end
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
G
run "Bundler.require"
@@ -434,17 +464,15 @@ RSpec.describe "Bundler.require with platform specific dependencies" do
end
it "requires gems pinned to multiple platforms, including the current one" do
- skip "platform issues" if Gem.win_platform?
-
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
platforms :#{not_local_tag}, :#{local_tag} do
- gem "rack", :require => "rack"
+ gem "myrack", :require => "myrack"
end
G
- run "Bundler.require; puts RACK"
+ run "Bundler.require; puts MYRACK"
expect(out).to eq("1.0.0")
expect(err).to be_empty
diff --git a/spec/bundler/runtime/requiring_spec.rb b/spec/bundler/runtime/requiring_spec.rb
new file mode 100644
index 0000000000..f0e0aeacaf
--- /dev/null
+++ b/spec/bundler/runtime/requiring_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+RSpec.describe "Requiring bundler" do
+ it "takes care of requiring rubygems when entrypoint is bundler/setup" do
+ sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler/setup -e'puts true'", env: { "RUBYOPT" => "--disable=gems" })
+
+ expect(stdboth).to eq("true")
+ end
+
+ it "takes care of requiring rubygems when requiring just bundler" do
+ sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler -e'puts true'", env: { "RUBYOPT" => "--disable=gems" })
+
+ expect(stdboth).to eq("true")
+ end
+end
diff --git a/spec/bundler/runtime/self_management_spec.rb b/spec/bundler/runtime/self_management_spec.rb
new file mode 100644
index 0000000000..176c2a3121
--- /dev/null
+++ b/spec/bundler/runtime/self_management_spec.rb
@@ -0,0 +1,279 @@
+# frozen_string_literal: true
+
+RSpec.describe "Self management" do
+ describe "auto switching" do
+ let(:previous_minor) do
+ "9.3.0"
+ end
+
+ let(:current_version) do
+ "9.4.0"
+ end
+
+ before do
+ build_repo4 do
+ build_bundler previous_minor
+
+ build_bundler current_version
+
+ build_gem "myrack", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "myrack"
+ G
+
+ pristine_system_gems "bundler-#{current_version}"
+ end
+
+ it "installs locked version when using system path and uses it" do
+ lockfile_bundled_with(previous_minor)
+
+ bundle_config "path.system true"
+ bundle "install"
+ expect(out).to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+
+ # It uninstalls the older system bundler
+ bundle "clean --force", artifice: nil
+ expect(out).to eq("Removing bundler (#{current_version})")
+
+ # App now uses locked version
+ bundle "-v", artifice: nil
+ expect(out).to eq(previous_minor)
+
+ # ruby-core test setup has always "lib" in $LOAD_PATH so `require "bundler/setup"` always activate the local version rather than using RubyGems gem activation stuff
+ unless ruby_core?
+ # App now uses locked version, even when not using the CLI directly
+ file = bundled_app("bin/bundle_version.rb")
+ create_file file, <<-RUBY
+ #!#{Gem.ruby}
+ require 'bundler/setup'
+ puts '#{previous_minor}'
+ RUBY
+ file.chmod(0o777)
+ cmd = Gem.win_platform? ? "#{Gem.ruby} bin/bundle_version.rb" : "bin/bundle_version.rb"
+ in_bundled_app cmd
+ expect(out).to eq(previous_minor)
+ end
+
+ # Subsequent installs use the locked version without reinstalling
+ bundle "install --verbose", artifice: nil
+ expect(out).to include("Using bundler #{previous_minor}")
+ expect(out).not_to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ end
+
+ it "installs locked version when using local path and uses it" do
+ lockfile_bundled_with(previous_minor)
+
+ bundle_config "path vendor/bundle"
+ bundle "install"
+ expect(out).to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ expect(vendored_gems("gems/bundler-#{previous_minor}")).to exist
+
+ # It does not uninstall the locked bundler
+ bundle "clean"
+ expect(out).to be_empty
+
+ # App now uses locked version
+ bundle "-v"
+ expect(out).to eq(previous_minor)
+
+ # Preserves original gem home when auto-switching
+ bundle "exec ruby -e 'puts Bundler.original_env[\"GEM_HOME\"]'"
+ expect(out).to eq(ENV["GEM_HOME"])
+
+ # ruby-core test setup has always "lib" in $LOAD_PATH so `require "bundler/setup"` always activate the local version rather than using RubyGems gem activation stuff
+ unless ruby_core?
+ # App now uses locked version, even when not using the CLI directly
+ file = bundled_app("bin/bundle_version.rb")
+ create_file file, <<-RUBY
+ #!#{Gem.ruby}
+ require 'bundler/setup'
+ puts '#{previous_minor}'
+ RUBY
+ file.chmod(0o777)
+ cmd = Gem.win_platform? ? "#{Gem.ruby} bin/bundle_version.rb" : "bin/bundle_version.rb"
+ in_bundled_app cmd
+ expect(out).to eq(previous_minor)
+ end
+
+ # Subsequent installs use the locked version without reinstalling
+ bundle "install --verbose"
+ expect(out).to include("Using bundler #{previous_minor}")
+ expect(out).not_to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ end
+
+ it "installs locked version when using deployment option and uses it" do
+ lockfile_bundled_with(previous_minor)
+
+ bundle_config "deployment true"
+ bundle "install"
+ expect(out).to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ expect(vendored_gems("gems/bundler-#{previous_minor}")).to exist
+
+ # It does not uninstall the locked bundler
+ bundle "clean"
+ expect(out).to be_empty
+
+ # App now uses locked version
+ bundle "-v"
+ expect(out).to eq(previous_minor)
+
+ # Subsequent installs use the locked version without reinstalling
+ bundle "install --verbose"
+ expect(out).to include("Using bundler #{previous_minor}")
+ expect(out).not_to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ end
+
+ it "does not try to install a development version" do
+ lockfile_bundled_with("#{previous_minor}.dev")
+
+ bundle "install --verbose"
+ expect(out).not_to match(/restarting using that version/)
+
+ bundle "-v"
+ expect(out).to eq(current_version)
+ end
+
+ it "does not try to install when --local is passed" do
+ lockfile_bundled_with(previous_minor)
+ system_gems "myrack-1.0.0", path: local_gem_path
+
+ bundle "install --local"
+ expect(out).not_to match(/Installing Bundler/)
+
+ bundle "-v"
+ expect(out).to eq(current_version)
+ end
+
+ it "shows a discrete message if locked bundler does not exist" do
+ missing_minor = "#{current_version[0]}.999.999"
+
+ lockfile_bundled_with(missing_minor)
+
+ bundle "install"
+ expect(err).to eq("Your lockfile is locked to a version of bundler (#{missing_minor}) that doesn't exist at https://rubygems.org/. Going on using #{current_version}")
+
+ bundle "-v"
+ expect(out).to eq(current_version)
+ end
+
+ it "installs BUNDLE_VERSION version when using bundle config version x.y.z" do
+ lockfile_bundled_with(current_version)
+
+ bundle_config "version #{previous_minor}"
+ bundle "install"
+ expect(out).to include("Bundler #{current_version} is running, but your configuration was #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+
+ bundle "-v"
+ expect(out).to eq(previous_minor)
+ end
+
+ it "requires the right bundler version from the config and run bundle CLI without re-exec" do
+ unless Bundler.rubygems.provides?(">= 4.1.0.dev")
+ skip "This spec can only run when Gem::BundlerVersionFinder.bundler_versions reads bundler configs"
+ end
+
+ lockfile_bundled_with(current_version)
+
+ bundle_config "version #{previous_minor}"
+ bundle_config "path.system true"
+ bundle "install"
+
+ script = bundled_app("script.rb")
+ create_file(script, "p 'executed once'")
+
+ bundle "-v", env: { "RUBYOPT" => "-r#{script}" }
+ expect(out).to eq(%("executed once"\n9.3.0))
+ end
+
+ it "does not try to install when using bundle config version global" do
+ lockfile_bundled_with(previous_minor)
+
+ bundle_config "version system"
+ bundle "install"
+ expect(out).not_to match(/restarting using that version/)
+
+ bundle "-v"
+ expect(out).to eq(current_version)
+ end
+
+ it "does not try to install when using bundle config version <dev-version>" do
+ lockfile_bundled_with(previous_minor)
+
+ bundle_config "version #{previous_minor}.dev"
+ bundle "install"
+ expect(out).not_to match(/restarting using that version/)
+
+ bundle "-v"
+ expect(out).to eq(current_version)
+ end
+
+ it "ignores malformed lockfile version" do
+ lockfile_bundled_with("2.3.")
+
+ bundle "install --verbose"
+ expect(out).to include("Using bundler #{current_version}")
+ end
+
+ it "uses the right original script when re-execing, if `$0` has been changed to something that's not a script", :ruby_repo do
+ system_gems "bundler-9.9.9", path: local_gem_path
+
+ test = bundled_app("test.rb")
+
+ create_file test, <<~RUBY
+ $0 = "this is the program name"
+ require "bundler/setup"
+ RUBY
+
+ lockfile_bundled_with("9.9.9")
+
+ in_bundled_app "#{Gem.ruby} #{test}", raise_on_error: false
+ expect(err).to include("Could not find myrack-1.0.0")
+ expect(err).not_to include("this is the program name")
+ end
+
+ it "uses modified $0 when re-execing, if `$0` has been changed to a script", :ruby_repo do
+ system_gems "bundler-9.9.9", path: local_gem_path
+
+ runner = bundled_app("runner.rb")
+
+ create_file runner, <<~RUBY
+ $0 = ARGV.shift
+ load $0
+ RUBY
+
+ script = bundled_app("script.rb")
+ create_file script, <<~RUBY
+ require "bundler/setup"
+ RUBY
+
+ lockfile_bundled_with("9.9.9")
+
+ in_bundled_app "#{Gem.ruby} #{runner} #{script}", raise_on_error: false
+ expect(err).to include("Could not find myrack-1.0.0")
+ end
+
+ private
+
+ def lockfile_bundled_with(version)
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+
+ BUNDLED WITH
+ #{version}
+ L
+ end
+ end
+end
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb
index 22c07f0be0..ceb6fcf66a 100644
--- a/spec/bundler/runtime/setup_spec.rb
+++ b/spec/bundler/runtime/setup_spec.rb
@@ -1,22 +1,21 @@
# frozen_string_literal: true
require "tmpdir"
-require "tempfile"
RSpec.describe "Bundler.setup" do
describe "with no arguments" do
it "makes all groups available" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :group => :test
+ source "https://gem.repo1"
+ gem "myrack", :group => :test
G
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.setup
- require 'rack'
- puts RACK
+ require 'myrack'
+ puts MYRACK
RUBY
expect(err).to be_empty
expect(out).to eq("1.0.0")
@@ -26,19 +25,19 @@ RSpec.describe "Bundler.setup" do
describe "when called with groups" do
before(:each) do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "yard"
- gem "rack", :group => :test
+ gem "myrack", :group => :test
G
end
it "doesn't make all groups available" do
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.setup(:default)
begin
- require 'rack'
+ require 'myrack'
rescue LoadError
puts "WIN"
end
@@ -49,11 +48,11 @@ RSpec.describe "Bundler.setup" do
it "accepts string for group name" do
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.setup(:default, 'test')
- require 'rack'
- puts RACK
+ require 'myrack'
+ puts MYRACK
RUBY
expect(err).to be_empty
expect(out).to eq("1.0.0")
@@ -61,12 +60,12 @@ RSpec.describe "Bundler.setup" do
it "leaves all groups available if they were already" do
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.setup
Bundler.setup(:default)
- require 'rack'
- puts RACK
+ require 'myrack'
+ puts MYRACK
RUBY
expect(err).to be_empty
expect(out).to eq("1.0.0")
@@ -74,7 +73,7 @@ RSpec.describe "Bundler.setup" do
it "leaves :default available if setup is called twice" do
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.setup(:default)
Bundler.setup(:default, :test)
@@ -90,16 +89,16 @@ RSpec.describe "Bundler.setup" do
end
it "handles multiple non-additive invocations" do
- ruby <<-RUBY, :raise_on_error => false
- require '#{lib_dir}/bundler'
+ ruby <<-RUBY, raise_on_error: false
+ require 'bundler'
Bundler.setup(:default, :test)
Bundler.setup(:default)
- require 'rack'
+ require 'myrack'
puts "FAIL"
RUBY
- expect(err).to match("rack")
+ expect(err).to match("myrack")
expect(err).to match("LoadError")
expect(out).not_to match("FAIL")
end
@@ -108,43 +107,44 @@ 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
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
ENV["RUBYOPT"] = "#{ENV["RUBYOPT"]} -Idash_i_dir"
ENV["RUBYLIB"] = "rubylib_dir"
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
Bundler.setup
puts $LOAD_PATH
RUBY
load_path = out.split("\n")
- rack_load_order = load_path.index {|path| path.include?("rack") }
+ myrack_load_order = load_path.index {|path| path.include?("myrack") }
expect(err).to be_empty
expect(load_path).to include(a_string_ending_with("dash_i_dir"), "rubylib_dir")
- expect(rack_load_order).to be > 0
+ expect(myrack_load_order).to be > 0
end
it "orders the load path correctly when there are dependencies" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "rails"
G
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
+ gem "bundler", "#{Bundler::VERSION}" if #{ruby_core?}
Bundler.setup
puts $LOAD_PATH
RUBY
@@ -153,26 +153,27 @@ 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",
"/gems/actionmailer-2.3.2/lib",
"/gems/activesupport-2.3.2/lib",
- "/gems/rake-13.0.1/lib"
+ "/gems/rake-#{rake_version}/lib"
)
end
it "falls back to order the load path alphabetically for backwards compatibility" do
+ bundle_config "path.system true"
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "weakling"
gem "duradura"
gem "terranova"
G
ruby <<-RUBY
- require '#{lib_dir}/bundler/setup'
+ require 'bundler/setup'
puts $LOAD_PATH
RUBY
@@ -188,12 +189,12 @@ RSpec.describe "Bundler.setup" do
it "raises if the Gemfile was not yet installed" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
ruby <<-R
- require '#{lib_dir}/bundler'
+ require 'bundler'
begin
Bundler.setup
@@ -208,12 +209,12 @@ RSpec.describe "Bundler.setup" do
it "doesn't create a Gemfile.lock if the setup fails" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- ruby <<-R, :raise_on_error => false
- require '#{lib_dir}/bundler'
+ ruby <<-R, raise_on_error: false
+ require 'bundler'
Bundler.setup
R
@@ -223,20 +224,20 @@ RSpec.describe "Bundler.setup" do
it "doesn't change the Gemfile.lock if the setup fails" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
lockfile = File.read(bundled_app_lock)
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
gem "nosuchgem", "10.0"
G
- ruby <<-R, :raise_on_error => false
- require '#{lib_dir}/bundler'
+ ruby <<-R, raise_on_error: false
+ require 'bundler'
Bundler.setup
R
@@ -246,8 +247,8 @@ RSpec.describe "Bundler.setup" do
it "makes a Gemfile.lock if setup succeeds" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
File.read(bundled_app_lock)
@@ -262,12 +263,12 @@ RSpec.describe "Bundler.setup" do
context "user provides an absolute path" do
it "uses BUNDLE_GEMFILE to locate the gemfile if present" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
gemfile bundled_app("4realz"), <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport", "2.3.5"
G
@@ -281,15 +282,15 @@ RSpec.describe "Bundler.setup" do
context "an absolute path is not provided" do
it "uses BUNDLE_GEMFILE to locate the gemfile if present and doesn't fail in deployment mode" do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
G
bundle "install"
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
ENV["BUNDLE_GEMFILE"] = "Gemfile"
ruby <<-R
- require '#{lib_dir}/bundler'
+ require 'bundler'
begin
Bundler.setup
@@ -302,28 +303,54 @@ RSpec.describe "Bundler.setup" do
expect(out).to eq("WIN")
end
end
+
+ context "user sets it via `config set --local gemfile`" do
+ it "uses the value in the config" do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ gemfile bundled_app("CustomGemfile"), <<-G
+ source "https://gem.repo1"
+ gem "activesupport", "2.3.5"
+ G
+
+ bundle_config "gemfile #{bundled_app("CustomGemfile")}"
+ bundle "install"
+
+ ruby <<-R
+ require 'bundler'
+ Bundler.setup
+ require 'activesupport'
+ puts ACTIVESUPPORT
+ R
+
+ expect(out).to eq("2.3.5")
+ end
+ end
end
it "prioritizes gems in BUNDLE_PATH over gems in GEM_HOME" do
ENV["BUNDLE_PATH"] = bundled_app(".bundle").to_s
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
G
- build_gem "rack", "1.0", :to_system => true do |s|
- s.write "lib/rack.rb", "RACK = 'FAIL'"
+ build_gem "myrack", "1.0", to_system: true do |s|
+ s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
end
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
describe "integrate with rubygems" do
describe "by replacing #gem" do
before :each do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "0.9.1"
+ source "https://gem.repo1"
+ gem "myrack", "0.9.1"
G
end
@@ -340,23 +367,10 @@ RSpec.describe "Bundler.setup" do
expect(out).to eq("WIN")
end
- it "version_requirement is now deprecated in rubygems 1.4.0+ when gem is missing" do
- run <<-R
- begin
- gem "activesupport"
- puts "FAIL"
- rescue LoadError
- puts "WIN"
- end
- R
-
- expect(err).to be_empty
- end
-
it "replaces #gem but raises when the version is wrong" do
run <<-R
begin
- gem "rack", "1.0.0"
+ gem "myrack", "1.0.0"
puts "FAIL"
rescue LoadError
puts "WIN"
@@ -365,39 +379,27 @@ RSpec.describe "Bundler.setup" do
expect(out).to eq("WIN")
end
-
- it "version_requirement is now deprecated in rubygems 1.4.0+ when the version is wrong" do
- run <<-R
- begin
- gem "rack", "1.0.0"
- puts "FAIL"
- rescue LoadError
- puts "WIN"
- end
- R
-
- expect(err).to be_empty
- end
end
describe "by hiding system gems" do
before :each do
system_gems "activesupport-2.3.5"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "yard"
G
end
it "removes system gems from Gem.source_index" do
run "require 'yard'"
- expect(out).to eq("bundler-#{Bundler::VERSION}\nyard-1.0")
+ expect(out).to include("bundler-#{Bundler::VERSION}").and include("yard-1.0")
+ expect(out).not_to include("activesupport-2.3.5")
end
context "when the ruby stdlib is a substring of Gem.path" do
it "does not reject the stdlib from $LOAD_PATH" do
- substring = "/" + $LOAD_PATH.find {|p| p =~ /vendor_ruby/ }.split("/")[2]
- run "puts 'worked!'", :env => { "GEM_PATH" => substring }
+ substring = "/" + $LOAD_PATH.find {|p| p.include?("vendor_ruby") }.split("/")[2]
+ run "puts 'worked!'", env: { "GEM_PATH" => substring }
expect(out).to eq("worked!")
end
end
@@ -406,36 +408,37 @@ RSpec.describe "Bundler.setup" do
describe "with paths" do
it "activates the gems in the path source" do
- system_gems "rack-1.0.0"
+ system_gems "myrack-1.0.0"
- build_lib "rack", "1.0.0" do |s|
- s.write "lib/rack.rb", "puts 'WIN'"
+ build_lib "myrack", "1.0.0" do |s|
+ s.write "lib/myrack.rb", "puts 'WIN'"
end
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- path "#{lib_path("rack-1.0.0")}" do
- gem "rack"
+ source "https://gem.repo1"
+ path "#{lib_path("myrack-1.0.0")}" do
+ gem "myrack"
end
G
- run "require 'rack'"
+ run "require 'myrack'"
expect(out).to eq("WIN")
end
end
describe "with git" do
before do
- build_git "rack", "1.0.0"
+ build_git "myrack", "1.0.0"
gemfile <<-G
- gem "rack", :git => "#{lib_path("rack-1.0.0")}"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-1.0.0")}"
G
end
it "provides a useful exception when the git repo is not checked out yet" do
- run "1", :raise_on_error => false
- expect(err).to match(/the git source #{lib_path('rack-1.0.0')} is not yet checked out. Please run `bundle install`/i)
+ run "1", raise_on_error: false
+ expect(err).to match(/the git source #{lib_path("myrack-1.0.0")} is not yet checked out. Please run `bundle install`/i)
end
it "does not hit the git binary if the lockfile is available and up to date" do
@@ -444,7 +447,7 @@ RSpec.describe "Bundler.setup" do
break_git!
ruby <<-R
- require '#{lib_dir}/bundler'
+ require 'bundler'
begin
Bundler.setup
@@ -465,7 +468,7 @@ RSpec.describe "Bundler.setup" do
break_git!
ruby <<-R
- require "#{lib_dir}/bundler"
+ require "bundler"
begin
Bundler.setup
@@ -475,126 +478,125 @@ RSpec.describe "Bundler.setup" do
end
R
- run "puts 'FAIL'", :raise_on_error => false
+ run "puts 'FAIL'", raise_on_error: false
expect(err).not_to include "This is not the git you are looking for"
end
it "works even when the cache directory has been deleted" do
- bundle "config --local path vendor/bundle"
bundle :install
- FileUtils.rm_rf vendored_gems("cache")
- expect(the_bundle).to include_gems "rack 1.0.0"
+ FileUtils.rm_r default_cache_path
+ expect(the_bundle).to include_gems "myrack 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 "path vendor/bundle"
bundle :install
with_read_only("#{bundled_app}/**/*") do
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
it "finds git gem when default bundle path becomes read only" do
- bundle "config set --local path .bundle"
+ bundle_config "path .bundle"
bundle "install"
with_read_only("#{bundled_app(".bundle")}/**/*") do
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
end
describe "when specifying local override" do
it "explodes if given path does not exist on runtime" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
bundle :install
- FileUtils.rm_rf(lib_path("local-rack"))
- run "require 'rack'", :raise_on_error => false
- expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/)
+ FileUtils.rm_r(lib_path("local-myrack"))
+ run "require 'myrack'", raise_on_error: false
+ expect(err).to match(/Cannot use local override for myrack-0.8 because #{Regexp.escape(lib_path("local-myrack").to_s)} does not exist/)
end
it "explodes if branch is not given on runtime" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
bundle :install
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}"
G
- run "require 'rack'", :raise_on_error => false
+ run "require 'myrack'", raise_on_error: false
expect(err).to match(/because :branch is not specified in Gemfile/)
end
it "explodes on different branches on runtime" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
bundle :install
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "changed"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "changed"
G
- run "require 'rack'", :raise_on_error => false
- expect(err).to match(/is using branch master but Gemfile specifies changed/)
+ run "require 'myrack'", raise_on_error: false
+ expect(err).to match(/is using branch main but Gemfile specifies changed/)
end
it "explodes on refs with different branches on runtime" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
- FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack"))
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :ref => "main", :branch => "main"
G
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "nonexistant"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :ref => "main", :branch => "nonexistent"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
- run "require 'rack'", :raise_on_error => false
- expect(err).to match(/is using branch master but Gemfile specifies nonexistant/)
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
+ run "require 'myrack'", raise_on_error: false
+ expect(err).to match(/is using branch main but Gemfile specifies nonexistent/)
end
end
describe "when excluding groups" do
it "doesn't change the resolve if --without is used" do
- bundle "config --local without rails"
+ bundle_config "without rails"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport"
group :rails do
@@ -604,13 +606,13 @@ RSpec.describe "Bundler.setup" do
system_gems "activesupport-2.3.5"
- expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => :default
+ expect(the_bundle).to include_gems "activesupport 2.3.2", groups: :default
end
it "remembers --without and does not bail on bare Bundler.setup" do
- bundle "config --local without rails"
+ bundle_config "without rails"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport"
group :rails do
@@ -623,14 +625,80 @@ RSpec.describe "Bundler.setup" do
expect(the_bundle).to include_gems "activesupport 2.3.2"
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 "without development"
+
+ path = bundled_app(File.join("vendor", "foo"))
+ build_lib "foo", path: path
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "activesupport", "2.3.2"
+ gem 'foo', :path => 'vendor/foo', :group => :development
+ G
+
+ FileUtils.rm_r(path)
+
+ ruby "require 'bundler'; Bundler.setup", env: { "DEBUG" => "1" }
+ expect(out).to include("Assuming that source at `vendor/foo` has not changed since fetching its specs errored")
+ expect(out).to include("Found no changes, using resolution from the lockfile")
+ 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 "https://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(out).not_to include("lockfile does not have all gems needed for the current platform")
+ expect(err).to be_empty
+ end
+
+ it "doesn't fail in frozen mode when bundler is a Gemfile dependency" do
+ install_gemfile <<~G
+ source "https://gem.repo4"
+ gem "bundler"
+ G
+
+ bundle "install --verbose", env: { "BUNDLE_FROZEN" => "true" }
+ expect(err).to be_empty
+ end
+
+ it "doesn't re-resolve when deleting dependencies" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "actionpack"
+ G
+
+ install_gemfile <<-G, verbose: true
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ expect(out).to include("Some dependencies were deleted, using a subset of the 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 "without rails"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport"
- group :rack do
- gem "rack"
+ group :myrack do
+ gem "myrack"
end
group :rails do
@@ -638,19 +706,19 @@ RSpec.describe "Bundler.setup" do
end
G
- expect(the_bundle).not_to include_gems "activesupport 2.3.2", :groups => :rack
- expect(the_bundle).to include_gems "rack 1.0.0", :groups => :rack
+ expect(the_bundle).not_to include_gems "activesupport 2.3.2", groups: :myrack
+ expect(the_bundle).to include_gems "myrack 1.0.0", groups: :myrack
end
end
# RubyGems returns loaded_from as a string
it "has loaded_from as a string on all specs" do
build_git "foo"
- build_git "no-gemspec", :gemspec => false
+ build_git "no-gemspec", gemspec: false
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
gem "foo", :git => "#{lib_path("foo-1.0")}"
gem "no-gemspec", "1.0", :git => "#{lib_path("no-gemspec-1.0")}"
G
@@ -664,38 +732,80 @@ RSpec.describe "Bundler.setup" do
expect(out).to be_empty
end
+ it "has gem_dir pointing to local repo" do
+ build_lib "foo", "1.0", path: bundled_app
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gemspec
+ G
+
+ run <<-R
+ puts Gem.loaded_specs['foo'].gem_dir
+ R
+
+ expect(out).to eq(bundled_app.to_s)
+ end
+
it "does not load all gemspecs" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
run <<-R
- File.open(File.join(Gem.dir, "specifications", "broken.gemspec"), "w") do |f|
+ File.open(File.join(Gem.dir, "specifications", "invalid.gemspec"), "w") do |f|
f.write <<-RUBY
# -*- encoding: utf-8 -*-
-# stub: broken 1.0.0 ruby lib
+# stub: invalid 1.0.0 ruby lib
Gem::Specification.new do |s|
- s.name = "broken"
+ s.name = "invalid"
s.version = "1.0.0"
- raise "BROKEN GEMSPEC"
+ s.authors = ["Invalid Author"]
+ s.files = ["lib/invalid.rb"]
+ s.add_dependency "nonexistent-gem", "~> 999.999.999"
+ s.validate!
end
RUBY
end
R
run <<-R
- puts "WIN"
+ File.open(File.join(Gem.dir, "specifications", "invalid-ext.gemspec"), "w") do |f|
+ f.write <<-RUBY
+# -*- encoding: utf-8 -*-
+# stub: invalid-ext 1.0.0 ruby lib
+# stub: a.ext\\0b.ext
+
+Gem::Specification.new do |s|
+ s.name = "invalid-ext"
+ s.version = "1.0.0"
+ s.authors = ["Invalid Author"]
+ s.files = ["lib/invalid.rb"]
+ s.required_ruby_version = "~> 0.8.0"
+ s.validate!
+end
+ RUBY
+ end
+ # Need to write the gem.build_complete file,
+ # otherwise the full spec is loaded to check the installed_by_version
+ extensions_dir = Gem.default_ext_dir_for(Gem.dir) || File.join(Gem.dir, "extensions", Gem::Platform.local.to_s, Gem.extension_api_version)
+ Bundler::FileUtils.mkdir_p(File.join(extensions_dir, "invalid-ext-1.0.0"))
+ File.open(File.join(extensions_dir, "invalid-ext-1.0.0", "gem.build_complete"), "w") {}
R
- expect(out).to eq("WIN")
+ run <<-R
+ puts "Success"
+ R
+
+ expect(out).to eq("Success")
end
it "ignores empty gem paths" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
ENV["GEM_HOME"] = ""
@@ -704,41 +814,80 @@ end
expect(err).to be_empty
end
- describe "$MANPATH" do
- before do
+ it "can require rubygems without warnings, when using a local cache", :truffleruby do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "package"
+ bundle %(exec ruby -w -e "require 'rubygems'")
+
+ expect(err).to be_empty
+ end
+
+ 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 "https://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 "https://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 "https://gem.repo4"
+ gem "with_man_overriding_system_man"
+ G
+
+ run <<~RUBY
+ puts ENV['MANPATH']
+ require "open3"
+ puts Open3.capture2e({ "LC_ALL" => "C" }, "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
@@ -752,7 +901,7 @@ end
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
gem "requirepaths", :require => nil
G
@@ -760,25 +909,18 @@ end
expect(out).to eq("yay")
end
- it "should clean $LOAD_PATH properly", :ruby_repo do
+ it "should clean $LOAD_PATH properly" do
gem_name = "very_simple_binary"
full_gem_name = gem_name + "-1.0"
- ext_dir = File.join(tmp("extensions", full_gem_name))
system_gems full_gem_name
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
G
ruby <<-R
- s = Gem::Specification.find_by_name '#{gem_name}'
- s.extension_dir = '#{ext_dir}'
-
- # Don't build extensions.
- s.class.send(:define_method, :build_extensions) { nil }
-
- require '#{lib_dir}/bundler'
+ require 'bundler'
gem '#{gem_name}'
puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} >= 2
@@ -793,23 +935,21 @@ end
context "with bundler is located in symlinked GEM_HOME" do
let(:gem_home) { Dir.mktmpdir }
- let(:symlinked_gem_home) { Tempfile.new("gem_home").path }
+ let(:symlinked_gem_home) { tmp("gem_home-symlink").to_s }
let(:full_name) { "bundler-#{Bundler::VERSION}" }
before do
- skip "symlink destination exists" if Gem.win_platform?
-
- FileUtils.ln_sf(gem_home, symlinked_gem_home)
+ File.symlink(gem_home, symlinked_gem_home)
gems_dir = File.join(gem_home, "gems")
specifications_dir = File.join(gem_home, "specifications")
Dir.mkdir(gems_dir)
Dir.mkdir(specifications_dir)
- FileUtils.ln_s(source_root, File.join(gems_dir, full_name))
+ File.symlink(source_root, File.join(gems_dir, full_name))
gemspec_content = File.binread(gemspec).
sub("Bundler::VERSION", %("#{Bundler::VERSION}")).
- lines.reject {|line| line =~ %r{lib/bundler/version} }.join
+ lines.reject {|line| line.include?("lib/bundler/version") }.join
File.open(File.join(specifications_dir, "#{full_name}.gemspec"), "wb") do |f|
f.write(gemspec_content)
@@ -817,9 +957,9 @@ 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 'https://gem.repo1'"
- ruby <<-R, :env => { "GEM_PATH" => symlinked_gem_home }
+ ruby <<-R, env: { "GEM_PATH" => symlinked_gem_home }
TracePoint.trace(:class) do |tp|
if tp.path.include?("bundler") && !tp.path.start_with?("#{source_root}")
puts "OMG. Defining a class from another bundler at \#{tp.path}:\#{tp.lineno}"
@@ -834,17 +974,17 @@ end
end
it "does not reveal system gems even when Gem.refresh is called" do
- system_gems "rack-1.0.0"
+ system_gems "myrack-1.0.0"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport"
G
run <<-R
- puts Bundler.rubygems.all_specs.map(&:name)
+ puts Bundler.rubygems.installed_specs.map(&:name)
Gem.refresh
- puts Bundler.rubygems.all_specs.map(&:name)
+ puts Bundler.rubygems.installed_specs.map(&:name)
R
expect(out).to eq("activesupport\nbundler\nactivesupport\nbundler")
@@ -859,17 +999,18 @@ end
it "should resolve paths relative to the Gemfile" do
path = bundled_app(File.join("vendor", "foo"))
- build_lib "foo", :path => path
+ build_lib "foo", path: path
# If the .gemspec exists, then Bundler handles the path differently.
# See Source::Path.load_spec_files for details.
FileUtils.rm(File.join(path, "foo.gemspec"))
install_gemfile <<-G
+ source "https://gem.repo1"
gem 'foo', '1.2.3', :path => 'vendor/foo'
G
- run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, :dir => bundled_app.parent
+ run <<-R, env: { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, dir: bundled_app.parent
require 'foo'
R
expect(err).to be_empty
@@ -879,19 +1020,20 @@ end
relative_path = File.join("vendor", Dir.pwd.gsub(/^#{filesystem_root}/, ""))
absolute_path = bundled_app(relative_path)
FileUtils.mkdir_p(absolute_path)
- build_lib "foo", :path => absolute_path
+ build_lib "foo", path: absolute_path
# If the .gemspec exists, then Bundler handles the path differently.
# See Source::Path.load_spec_files for details.
FileUtils.rm(File.join(absolute_path, "foo.gemspec"))
gemfile <<-G
+ source "https://gem.repo1"
gem 'foo', '1.2.3', :path => '#{relative_path}'
G
bundle :install
- run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, :dir => bundled_app.parent
+ run <<-R, env: { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, dir: bundled_app.parent
require 'foo'
R
@@ -901,9 +1043,10 @@ end
describe "with git gems that don't have gemspecs" do
before :each do
- build_git "no_gemspec", :gemspec => false
+ build_git "no_gemspec", gemspec: false
install_gemfile <<-G
+ source "https://gem.repo1"
gem "no_gemspec", "1.0", :git => "#{lib_path("no_gemspec-1.0")}"
G
end
@@ -920,10 +1063,10 @@ end
describe "with bundled and system gems" do
before :each do
- system_gems "rack-1.0.0"
+ system_gems "myrack-1.0.0"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
+ source "https://gem.repo1"
gem "activesupport", "2.3.5"
G
@@ -932,7 +1075,7 @@ end
it "does not pull in system gems" do
run <<-R
begin;
- require 'rack'
+ require 'myrack'
rescue LoadError
puts 'WIN'
end
@@ -954,13 +1097,13 @@ end
it "raises an exception if gem is used to invoke a system gem not in the bundle" do
run <<-R
begin
- gem 'rack'
+ gem 'myrack'
rescue LoadError => e
puts e.message
end
R
- expect(out).to eq("rack is not part of the bundle. Add it to your Gemfile.")
+ expect(out).to eq("myrack is not part of the bundle. Add it to your Gemfile.")
end
it "sets GEM_HOME appropriately" do
@@ -971,12 +1114,12 @@ end
describe "with system gems in the bundle" do
before :each do
- bundle "config set path.system true"
- system_gems "rack-1.0.0"
+ bundle_config "path.system true"
+ system_gems "myrack-1.0.0"
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", "1.0.0"
+ source "https://gem.repo1"
+ gem "myrack", "1.0.0"
gem "activesupport", "2.3.5"
G
end
@@ -990,7 +1133,7 @@ end
describe "with a gemspec that requires other files" do
before :each do
- build_git "bar", :gemspec => false do |s|
+ 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'
@@ -1006,6 +1149,7 @@ end
end
gemfile <<-G
+ source "https://gem.repo1"
gem "bar", :git => "#{lib_path("bar-1.0")}"
G
end
@@ -1019,14 +1163,13 @@ end
it "error intelligently if the gemspec has a LoadError" do
skip "whitespace issue?" if Gem.win_platform?
- ref = update_git "bar", :gemspec => false do |s|
+ ref = update_git "bar", gemspec: false do |s|
s.write "bar.gemspec", "require 'foobarbaz'"
end.ref_for("HEAD")
- bundle :install, :raise_on_error => false
+ bundle :install, raise_on_error: false
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'"
)
@@ -1036,11 +1179,11 @@ end
bundle "install"
ruby <<-RUBY
- require '#{lib_dir}/bundler'
+ require 'bundler'
bundler_module = class << Bundler; self; end
bundler_module.send(:remove_method, :require)
def Bundler.require(path)
- raise "LOSE"
+ raise StandardError, "didn't use binding from top level"
end
Bundler.load
RUBY
@@ -1053,6 +1196,7 @@ end
describe "when Bundler is bundled" do
it "doesn't blow up" do
install_gemfile <<-G
+ source "https://gem.repo1"
gem "bundler", :path => "#{root}"
G
@@ -1063,56 +1207,59 @@ end
describe "when BUNDLED WITH" do
def lock_with(bundler_version = nil)
- lock = <<-L
+ lock = <<~L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack
L
if bundler_version
- lock += "\n BUNDLED WITH\n #{bundler_version}\n"
+ lock += "\nBUNDLED WITH\n #{bundler_version}\n"
end
lock
end
before do
+ bundle_config "path.system true"
+
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
end
context "is not present" do
it "does not change the lock" do
lockfile lock_with(nil)
- ruby "require '#{lib_dir}/bundler/setup'"
- lockfile_should_be lock_with(nil)
+ ruby "require 'bundler/setup'"
+ expect(lockfile).to eq lock_with(nil)
end
end
context "is newer" do
it "does not change the lock or warn" do
lockfile lock_with(Bundler::VERSION.succ)
- ruby "require '#{lib_dir}/bundler/setup'"
+ 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
+ system_gems "bundler-1.10.1"
lockfile lock_with("1.10.1")
- ruby "require '#{lib_dir}/bundler/setup'"
- lockfile_should_be lock_with("1.10.1")
+ ruby "require 'bundler/setup'"
+ expect(lockfile).to eq lock_with("1.10.1")
end
end
end
@@ -1121,27 +1268,32 @@ end
let(:ruby_version) { nil }
def lock_with(ruby_version = nil)
- lock = <<-L
+ checksums = checksums_section do |c|
+ c.checksum gem_repo1, "myrack", "1.0.0"
+ end
+
+ lock = <<~L
GEM
- remote: #{file_uri_for(gem_repo1)}/
+ remote: https://gem.repo1/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
- rack
+ myrack
+ #{checksums}
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}
+ #{Bundler::VERSION}
L
lock
@@ -1150,22 +1302,27 @@ end
before do
install_gemfile <<-G
ruby ">= 0"
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
lockfile lock_with(ruby_version)
end
context "is not present" do
- it "does not change the lock" do
- expect { ruby "require '#{lib_dir}/bundler/setup'" }.not_to change { lockfile }
+ # Skipped on ruby-core because `ruby "require 'bundler/setup'"` does not
+ # activate bundler as a gem there, so Source::Metadata falls back to a
+ # synthetic spec whose cache_file does not exist on disk and
+ # LockfileGenerator#bundler_checksum drops the bundler checksum, while
+ # the on-disk lockfile still has it.
+ it "does not change the lock", :ruby_repo do
+ expect { ruby "require 'bundler/setup'" }.not_to change { lockfile }
end
end
context "is newer" do
let(:ruby_version) { "5.5.5" }
it "does not change the lock or warn" do
- expect { ruby "require '#{lib_dir}/bundler/setup'" }.not_to change { lockfile }
+ expect { ruby "require 'bundler/setup'" }.not_to change { lockfile }
expect(out).to be_empty
expect(err).to be_empty
end
@@ -1174,16 +1331,49 @@ end
context "is older" do
let(:ruby_version) { "1.0.0" }
it "does not change the lock" do
- expect { ruby "require '#{lib_dir}/bundler/setup'" }.not_to change { lockfile }
+ expect { ruby "require 'bundler/setup'" }.not_to change { lockfile }
end
end
end
describe "with gemified standard libraries" do
+ it "does not load Digest", :ruby_repo do
+ 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_dependency 'digest'
+ end
+ G
+ end
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+
+ bundle :install, env: { "BUNDLE_LOCKFILE_CHECKSUMS" => "false" }
+
+ ruby <<-RUBY, artifice: nil
+ require 'bundler/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 ""
+ gemfile "source 'https://gem.repo1'"
ruby <<-RUBY
- require '#{lib_dir}/bundler/setup'
+ require 'bundler/setup'
puts defined?(Psych::VERSION) ? Psych::VERSION : "undefined"
require 'psych'
puts Psych::VERSION
@@ -1194,9 +1384,9 @@ end
end
it "does not load openssl" do
- install_gemfile ""
- ruby <<-RUBY
- require "#{lib_dir}/bundler/setup"
+ install_gemfile "source 'https://gem.repo1'"
+ ruby <<-RUBY, artifice: nil
+ require "bundler/setup"
puts defined?(OpenSSL) || "undefined"
require "openssl"
puts defined?(OpenSSL) || "undefined"
@@ -1204,19 +1394,62 @@ end
expect(out).to eq("undefined\nconstant")
end
+ it "does not load uri while reading gemspecs", rubygems: ">= 3.6.0.dev" do
+ Dir.mkdir bundled_app("test")
+
+ create_file(bundled_app("test/test.gemspec"), <<-G)
+ Gem::Specification.new do |s|
+ s.name = "test"
+ s.version = "1.0.0"
+ s.summary = "test"
+ s.authors = ['John Doe']
+ s.homepage = 'https://example.com'
+ end
+ G
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "test", path: "#{bundled_app("test")}"
+ G
+
+ ruby <<-RUBY, artifice: nil
+ require "bundler/setup"
+ puts defined?(URI) || "undefined"
+ require "uri"
+ puts defined?(URI) || "undefined"
+ RUBY
+ expect(out).to eq("undefined\nconstant")
+ end
+
+ it "activates default gems when they are part of the bundle, but not installed explicitly", :ruby_repo do
+ default_delegate_version = ruby "gem 'delegate'; require 'delegate'; puts Delegator::VERSION"
+
+ build_repo2 do
+ build_gem "delegate", default_delegate_version
+ end
+
+ gemfile "source \"https://gem.repo2\"; gem 'delegate'"
+
+ ruby <<-RUBY
+ require "bundler/setup"
+ require "delegate"
+ puts defined?(::Delegator) ? "Delegator defined" : "Delegator undefined"
+ RUBY
+
+ expect(out).to eq("Delegator defined")
+ expect(err).to be_empty
+ end
+
describe "default gem activation" do
let(:exemptions) do
- exempts = if Gem::Version.new(Gem::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 = %w[did_you_mean bundler uri pathname]
+ 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 << "syntax_suggest" # added in Ruby 3.2 as a default gem
exempts
end
- let(:activation_warning_hack) { strip_whitespace(<<-RUBY) }
+ let(:activation_warning_hack) { <<~RUBY }
require #{spec_dir.join("support/hax").to_s.dump}
Gem::Specification.send(:alias_method, :bundler_spec_activate, :activate)
@@ -1236,7 +1469,7 @@ end
"-r#{bundled_app("activation_warning_hack.rb")} #{ENV["RUBYOPT"]}"
end
- let(:code) { strip_whitespace(<<-RUBY) }
+ let(:code) { <<~RUBY }
require "pp"
loaded_specs = Gem.loaded_specs.dup
#{exemptions.inspect}.each {|s| loaded_specs.delete(s) }
@@ -1250,25 +1483,25 @@ end
RUBY
it "activates no gems with -rbundler/setup" do
- install_gemfile ""
- ruby code, :env => { "RUBYOPT" => activation_warning_hack_rubyopt + " -r#{lib_dir}/bundler/setup" }
+ install_gemfile "source 'https://gem.repo1'"
+ ruby code, env: { "RUBYOPT" => activation_warning_hack_rubyopt + " -rbundler/setup" }, artifice: nil
expect(out).to eq("{}")
end
it "activates no gems with bundle exec" do
- install_gemfile ""
+ install_gemfile "source 'https://gem.repo1'"
create_file("script.rb", code)
- bundle "exec ruby ./script.rb", :env => { "RUBYOPT" => activation_warning_hack_rubyopt }
+ bundle "exec ruby ./script.rb", env: { "RUBYOPT" => activation_warning_hack_rubyopt }
expect(out).to eq("{}")
end
it "activates no gems with bundle exec that is loaded" do
skip "not executable" if Gem.win_platform?
- install_gemfile ""
+ install_gemfile "source 'https://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 }
+ bundle "exec ./script.rb", env: { "RUBYOPT" => activation_warning_hack_rubyopt }
expect(out).to eq("{}")
end
@@ -1277,14 +1510,14 @@ end
build_gem "net-http-pipeline", "1.0.1"
end
- system_gems "net-http-pipeline-1.0.1", :gem_repo => gem_repo4
+ system_gems "net-http-pipeline-1.0.1", gem_repo: gem_repo4
gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem "net-http-pipeline", "1.0.1"
G
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
@@ -1293,31 +1526,8 @@ end
expect(out).to eq("The Gemfile's dependencies are satisfied")
end
- # bundler respects paths specified direclty in RUBYLIB or RUBYOPT, and
- # that happens when running ruby from the ruby-core setup. To
- # workaround, we manually remove those for these tests when they would
- # override the default gem.
- def load_path_exclusions_hack_for(name)
- if ruby_core?
- ext_folder = source_root.join(".ext/common")
- require_name = name.tr("-", "/")
- if File.exist?(ext_folder.join("#{require_name}.rb"))
- { :exclude_from_load_path => ext_folder.to_s }
- else
- lib_folder = source_root.join("lib")
- if File.exist?(lib_folder.join("#{require_name}.rb"))
- { :exclude_from_load_path => lib_folder.to_s }
- else
- {}
- end
- end
- else
- {}
- end
- end
-
Gem::Specification.select(&:default_gem?).map(&:name).each do |g|
- it "activates newer versions of #{g}" do
+ it "activates newer versions of #{g}", :ruby_repo do
skip if exemptions.include?(g)
build_repo4 do
@@ -1325,15 +1535,14 @@ end
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem "#{g}", "999999"
G
- opts = { :env => { "RUBYOPT" => activation_warning_hack_rubyopt } }
- expect(the_bundle).to include_gem("#{g} 999999", opts.merge(load_path_exclusions_hack_for(g)))
+ expect(the_bundle).to include_gem("#{g} 999999", env: { "RUBYOPT" => activation_warning_hack_rubyopt }, artifice: nil)
end
- it "activates older versions of #{g}" do
+ it "activates older versions of #{g}", :ruby_repo do
skip if exemptions.include?(g)
build_repo4 do
@@ -1341,69 +1550,161 @@ end
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
gem "#{g}", "0.0.0.a"
G
- opts = { :env => { "RUBYOPT" => activation_warning_hack_rubyopt } }
- expect(the_bundle).to include_gem("#{g} 0.0.0.a", opts.merge(load_path_exclusions_hack_for(g)))
+ expect(the_bundle).to include_gem("#{g} 0.0.0.a", env: { "RUBYOPT" => activation_warning_hack_rubyopt }, artifice: nil)
end
end
end
end
describe "after setup" do
- it "allows calling #gem on random objects", :bundler => "< 3" do
+ it "keeps Kernel#gem private" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- ruby <<-RUBY
- require "#{lib_dir}/bundler/setup"
- Object.new.gem "rack"
- puts Gem.loaded_specs["rack"].full_name
+ ruby <<-RUBY, raise_on_error: false
+ require "bundler/setup"
+ Object.new.gem "myrack"
+ puts "FAIL"
RUBY
- expect(out).to eq("rack-1.0.0")
+ expect(stdboth).not_to include "FAIL"
+ expect(err).to match(/private method [`']gem'/)
end
- it "keeps Kernel#gem private", :bundler => "3" do
+ it "keeps Kernel#require private" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ source "https://gem.repo1"
+ gem "myrack"
G
- ruby <<-RUBY, :raise_on_error => false
- require "#{lib_dir}/bundler/setup"
- Object.new.gem "rack"
+ ruby <<-RUBY, raise_on_error: false
+ require "bundler/setup"
+ Object.new.require "myrack"
puts "FAIL"
RUBY
- expect(last_command.stdboth).not_to include "FAIL"
- expect(err).to include "private method `gem'"
+ expect(stdboth).not_to include "FAIL"
+ expect(err).to match(/private method [`']require'/)
end
- it "keeps Kernel#require private" do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
+ 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 "https://gem.repo1"
+ gem "yard"
+ gem "myrack", :group => :test
G
- ruby <<-RUBY, :raise_on_error => false
- require "#{lib_dir}/bundler/setup"
- Object.new.require "rack"
- puts "FAIL"
+ 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("myrack, 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 'https://gem.repo1'"
+ ruby <<-RUBY
+ require "json"
+ require "bundler/setup"
+ require "json"
RUBY
- expect(last_command.stdboth).not_to include "FAIL"
- expect(err).to include "private method `require'"
+ expect(err).to be_empty
+ end
+ end
+
+ it "does not undo the Kernel.require decorations", rubygems: ">= 3.4.6" do
+ install_gemfile "source 'https://gem.repo1'"
+ script = bundled_app("bin/script")
+ create_file(script, <<~RUBY)
+ module Kernel
+ module_function
+
+ alias_method :require_before_extra_monkeypatches, :require
+
+ def require(path)
+ puts "requiring \#{path} used the monkeypatch"
+
+ require_before_extra_monkeypatches(path)
+ end
+ end
+
+ require "bundler/setup"
+
+ require "foo"
+ RUBY
+
+ sys_exec "#{Gem.ruby} #{script}", raise_on_error: false
+ expect(out).to include("requiring foo used the monkeypatch")
+ end
+
+ it "performs an automatic bundle install" do
+ build_repo4 do
+ build_gem "myrack", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack", :group => :test
+ G
+
+ bundle_config "auto_install 1"
+
+ ruby <<-RUBY, artifice: "compact_index"
+ require 'bundler/setup'
+ RUBY
+ expect(err).to be_empty
+ expect(out).to include("Installing myrack 1.0.0")
+ end
+
+ context "in a read-only filesystem" do
+ before do
+ gemfile <<-G
+ source "https://gem.repo4"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+
+ PLATFORMS
+ x86_64-darwin-19
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
- it "takes care of requiring rubygems" do
- sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler/setup -e'puts true'", :env => { "RUBYOPT" => opt_add("--disable=gems", ENV["RUBYOPT"]) })
+ it "should fail loudly if the lockfile platforms don't include the current platform" do
+ simulate_platform "x86_64-linux" do
+ ruby <<-RUBY, raise_on_error: false, env: { "BUNDLER_SPEC_READ_ONLY" => "true", "BUNDLER_FORCE_TTY" => "true" }
+ require "bundler/setup"
+ RUBY
+ end
- expect(last_command.stdboth).to eq("true")
+ expect(err).to include("Your lockfile is missing the current platform, but can't be updated because file system is read-only")
end
end
end
diff --git a/spec/bundler/runtime/with_unbundled_env_spec.rb b/spec/bundler/runtime/with_unbundled_env_spec.rb
deleted file mode 100644
index 67a25f6ff2..0000000000
--- a/spec/bundler/runtime/with_unbundled_env_spec.rb
+++ /dev/null
@@ -1,290 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "Bundler.with_env helpers" do
- def bundle_exec_ruby(args, options = {})
- build_bundler_context options
- bundle "exec '#{Gem.ruby}' #{args}", options
- end
-
- def build_bundler_context(options = {})
- bundle "config set path vendor/bundle"
- gemfile ""
- bundle "install", options
- end
-
- def run_bundler_script(env, script)
- system(env, "ruby", "-I#{lib_dir}", "-rbundler", script.to_s)
- end
-
- describe "Bundler.original_env" do
- it "should return the PATH present before bundle was activated" do
- create_file("source.rb", <<-RUBY)
- print Bundler.original_env["PATH"]
- RUBY
- path = `getconf PATH`.strip + "#{File::PATH_SEPARATOR}/foo"
- with_path_as(path) do
- bundle_exec_ruby(bundled_app("source.rb").to_s)
- expect(last_command.stdboth).to eq(path)
- end
- end
-
- it "should return the GEM_PATH present before bundle was activated" do
- create_file("source.rb", <<-RUBY)
- print Bundler.original_env['GEM_PATH']
- RUBY
- gem_path = ENV["GEM_PATH"] + "#{File::PATH_SEPARATOR}/foo"
- with_gem_path_as(gem_path) do
- bundle_exec_ruby(bundled_app("source.rb").to_s)
- expect(last_command.stdboth).to eq(gem_path)
- end
- end
-
- it "works with nested bundle exec invocations", :ruby_repo do
- create_file("exe.rb", <<-'RUBY')
- count = ARGV.first.to_i
- exit if count < 0
- STDERR.puts "#{count} #{ENV["PATH"].end_with?("#{File::PATH_SEPARATOR}/foo")}"
- if count == 2
- ENV["PATH"] = "#{ENV["PATH"]}#{File::PATH_SEPARATOR}/foo"
- end
- exec(Gem.ruby, __FILE__, (count - 1).to_s)
- RUBY
- path = `getconf PATH`.strip + File::PATH_SEPARATOR + File.dirname(Gem.ruby)
- with_path_as(path) do
- build_bundler_context
- bundle_exec_ruby("#{bundled_app("exe.rb")} 2")
- end
- expect(err).to eq <<-EOS.strip
-2 false
-1 true
-0 true
- EOS
- end
-
- it "removes variables that bundler added", :ruby_repo do
- # Simulate bundler has not yet been loaded
- ENV.replace(ENV.to_hash.delete_if {|k, _v| k.start_with?(Bundler::EnvironmentPreserver::BUNDLER_PREFIX) })
-
- original = ruby('puts ENV.to_a.map {|e| e.join("=") }.sort.join("\n")')
- create_file("source.rb", <<-RUBY)
- puts Bundler.original_env.to_a.map {|e| e.join("=") }.sort.join("\n")
- RUBY
- bundle_exec_ruby bundled_app("source.rb")
- expect(out).to eq original
- end
- end
-
- shared_examples_for "an unbundling helper" do
- it "should delete BUNDLE_PATH" do
- create_file("source.rb", <<-RUBY)
- print #{modified_env}.has_key?('BUNDLE_PATH')
- RUBY
- ENV["BUNDLE_PATH"] = "./foo"
- bundle_exec_ruby bundled_app("source.rb")
- expect(last_command.stdboth).to include "false"
- end
-
- it "should remove '-rbundler/setup' from RUBYOPT" do
- create_file("source.rb", <<-RUBY)
- print #{modified_env}['RUBYOPT']
- RUBY
- ENV["RUBYOPT"] = "-W2 -rbundler/setup #{ENV["RUBYOPT"]}"
- simulate_bundler_version_when_missing_prerelease_default_gem_activation do
- bundle_exec_ruby bundled_app("source.rb")
- end
- expect(last_command.stdboth).not_to include("-rbundler/setup")
- end
-
- it "should restore RUBYLIB", :ruby_repo do
- create_file("source.rb", <<-RUBY)
- print #{modified_env}['RUBYLIB']
- RUBY
- ENV["RUBYLIB"] = lib_dir.to_s + File::PATH_SEPARATOR + "/foo"
- ENV["BUNDLER_ORIG_RUBYLIB"] = lib_dir.to_s + File::PATH_SEPARATOR + "/foo-original"
- bundle_exec_ruby bundled_app("source.rb")
- expect(last_command.stdboth).to include("/foo-original")
- end
-
- it "should restore the original MANPATH" do
- create_file("source.rb", <<-RUBY)
- print #{modified_env}['MANPATH']
- RUBY
- ENV["MANPATH"] = "/foo"
- ENV["BUNDLER_ORIG_MANPATH"] = "/foo-original"
- bundle_exec_ruby bundled_app("source.rb")
- expect(last_command.stdboth).to include("/foo-original")
- end
- end
-
- describe "Bundler.unbundled_env" do
- let(:modified_env) { "Bundler.unbundled_env" }
-
- it_behaves_like "an unbundling helper"
- end
-
- describe "Bundler.clean_env", :bundler => 2 do
- let(:modified_env) { "Bundler.clean_env" }
-
- it_behaves_like "an unbundling helper"
- end
-
- describe "Bundler.with_original_env" do
- it "should set ENV to original_env in the block" do
- expected = Bundler.original_env
- actual = Bundler.with_original_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) }
- expect(actual).to eq(expected)
- end
-
- it "should restore the environment after execution" do
- Bundler.with_original_env do
- ENV["FOO"] = "hello"
- end
-
- expect(ENV).not_to have_key("FOO")
- end
- end
-
- describe "Bundler.with_clean_env", :bundler => 2 do
- it "should set ENV to unbundled_env in the block" do
- expected = Bundler.unbundled_env
-
- actual = Bundler.ui.silence do
- Bundler.with_clean_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) }
- end
-
- expect(actual).to eq(expected)
- end
-
- it "should restore the environment after execution" do
- Bundler.ui.silence do
- Bundler.with_clean_env { ENV["FOO"] = "hello" }
- end
-
- expect(ENV).not_to have_key("FOO")
- end
- end
-
- describe "Bundler.with_unbundled_env" do
- it "should set ENV to unbundled_env in the block" do
- expected = Bundler.unbundled_env
- actual = Bundler.with_unbundled_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) }
- expect(actual).to eq(expected)
- end
-
- it "should restore the environment after execution" do
- Bundler.with_unbundled_env do
- ENV["FOO"] = "hello"
- end
-
- expect(ENV).not_to have_key("FOO")
- end
- end
-
- describe "Bundler.original_system" do
- before do
- create_file("source.rb", <<-'RUBY')
- Bundler.original_system("ruby", "-e", "exit(42) if ENV['BUNDLE_FOO'] == 'bar'")
-
- exit $?.exitstatus
- RUBY
- end
-
- it "runs system inside with_original_env" do
- run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
- expect($?.exitstatus).to eq(42)
- end
- end
-
- describe "Bundler.clean_system", :bundler => 2 do
- before do
- create_file("source.rb", <<-'RUBY')
- Bundler.ui.silence { Bundler.clean_system("ruby", "-e", "exit(42) unless ENV['BUNDLE_FOO'] == 'bar'") }
-
- exit $?.exitstatus
- RUBY
- end
-
- it "runs system inside with_clean_env" do
- run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
- expect($?.exitstatus).to eq(42)
- end
- end
-
- describe "Bundler.unbundled_system" do
- before do
- create_file("source.rb", <<-'RUBY')
- Bundler.unbundled_system("ruby", "-e", "exit(42) unless ENV['BUNDLE_FOO'] == 'bar'")
-
- exit $?.exitstatus
- RUBY
- end
-
- it "runs system inside with_unbundled_env" do
- run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
- expect($?.exitstatus).to eq(42)
- end
- end
-
- describe "Bundler.original_exec" do
- before do
- create_file("source.rb", <<-'RUBY')
- Process.fork do
- exit Bundler.original_exec(%(test "\$BUNDLE_FOO" = "bar"))
- end
-
- _, status = Process.wait2
-
- exit(status.exitstatus)
- RUBY
- end
-
- it "runs exec inside with_original_env" do
- skip "Fork not implemented" if Gem.win_platform?
-
- run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
- expect($?.exitstatus).to eq(0)
- end
- end
-
- describe "Bundler.clean_exec", :bundler => 2 do
- before do
- create_file("source.rb", <<-'RUBY')
- Process.fork do
- exit Bundler.ui.silence { Bundler.clean_exec(%(test "\$BUNDLE_FOO" = "bar")) }
- end
-
- _, status = Process.wait2
-
- exit(status.exitstatus)
- RUBY
- end
-
- it "runs exec inside with_clean_env" do
- skip "Fork not implemented" if Gem.win_platform?
-
- run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
- expect($?.exitstatus).to eq(1)
- end
- end
-
- describe "Bundler.unbundled_exec" do
- before do
- create_file("source.rb", <<-'RUBY')
- Process.fork do
- exit Bundler.unbundled_exec(%(test "\$BUNDLE_FOO" = "bar"))
- end
-
- _, status = Process.wait2
-
- exit(status.exitstatus)
- RUBY
- end
-
- it "runs exec inside with_clean_env" do
- skip "Fork not implemented" if Gem.win_platform?
-
- run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
- expect($?.exitstatus).to eq(1)
- end
- end
-end
diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb
index 7ed5bda8e6..27ddc6a771 100644
--- a/spec/bundler/spec_helper.rb
+++ b/spec/bundler/spec_helper.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "bundler/psyched_yaml"
+require "psych"
require "bundler/vendored_fileutils"
require "bundler/vendored_uri"
require "digest"
@@ -9,20 +9,67 @@ if File.expand_path(__FILE__) =~ %r{([^\w/\.:\-])}
abort "The bundler specs cannot be run from a path that contains special characters (particularly #{$1.inspect})"
end
+# Bundler CLI will have different help text depending on whether any of these
+# variables is set, since the `-e` flag `bundle gem` with require an explicit
+# value if they are not set, but will use their value by default if set. So make
+# sure they are `nil` before loading bundler to get a consistent help text,
+# since some tests rely on that.
+ENV["EDITOR"] = nil
+ENV["VISUAL"] = nil
+ENV["BUNDLER_EDITOR"] = nil
require "bundler"
+
+# If we use shared GEM_HOME and install multiple versions, it may cause
+# unexpected test failures.
+gem "diff-lcs", "< 2.0"
+
require "rspec/core"
require "rspec/expectations"
require "rspec/mocks"
+require "rspec/support/differ"
+gem "rubygems-generate_index"
+require "rubygems/indexer"
require_relative "support/builders"
+require_relative "support/checksums"
require_relative "support/filters"
require_relative "support/helpers"
require_relative "support/indexes"
require_relative "support/matchers"
require_relative "support/permissions"
require_relative "support/platforms"
-require_relative "support/sometimes"
-require_relative "support/sudo"
+require_relative "support/shards"
+
+begin
+ raise LoadError if File.exist?(File.expand_path("../../lib/bundler/bundler.gemspec", __dir__))
+
+ gem "simplecov_json_formatter"
+ require "simplecov"
+
+ SimpleCov.start do
+ command_name "bundler:#{Process.pid}"
+ root File.expand_path("../bundler", __dir__)
+ coverage_dir File.expand_path("../coverage", __dir__)
+
+ add_filter "/spec/"
+ add_filter "/test/"
+ add_filter "/lib/rubygems/"
+ add_filter "/lib/bundler/vendor/"
+ add_filter "/tool/"
+ add_filter "/tmp/"
+ add_filter ".gemspec"
+ end
+
+ SimpleCov.print_error_status = false
+ SimpleCov.at_exit do
+ $stdout = File.open(File::NULL, "w")
+ SimpleCov.result.format!
+ ensure
+ $stdout = STDOUT
+ end
+rescue LoadError
+ # SimpleCov is not installed
+end
$debug = false
@@ -34,19 +81,23 @@ end
RSpec.configure do |config|
config.include Spec::Builders
+ config.include Spec::Checksums
config.include Spec::Helpers
config.include Spec::Indexes
config.include Spec::Matchers
config.include Spec::Path
config.include Spec::Platforms
- config.include Spec::Sudo
config.include Spec::Permissions
+ config.include Spec::Shards
# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = ".rspec_status"
config.silence_filter_announcements = !ENV["TEST_ENV_NUMBER"].nil?
+ config.backtrace_exclusion_patterns <<
+ %r{./spec/(spec_helper\.rb|support/.+)}
+
config.disable_monkey_patching!
# Since failures cause us to keep a bunch of long strings in memory, stop
@@ -59,62 +110,83 @@ RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = :expect
+
+ c.max_formatted_output_length = 1000
end
config.mock_with :rspec do |mocks|
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"
+
+ # Disable retry delays in tests to speed them up
+ Bundler::Retry.default_base_delay = 0
+
+ # Simulate bundler has not yet been loaded
+ ENV.replace(ENV.to_hash.delete_if {|k, _v| k.start_with?(Bundler::EnvironmentPreserver::BUNDLER_PREFIX) })
+
+ ENV["BUNDLER_SPEC_RUN"] = "true"
ENV["BUNDLE_USER_CONFIG"] = ENV["BUNDLE_USER_CACHE"] = ENV["BUNDLE_USER_PLUGIN"] = nil
+ ENV["BUNDLE_APP_CONFIG"] = nil
+ ENV["BUNDLE_SILENCE_ROOT_WARNING"] = nil
+ ENV["RUBYGEMS_GEMDEPS"] = nil
+ ENV["XDG_CONFIG_HOME"] = nil
+ ENV["XDG_CACHE_HOME"] = nil
ENV["GEMRC"] = nil
+ # Prevent tests from modifying the user's global git config.
+ # GIT_CONFIG_GLOBAL and GIT_CONFIG_NOSYSTEM are available since Git 2.32.
+ git_version = `git --version`[/(\d+\.\d+\.\d+)/, 1]
+ if Gem::Version.new(git_version) >= Gem::Version.new("2.32")
+ ENV["GIT_CONFIG_GLOBAL"] = File.join(ENV["HOME"], ".gitconfig")
+ ENV["GIT_CONFIG_NOSYSTEM"] = "1"
+ end
+
# Don't wrap output in tests
ENV["THOR_COLUMNS"] = "10000"
- extend(Spec::Helpers)
- system_gems :bundler, :path => pristine_system_gem_path
- end
+ extend(Spec::Builders)
- config.before :all do
build_repo1
- reset_paths!
+ reset!
end
config.around :each do |example|
- begin
- FileUtils.cp_r pristine_system_gem_path, system_gem_path
-
- with_gem_path_as(system_gem_path) do
- Bundler.ui.silence { example.run }
-
- all_output = all_commands_output
- if example.exception && !all_output.empty?
- message = example.exception.message + all_output
- (class << example.exception; self; end).send(:define_method, :message) do
- message
- end
+ default_system_gems
+
+ with_gem_path_as(system_gem_path) do
+ Bundler.ui.silence { example.run }
+
+ all_output = all_commands_output
+ if example.exception && !all_output.empty?
+ message = all_output + "\n" + example.exception.message
+ (class << example.exception; self; end).send(:define_method, :message) do
+ message
end
end
- ensure
- reset!
end
+ ensure
+ reset!
end
- config.after :suite do
- FileUtils.rm_r Spec::Path.pristine_system_gem_path
+ Spec::Shards::EXAMPLE_MAPPINGS.each do |tag, file_paths|
+ file_pattern = Regexp.union(file_paths.map {|path| Regexp.new(Regexp.escape(path) + "$") })
+
+ config.define_derived_metadata(file_path: file_pattern) do |metadata|
+ metadata[tag] = true
+ end
end
+
+ config.before(:context) do |example|
+ metadata = example.class.metadata
+ if metadata[:type] != :aruba && !metadata[:realworld] && metadata.keys.none? {|k| Spec::Shards::EXAMPLE_MAPPINGS.keys.include?(k) }
+ warn "#{metadata[:file_path]} is not assigned to any shard. see spec/support/shards.rb for details."
+ end
+ end unless Spec::Path.ruby_core?
end
diff --git a/spec/bundler/support/activate.rb b/spec/bundler/support/activate.rb
new file mode 100644
index 0000000000..143b77833d
--- /dev/null
+++ b/spec/bundler/support/activate.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require "rubygems"
+Gem.instance_variable_set(:@ruby, ENV["RUBY"]) if ENV["RUBY"]
+
+require_relative "path"
+bundler_gemspec = Spec::Path.loaded_gemspec
+bundler_gemspec.instance_variable_set(:@full_gem_path, Spec::Path.source_root.to_s)
+bundler_gemspec.activate if bundler_gemspec.respond_to?(:activate)
diff --git a/spec/bundler/support/artifice/compact_index.rb b/spec/bundler/support/artifice/compact_index.rb
index 5cf3a79f29..ebc4d0ae5b 100644
--- a/spec/bundler/support/artifice/compact_index.rb
+++ b/spec/bundler/support/artifice/compact_index.rb
@@ -1,120 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-$LOAD_PATH.unshift Dir[Spec::Path.base_system_gems.join("gems/compact_index*/lib")].first.to_s
-require "compact_index"
-
-class CompactIndexAPI < Endpoint
- helpers do
- include Spec::Path
-
- def load_spec(name, version, platform, gem_repo)
- full_name = "#{name}-#{version}"
- full_name += "-#{platform}" if platform != "ruby"
- Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
- end
-
- def etag_response
- response_body = yield
- checksum = Digest(:MD5).hexdigest(response_body)
- return if not_modified?(checksum)
- headers "ETag" => quote(checksum)
- headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
- content_type "text/plain"
- requested_range_for(response_body)
- rescue StandardError => e
- puts e
- puts e.backtrace
- raise
- end
-
- def not_modified?(checksum)
- etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"])
-
- return unless etags.include?(checksum)
- headers "ETag" => quote(checksum)
- status 304
- body ""
- end
-
- def requested_range_for(response_body)
- ranges = Rack::Utils.byte_ranges(env, response_body.bytesize)
-
- if ranges
- status 206
- body ranges.map! {|range| slice_body(response_body, range) }.join
- else
- status 200
- body response_body
- end
- end
-
- def quote(string)
- %("#{string}")
- end
-
- def parse_etags(value)
- value ? value.split(/, ?/).select {|s| s.sub!(/"(.*)"/, '\1') } : []
- end
-
- def slice_body(body, range)
- body.byteslice(range)
- end
-
- def gems(gem_repo = GEM_REPO)
- @gems ||= {}
- @gems[gem_repo] ||= begin
- specs = Bundler::Deprecate.skip_during do
- %w[specs.4.8 prerelease_specs.4.8].map do |filename|
- Marshal.load(File.open(gem_repo.join(filename)).read).map do |name, version, platform|
- load_spec(name, version, platform, gem_repo)
- end
- end.flatten
- end
-
- specs.group_by(&:name).map do |name, versions|
- gem_versions = versions.map do |spec|
- deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d|
- reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ")
- CompactIndex::Dependency.new(d.name, reqs)
- end
- checksum = begin
- Digest(:SHA256).file("#{GEM_REPO}/gems/#{spec.original_name}.gem").base64digest
- rescue StandardError
- nil
- end
- CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
- deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
- end
- CompactIndex::Gem.new(name, gem_versions)
- end
- end
- end
- end
-
- get "/names" do
- etag_response do
- CompactIndex.names(gems.map(&:name))
- end
- end
-
- get "/versions" do
- etag_response do
- file = tmp("versions.list")
- FileUtils.rm_f(file)
- file = CompactIndex::VersionsFile.new(file.to_s)
- file.create(gems)
- file.contents
- end
- end
-
- get "/info/:name" do
- etag_response do
- gem = gems.find {|g| g.name == params[:name] }
- CompactIndex.info(gem ? gem.versions : [])
- end
- end
-end
+require_relative "helpers/compact_index"
+require_relative "helpers/artifice"
Artifice.activate_with(CompactIndexAPI)
diff --git a/spec/bundler/support/artifice/compact_index_api_missing.rb b/spec/bundler/support/artifice/compact_index_api_missing.rb
index 6514fde01e..f771f7d1f0 100644
--- a/spec/bundler/support/artifice/compact_index_api_missing.rb
+++ b/spec/bundler/support/artifice/compact_index_api_missing.rb
@@ -1,18 +1,13 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexApiMissing < CompactIndexAPI
get "/fetch/actual/gem/:id" do
- warn params[:id]
- if params[:id] == "rack-1.0.gemspec.rz"
- halt 404
- else
- File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
- end
+ halt 404
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexApiMissing)
diff --git a/spec/bundler/support/artifice/compact_index_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_basic_authentication.rb
index 775f1a3977..b9115cdd86 100644
--- a/spec/bundler/support/artifice/compact_index_basic_authentication.rb
+++ b/spec/bundler/support/artifice/compact_index_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexBasicAuthentication < CompactIndexAPI
before do
@@ -12,4 +10,6 @@ class CompactIndexBasicAuthentication < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexBasicAuthentication)
diff --git a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
index 1abe64236c..83b147d2ae 100644
--- a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
+++ b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb
@@ -1,16 +1,16 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexChecksumMismatch < CompactIndexAPI
get "/versions" do
- headers "ETag" => quote("123")
+ headers "Repr-Digest" => "sha-256=:ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=:"
headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
content_type "text/plain"
- body ""
+ body "content does not match the checksum"
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexChecksumMismatch)
diff --git a/spec/bundler/support/artifice/compact_index_concurrent_download.rb b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
index 14c31f35a4..5d55b8a72b 100644
--- a/spec/bundler/support/artifice/compact_index_concurrent_download.rb
+++ b/spec/bundler/support/artifice/compact_index_concurrent_download.rb
@@ -1,19 +1,18 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexConcurrentDownload < CompactIndexAPI
get "/versions" do
versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
"localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions")
- # Verify the original (empty) content hasn't been deleted, e.g. on a retry
- File.binread(versions) == "" || raise("Original file should be present and empty")
+ # Verify the original content hasn't been deleted, e.g. on a retry
+ data = File.binread(versions)
+ data == "created_at" || raise("Original file should be present with expected content")
# Verify this is only requested once for a partial download
- env["HTTP_RANGE"] || raise("Missing Range header for expected partial download")
+ env["HTTP_RANGE"] == "bytes=#{data.bytesize - 1}-" || raise("Missing Range header for expected partial download")
# Overwrite the file in parallel, which should be then overwritten
# after a successful download to prevent corruption
@@ -29,4 +28,6 @@ class CompactIndexConcurrentDownload < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexConcurrentDownload)
diff --git a/spec/bundler/support/artifice/compact_index_cooldown.rb b/spec/bundler/support/artifice/compact_index_cooldown.rb
new file mode 100644
index 0000000000..85e3173c98
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_cooldown.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index_cooldown"
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexCooldownAPI)
diff --git a/spec/bundler/support/artifice/compact_index_creds_diff_host.rb b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
index cfe22c7f51..282e9c8961 100644
--- a/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
+++ b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexCredsDiffHost < CompactIndexAPI
helpers do
@@ -26,7 +24,7 @@ class CompactIndexCredsDiffHost < CompactIndexAPI
end
get "/gems/:id" do
- redirect "http://diffhost.com/no/creds/#{params[:id]}"
+ redirect "http://diffhost.test/no/creds/#{params[:id]}"
end
get "/no/creds/:id" do
@@ -36,4 +34,6 @@ class CompactIndexCredsDiffHost < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexCredsDiffHost)
diff --git a/spec/bundler/support/artifice/compact_index_etag_match.rb b/spec/bundler/support/artifice/compact_index_etag_match.rb
new file mode 100644
index 0000000000..6c62166051
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_etag_match.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+class CompactIndexEtagMatch < CompactIndexAPI
+ get "/versions" do
+ raise ArgumentError, "ETag header should be present" unless env["HTTP_IF_NONE_MATCH"]
+ headers "ETag" => env["HTTP_IF_NONE_MATCH"]
+ status 304
+ body ""
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexEtagMatch)
diff --git a/spec/bundler/support/artifice/compact_index_extra.rb b/spec/bundler/support/artifice/compact_index_extra.rb
index cec368276a..cd41b3ecca 100644
--- a/spec/bundler/support/artifice/compact_index_extra.rb
+++ b/spec/bundler/support/artifice/compact_index_extra.rb
@@ -1,37 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
-
-class CompactIndexExtra < CompactIndexAPI
- get "/extra/versions" do
- halt 404
- end
-
- get "/extra/api/v1/dependencies" do
- halt 404
- end
-
- get "/extra/specs.4.8.gz" do
- File.binread("#{gem_repo2}/specs.4.8.gz")
- end
-
- get "/extra/prerelease_specs.4.8.gz" do
- File.binread("#{gem_repo2}/prerelease_specs.4.8.gz")
- end
-
- get "/extra/quick/Marshal.4.8/:id" do
- redirect "/extra/fetch/actual/gem/#{params[:id]}"
- end
-
- get "/extra/fetch/actual/gem/:id" do
- File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
- end
-
- get "/extra/gems/:id" do
- File.binread("#{gem_repo2}/gems/#{params[:id]}")
- end
-end
+require_relative "helpers/compact_index_extra"
+require_relative "helpers/artifice"
Artifice.activate_with(CompactIndexExtra)
diff --git a/spec/bundler/support/artifice/compact_index_extra_api.rb b/spec/bundler/support/artifice/compact_index_extra_api.rb
index 5cc13421a8..8b9d304ab4 100644
--- a/spec/bundler/support/artifice/compact_index_extra_api.rb
+++ b/spec/bundler/support/artifice/compact_index_extra_api.rb
@@ -1,52 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
-
-class CompactIndexExtraApi < CompactIndexAPI
- get "/extra/names" do
- etag_response do
- CompactIndex.names(gems(gem_repo4).map(&:name))
- end
- end
-
- get "/extra/versions" do
- etag_response do
- file = tmp("versions.list")
- FileUtils.rm_f(file)
- file = CompactIndex::VersionsFile.new(file.to_s)
- file.create(gems(gem_repo4))
- file.contents
- end
- end
-
- get "/extra/info/:name" do
- etag_response do
- gem = gems(gem_repo4).find {|g| g.name == params[:name] }
- CompactIndex.info(gem ? gem.versions : [])
- end
- end
-
- get "/extra/specs.4.8.gz" do
- File.binread("#{gem_repo4}/specs.4.8.gz")
- end
-
- get "/extra/prerelease_specs.4.8.gz" do
- File.binread("#{gem_repo4}/prerelease_specs.4.8.gz")
- end
-
- get "/extra/quick/Marshal.4.8/:id" do
- redirect "/extra/fetch/actual/gem/#{params[:id]}"
- end
-
- get "/extra/fetch/actual/gem/:id" do
- File.binread("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}")
- end
-
- get "/extra/gems/:id" do
- File.binread("#{gem_repo4}/gems/#{params[:id]}")
- end
-end
+require_relative "helpers/compact_index_extra_api"
+require_relative "helpers/artifice"
Artifice.activate_with(CompactIndexExtraApi)
diff --git a/spec/bundler/support/artifice/compact_index_extra_api_missing.rb b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
index b9d757c266..df6ede584c 100644
--- a/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
+++ b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index_extra_api"
-
-Artifice.deactivate
+require_relative "helpers/compact_index_extra_api"
class CompactIndexExtraAPIMissing < CompactIndexExtraApi
get "/extra/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class CompactIndexExtraAPIMissing < CompactIndexExtraApi
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexExtraAPIMissing)
diff --git a/spec/bundler/support/artifice/compact_index_extra_missing.rb b/spec/bundler/support/artifice/compact_index_extra_missing.rb
index ff1e47a1bb..255c89afdb 100644
--- a/spec/bundler/support/artifice/compact_index_extra_missing.rb
+++ b/spec/bundler/support/artifice/compact_index_extra_missing.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index_extra"
-
-Artifice.deactivate
+require_relative "helpers/compact_index_extra"
class CompactIndexExtraMissing < CompactIndexExtra
get "/extra/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class CompactIndexExtraMissing < CompactIndexExtra
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexExtraMissing)
diff --git a/spec/bundler/support/artifice/compact_index_forbidden.rb b/spec/bundler/support/artifice/compact_index_forbidden.rb
index 3eebe0fbd8..18c30ed9a2 100644
--- a/spec/bundler/support/artifice/compact_index_forbidden.rb
+++ b/spec/bundler/support/artifice/compact_index_forbidden.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexForbidden < CompactIndexAPI
get "/versions" do
@@ -10,4 +8,6 @@ class CompactIndexForbidden < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexForbidden)
diff --git a/spec/bundler/support/artifice/compact_index_host_redirect.rb b/spec/bundler/support/artifice/compact_index_host_redirect.rb
index 304c897d68..4f82bf3812 100644
--- a/spec/bundler/support/artifice/compact_index_host_redirect.rb
+++ b/spec/bundler/support/artifice/compact_index_host_redirect.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexHostRedirect < CompactIndexAPI
- get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
+ get "/fetch/actual/gem/:id", host_name: "localgemserver.test" do
redirect "http://bundler.localgemserver.test#{request.path_info}"
end
@@ -18,4 +16,6 @@ class CompactIndexHostRedirect < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexHostRedirect)
diff --git a/spec/bundler/support/artifice/compact_index_mirror_down.rb b/spec/bundler/support/artifice/compact_index_mirror_down.rb
new file mode 100644
index 0000000000..88983c715d
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_mirror_down.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+require_relative "helpers/artifice"
+require_relative "helpers/rack_request"
+
+module Artifice
+ module Net
+ class HTTPMirrorDown < HTTP
+ def connect
+ raise SocketError if address == "gem.mirror"
+
+ super
+ end
+ end
+
+ HTTP.endpoint = CompactIndexAPI
+ end
+
+ replace_net_http(Net::HTTPMirrorDown)
+end
diff --git a/spec/bundler/support/artifice/compact_index_no_checksums.rb b/spec/bundler/support/artifice/compact_index_no_checksums.rb
new file mode 100644
index 0000000000..ecb7fc7d7c
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_no_checksums.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+class CompactIndexNoChecksums < CompactIndexAPI
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ gem.versions.map(&:number).join("\n")
+ end
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexNoChecksums)
diff --git a/spec/bundler/support/artifice/compact_index_no_gem.rb b/spec/bundler/support/artifice/compact_index_no_gem.rb
index 0a4be08a46..71f6629688 100644
--- a/spec/bundler/support/artifice/compact_index_no_gem.rb
+++ b/spec/bundler/support/artifice/compact_index_no_gem.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexNoGem < CompactIndexAPI
get "/gems/:id" do
@@ -10,4 +8,6 @@ class CompactIndexNoGem < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexNoGem)
diff --git a/spec/bundler/support/artifice/compact_index_partial_update.rb b/spec/bundler/support/artifice/compact_index_partial_update.rb
index cb1c7b9481..f111d91ef9 100644
--- a/spec/bundler/support/artifice/compact_index_partial_update.rb
+++ b/spec/bundler/support/artifice/compact_index_partial_update.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexPartialUpdate < CompactIndexAPI
# Stub the server to never return 304s. This simulates the behaviour of
@@ -25,7 +23,7 @@ class CompactIndexPartialUpdate < CompactIndexAPI
# Verify that a partial request is made, starting from the index of the
# final byte of the cached file.
unless env["HTTP_RANGE"] == "bytes=#{File.binread(cached_versions_path).bytesize - 1}-"
- raise("Range header should be present, and start from the index of the final byte of the cache.")
+ raise("Range header should be present, and start from the index of the final byte of the cache. #{env["HTTP_RANGE"].inspect}")
end
etag_response do
@@ -35,4 +33,6 @@ class CompactIndexPartialUpdate < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexPartialUpdate)
diff --git a/spec/bundler/support/artifice/compact_index_partial_update_bad_digest.rb b/spec/bundler/support/artifice/compact_index_partial_update_bad_digest.rb
new file mode 100644
index 0000000000..ac04336636
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_partial_update_bad_digest.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+# The purpose of this Artifice is to test that an incremental response is invalidated
+# and a second request is issued for the full content.
+class CompactIndexPartialUpdateBadDigest < CompactIndexAPI
+ def partial_update_bad_digest
+ response_body = yield
+ if request.env["HTTP_RANGE"]
+ headers "Repr-Digest" => "sha-256=:#{Digest::SHA256.base64digest("wrong digest on ranged request")}:"
+ else
+ headers "Repr-Digest" => "sha-256=:#{Digest::SHA256.base64digest(response_body)}:"
+ end
+ 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_bad_digest do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents([], calculate_info_checksums: true)
+ end
+ end
+
+ get "/info/:name" do
+ partial_update_bad_digest do
+ gem = gems.find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexPartialUpdateBadDigest)
diff --git a/spec/bundler/support/artifice/compact_index_partial_update_no_digest_not_incremental.rb b/spec/bundler/support/artifice/compact_index_partial_update_no_digest_not_incremental.rb
new file mode 100644
index 0000000000..99bae039f0
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_partial_update_no_digest_not_incremental.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+# The purpose of this Artifice is to test that an incremental response is ignored
+# when the digest is not present to verify that the partial response is valid.
+class CompactIndexPartialUpdateNoDigestNotIncremental < CompactIndexAPI
+ def partial_update_no_digest
+ 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_digest 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_digest 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
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexPartialUpdateNoDigestNotIncremental)
diff --git a/spec/bundler/support/artifice/compact_index_precompiled_before.rb b/spec/bundler/support/artifice/compact_index_precompiled_before.rb
new file mode 100644
index 0000000000..b5f72f546a
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_precompiled_before.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+class CompactIndexPrecompiledBefore < CompactIndexAPI
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ move_ruby_variant_to_the_end(CompactIndex.info(gem ? gem.versions : []))
+ end
+ end
+
+ private
+
+ def move_ruby_variant_to_the_end(response)
+ lines = response.split("\n")
+ ruby = lines.find {|line| /\A\d+\.\d+\.\d* \|/.match(line) }
+ lines.delete(ruby)
+ lines.push(ruby).join("\n")
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexPrecompiledBefore)
diff --git a/spec/bundler/support/artifice/compact_index_range_ignored.rb b/spec/bundler/support/artifice/compact_index_range_ignored.rb
new file mode 100644
index 0000000000..2303682c1f
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_range_ignored.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+class CompactIndexRangeIgnored < CompactIndexAPI
+ # Stub the server to not return 304 so that we don't bypass all the logic
+ def not_modified?(_checksum)
+ false
+ end
+
+ get "/versions" do
+ cached_versions_path = File.join(
+ Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
+ )
+
+ # Verify a cached copy of the versions file exists
+ unless File.binread(cached_versions_path).size > 0
+ raise("Cached versions file should be present and have content")
+ end
+
+ # Verify that a partial request is made, starting from the index of the
+ # final byte of the cached file.
+ unless env.delete("HTTP_RANGE")
+ raise("Expected client to write the full response on the first try")
+ end
+
+ etag_response do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents
+ end
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexRangeIgnored)
diff --git a/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
index bb616125bb..8a7c4b79b0 100644
--- a/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
+++ b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexRangeNotSatisfiable < CompactIndexAPI
get "/versions" do
@@ -31,4 +29,6 @@ class CompactIndexRangeNotSatisfiable < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexRangeNotSatisfiable)
diff --git a/spec/bundler/support/artifice/compact_index_rate_limited.rb b/spec/bundler/support/artifice/compact_index_rate_limited.rb
index ba17476045..4495491635 100644
--- a/spec/bundler/support/artifice/compact_index_rate_limited.rb
+++ b/spec/bundler/support/artifice/compact_index_rate_limited.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexRateLimited < CompactIndexAPI
class RequestCounter
def self.queue
- @queue ||= Queue.new
+ @queue ||= Thread::Queue.new
end
def self.size
@@ -45,4 +43,6 @@ class CompactIndexRateLimited < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexRateLimited)
diff --git a/spec/bundler/support/artifice/compact_index_redirects.rb b/spec/bundler/support/artifice/compact_index_redirects.rb
index 99adc797bf..f7ba393239 100644
--- a/spec/bundler/support/artifice/compact_index_redirects.rb
+++ b/spec/bundler/support/artifice/compact_index_redirects.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexRedirect < CompactIndexAPI
get "/fetch/actual/gem/:id" do
@@ -18,4 +16,6 @@ class CompactIndexRedirect < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexRedirect)
diff --git a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
index 7d427b5382..96259385e7 100644
--- a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
+++ b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexStrictBasicAuthentication < CompactIndexAPI
before do
@@ -12,9 +10,11 @@ class CompactIndexStrictBasicAuthentication < CompactIndexAPI
# Only accepts password == "password"
unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
- halt 403, "Authentication failed"
+ halt 401, "Authentication failed"
end
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexStrictBasicAuthentication)
diff --git a/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
index 036fac70b3..15850599b6 100644
--- a/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
+++ b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexWrongDependencies < CompactIndexAPI
get "/info/:name" do
@@ -14,4 +12,6 @@ class CompactIndexWrongDependencies < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexWrongDependencies)
diff --git a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
index 8add32b88f..9bd2ca0a9d 100644
--- a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
+++ b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
@@ -1,15 +1,14 @@
# frozen_string_literal: true
-require_relative "compact_index"
-
-Artifice.deactivate
+require_relative "helpers/compact_index"
class CompactIndexWrongGemChecksum < CompactIndexAPI
get "/info/:name" do
etag_response do
name = params[:name]
gem = gems.find {|g| g.name == name }
- checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") { "ab" * 22 }
+ # This generates the hexdigest "2222222222222222222222222222222222222222222222222222222222222222"
+ checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") { "IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI=" }
versions = gem ? gem.versions : []
versions.each {|v| v.checksum = checksum }
CompactIndex.info(versions)
@@ -17,4 +16,6 @@ class CompactIndexWrongGemChecksum < CompactIndexAPI
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(CompactIndexWrongGemChecksum)
diff --git a/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb b/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb
deleted file mode 100644
index c341c3993f..0000000000
--- a/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "endpoint_marshal_fail"
-
-Artifice.deactivate
-
-class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail
- before do
- unless env["HTTP_AUTHORIZATION"]
- halt 401, "Authentication info not supplied"
- end
- end
-end
-
-Artifice.activate_with(EndpointMarshalFailBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint.rb b/spec/bundler/support/artifice/endpoint.rb
index 1dc7101389..15242a7942 100644
--- a/spec/bundler/support/artifice/endpoint.rb
+++ b/spec/bundler/support/artifice/endpoint.rb
@@ -1,97 +1,6 @@
# frozen_string_literal: true
-require_relative "../path"
-
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
-
-require "artifice"
-require "sinatra/base"
-
-ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant
-ALL_REQUESTS_MUTEX = Mutex.new
-
-at_exit do
- if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"]
- expected = expected.split("\n").sort
- actual = ALL_REQUESTS.sort
-
- unless expected == actual
- raise "Unexpected requests!\nExpected:\n\t#{expected.join("\n\t")}\n\nActual:\n\t#{actual.join("\n\t")}"
- end
- end
-end
-
-class Endpoint < Sinatra::Base
- def self.all_requests
- @all_requests ||= []
- end
-
- GEM_REPO = Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"] || Spec::Path.gem_repo1)
- set :raise_errors, true
- set :show_exceptions, false
-
- def call!(*)
- super.tap do
- ALL_REQUESTS_MUTEX.synchronize do
- ALL_REQUESTS << @request.url
- end
- end
- end
-
- helpers do
- include Spec::Path
-
- def dependencies_for(gem_names, gem_repo = GEM_REPO)
- return [] if gem_names.nil? || gem_names.empty?
-
- all_specs = %w[specs.4.8 prerelease_specs.4.8].map do |filename|
- Marshal.load(File.open(gem_repo.join(filename)).read)
- end.inject(:+)
-
- all_specs.map do |name, version, platform|
- spec = load_spec(name, version, platform, gem_repo)
- next unless gem_names.include?(spec.name)
- {
- :name => spec.name,
- :number => spec.version.version,
- :platform => spec.platform.to_s,
- :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep|
- [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")]
- end,
- }
- end.compact
- end
-
- def load_spec(name, version, platform, gem_repo)
- full_name = "#{name}-#{version}"
- full_name += "-#{platform}" if platform != "ruby"
- Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
- end
- end
-
- get "/quick/Marshal.4.8/:id" do
- redirect "/fetch/actual/gem/#{params[:id]}"
- end
-
- get "/fetch/actual/gem/:id" do
- File.binread("#{GEM_REPO}/quick/Marshal.4.8/#{params[:id]}")
- end
-
- get "/gems/:id" do
- File.binread("#{GEM_REPO}/gems/#{params[:id]}")
- end
-
- get "/api/v1/dependencies" do
- Marshal.dump(dependencies_for(params[:gems]))
- end
-
- get "/specs.4.8.gz" do
- File.binread("#{GEM_REPO}/specs.4.8.gz")
- end
-
- get "/prerelease_specs.4.8.gz" do
- File.binread("#{GEM_REPO}/prerelease_specs.4.8.gz")
- end
-end
+require_relative "helpers/endpoint"
+require_relative "helpers/artifice"
Artifice.activate_with(Endpoint)
diff --git a/spec/bundler/support/artifice/endpoint_500.rb b/spec/bundler/support/artifice/endpoint_500.rb
index 0ce8dfeaad..9dd373bbf6 100644
--- a/spec/bundler/support/artifice/endpoint_500.rb
+++ b/spec/bundler/support/artifice/endpoint_500.rb
@@ -2,17 +2,16 @@
require_relative "../path"
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+$LOAD_PATH.unshift(*Spec::Path.sinatra_dependency_paths)
-require "artifice"
require "sinatra/base"
-Artifice.deactivate
-
class Endpoint500 < Sinatra::Base
before do
halt 500
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(Endpoint500)
diff --git a/spec/bundler/support/artifice/endpoint_api_forbidden.rb b/spec/bundler/support/artifice/endpoint_api_forbidden.rb
index edc2463424..6bdc5896d6 100644
--- a/spec/bundler/support/artifice/endpoint_api_forbidden.rb
+++ b/spec/bundler/support/artifice/endpoint_api_forbidden.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointApiForbidden < Endpoint
get "/api/v1/dependencies" do
@@ -10,4 +8,6 @@ class EndpointApiForbidden < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointApiForbidden)
diff --git a/spec/bundler/support/artifice/endpoint_api_missing.rb b/spec/bundler/support/artifice/endpoint_api_missing.rb
deleted file mode 100644
index 755c42e836..0000000000
--- a/spec/bundler/support/artifice/endpoint_api_missing.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "endpoint"
-
-Artifice.deactivate
-
-class EndpointApiMissing < Endpoint
- get "/fetch/actual/gem/:id" do
- warn params[:id]
- if params[:id] == "rack-1.0.gemspec.rz"
- halt 404
- else
- File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
- end
- end
-end
-
-Artifice.activate_with(EndpointApiMissing)
diff --git a/spec/bundler/support/artifice/endpoint_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_basic_authentication.rb
index ff3d1493d6..e8e3569e63 100644
--- a/spec/bundler/support/artifice/endpoint_basic_authentication.rb
+++ b/spec/bundler/support/artifice/endpoint_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointBasicAuthentication < Endpoint
before do
@@ -12,4 +10,6 @@ class EndpointBasicAuthentication < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_creds_diff_host.rb b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
index 8b8972cedd..9cbb4de61a 100644
--- a/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
+++ b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointCredsDiffHost < Endpoint
helpers do
@@ -26,7 +24,7 @@ class EndpointCredsDiffHost < Endpoint
end
get "/gems/:id" do
- redirect "http://diffhost.com/no/creds/#{params[:id]}"
+ redirect "http://diffhost.test/no/creds/#{params[:id]}"
end
get "/no/creds/:id" do
@@ -36,4 +34,6 @@ class EndpointCredsDiffHost < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointCredsDiffHost)
diff --git a/spec/bundler/support/artifice/endpoint_extra.rb b/spec/bundler/support/artifice/endpoint_extra.rb
index 942c4352b7..021fd435fe 100644
--- a/spec/bundler/support/artifice/endpoint_extra.rb
+++ b/spec/bundler/support/artifice/endpoint_extra.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointExtra < Endpoint
get "/extra/api/v1/dependencies" do
@@ -30,4 +28,6 @@ class EndpointExtra < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointExtra)
diff --git a/spec/bundler/support/artifice/endpoint_extra_api.rb b/spec/bundler/support/artifice/endpoint_extra_api.rb
index 1cfef7a7fc..a965af6e73 100644
--- a/spec/bundler/support/artifice/endpoint_extra_api.rb
+++ b/spec/bundler/support/artifice/endpoint_extra_api.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointExtraApi < Endpoint
get "/extra/api/v1/dependencies" do
@@ -31,4 +29,6 @@ class EndpointExtraApi < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointExtraApi)
diff --git a/spec/bundler/support/artifice/endpoint_extra_missing.rb b/spec/bundler/support/artifice/endpoint_extra_missing.rb
index 5fd9238207..73e2defb32 100644
--- a/spec/bundler/support/artifice/endpoint_extra_missing.rb
+++ b/spec/bundler/support/artifice/endpoint_extra_missing.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_extra"
-
-Artifice.deactivate
+require_relative "helpers/endpoint_extra"
class EndpointExtraMissing < EndpointExtra
get "/extra/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class EndpointExtraMissing < EndpointExtra
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointExtraMissing)
diff --git a/spec/bundler/support/artifice/endpoint_fallback.rb b/spec/bundler/support/artifice/endpoint_fallback.rb
index 08edf232e3..742e563f07 100644
--- a/spec/bundler/support/artifice/endpoint_fallback.rb
+++ b/spec/bundler/support/artifice/endpoint_fallback.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointFallback < Endpoint
DEPENDENCY_LIMIT = 60
@@ -16,4 +14,6 @@ class EndpointFallback < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointFallback)
diff --git a/spec/bundler/support/artifice/endpoint_host_redirect.rb b/spec/bundler/support/artifice/endpoint_host_redirect.rb
index 338cbcad00..6ce51bed93 100644
--- a/spec/bundler/support/artifice/endpoint_host_redirect.rb
+++ b/spec/bundler/support/artifice/endpoint_host_redirect.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointHostRedirect < Endpoint
- get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do
+ get "/fetch/actual/gem/:id", host_name: "localgemserver.test" do
redirect "http://bundler.localgemserver.test#{request.path_info}"
end
@@ -14,4 +12,6 @@ class EndpointHostRedirect < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointHostRedirect)
diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/endpoint_marshal_fail.rb
index 22c13e3e17..74ce321de6 100644
--- a/spec/bundler/support/artifice/endpoint_marshal_fail.rb
+++ b/spec/bundler/support/artifice/endpoint_marshal_fail.rb
@@ -1,13 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_fallback"
-
-Artifice.deactivate
-
-class EndpointMarshalFail < EndpointFallback
- get "/api/v1/dependencies" do
- "f0283y01hasf"
- end
-end
+require_relative "helpers/endpoint_marshal_fail"
+require_relative "helpers/artifice"
Artifice.activate_with(EndpointMarshalFail)
diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb
new file mode 100644
index 0000000000..ea4cfbe965
--- /dev/null
+++ b/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require_relative "helpers/endpoint_marshal_fail"
+
+class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail
+ before do
+ unless env["HTTP_AUTHORIZATION"]
+ halt 401, "Authentication info not supplied"
+ end
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(EndpointMarshalFailBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_mirror_source.rb b/spec/bundler/support/artifice/endpoint_mirror_source.rb
index 788a9027f3..fed7a746b9 100644
--- a/spec/bundler/support/artifice/endpoint_mirror_source.rb
+++ b/spec/bundler/support/artifice/endpoint_mirror_source.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
-require_relative "endpoint"
+require_relative "helpers/endpoint"
class EndpointMirrorSource < Endpoint
get "/gems/:id" do
- if request.env["HTTP_X_GEMFILE_SOURCE"] == "https://server.example.org/"
+ if request.env["HTTP_X_GEMFILE_SOURCE"] == "https://server.example.org/" && request.env["HTTP_USER_AGENT"].start_with?("bundler")
File.binread("#{gem_repo1}/gems/#{params[:id]}")
else
halt 500
@@ -12,4 +12,6 @@ class EndpointMirrorSource < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointMirrorSource)
diff --git a/spec/bundler/support/artifice/endpoint_redirect.rb b/spec/bundler/support/artifice/endpoint_redirect.rb
index ee97fccf64..84f546ba9d 100644
--- a/spec/bundler/support/artifice/endpoint_redirect.rb
+++ b/spec/bundler/support/artifice/endpoint_redirect.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointRedirect < Endpoint
get "/fetch/actual/gem/:id" do
@@ -14,4 +12,6 @@ class EndpointRedirect < Endpoint
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointRedirect)
diff --git a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
index 4d4da08770..dff360c5c5 100644
--- a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
+++ b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint"
-
-Artifice.deactivate
+require_relative "helpers/endpoint"
class EndpointStrictBasicAuthentication < Endpoint
before do
@@ -12,9 +10,11 @@ class EndpointStrictBasicAuthentication < Endpoint
# Only accepts password == "password"
unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz"
- halt 403, "Authentication failed"
+ halt 401, "Authentication failed"
end
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointStrictBasicAuthentication)
diff --git a/spec/bundler/support/artifice/endpoint_timeout.rb b/spec/bundler/support/artifice/endpoint_timeout.rb
index c118da1893..86b793e499 100644
--- a/spec/bundler/support/artifice/endpoint_timeout.rb
+++ b/spec/bundler/support/artifice/endpoint_timeout.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "endpoint_fallback"
-
-Artifice.deactivate
+require_relative "helpers/endpoint_fallback"
class EndpointTimeout < EndpointFallback
SLEEP_TIMEOUT = 3
@@ -12,4 +10,6 @@ class EndpointTimeout < EndpointFallback
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(EndpointTimeout)
diff --git a/spec/bundler/support/artifice/fail.rb b/spec/bundler/support/artifice/fail.rb
index f69f2eccc6..5ddbc4e590 100644
--- a/spec/bundler/support/artifice/fail.rb
+++ b/spec/bundler/support/artifice/fail.rb
@@ -1,15 +1,11 @@
# frozen_string_literal: true
-require "net/http"
+require_relative "../vendored_net_http"
-# We can't use artifice here because it uses rack
-
-module Artifice; end # for < 2.0, Net::HTTP::Persistent::SSLReuse
-
-class Fail < Net::HTTP
- # Net::HTTP uses a @newimpl instance variable to decide whether
+class Fail < Gem::Net::HTTP
+ # Gem::Net::HTTP uses a @newimpl instance variable to decide whether
# to use a legacy implementation. Since we are subclassing
- # Net::HTTP, we must set it
+ # Gem::Net::HTTP, we must set it
@newimpl = true
def request(req, body = nil, &block)
@@ -21,14 +17,11 @@ class Fail < Net::HTTP
end
def exception(req)
- name = ENV.fetch("BUNDLER_SPEC_EXCEPTION") { "Errno::ENETUNREACH" }
- const = name.split("::").reduce(Object) {|mod, sym| mod.const_get(sym) }
- const.new("host down: Bundler spec artifice fail! #{req["PATH_INFO"]}")
+ Errno::ENETUNREACH.new("host down: Bundler spec artifice fail! #{req["PATH_INFO"]}")
end
end
-# Replace Net::HTTP with our failing subclass
-::Net.class_eval do
- remove_const(:HTTP)
- const_set(:HTTP, ::Fail)
-end
+require_relative "helpers/artifice"
+
+# Replace Gem::Net::HTTP with our failing subclass
+Artifice.replace_net_http(::Fail)
diff --git a/spec/bundler/support/artifice/helpers/artifice.rb b/spec/bundler/support/artifice/helpers/artifice.rb
new file mode 100644
index 0000000000..788268295c
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/artifice.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+# This module was initially borrowed from https://github.com/wycats/artifice
+module Artifice
+ # Activate Artifice with a particular Rack endpoint.
+ #
+ # Calling this method will replace the Gem::Net::HTTP system
+ # with a replacement that routes all requests to the
+ # Rack endpoint.
+ #
+ # @param [#call] endpoint A valid Rack endpoint
+ def self.activate_with(endpoint)
+ require_relative "rack_request"
+
+ Net::HTTP.endpoint = endpoint
+ replace_net_http(Artifice::Net::HTTP)
+ end
+
+ # Deactivate the Artifice replacement.
+ def self.deactivate
+ replace_net_http(::Gem::Net::HTTP)
+ end
+
+ def self.replace_net_http(value)
+ ::Gem::Net.class_eval do
+ remove_const(:HTTP)
+ const_set(:HTTP, value)
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/compact_index.rb b/spec/bundler/support/artifice/helpers/compact_index.rb
new file mode 100644
index 0000000000..e684aa8628
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+require_relative "endpoint"
+
+$LOAD_PATH.unshift Spec::Path.tmp_root.join("compact_index/lib").to_s
+require "compact_index"
+require "digest"
+
+class CompactIndexAPI < Endpoint
+ helpers do
+ include Spec::Path
+
+ def load_spec(name, version, platform, gem_repo)
+ full_name = "#{name}-#{version}"
+ full_name += "-#{platform}" if platform != "ruby"
+ Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
+ end
+
+ def etag_response
+ response_body = yield
+ etag = Digest::MD5.hexdigest(response_body)
+ headers "ETag" => quote(etag)
+ return if not_modified?(etag)
+ headers "Repr-Digest" => "sha-256=:#{Digest::SHA256.base64digest(response_body)}:"
+ headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60"
+ content_type "text/plain"
+ requested_range_for(response_body)
+ rescue StandardError => e
+ puts e
+ puts e.backtrace
+ raise
+ end
+
+ def not_modified?(etag)
+ etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"])
+
+ return unless etags.include?(etag)
+ status 304
+ body ""
+ end
+
+ def requested_range_for(response_body)
+ ranges = Rack::Utils.get_byte_ranges(env["HTTP_RANGE"], response_body.bytesize)
+
+ if ranges
+ status 206
+ body ranges.map! {|range| slice_body(response_body, range) }.join
+ else
+ status 200
+ body response_body
+ end
+ end
+
+ def quote(string)
+ %("#{string}")
+ end
+
+ def parse_etags(value)
+ value ? value.split(/, ?/).select {|s| s.sub!(/"(.*)"/, '\1') } : []
+ end
+
+ def slice_body(body, range)
+ body.byteslice(range)
+ end
+
+ def gems(gem_repo = default_gem_repo)
+ @gems ||= {}
+ @gems[gem_repo] ||= begin
+ specs = Bundler::Deprecate.skip_during do
+ %w[specs.4.8 prerelease_specs.4.8].flat_map do |filename|
+ spec_index = gem_repo.join(filename)
+ next [] unless File.exist?(spec_index)
+
+ Marshal.load(File.binread(spec_index)).map do |name, version, platform|
+ load_spec(name, version, platform, gem_repo)
+ end
+ end
+ end
+
+ specs.group_by(&:name).map do |name, versions|
+ gem_versions = versions.map do |spec|
+ deps = spec.runtime_dependencies.map do |d|
+ reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ")
+ CompactIndex::Dependency.new(d.name, reqs)
+ end
+ begin
+ checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") do
+ Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").hexdigest
+ end
+ rescue StandardError
+ checksum = nil
+ end
+ build_gem_version(spec, deps, checksum)
+ end
+ CompactIndex::Gem.new(name, gem_versions)
+ end
+ end
+ end
+
+ def build_gem_version(spec, deps, checksum)
+ CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
+ deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
+ end
+ end
+
+ get "/names" do
+ etag_response do
+ CompactIndex.names(gems.map(&:name))
+ end
+ end
+
+ get "/versions" do
+ etag_response do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems)
+ file.contents
+ end
+ end
+
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/compact_index_cooldown.rb b/spec/bundler/support/artifice/helpers/compact_index_cooldown.rb
new file mode 100644
index 0000000000..9920fd2c95
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index_cooldown.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require_relative "compact_index"
+
+class CompactIndexCooldownAPI < CompactIndexAPI
+ helpers do
+ def build_gem_version(spec, deps, checksum)
+ created_at = spec.date&.utc&.iso8601
+ CompactIndex::GemVersionV2.new(spec.version.version, spec.platform.to_s, checksum, nil,
+ deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s, created_at)
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/compact_index_extra.rb b/spec/bundler/support/artifice/helpers/compact_index_extra.rb
new file mode 100644
index 0000000000..9e742630dd
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index_extra.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require_relative "compact_index"
+
+class CompactIndexExtra < CompactIndexAPI
+ get "/extra/versions" do
+ halt 404
+ end
+
+ get "/extra/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.binread("#{gem_repo2}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.binread("#{gem_repo2}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.binread("#{gem_repo2}/gems/#{params[:id]}")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb b/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb
new file mode 100644
index 0000000000..d9a7d83d23
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require_relative "compact_index"
+
+class CompactIndexExtraApi < CompactIndexAPI
+ get "/extra/names" do
+ etag_response do
+ CompactIndex.names(gems(gem_repo4).map(&:name))
+ end
+ end
+
+ get "/extra/versions" do
+ etag_response do
+ file = tmp("versions.list")
+ FileUtils.rm_f(file)
+ file = CompactIndex::VersionsFile.new(file.to_s)
+ file.create(gems(gem_repo4))
+ file.contents
+ end
+ end
+
+ get "/extra/info/:name" do
+ etag_response do
+ gem = gems(gem_repo4).find {|g| g.name == params[:name] }
+ CompactIndex.info(gem ? gem.versions : [])
+ end
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.binread("#{gem_repo4}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.binread("#{gem_repo4}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.binread("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.binread("#{gem_repo4}/gems/#{params[:id]}")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint.rb b/spec/bundler/support/artifice/helpers/endpoint.rb
new file mode 100644
index 0000000000..9590611dfe
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+require_relative "../../path"
+
+$LOAD_PATH.unshift(*Spec::Path.sinatra_dependency_paths)
+
+require "sinatra/base"
+
+ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant
+ALL_REQUESTS_MUTEX = Thread::Mutex.new
+
+at_exit do
+ if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"]
+ expected = expected.split("\n").sort
+ actual = ALL_REQUESTS.sort
+
+ unless expected == actual
+ raise "Unexpected requests!\nExpected:\n\t#{expected.join("\n\t")}\n\nActual:\n\t#{actual.join("\n\t")}"
+ end
+ end
+end
+
+class Endpoint < Sinatra::Base
+ def self.all_requests
+ @all_requests ||= []
+ end
+
+ set :raise_errors, true
+ set :show_exceptions, false
+ set :host_authorization, permitted_hosts: [".example.org", ".local", ".mirror", ".repo", ".repo1", ".repo2", ".repo3", ".repo4", ".rubygems.org", ".security", ".source", ".test", "127.0.0.1"]
+
+ def call!(*)
+ super.tap do
+ ALL_REQUESTS_MUTEX.synchronize do
+ ALL_REQUESTS << @request.url
+ end
+ end
+ end
+
+ helpers do
+ include Spec::Path
+
+ 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|
+ Marshal.load(File.binread(gem_repo.join(filename)))
+ end.inject(:+)
+
+ all_specs.filter_map do |name, version, platform|
+ spec = load_spec(name, version, platform, gem_repo)
+ next unless gem_names.include?(spec.name)
+ {
+ name: spec.name,
+ number: spec.version.version,
+ platform: spec.platform.to_s,
+ dependencies: spec.runtime_dependencies.map do |dep|
+ [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")]
+ end,
+ }
+ end
+ end
+
+ def load_spec(name, version, platform, gem_repo)
+ full_name = "#{name}-#{version}"
+ full_name += "-#{platform}" if platform != "ruby"
+ Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz"))))
+ end
+ end
+
+ get "/quick/Marshal.4.8/:id" do
+ redirect "/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/fetch/actual/gem/:id" do
+ File.binread("#{default_gem_repo}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/gems/:id" do
+ File.binread("#{default_gem_repo}/gems/#{params[:id]}")
+ end
+
+ get "/api/v1/dependencies" do
+ Marshal.dump(dependencies_for(params[:gems]))
+ end
+
+ get "/specs.4.8.gz" do
+ File.binread("#{default_gem_repo}/specs.4.8.gz")
+ end
+
+ get "/prerelease_specs.4.8.gz" do
+ File.binread("#{default_gem_repo}/prerelease_specs.4.8.gz")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint_extra.rb b/spec/bundler/support/artifice/helpers/endpoint_extra.rb
new file mode 100644
index 0000000000..ad08495b50
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint_extra.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require_relative "endpoint"
+
+class EndpointExtra < Endpoint
+ get "/extra/api/v1/dependencies" do
+ halt 404
+ end
+
+ get "/extra/specs.4.8.gz" do
+ File.binread("#{gem_repo2}/specs.4.8.gz")
+ end
+
+ get "/extra/prerelease_specs.4.8.gz" do
+ File.binread("#{gem_repo2}/prerelease_specs.4.8.gz")
+ end
+
+ get "/extra/quick/Marshal.4.8/:id" do
+ redirect "/extra/fetch/actual/gem/#{params[:id]}"
+ end
+
+ get "/extra/fetch/actual/gem/:id" do
+ File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}")
+ end
+
+ get "/extra/gems/:id" do
+ File.binread("#{gem_repo2}/gems/#{params[:id]}")
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint_fallback.rb b/spec/bundler/support/artifice/helpers/endpoint_fallback.rb
new file mode 100644
index 0000000000..a232930b67
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint_fallback.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require_relative "endpoint"
+
+class EndpointFallback < Endpoint
+ DEPENDENCY_LIMIT = 60
+
+ get "/api/v1/dependencies" do
+ if params[:gems] && params[:gems].size <= DEPENDENCY_LIMIT
+ Marshal.dump(dependencies_for(params[:gems]))
+ else
+ halt 413, "Too many gems to resolve, please request less than #{DEPENDENCY_LIMIT} gems"
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb
new file mode 100644
index 0000000000..c409d39d99
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require_relative "endpoint_fallback"
+
+class EndpointMarshalFail < EndpointFallback
+ get "/api/v1/dependencies" do
+ "f0283y01hasf"
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/rack_request.rb b/spec/bundler/support/artifice/helpers/rack_request.rb
new file mode 100644
index 0000000000..05ff034463
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/rack_request.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require "rack/test"
+require_relative "../../vendored_net_http"
+
+module Artifice
+ module Net
+ # This is an internal object that can receive Rack requests
+ # to the application using the Rack::Test API
+ class RackRequest
+ include Rack::Test::Methods
+ attr_reader :app
+
+ def initialize(app)
+ @app = app
+ end
+ end
+
+ class HTTP < ::Gem::Net::HTTP
+ class << self
+ attr_accessor :endpoint
+ end
+
+ # Gem::Net::HTTP uses a @newimpl instance variable to decide whether
+ # to use a legacy implementation. Since we are subclassing
+ # Gem::Net::HTTP, we must set it
+ @newimpl = true
+
+ # We don't need to connect, so blank out this method
+ def connect
+ end
+
+ # Replace the Gem::Net::HTTP request method with a method
+ # that converts the request into a Rack request and
+ # dispatches it to the Rack endpoint.
+ #
+ # @param [Net::HTTPRequest] req A Gem::Net::HTTPRequest
+ # object, or one if its subclasses
+ # @param [optional, String, #read] body This should
+ # be sent as "rack.input". If it's a String, it will
+ # be converted to a StringIO.
+ # @return [Net::HTTPResponse]
+ #
+ # @yield [Net::HTTPResponse] If a block is provided,
+ # this method will yield the Gem::Net::HTTPResponse to
+ # it after the body is read.
+ def request(req, body = nil, &block)
+ rack_request = RackRequest.new(self.class.endpoint)
+
+ req.each_header do |header, value|
+ rack_request.header(header, value)
+ end
+
+ scheme = use_ssl? ? "https" : "http"
+ prefix = "#{scheme}://#{addr_port}"
+ body_stream_contents = req.body_stream.read if req.body_stream
+
+ response = rack_request.request("#{prefix}#{req.path}",
+ { method: req.method, input: body || req.body || body_stream_contents })
+
+ make_net_http_response(response, &block)
+ end
+
+ private
+
+ # This method takes a Rack response and creates a Gem::Net::HTTPResponse
+ # Instead of trying to mock HTTPResponse directly, we just convert
+ # the Rack response into a String that looks like a normal HTTP
+ # response and call Gem::Net::HTTPResponse.read_new
+ #
+ # @param [Array(#to_i, Hash, #each)] response a Rack response
+ # @return [Net::HTTPResponse]
+ # @yield [Net::HTTPResponse] If a block is provided, yield the
+ # response to it after the body is read
+ def make_net_http_response(response)
+ status = response.status
+ headers = response.headers
+ body = response.body
+
+ response_string = []
+ response_string << "HTTP/1.1 #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}"
+
+ headers.each do |header, value|
+ response_string << "#{header}: #{value}"
+ end
+
+ response_string << "" << body
+
+ response_io = ::Gem::Net::BufferedIO.new(StringIO.new(response_string.join("\n")))
+ res = ::Gem::Net::HTTPResponse.read_new(response_io)
+
+ res.reading_body(response_io, true) do
+ yield res if block_given?
+ end
+
+ res
+ end
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb
index 19670287b3..0bf5ade8f6 100644
--- a/spec/bundler/support/artifice/vcr.rb
+++ b/spec/bundler/support/artifice/vcr.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
-require "net/http"
+require_relative "../vendored_net_http"
require_relative "../path"
-CASSETTE_PATH = "#{Spec::Path.spec_dir}/support/artifice/vcr_cassettes"
+CASSETTE_PATH = "#{Spec::Path.spec_dir}/support/artifice/vcr_cassettes".freeze
+USED_CASSETTES_PATH = "#{Spec::Path.spec_dir}/support/artifice/used_cassettes.txt".freeze
CASSETTE_NAME = ENV.fetch("BUNDLER_SPEC_VCR_CASSETTE_NAME") { "realworld" }
-class BundlerVCRHTTP < Net::HTTP
+class BundlerVCRHTTP < Gem::Net::HTTP
class RequestHandler
attr_reader :http, :request, :body, :response_block
def initialize(http, request, body = nil, &response_block)
@@ -22,6 +23,10 @@ class BundlerVCRHTTP < Net::HTTP
@__vcr_request_handler = handler
end
+ File.open(USED_CASSETTES_PATH, "a+") do |f|
+ f.puts request_pair_paths.map {|path| Pathname.new(path).relative_path_from(Spec::Path.git_root).to_s }.join("\n")
+ end
+
if recorded_response?
recorded_response
else
@@ -36,13 +41,13 @@ class BundlerVCRHTTP < Net::HTTP
def recorded_response
File.open(request_pair_paths.last, "rb:ASCII-8BIT") do |response_file|
- response_io = ::Net::BufferedIO.new(response_file)
- ::Net::HTTPResponse.read_new(response_io).tap do |response|
+ response_io = ::Gem::Net::BufferedIO.new(response_file)
+ ::Gem::Net::HTTPResponse.read_new(response_io).tap do |response|
response.decode_content = request.decode_content if request.respond_to?(:decode_content)
- response.uri = request.uri if request.respond_to?(:uri)
+ response.uri = request.uri
response.reading_body(response_io, request.response_body_permitted?) do
- response_block.call(response) if response_block
+ response_block&.call(response)
end
end
end
@@ -56,6 +61,7 @@ class BundlerVCRHTTP < Net::HTTP
response = http.request_without_vcr(request, body, &response_block)
@recording = false
unless @recording
+ require "fileutils"
FileUtils.mkdir_p(File.dirname(request_path))
binwrite(request_path, request_to_string(request))
binwrite(response_path, response_to_string(response))
@@ -68,30 +74,13 @@ class BundlerVCRHTTP < Net::HTTP
end
def file_name_for_key(key)
- key.join("/").gsub(/[\:*?"<>|]/, "-")
+ File.join(*key).gsub(/[\:*?"<>|]/, "-")
end
def request_pair_paths
%w[request response].map do |kind|
- File.join(CASSETTE_PATH, CASSETTE_NAME, file_name_for_key(key + [kind]))
- end
- end
-
- def read_stored_request(path)
- contents = File.binread(path)
- headers = {}
- method = nil
- path = nil
- contents.lines.grep(/^> /).each do |line|
- if line =~ /^> (GET|HEAD|POST|PATCH|PUT|DELETE) (.*)/
- method = $1
- path = $2.strip
- elsif line =~ /^> (.*?): (.*)/
- headers[$1] = $2
- end
+ File.join(CASSETTE_PATH, CASSETTE_NAME, file_name_for_key(key), kind)
end
- body = contents =~ /^([^>].*)/m && $1
- Net::HTTP.const_get(method.capitalize).new(path, headers).tap {|r| r.body = body if body }
end
def request_to_string(request)
@@ -132,6 +121,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)
@@ -144,8 +146,7 @@ class BundlerVCRHTTP < Net::HTTP
alias_method :request, :request_with_vcr
end
-# Replace Net::HTTP with our VCR subclass
-::Net.class_eval do
- remove_const(:HTTP)
- const_set(:HTTP, BundlerVCRHTTP)
-end
+require_relative "helpers/artifice"
+
+# Replace Gem::Net::HTTP with our VCR subclass
+Artifice.replace_net_http(BundlerVCRHTTP)
diff --git a/spec/bundler/support/artifice/windows.rb b/spec/bundler/support/artifice/windows.rb
index f5b4baae30..3056540beb 100644
--- a/spec/bundler/support/artifice/windows.rb
+++ b/spec/bundler/support/artifice/windows.rb
@@ -2,19 +2,16 @@
require_relative "../path"
-$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s))
+$LOAD_PATH.unshift(*Spec::Path.sinatra_dependency_paths)
-require "artifice"
require "sinatra/base"
-Artifice.deactivate
-
class Windows < Sinatra::Base
set :raise_errors, true
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 +23,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
@@ -43,4 +40,6 @@ class Windows < Sinatra::Base
end
end
+require_relative "helpers/artifice"
+
Artifice.activate_with(Windows)
diff --git a/spec/bundler/support/build_metadata.rb b/spec/bundler/support/build_metadata.rb
new file mode 100644
index 0000000000..2eade4137b
--- /dev/null
+++ b/spec/bundler/support/build_metadata.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require_relative "path"
+require_relative "helpers"
+
+module Spec
+ module BuildMetadata
+ include Spec::Path
+ include Spec::Helpers
+
+ def write_build_metadata(dir: source_root, version: Bundler::VERSION)
+ build_metadata = {
+ git_commit_sha: git_commit_sha,
+ built_at: release_date_for(version, dir: dir),
+ }
+
+ replace_build_metadata(build_metadata, dir: dir)
+ end
+
+ def reset_build_metadata(dir: source_root)
+ build_metadata = {
+ built_at: nil,
+ }
+
+ replace_build_metadata(build_metadata, dir: dir)
+ end
+
+ private
+
+ def replace_build_metadata(build_metadata, dir:)
+ build_metadata_file = File.expand_path("lib/bundler/build_metadata.rb", dir)
+
+ ivars = build_metadata.sort.map do |k, v|
+ " @#{k} = #{loaded_gemspec.send(:ruby_code, v)}"
+ end.join("\n")
+
+ contents = File.read(build_metadata_file)
+ contents.sub!(/^(\s+# begin ivars).+(^\s+# end ivars)/m, "\\1\n#{ivars}\n\\2")
+ File.open(build_metadata_file, "w") {|f| f << contents }
+ end
+
+ def git_commit_sha
+ ruby_core_tarball? ? "unknown" : git("rev-parse --short HEAD", source_root).strip
+ end
+
+ def release_date_for(version, dir:)
+ changelog = File.expand_path("CHANGELOG.md", dir)
+ File.readlines(changelog)[2].scan(/^## #{Regexp.escape(version)} \((.*)\)/).first&.first if File.exist?(changelog)
+ end
+
+ extend self
+ end
+end
diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb
index 65f30d1f38..43ab7e053d 100644
--- a/spec/bundler/support/builders.rb
+++ b/spec/bundler/support/builders.rb
@@ -2,9 +2,18 @@
require "bundler/shared_helpers"
require "shellwords"
+require "fileutils"
+require "rubygems/package"
+
+require_relative "build_metadata"
module Spec
module Builders
+ def self.extended(mod)
+ mod.extend Path
+ mod.extend Helpers
+ end
+
def self.constantize(name)
name.delete("-").upcase
end
@@ -17,42 +26,37 @@ module Spec
Gem::Platform.new(platform)
end
- # Returns a number smaller than the size of the index. Useful for specs that
- # need the API request limit to be reached for some reason.
- def low_api_request_limit_for(gem_repo)
- all_gems = Dir[gem_repo.join("gems/*.gem")]
-
- all_gem_names = all_gems.map do |file|
- File.basename(file, ".gem").match(/\A(?<gem_name>[^-]+)-.*\z/)[:gem_name]
- end.uniq
-
- (all_gem_names - ["bundler"]).size
- end
-
def build_repo1
build_repo gem_repo1 do
- build_gem "rack", %w[0.9.1 1.0.0] do |s|
- s.executables = "rackup"
- s.post_install_message = "Rack's post install message"
+ FileUtils.cp rake_path, "#{gem_repo1}/gems/"
+
+ build_gem "coffee-script-source"
+ build_gem "git"
+ build_gem "puma"
+ build_gem "minitest"
+
+ build_gem "myrack", %w[0.9.1 1.0.0] do |s|
+ s.executables = "myrackup"
+ s.post_install_message = "Myrack's post install message"
end
build_gem "thin" do |s|
- s.add_dependency "rack"
+ s.add_dependency "myrack"
s.post_install_message = "Thin's post install message"
end
- build_gem "rack-obama" do |s|
- s.add_dependency "rack"
- s.post_install_message = "Rack-obama's post install message"
+ build_gem "myrack-obama" do |s|
+ s.add_dependency "myrack"
+ s.post_install_message = "Myrack-obama's post install message"
end
- build_gem "rack_middleware", "1.0" do |s|
- s.add_dependency "rack", "0.9.1"
+ build_gem "myrack_middleware", "1.0" do |s|
+ s.add_dependency "myrack", "0.9.1"
end
build_gem "rails", "2.3.2" do |s|
s.executables = "rails"
- s.add_dependency "rake", "13.0.1"
+ s.add_dependency "rake", rake_version
s.add_dependency "actionpack", "2.3.2"
s.add_dependency "activerecord", "2.3.2"
s.add_dependency "actionmailer", "2.3.2"
@@ -76,40 +80,28 @@ module Spec
s.add_dependency "activesupport", ">= 2.0.0"
end
- build_gem "rails_pinned_to_old_activesupport" do |s|
- s.add_dependency "activesupport", "= 1.2.3"
- end
-
- build_gem "missing_dep" do |s|
- s.add_dependency "not_here"
- end
-
- build_gem "rspec", "1.2.7", :no_default => true do |s|
+ build_gem "rspec", "1.2.7", no_default: true do |s|
s.write "lib/spec.rb", "SPEC = '1.2.7'"
end
- build_gem "rack-test", :no_default => true do |s|
- s.write "lib/rack/test.rb", "RACK_TEST = '1.0'"
- end
-
- build_gem "platform_specific" do |s|
- s.platform = Bundler.local_platform
- s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'"
+ build_gem "myrack-test", no_default: true do |s|
+ s.write "lib/myrack/test.rb", "MYRACK_TEST = '1.0'"
end
build_gem "platform_specific" do |s|
s.platform = "java"
- s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 JAVA'"
end
build_gem "platform_specific" do |s|
s.platform = "ruby"
- s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 RUBY'"
end
build_gem "platform_specific" do |s|
s.platform = "x86-mswin32"
- s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 MSWIN'"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "x64-mswin64"
end
build_gem "platform_specific" do |s|
@@ -117,39 +109,37 @@ module Spec
end
build_gem "platform_specific" do |s|
- s.platform = "x64-mingw32"
+ s.platform = "x64-mingw-ucrt"
+ end
+
+ build_gem "platform_specific" do |s|
+ s.platform = "aarch64-mingw-ucrt"
end
build_gem "platform_specific" do |s|
s.platform = "x86-darwin-100"
- s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 x86-darwin-100'"
end
build_gem "only_java", "1.0" do |s|
s.platform = "java"
- s.write "lib/only_java.rb", "ONLY_JAVA = '1.0.0 JAVA'"
end
build_gem "only_java", "1.1" do |s|
s.platform = "java"
- s.write "lib/only_java.rb", "ONLY_JAVA = '1.1.0 JAVA'"
end
build_gem "nokogiri", "1.4.2"
build_gem "nokogiri", "1.4.2" do |s|
s.platform = "java"
- s.write "lib/nokogiri.rb", "NOKOGIRI = '1.4.2 JAVA'"
s.add_dependency "weakling", ">= 0.0.3"
end
build_gem "laduradura", "5.15.2"
build_gem "laduradura", "5.15.2" do |s|
s.platform = "java"
- s.write "lib/laduradura.rb", "LADURADURA = '5.15.2 JAVA'"
end
build_gem "laduradura", "5.15.3" do |s|
s.platform = "java"
- s.write "lib/laduradura.rb", "LADURADURA = '5.15.2 JAVA'"
end
build_gem "weakling", "0.0.3"
@@ -158,55 +148,12 @@ module Spec
build_gem "duradura", "7.0"
- build_gem "multiple_versioned_deps" do |s|
- s.add_dependency "weakling", ">= 0.0.1", "< 0.1"
- end
-
- build_gem "not_released", "1.0.pre"
-
- build_gem "has_prerelease", "1.0"
- build_gem "has_prerelease", "1.1.pre"
-
- build_gem "with_development_dependency" do |s|
- s.add_development_dependency "activesupport", "= 2.3.5"
- end
-
- build_gem "with_license" do |s|
- s.license = "MIT"
- end
-
- 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
build_gem "bundler", "0.9" do |s|
s.executables = "bundle"
- s.write "bin/bundle", "puts 'FAIL'"
+ s.write "bin/bundle", "#!/usr/bin/env ruby\nputs 'FAIL'"
end
# The bundler 0.8 gem has a rubygems plugin that always loads :(
@@ -215,10 +162,6 @@ module Spec
s.write "lib/rubygems_plugin.rb", "require 'bundler/omg' ; puts 'FAIL'"
end
- build_gem "bundler_dep" do |s|
- s.add_dependency "bundler"
- end
-
# The yard gem iterates over Gem.source_index looking for plugins
build_gem "yard" do |s|
s.write "lib/yard.rb", <<-Y
@@ -228,143 +171,54 @@ module Spec
Y
end
- # The rcov gem is platform mswin32, but has no arch
- build_gem "rcov" do |s|
- s.platform = Gem::Platform.new([nil, "mswin32", nil])
- s.write "lib/rcov.rb", "RCOV = '1.0.0'"
- end
-
build_gem "net-ssh"
build_gem "net-sftp", "1.1.1" do |s|
s.add_dependency "net-ssh", ">= 1.0.0", "< 1.99.0"
end
- # Test complicated gem dependencies for install
- build_gem "net_a" do |s|
- s.add_dependency "net_b"
- s.add_dependency "net_build_extensions"
- end
-
- build_gem "net_b"
-
- build_gem "net_build_extensions" do |s|
- s.add_dependency "rake"
- s.extensions << "Rakefile"
- s.write "Rakefile", <<-RUBY
- task :default do
- path = File.expand_path("../lib", __FILE__)
- FileUtils.mkdir_p(path)
- File.open("\#{path}/net_build_extensions.rb", "w") do |f|
- f.puts "NET_BUILD_EXTENSIONS = 'YES'"
- end
- end
- RUBY
- end
-
- build_gem "net_c" do |s|
- s.add_dependency "net_a"
- s.add_dependency "net_d"
- end
-
- build_gem "net_d"
-
- build_gem "net_e" do |s|
- s.add_dependency "net_d"
- end
-
- # Capistrano did this (at least until version 2.5.10)
- # RubyGems 2.2 doesn't allow the specifying of a dependency twice
- # See https://github.com/rubygems/rubygems/commit/03dbac93a3396a80db258d9bc63500333c25bd2f
- build_gem "double_deps", "1.0", :skip_validation => true do |s|
- s.add_dependency "net-ssh", ">= 1.0.0"
- s.add_dependency "net-ssh"
- end
-
build_gem "foo"
-
- # A minimal fake pry console
- build_gem "pry" do |s|
- s.write "lib/pry.rb", <<-RUBY
- class Pry
- class << self
- def toplevel_binding
- unless defined?(@toplevel_binding) && @toplevel_binding
- TOPLEVEL_BINDING.eval %{
- def self.__pry__; binding; end
- Pry.instance_variable_set(:@toplevel_binding, __pry__)
- class << self; undef __pry__; end
- }
- end
- @toplevel_binding.eval('private')
- @toplevel_binding
- end
-
- def __pry__
- while line = gets
- begin
- puts eval(line, toplevel_binding).inspect.sub(/^"(.*)"$/, '=> \\1')
- rescue Exception => e
- puts "\#{e.class}: \#{e.message}"
- puts e.backtrace.first
- end
- end
- end
- alias start __pry__
- end
- end
- RUBY
- end
-
- build_gem "has_metadata" do |s|
- s.metadata = {
- "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
- "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
- "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
- "homepage_uri" => "https://bestgemever.example.io",
- "mailing_list_uri" => "https://groups.example.com/bestgemever",
- "source_code_uri" => "https://example.com/user/bestgemever",
- "wiki_uri" => "https://example.com/user/bestgemever/wiki",
- }
- end
end
end
- def build_repo2(&blk)
- FileUtils.rm_rf gem_repo2
- FileUtils.cp_r gem_repo1, gem_repo2
- update_repo2(&blk) if block_given?
+ def build_repo2(**kwargs, &blk)
+ FileUtils.cp_r gem_repo1, gem_repo2, remove_destination: true
+ update_repo2(**kwargs, &blk) if block_given?
end
- def build_repo3
- build_repo gem_repo3 do
- build_gem "rack"
+ # A repo that has no pre-installed gems included. (The caller completely
+ # determines the contents with the block.)
+ #
+ # If the repo already exists, `#update_repo` will be called.
+ def build_repo3(**kwargs, &blk)
+ if File.exist?(gem_repo3)
+ update_repo(gem_repo3, &blk)
+ else
+ build_repo gem_repo3, **kwargs, &blk
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)
- FileUtils.rm_rf gem_repo4
- build_repo(gem_repo4, &blk)
+ # Like build_repo3, this is a repo that has no pre-installed gems included.
+ #
+ # If the repo already exists, `#udpate_repo` will be called
+ def build_repo4(**kwargs, &blk)
+ if File.exist?(gem_repo4)
+ update_repo gem_repo4, &blk
+ else
+ build_repo gem_repo4, **kwargs, &blk
+ end
end
- def update_repo4(&blk)
- update_repo(gem_repo4, &blk)
+ def update_repo2(**kwargs, &blk)
+ update_repo(gem_repo2, **kwargs, &blk)
end
- def update_repo2
- update_repo gem_repo2 do
- build_gem "rack", "1.2" do |s|
- s.executables = "rackup"
- end
- yield if block_given?
- end
+ def update_repo3(&blk)
+ update_repo(gem_repo3, &blk)
end
def build_security_repo
build_repo security_repo do
- build_gem "rack"
+ build_gem "myrack"
build_gem "signed_gem" do |s|
cert = "signing-cert.pem"
@@ -377,37 +231,60 @@ module Spec
end
end
- def build_repo(path, &blk)
- return if File.directory?(path)
- rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
+ # A minimal fake irb console
+ def build_dummy_irb(version = "9.9.9")
+ build_gem "irb", version do |s|
+ s.write "lib/irb.rb", <<-RUBY
+ class IRB
+ class << self
+ def toplevel_binding
+ unless defined?(@toplevel_binding) && @toplevel_binding
+ TOPLEVEL_BINDING.eval %{
+ def self.__irb__; binding; end
+ IRB.instance_variable_set(:@toplevel_binding, __irb__)
+ class << self; undef __irb__; end
+ }
+ end
+ @toplevel_binding.eval('private')
+ @toplevel_binding
+ end
- if rake_path.nil?
- FileUtils.rm_rf(Path.base_system_gems)
- Spec::Rubygems.install_test_deps
- rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first
+ def __irb__
+ while line = gets
+ begin
+ puts eval(line, toplevel_binding).inspect.sub(/^"(.*)"$/, '=> \\1')
+ rescue Exception => e
+ puts "\#{e.class}: \#{e.message}"
+ puts e.backtrace.first
+ end
+ end
+ end
+ alias start __irb__
+ end
+ end
+ RUBY
end
+ end
- if rake_path
- FileUtils.mkdir_p("#{path}/gems")
- FileUtils.cp rake_path, "#{path}/gems/"
- else
- abort "Your test gems are missing! Run `rm -rf #{tmp}` and try again."
- end
+ def build_repo(path, **kwargs, &blk)
+ return if File.directory?(path)
- update_repo(path, &blk)
+ FileUtils.mkdir_p("#{path}/gems")
+
+ update_repo(path,**kwargs, &blk)
end
- def update_repo(path)
- if path == gem_repo1 && caller.first.split(" ").last == "`build_repo`"
+ def update_repo(path, build_compact_index: true)
+ exempted_caller = Gem.ruby_version >= Gem::Version.new("3.4.0.dev") && RUBY_ENGINE != "jruby" ? "#{Module.nesting.first}#build_repo" : "build_repo"
+ if path == gem_repo1 && caller_locations(1, 1).first.label != exempted_caller
raise "Updating gem_repo1 is unsupported -- use gem_repo2 instead"
end
return unless block_given?
@_build_path = "#{path}/gems"
@_build_repo = File.basename(path)
yield
- with_gem_path_as Path.base_system_gems do
- gem_command :generate_index, :dir => path
- end
+ options = { build_compact: build_compact_index }
+ Gem::Indexer.new(path, options).generate_index
ensure
@_build_path = nil
@_build_repo = nil
@@ -432,14 +309,14 @@ module Spec
end
end
- def build_dep(name, requirements = Gem::Requirement.default, type = :runtime)
- Bundler::Dependency.new(name, :version => requirements)
- end
-
def build_lib(name, *args, &blk)
build_with(LibBuilder, name, args, &blk)
end
+ def build_bundler(*args, &blk)
+ build_with(BundlerBuilder, "bundler", args, &blk)
+ end
+
def build_gem(name, *args, &blk)
build_with(GemBuilder, name, args, &blk)
end
@@ -461,7 +338,7 @@ module Spec
build_with(PluginBuilder, name, args, &blk)
end
- private
+ private
def build_with(builder, name, args, &blk)
@_build_path ||= nil
@@ -545,6 +422,58 @@ module Spec
alias_method :dep, :runtime
end
+ class BundlerBuilder
+ def initialize(context, name, version)
+ @context = context
+ @spec = Spec::Path.loaded_gemspec.dup
+ @spec.version = version || Bundler::VERSION
+ end
+
+ def required_ruby_version
+ @spec.required_ruby_version
+ end
+
+ def required_ruby_version=(x)
+ @spec.required_ruby_version = x
+ end
+
+ def _build(options = {})
+ full_name = "bundler-#{@spec.version}"
+ build_path = (options[:build_path] || @context.tmp) + full_name
+ bundler_path = build_path + "#{full_name}.gem"
+
+ FileUtils.mkdir_p build_path
+
+ @context.shipped_files.each do |shipped_file|
+ target_shipped_file = shipped_file
+ target_shipped_file = shipped_file.sub(/\Alibexec/, "exe") if @context.ruby_core?
+ target_shipped_file = build_path + target_shipped_file
+ target_shipped_dir = File.dirname(target_shipped_file)
+ FileUtils.mkdir_p target_shipped_dir unless File.directory?(target_shipped_dir)
+ FileUtils.cp File.expand_path(shipped_file, @context.source_root), target_shipped_file, preserve: true
+ end
+
+ @context.replace_version_file(@spec.version, dir: build_path)
+ @context.replace_changelog(@spec.version, dir: build_path) if options[:released]
+
+ Spec::BuildMetadata.write_build_metadata(dir: build_path, version: @spec.version.to_s)
+
+ Dir.chdir build_path do
+ Gem::DefaultUserInteraction.use_ui(Gem::SilentUI.new) do
+ Gem::Package.build(@spec)
+ end
+ end
+
+ if block_given?
+ yield(bundler_path)
+ else
+ FileUtils.mv bundler_path, options[:path]
+ end
+ ensure
+ FileUtils.rm_rf build_path
+ end
+ end
+
class LibBuilder
def initialize(context, name, version)
@context = context
@@ -558,6 +487,7 @@ module Spec
s.email = "foo@bar.baz"
s.homepage = "http://example.com"
s.license = "MIT"
+ s.required_ruby_version = ">= 3.0"
end
@files = {}
end
@@ -574,25 +504,17 @@ module Spec
@spec.executables = Array(val)
@spec.executables.each do |file|
executable = "#{@spec.bindir}/#{file}"
- shebang = if Bundler.current_ruby.jruby?
- "#!/usr/bin/env jruby\n"
- else
- "#!/usr/bin/env ruby\n"
- end
+ shebang = "#!/usr/bin/env ruby\n"
@spec.files << executable
write executable, "#{shebang}require_relative '../lib/#{@name}' ; puts #{Builders.constantize(@name)}"
end
end
def add_c_extension
- require_paths << "ext"
extensions << "ext/extconf.rb"
write "ext/extconf.rb", <<-RUBY
require "mkmf"
-
- # exit 1 unless with_config("simple")
-
extension_name = "#{name}_c"
if extra_lib_dir = with_config("ext-lib")
# add extra libpath if --with-ext-lib is
@@ -606,7 +528,7 @@ module Spec
write "ext/#{name}.c", <<-C
#include "ruby.h"
- void Init_#{name}_c() {
+ void Init_#{name}_c(void) {
rb_define_module("#{Builders.constantize(name)}_IN_C");
}
C
@@ -617,7 +539,6 @@ module Spec
if options[:rubygems_version]
@spec.rubygems_version = options[:rubygems_version]
- def @spec.mark_version; end
def @spec.validate(*); end
end
@@ -630,23 +551,22 @@ module Spec
end
@spec.authors = ["no one"]
- @spec.files = @files.keys
+ @spec.files += @files.keys
case options[:gemspec]
when false
# do nothing
when :yaml
- @spec.files << "#{name}.gemspec"
@files["#{name}.gemspec"] = @spec.to_yaml
else
- @spec.files << "#{name}.gemspec"
@files["#{name}.gemspec"] = @spec.to_ruby
end
@files.each do |file, source|
- file = Pathname.new(path).join(file)
- FileUtils.mkdir_p(file.dirname)
- File.open(file, "w") {|f| f.puts source }
+ full_path = Pathname.new(path).join(file)
+ FileUtils.mkdir_p(full_path.dirname)
+ File.open(full_path, "w") {|f| f.puts source }
+ FileUtils.chmod("+x", full_path) if @spec.executables.map {|exe| "#{@spec.bindir}/#{exe}" }.include?(file)
end
path
end
@@ -670,9 +590,11 @@ module Spec
class GitBuilder < LibBuilder
def _build(options)
+ default_branch = options[:default_branch] || "main"
path = options[:path] || _default_path
source = options[:source] || "git@#{path}"
- super(options.merge(:path => path, :source => source))
+ super(options.merge(path: path, source: source))
+ @context.git("config --global init.defaultBranch #{default_branch}", path)
@context.git("init", path)
@context.git("add *", path)
@context.git("config user.email lol@wut.com", path)
@@ -685,7 +607,7 @@ module Spec
class GitBareBuilder < LibBuilder
def _build(options)
path = options[:path] || _default_path
- super(options.merge(:path => path))
+ super(options.merge(path: path))
@context.git("init --bare", path)
end
end
@@ -696,17 +618,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]
@@ -719,9 +632,8 @@ module Spec
_default_files.keys.each do |path|
_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)
+ super(options.merge(path: libpath, gemspec: update_gemspec, source: source))
+ @context.git("commit -am BUMP", libpath)
end
end
@@ -742,25 +654,26 @@ module Spec
class GemBuilder < LibBuilder
def _build(opts)
- lib_path = super(opts.merge(:path => @context.tmp(".tmp/#{@spec.full_name}"), :no_default => opts[:no_default]))
+ lib_path = opts[:lib_path] || @context.tmp(".tmp/#{@spec.full_name}")
+ lib_path = super(opts.merge(path: lib_path, no_default: opts[:no_default]))
destination = opts[:path] || _default_path
FileUtils.mkdir_p(lib_path.join(destination))
- if opts[:gemspec] == :yaml || opts[:gemspec] == false
+ if [:yaml, false].include?(opts[:gemspec])
Dir.chdir(lib_path) do
Bundler.rubygems.build(@spec, opts[:skip_validation])
end
elsif opts[:skip_validation]
- @context.gem_command "build --force #{@spec.name}", :dir => lib_path
+ Dir.chdir(lib_path) { Gem::Package.build(@spec, true) }
else
- @context.gem_command "build #{@spec.name}", :dir => lib_path
+ Dir.chdir(lib_path) { Gem::Package.build(@spec) }
end
gem_path = File.expand_path("#{@spec.full_name}.gem", lib_path)
if opts[:to_system]
- @context.system_gems gem_path
+ @context.system_gems gem_path, default: opts[:default]
elsif opts[:to_bundle]
- @context.system_gems gem_path, :path => @context.default_bundle_path
+ @context.system_gems gem_path, path: @context.default_bundle_path
else
FileUtils.mv(gem_path, destination)
end
@@ -780,56 +693,56 @@ module Spec
end
end
- TEST_CERT = <<-CERT.gsub(/^\s*/, "")
+ TEST_CERT = <<~CERT
-----BEGIN CERTIFICATE-----
- MIIDMjCCAhqgAwIBAgIBATANBgkqhkiG9w0BAQUFADAnMQwwCgYDVQQDDAN5b3Ux
+ MIIDNTCCAh2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAnMQwwCgYDVQQDDAN5b3Ux
FzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTE1MDIwODAwMTIyM1oXDTQyMDYy
NTAwMTIyM1owJzEMMAoGA1UEAwwDeW91MRcwFQYKCZImiZPyLGQBGRYHZXhhbXBs
- ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANlvFdpN43c4DMS9Jo06
- m0a7k3bQ3HWQ1yrYhZMi77F1F73NpBknYHIzDktQpGn6hs/4QFJT4m4zNEBF47UL
- jHU5nTK5rjkS3niGYUjvh3ZEzVeo9zHUlD/UwflDo4ALl3TSo2KY/KdPS/UTdLXL
- ajkQvaVJtEDgBPE3DPhlj5whp+Ik3mDHej7qpV6F502leAwYaFyOtlEG/ZGNG+nZ
- L0clH0j77HpP42AylHDi+vakEM3xcjo9BeWQ6Vkboic93c9RTt6CWBWxMQP7Nol1
- MOebz9XOSQclxpxWteXNfPRtMdAhmRl76SMI8ywzThNPpa4EH/yz34ftebVOgKyM
- nd0CAwEAAaNpMGcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFA7D
- n9qo0np23qi3aOYuAAPn/5IdMBYGA1UdEQQPMA2BC3lvdUBleGFtcGxlMBYGA1Ud
- EgQPMA2BC3lvdUBleGFtcGxlMA0GCSqGSIb3DQEBBQUAA4IBAQA7Gyk62sWOUX/N
- vk4tJrgKESph6Ns8+E36A7n3jt8zCep8ldzMvwTWquf9iqhsC68FilEoaDnUlWw7
- d6oNuaFkv7zfrWGLlvqQJC+cu2X5EpcCksg5oRp8VNbwJysJ6JgwosxzROII8eXc
- R+j1j6mDvQYqig2QOnzf480pjaqbP+tspfDFZbhKPrgM3Blrb3ZYuFpv4zkqI7aB
- 6fuk2DUhNO1CuwrJA84TqC+jGo73bDKaT5hrIDiaJRrN5+zcWja2uEWrj5jSbep4
- oXdEdyH73hOHMBP40uds3PqnUsxEJhzjB2sCCe1geV24kw9J4m7EQXPVkUKDgKrt
- LlpDmOoo
+ ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMkupYkg3Nd1oXM3fo0d
+ mVJBWNrni88lKDuIIQXwcKe6XCgiloZG708ecLTOws9+o9MkTl9Wtpf/WGXT98NK
+ EPUYakd2Fv1SuD1jWYlP7iDR6hB3RkWBm5ziujYftVJ4ZrPD42PLjDASvlh75Tvr
+ MeM7yq/qkcgNsd9dQyUvMNPks3tla9je7Dt7Auli2IN3CNXys7gIOfwJH0Bb/M6t
+ y7oUfpoUKAfLzwe61abztgDu1lSNgdFBM1kcxYflyh/FkX5TlAcWeAXzLrnxAXGR
+ UxXrxW4oPC+kZi/pDRBd7X4zQDx7bCmr1+FsS3M05i3w5E08Tt9iKRk4V8nCmE4i
+ k6UCAwEAAaNsMGowCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYE
+ FOOOFw5TNAqt/TcRRZEU3Dg/58XuMBYGA1UdEQQPMA2BC3lvdUBleGFtcGxlMBYG
+ A1UdEgQPMA2BC3lvdUBleGFtcGxlMA0GCSqGSIb3DQEBCwUAA4IBAQAy3xnmobxU
+ 1SyhHvoIXTJmG0wt1DQ/Dqwjy362LpEf1UHt29wtg1Mph58eVtl93z5Vd2t4/O77
+ E2BHpSu9ujc6/Br4+2uA/Qk/xRyLBtZAwty6J4uFvOOg985HonN+RCUZbKSUTmtA
+ TZvNtIDAZFQ8Tu75K4gIBxDcz7biGi4i1VJ3F3GNCNeossr9IQwKvb+UWFq14U5R
+ IzUnGgMIzcjUG2kKQvddRD1CjS+egtcLvShbOfm5bs4w4rfQ2FPF+Aaf9v7fxa/c
+ Jrf3K+cB19eAy7O4nlPG1xurvnZd0QpqRk++werrBuKe1Pgga7YBLePfJhzwqcZv
+ wVOSsB870yeO
-----END CERTIFICATE-----
CERT
- TEST_PKEY = <<-PKEY.gsub(/^\s*/, "")
+ TEST_PKEY = <<~PKEY
-----BEGIN RSA PRIVATE KEY-----
- MIIEowIBAAKCAQEA2W8V2k3jdzgMxL0mjTqbRruTdtDcdZDXKtiFkyLvsXUXvc2k
- GSdgcjMOS1CkafqGz/hAUlPibjM0QEXjtQuMdTmdMrmuORLeeIZhSO+HdkTNV6j3
- MdSUP9TB+UOjgAuXdNKjYpj8p09L9RN0tctqORC9pUm0QOAE8TcM+GWPnCGn4iTe
- YMd6PuqlXoXnTaV4DBhoXI62UQb9kY0b6dkvRyUfSPvsek/jYDKUcOL69qQQzfFy
- Oj0F5ZDpWRuiJz3dz1FO3oJYFbExA/s2iXUw55vP1c5JByXGnFa15c189G0x0CGZ
- GXvpIwjzLDNOE0+lrgQf/LPfh+15tU6ArIyd3QIDAQABAoIBACbDqz20TS1gDMa2
- gj0DidNedbflHKjJHdNBru7Ad8NHgOgR1YO2hXdWquG6itVqGMbTF4SV9/R1pIcg
- 7qvEV1I+50u31tvOBWOvcYCzU48+TO2n7gowQA3xPHPYHzog1uu48fAOHl0lwgD7
- av9OOK3b0jO5pC08wyTOD73pPWU0NrkTh2+N364leIi1pNuI1z4V+nEuIIm7XpVd
- 5V4sXidMTiEMJwE6baEDfTjHKaoRndXrrPo3ryIXmcX7Ag1SwAQwF5fBCRToCgIx
- dszEZB1bJD5gA6r+eGnJLB/F60nK607az5o3EdguoB2LKa6q6krpaRCmZU5svvoF
- J7xgBPECgYEA8RIzHAQ3zbaibKdnllBLIgsqGdSzebTLKheFuigRotEV3Or/z5Lg
- k/nVnThWVkTOSRqXTNpJAME6a4KTdcVSxYP+SdZVO1esazHrGb7xPVb7MWSE1cqp
- WEk3Yy8OUOPoPQMc4dyGzd30Mi8IBB6gnFIYOTrpUo0XtkBv8rGGhfsCgYEA5uYn
- 6QgL4NqNT84IXylmMb5ia3iBt6lhxI/A28CDtQvfScl4eYK0IjBwdfG6E1vJgyzg
- nJzv3xEVo9bz+Kq7CcThWpK5JQaPnsV0Q74Wjk0ShHet15txOdJuKImnh5F6lylC
- GTLR9gnptytfMH/uuw4ws0Q2kcg4l5NHKOWOnAcCgYEAvAwIVkhsB0n59Wu4gCZu
- FUZENxYWUk/XUyQ6KnZrG2ih90xQ8+iMyqFOIm/52R2fFKNrdoWoALC6E3ct8+ZS
- pMRLrelFXx8K3it4SwMJR2H8XBEfFW4bH0UtsW7Zafv+AunUs9LETP5gKG1LgXsq
- qgXX43yy2LQ61O365YPZfdUCgYBVbTvA3MhARbvYldrFEnUL3GtfZbNgdxuD9Mee
- xig0eJMBIrgfBLuOlqtVB70XYnM4xAbKCso4loKSHnofO1N99siFkRlM2JOUY2tz
- kMWZmmxKdFjuF0WZ5f/5oYxI/QsFGC+rUQEbbWl56mMKd5qkvEhKWudxoklF0yiV
- ufC8SwKBgDWb8iWqWN5a/kfvKoxFcDM74UHk/SeKMGAL+ujKLf58F+CbweM5pX9C
- EUsxeoUEraVWTiyFVNqD81rCdceus9TdBj0ZIK1vUttaRZyrMAwF0uQSfjtxsOpd
- l69BkyvzjgDPkmOHVGiSZDLi3YDvypbUpo6LOy4v5rVg5U2F/A0v
+ MIIEowIBAAKCAQEAyS6liSDc13Whczd+jR2ZUkFY2ueLzyUoO4ghBfBwp7pcKCKW
+ hkbvTx5wtM7Cz36j0yROX1a2l/9YZdP3w0oQ9RhqR3YW/VK4PWNZiU/uINHqEHdG
+ RYGbnOK6Nh+1Unhms8PjY8uMMBK+WHvlO+sx4zvKr+qRyA2x311DJS8w0+Sze2Vr
+ 2N7sO3sC6WLYg3cI1fKzuAg5/AkfQFv8zq3LuhR+mhQoB8vPB7rVpvO2AO7WVI2B
+ 0UEzWRzFh+XKH8WRflOUBxZ4BfMuufEBcZFTFevFbig8L6RmL+kNEF3tfjNAPHts
+ KavX4WxLczTmLfDkTTxO32IpGThXycKYTiKTpQIDAQABAoIBABpyrHEWRed5X7aN
+ kXCBzKSN/LLChT8VNnB6bppLnV501yVbmV2hDlg2EJZkfCMvwIptwnPcKs2uqZ4G
+ u2gMC6X9Bgkg/YK4u4nZJBiIzoMNYEUL48wYGYS1dcokaapO3nQ8M1+XjyAexrFL
+ 5btL1IIisScRTQWiGe6FtzcN43sSNkBISyDF5zG4Kodynqi0ekITmMl2q5XLWcsM
+ KBnmZcRFEmFae2YYczVy8SXNApkZEvN69znvAX1iDNnZ3sJFchXo1nRPt4stOOKw
+ mydgIYqaNQ22aF3OkblvoA4Y4m+X2Qt1sfkryKa5xTT7DSE81GmmazNI64EWqtES
+ 6Xde6P0CgYEA+V1vuSnE5fWX188abWMbVwNMC71WfHbntFmI+qwWYPEpickm+RGX
+ DDfXs5unlVX4KUmjfplgavO29op1GZTuD9TlRnUAV0+0aJnNq4DY6XsHfD84qsBr
+ gQGEHeJ1cMGNDnZR/EV3eudMalj9Qjpx9NoXNzMykb0/SUYZQemiqwcCgYEAzokC
+ s0GoHVJqan4dfU0h0G5QPncrajW9DGG1ySxK/A2eqbVB8W2ZQx39OS26/Gydb31p
+ cR7zm8PZpNbzLqlIMEbD4F6q22xxvYVtDx/HHPjxHMi87yxwQ9uLDUHoMa/LciTO
+ djv3D1xTDDGxbpjmsdmINetunAs3htxku7JY5PMCgYBs3/TVvXzwgmhHm28Ib4sS
+ VKgxP/uw4CGORsFd4SDsNp9SP3c6rAltFjyheMaUlzKApFwz/DdyuvIZdp5mCvZe
+ BzALsS3y8SPtv6lixiDu3/6GqvvM4bKOYuESQzvPfVJfDB4DrTjben2MuUnqTqZO
+ p6IXQc1EgIJPNcH1W1LgpQKBgAKZlPAevngIBpDqn4JpSyititMOevxuSr/yJvCu
+ Xw9HOJ0YTAk3APvoT7y9h6IP1/eEU6R56EUotP+vOQZ4WRFKgsK7TllOxyvElzfe
+ hYom1BoxqLc2Dv+7rsdu8fZWKTB5qCOy44xM9DquEXa79AN/IojTOuQ5++v1sErw
+ ls/jAoGBANneGe9ogN51mYkrLyg1fhU1i24gFRq+sPGEvsCUoE6Vjw/lawQQ80T8
+ v45TFqvhoGpgznqy3qxDJyguquZg6HN2yW6HE2Dvk7uk3XogcjdXgNDmWqb2j0eE
+ z9pKzHCqfwNVPuYf44Znyo2YeyZ2kHn42MU73oXuFshUs3QHcH+P
-----END RSA PRIVATE KEY-----
PKEY
end
diff --git a/spec/bundler/support/bundle b/spec/bundler/support/bundle
new file mode 100755
index 0000000000..8f8b535295
--- /dev/null
+++ b/spec/bundler/support/bundle
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require_relative "../bundler/support/activate"
+
+load File.expand_path("bundle", Spec::Path.exedir)
diff --git a/spec/bundler/support/bundle.rb b/spec/bundler/support/bundle.rb
new file mode 100644
index 0000000000..aa7b121706
--- /dev/null
+++ b/spec/bundler/support/bundle.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require_relative "path"
+
+warn "#{__FILE__} is deprecated. Please use #{Spec::Path.dev_binstub} instead"
+
+load Spec::Path.dev_binstub
diff --git a/spec/bundler/support/checksums.rb b/spec/bundler/support/checksums.rb
new file mode 100644
index 0000000000..7b69bba668
--- /dev/null
+++ b/spec/bundler/support/checksums.rb
@@ -0,0 +1,135 @@
+# frozen_string_literal: true
+
+module Spec
+ module Checksums
+ class ChecksumsBuilder
+ attr_reader :bundler_registered
+
+ def initialize(enabled = true, &block)
+ @enabled = enabled
+ @checksums = {}
+ yield self if block_given?
+ end
+
+ def initialize_copy(original)
+ super
+ @checksums = @checksums.dup
+ end
+
+ def checksum(repo, name, version, platform = Gem::Platform::RUBY, folder = "gems")
+ @bundler_registered = true if name == "bundler"
+
+ name_tuple = Gem::NameTuple.new(name, version, platform)
+ gem_file = File.join(repo, folder, "#{name_tuple.full_name}.gem")
+ File.open(gem_file, "rb") do |f|
+ register(name_tuple, Bundler::Checksum.from_gem(f, "#{gem_file} (via ChecksumsBuilder#checksum)"))
+ end
+ end
+
+ def no_checksum(name, version, platform = Gem::Platform::RUBY)
+ name_tuple = Gem::NameTuple.new(name, version, platform)
+ register(name_tuple, nil)
+ end
+
+ def delete(name, platform = nil)
+ @checksums.reject! {|k, _| k.name == name && (platform.nil? || k.platform == platform) }
+ end
+
+ def to_s
+ return "" unless @enabled
+
+ locked_checksums = @checksums.map do |name_tuple, checksum|
+ checksum &&= " #{checksum.to_lock}"
+ " #{name_tuple.lock_name}#{checksum}\n"
+ end
+
+ "\nCHECKSUMS\n#{locked_checksums.sort.join}"
+ end
+
+ private
+
+ def register(name_tuple, checksum)
+ delete(name_tuple.name, name_tuple.platform)
+ @checksums[name_tuple] = checksum
+ end
+ end
+
+ def checksums_section(enabled = true, bundler_checksum: true, &block)
+ ChecksumsBuilder.new(enabled, &block).tap do |builder|
+ next if builder.bundler_registered || !bundler_checksum
+
+ next if Bundler::VERSION.to_s.end_with?(".dev")
+ builder.checksum(system_gem_path, "bundler", Bundler::VERSION, Gem::Platform::RUBY, "cache")
+ end
+ end
+
+ def checksums_section_when_enabled(target_lockfile = nil, &block)
+ begin
+ enabled = (target_lockfile || lockfile).match?(/^CHECKSUMS$/)
+ rescue Errno::ENOENT
+ enabled = true
+ end
+ checksums_section(enabled, &block)
+ end
+
+ def checksum_to_lock(*args)
+ checksums_section(true, bundler_checksum: false) do |c|
+ c.checksum(*args)
+ end.to_s.sub(/^CHECKSUMS\n/, "").strip
+ end
+
+ def checksum_digest(*args)
+ checksum_to_lock(*args).split(Bundler::Checksum::ALGO_SEPARATOR, 2).last
+ end
+
+ # if prefixes is given, removes all checksums where the line
+ # has any of the prefixes on the line before the checksum
+ # otherwise, removes all checksums from the lockfile
+ def remove_checksums_from_lockfile(lockfile, *prefixes)
+ head, remaining = lockfile.split(/^CHECKSUMS$/, 2)
+ return lockfile unless remaining
+ checksums, tail = remaining.split("\n\n", 2)
+
+ prefixes =
+ if prefixes.empty?
+ nil
+ else
+ /(#{prefixes.map {|p| Regexp.escape(p) }.join("|")})/
+ end
+
+ checksums = checksums.each_line.map do |line|
+ if prefixes.nil? || line.match?(prefixes)
+ line.gsub(/ sha256=[a-f0-9]{64}/i, "")
+ else
+ line
+ end
+ end
+
+ head.concat(
+ "CHECKSUMS",
+ checksums.join,
+ "\n\n",
+ tail
+ )
+ end
+
+ def remove_checksums_section_from_lockfile(lockfile)
+ head, remaining = lockfile.split(/^CHECKSUMS$/, 2)
+ return lockfile unless remaining
+ _checksums, tail = remaining.split("\n\n", 2)
+ head.concat(tail)
+ end
+
+ def checksum_from_package(gem_file, name, version)
+ name_tuple = Gem::NameTuple.new(name, version)
+
+ checksum = nil
+
+ File.open(gem_file, "rb") do |f|
+ checksum = Bundler::Checksum.from_gem(f, gemfile)
+ end
+
+ "#{name_tuple.lock_name} #{checksum.to_lock}"
+ end
+ end
+end
diff --git a/spec/bundler/support/command_execution.rb b/spec/bundler/support/command_execution.rb
index 68e5c56c75..e2915b996d 100644
--- a/spec/bundler/support/command_execution.rb
+++ b/spec/bundler/support/command_execution.rb
@@ -1,7 +1,36 @@
# frozen_string_literal: true
module Spec
- CommandExecution = Struct.new(:command, :working_directory, :exitstatus, :stdout, :stderr) do
+ class CommandExecution
+ def initialize(command, timeout:)
+ @command = command
+ @timeout = timeout
+ @original_stdout = String.new
+ @original_stderr = String.new
+ end
+
+ attr_accessor :exitstatus, :command, :original_stdout, :original_stderr
+ attr_reader :timeout
+ attr_writer :failure_reason
+
+ def raise_error!
+ return unless failure?
+
+ error_header = if failure_reason == :timeout
+ "Invoking `#{command}` was aborted after #{timeout} seconds with output:"
+ else
+ "Invoking `#{command}` failed with output:"
+ end
+
+ raise <<~ERROR
+ #{error_header}
+
+ ----------------------------------------------------------------------
+ #{stdboth}
+ ----------------------------------------------------------------------
+ ERROR
+ end
+
def to_s
"$ #{command}"
end
@@ -11,6 +40,14 @@ module Spec
@stdboth ||= [stderr, stdout].join("\n").strip
end
+ def stdout
+ normalize(original_stdout)
+ end
+
+ def stderr
+ normalize(original_stderr)
+ end
+
def to_s_verbose
[
to_s,
@@ -29,5 +66,13 @@ module Spec
return true unless exitstatus
exitstatus > 0
end
+
+ private
+
+ attr_reader :failure_reason
+
+ def normalize(string)
+ string.dup.force_encoding(Encoding::UTF_8).scrub.strip.gsub("\r\n", "\n")
+ end
end
end
diff --git a/spec/bundler/support/env.rb b/spec/bundler/support/env.rb
new file mode 100644
index 0000000000..0899bd82a3
--- /dev/null
+++ b/spec/bundler/support/env.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Spec
+ module Env
+ def ruby_core?
+ File.exist?(File.expand_path("../../../lib/bundler/bundler.gemspec", __dir__))
+ end
+
+ def rubylib
+ ENV["RUBYLIB"].to_s.split(File::PATH_SEPARATOR)
+ end
+ end
+end
diff --git a/spec/bundler/support/filters.rb b/spec/bundler/support/filters.rb
index 6322efda8b..2be25b4a78 100644
--- a/spec/bundler/support/filters.rb
+++ b/spec/bundler/support/filters.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
-require_relative "sudo"
-
class RequirementChecker < Proc
- def self.against(present)
- provided = Gem::Version.new(present)
-
+ def self.against(provided)
new do |required|
- !Gem::Requirement.new(required).satisfied_by?(provided)
+ requirement = Gem::Requirement.new(required)
+
+ !requirement.satisfied_by?(provided)
end.tap do |checker|
checker.provided = provided
end
@@ -16,24 +14,29 @@ class RequirementChecker < Proc
attr_accessor :provided
def inspect
- "\"!= #{provided}\""
+ "\"#{provided}\""
end
end
-RSpec.configure do |config|
- config.filter_run_excluding :sudo => true
- config.filter_run_excluding :realworld => true
-
- git_version = Bundler::Source::Git::GitProxy.new(nil, nil, nil).version
+git_version = Gem::Version.new(`git --version`[/(\d+\.\d+\.\d+)/, 1])
- config.filter_run_excluding :rubygems => RequirementChecker.against(Gem::VERSION)
- config.filter_run_excluding :git => RequirementChecker.against(git_version)
- config.filter_run_excluding :bundler => RequirementChecker.against(Bundler::VERSION.split(".")[0])
- config.filter_run_excluding :ruby_repo => !ENV["GEM_COMMAND"].nil?
- config.filter_run_excluding :no_color_tty => Gem.win_platform? || !ENV["GITHUB_ACTION"].nil?
- config.filter_run_excluding :permissions => Gem.win_platform?
- config.filter_run_excluding :readline => Gem.win_platform?
- config.filter_run_excluding :jruby => RUBY_PLATFORM != "java"
+RSpec.configure do |config|
+ config.filter_run_excluding realworld: true
+
+ config.filter_run_excluding rubygems: RequirementChecker.against(Gem.rubygems_version)
+ config.filter_run_excluding git: RequirementChecker.against(git_version)
+ config.filter_run_excluding ruby_repo: !ENV["GEM_COMMAND"].nil?
+ config.filter_run_excluding no_color_tty: Gem.win_platform? || !ENV["GITHUB_ACTION"].nil?
+ config.filter_run_excluding permissions: Gem.win_platform?
+ config.filter_run_excluding readline: Gem.win_platform?
+ config.filter_run_excluding jruby_only: RUBY_ENGINE != "jruby"
+ config.filter_run_excluding truffleruby_only: RUBY_ENGINE != "truffleruby"
+ config.filter_run_excluding man: Gem.win_platform?
+ config.filter_run_excluding mri_only: RUBY_ENGINE != "ruby"
config.filter_run_when_matching :focus unless ENV["CI"]
+
+ config.before(:each, :bundler) do |example|
+ bundle_config "simulate_version #{example.metadata[:bundler]}"
+ end
end
diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb
index 7529dc460a..46718f5fa4 100644
--- a/spec/bundler/support/hax.rb
+++ b/spec/bundler/support/hax.rb
@@ -1,5 +1,10 @@
# frozen_string_literal: true
+if ENV["BUNDLER_SPEC_RUBY_PLATFORM"]
+ Object.send(:remove_const, :RUBY_PLATFORM)
+ RUBY_PLATFORM = ENV["BUNDLER_SPEC_RUBY_PLATFORM"]
+end
+
module Gem
def self.ruby=(ruby)
@ruby = ruby
@@ -9,52 +14,61 @@ module Gem
Gem.ruby = ENV["RUBY"]
end
- if ENV["BUNDLER_SPEC_PLATFORM"]
- class Platform
- @local = new(ENV["BUNDLER_SPEC_PLATFORM"])
+ if ENV["BUNDLER_GEM_DEFAULT_DIR"]
+ @default_dir = ENV["BUNDLER_GEM_DEFAULT_DIR"]
+ @default_specifications_dir = nil
+ end
+
+ spec_platform = ENV["BUNDLER_SPEC_PLATFORM"]
+ if spec_platform
+ if /mingw|mswin/.match?(spec_platform)
+ @@win_platform = nil # rubocop:disable Style/ClassVars
+ RbConfig::CONFIG["host_os"] = spec_platform.gsub(/^[^-]+-/, "").tr("-", "_")
end
- @platforms = [Gem::Platform::RUBY, Gem::Platform.local]
- if ENV["BUNDLER_SPEC_PLATFORM"] == "ruby"
- class << self
- remove_method :finish_resolve
+ RbConfig::CONFIG["arch"] = spec_platform
- def finish_resolve
- []
- end
- end
+ class Platform
+ @local = nil
end
+ @platforms = []
end
- # We only need this hack for rubygems versions without the BundlerVersionFinder
- if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
- @path_to_default_spec_map.delete_if do |_path, spec|
- spec.name == "bundler"
- end
+ if ENV["BUNDLER_SPEC_GEM_SOURCES"]
+ self.sources = [ENV["BUNDLER_SPEC_GEM_SOURCES"]]
end
-end
-if ENV["BUNDLER_SPEC_WINDOWS"] == "true"
- require_relative "path"
- require "bundler/constants"
+ if ENV["BUNDLER_SPEC_READ_ONLY"]
+ module ReadOnly
+ def open(file, mode)
+ if file != IO::NULL && mode == "wb"
+ raise Errno::EROFS
+ else
+ super
+ end
+ end
+ end
- module Bundler
- remove_const :WINDOWS if defined?(WINDOWS)
- WINDOWS = true
+ File.singleton_class.prepend ReadOnly
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
+ if ENV["BUNDLER_SPEC_FAKE_RESOLVE"]
+ module FakeResolv
+ def getaddrinfo(host, port)
+ if host == ENV["BUNDLER_SPEC_FAKE_RESOLVE"]
+ [["AF_INET", port, "127.0.0.1", "127.0.0.1", 2, 2, 17]]
+ else
+ super
+ end
end
end
+
+ Socket.singleton_class.prepend FakeResolv
end
end
+
+# mise installed rubygems_plugin.rb to system wide `site_ruby` directory.
+# This empty module avoid to call `mise` command.
+module ReshimInstaller
+ def self.reshim; end
+end
diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb
index b5648b84a8..b0d4b5008b 100644
--- a/spec/bundler/support/helpers.rb
+++ b/spec/bundler/support/helpers.rb
@@ -1,66 +1,52 @@
# frozen_string_literal: true
-require_relative "command_execution"
require_relative "the_bundle"
require_relative "path"
+require_relative "options"
+require_relative "subprocess"
module Spec
module Helpers
include Spec::Path
+ include Spec::Options
+ include Spec::Subprocess
+
+ def self.extended(mod)
+ mod.extend Spec::Path
+ mod.extend Spec::Options
+ mod.extend Spec::Subprocess
+ end
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))
- FileUtils.rm_rf(dir)
+ next if %w[base base_system remote1 rubocop standard gems rubygems . ..].include?(File.basename(dir))
+ FileUtils.rm_r(dir)
end
FileUtils.mkdir_p(home)
FileUtils.mkdir_p(tmpdir)
- reset_paths!
- end
-
- def reset_paths!
Bundler.reset!
+ Bundler::Source::Git::GitProxy.reset
Gem.clear_paths
end
- def the_bundle(*args)
- TheBundle.new(*args)
- end
-
- def command_executions
- @command_executions ||= []
+ def the_bundle
+ TheBundle.new
end
- def last_command
- command_executions.last || raise("There is no last command")
- end
-
- def out
- last_command.stdout
- end
-
- def err
- last_command.stderr
- end
-
- MAJOR_DEPRECATION = /^\[DEPRECATED\]\s*/.freeze
+ MAJOR_DEPRECATION = /^\[DEPRECATED\]\s*/
def err_without_deprecations
err.gsub(/#{MAJOR_DEPRECATION}.+[\n]?/, "")
end
def deprecations
- err.split("\n").select {|l| l =~ MAJOR_DEPRECATION }.join("\n").split(MAJOR_DEPRECATION)
- end
-
- def exitstatus
- last_command.exitstatus
+ err.split("\n").filter_map {|l| l.sub(MAJOR_DEPRECATION, "") if l.match?(MAJOR_DEPRECATION) }
end
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 'bundler' ; Bundler.ui.silence { Bundler.setup(#{groups}) }"
ruby([setup, cmd].join(" ; "), opts)
end
@@ -69,7 +55,7 @@ module Spec
begin
#{ruby}
rescue LoadError => e
- warn "ZOMG LOAD ERROR" if e.message.include?("-- #{name}")
+ warn e.message if e.message.include?("-- #{name}")
end
RUBY
opts = args.last.is_a?(Hash) ? args.pop : {}
@@ -77,10 +63,11 @@ module Spec
run(cmd, *args)
end
- def bundle(cmd, options = {}, &block)
- with_sudo = options.delete(:sudo)
- sudo = with_sudo == :preserve_env ? "sudo -E --preserve-env=RUBYOPT" : "sudo" if with_sudo
+ def in_bundled_app(cmd, options = {})
+ sys_exec(cmd, dir: bundled_app, raise_on_error: options[:raise_on_error])
+ end
+ def bundle(cmd, options = {}, &block)
bundle_bin = options.delete(:bundle_bin)
bundle_bin ||= installed_bindir.join("bundle")
@@ -88,27 +75,23 @@ module Spec
requires = options.delete(:requires) || []
- artifice = options.delete(:artifice) do
- if RSpec.current_example.metadata[:realworld]
- "vcr"
- else
- "fail"
- end
- end
- if artifice
- requires << "#{Path.spec_dir}/support/artifice/#{artifice}.rb"
- end
+ dir = options.delete(:dir) || bundled_app
+ custom_load_path = options.delete(:load_path)
load_path = []
- load_path << spec_dir
+ load_path << custom_load_path if custom_load_path
+
+ build_env_options = { load_path: load_path, requires: requires, env: env }
+ build_env_options.merge!(artifice: options.delete(:artifice)) if options.key?(:artifice) || cmd.start_with?("exec")
+
+ match_source(cmd)
+
+ env = build_env(build_env_options)
- dir = options.delete(:dir) || bundled_app
raise_on_error = options.delete(:raise_on_error)
args = options.map do |k, v|
case v
- when nil
- next
when true
" --#{k}"
when false
@@ -118,20 +101,31 @@ module Spec
end
end.join
- ruby_cmd = build_ruby_cmd({ :sudo => sudo, :load_path => load_path, :requires => requires })
- cmd = "#{ruby_cmd} #{bundle_bin} #{cmd}#{args}"
- sys_exec(cmd, { :env => env, :dir => dir, :raise_on_error => raise_on_error }, &block)
+ cmd = "#{Gem.ruby} #{bundle_bin} #{cmd}#{args}"
+ sys_exec(cmd, { env: env, dir: dir, raise_on_error: raise_on_error }, &block)
+ end
+
+ def main_source(dir)
+ gemfile = File.expand_path("Gemfile", dir)
+ return unless File.exist?(gemfile)
+
+ match = File.readlines(gemfile).first.match(/source ["'](?<source>[^"']+)["']/)
+ return unless match
+
+ match[:source]
end
def bundler(cmd, options = {})
- options[:bundle_bin] = system_gem_path.join("bin/bundler")
+ options[:bundle_bin] = system_gem_path("bin/bundler")
bundle(cmd, options)
end
def ruby(ruby, options = {})
- ruby_cmd = build_ruby_cmd
- escaped_ruby = RUBY_PLATFORM == "java" ? ruby.shellescape.dump : ruby.shellescape
- sys_exec(%(#{ruby_cmd} -w -e #{escaped_ruby}), options)
+ env = build_env({ artifice: nil }.merge(options))
+ escaped_ruby = ruby.shellescape
+ options[:env] = env if env
+ options[:dir] ||= bundled_app
+ sys_exec(%(#{Gem.ruby} -w -e #{escaped_ruby}), options)
end
def load_error_ruby(ruby, name, opts = {})
@@ -139,22 +133,49 @@ module Spec
begin
#{ruby}
rescue LoadError => e
- warn "ZOMG LOAD ERROR" if e.message.include?("-- #{name}")
+ warn e.message if e.message.include?("-- #{name}")
end
R
end
- def build_ruby_cmd(options = {})
- sudo = options.delete(:sudo)
+ def build_env(options = {})
+ env = options.delete(:env) || {}
+ libs = options.delete(:load_path) || []
+ env["RUBYOPT"] = opt_add("-I#{libs.join(File::PATH_SEPARATOR)}", env["RUBYOPT"]) if libs.any?
+
+ current_example = RSpec.current_example
- libs = options.delete(:load_path)
- lib_option = libs ? "-I#{libs.join(File::PATH_SEPARATOR)}" : []
+ main_source = @gemfile_source if defined?(@gemfile_source)
+ compact_index_main_source = main_source&.start_with?("https://gem.repo", "https://gems.security")
requires = options.delete(:requires) || []
- requires << "#{Path.spec_dir}/support/hax.rb"
- require_option = requires.map {|r| "-r#{r}" }
+ requires << hax
- [sudo, Gem.ruby, *lib_option, *require_option].compact.join(" ")
+ artifice = options.delete(:artifice) do
+ if current_example && current_example.metadata[:realworld]
+ "vcr"
+ elsif compact_index_main_source
+ env["BUNDLER_SPEC_GEM_REPO"] ||=
+ case main_source
+ when "https://gem.repo1" then gem_repo1.to_s
+ when "https://gem.repo2" then gem_repo2.to_s
+ when "https://gem.repo3" then gem_repo3.to_s
+ when "https://gem.repo4" then gem_repo4.to_s
+ when "https://gems.security" then security_repo.to_s
+ end
+
+ "compact_index"
+ else
+ "fail"
+ end
+ end
+ if artifice
+ requires << "#{Path.spec_dir}/support/artifice/#{artifice}.rb"
+ end
+
+ requires.each {|r| env["RUBYOPT"] = opt_add("-r#{r}", env["RUBYOPT"]) }
+
+ env
end
def gembin(cmd, options = {})
@@ -162,110 +183,91 @@ module Spec
sys_exec(cmd.to_s, options)
end
- def gem_command(command, options = {})
+ def sys_exec(cmd, options = {}, &block)
env = options[:env] || {}
- env["RUBYOPT"] = opt_add("-r#{spec_dir}/support/hax.rb", env["RUBYOPT"] || ENV["RUBYOPT"])
+ env["RUBYOPT"] = opt_add(opt_add("-r#{spec_dir}/support/switch_rubygems.rb", env["RUBYOPT"]), ENV["RUBYOPT"])
options[:env] = env
- sys_exec("#{Path.gem_bin} #{command}", options)
- end
-
- def rake
- "#{Gem.ruby} -S #{ENV["GEM_PATH"]}/bin/rake"
- end
- def git(cmd, path, options = {})
- sys_exec("git #{cmd}", options.merge(:dir => path))
+ sh(cmd, options, &block)
end
- def sys_exec(cmd, options = {})
- env = options[:env] || {}
- env["RUBYOPT"] = opt_add("-r#{spec_dir}/support/switch_rubygems.rb", env["RUBYOPT"] || ENV["RUBYOPT"])
- dir = options[:dir] || bundled_app
- command_execution = CommandExecution.new(cmd.to_s, dir)
-
- require "open3"
- require "shellwords"
- Open3.popen3(env, *cmd.shellsplit, :chdir => dir) do |stdin, stdout, stderr, wait_thr|
- yield stdin, stdout, wait_thr if block_given?
- stdin.close
-
- stdout_read_thread = Thread.new { stdout.read }
- stderr_read_thread = Thread.new { stderr.read }
- command_execution.stdout = stdout_read_thread.value.strip
- command_execution.stderr = stderr_read_thread.value.strip
-
- status = wait_thr.value
- command_execution.exitstatus = if status.exited?
- status.exitstatus
- elsif status.signaled?
- 128 + status.termsig
- end
- end
-
- unless options[:raise_on_error] == false || command_execution.success?
- raise <<~ERROR
-
- Invoking `#{cmd}` failed with output:
- ----------------------------------------------------------------------
- #{command_execution.stdboth}
- ----------------------------------------------------------------------
- ERROR
+ def bundle_config(config = nil, path = bundled_app(".bundle/config"))
+ if config.is_a?(String)
+ key, value = config.split(" ", 2)
+ config = { Bundler::Settings.key_for(key) => value }
end
- command_executions << command_execution
+ current = File.exist?(path) ? Psych.load_file(path) : {}
+ return current unless config
- command_execution.stdout
- end
+ current = {} if current.empty?
- def all_commands_output
- return [] if command_executions.empty?
+ FileUtils.mkdir_p(File.dirname(path))
- "\n\nCommands:\n#{command_executions.map(&:to_s_verbose).join("\n\n")}"
- end
+ new_config = current.merge(config).compact
- def config(config = nil, path = bundled_app(".bundle/config"))
- return YAML.load_file(path) unless config
- FileUtils.mkdir_p(File.dirname(path))
- File.open(path, "w") do |f|
- f.puts config.to_yaml
+ File.open(path, "w+") do |f|
+ f.puts new_config.to_yaml
end
- config
+
+ new_config
end
- def global_config(config = nil)
- config(config, home(".bundle/config"))
+ def bundle_config_global(config = nil)
+ bundle_config(config, home(".bundle/config"))
end
- def create_file(*args)
- path = bundled_app(args.shift)
- path = args.shift if args.first.is_a?(Pathname)
- str = args.shift || ""
+ def create_file(path, contents = "")
+ contents = strip_whitespace(contents)
+ path = Pathname.new(path).expand_path(bundled_app) unless path.is_a?(Pathname)
path.dirname.mkpath
- File.open(path.to_s, "w") do |f|
- f.puts strip_whitespace(str)
+ path.write(contents)
+
+ # if the file is a script, create respective bat file on Windows
+ if contents.start_with?("#!")
+ path.chmod(0o755)
+ if Gem.win_platform?
+ path.sub_ext(".bat").write <<~SCRIPT
+ @ECHO OFF
+ @"ruby.exe" "%~dpn0" %*
+ SCRIPT
+ end
end
end
def gemfile(*args)
- contents = args.shift
+ contents = args.pop
if contents.nil?
- File.open(bundled_app_gemfile, "r", &:read)
+ read_gemfile
else
- create_file("Gemfile", contents, *args)
+ match_source(contents)
+ create_file(args.pop || "Gemfile", contents)
end
end
def lockfile(*args)
- contents = args.shift
+ contents = args.pop
if contents.nil?
- File.open(bundled_app_lock, "r", &:read)
+ read_lockfile
else
- create_file("Gemfile.lock", contents, *args)
+ create_file(args.pop || "Gemfile.lock", contents)
end
end
+ def read_gemfile(file = "Gemfile")
+ read_bundled_app_file(file)
+ end
+
+ def read_lockfile(file = "Gemfile.lock")
+ read_bundled_app_file(file)
+ end
+
+ def read_bundled_app_file(file)
+ bundled_app(file).read
+ end
+
def strip_whitespace(str)
# Trim the leading spaces
spaces = str[/\A\s+/, 0] || ""
@@ -273,82 +275,141 @@ module Spec
end
def install_gemfile(*args)
+ opts = args.last.is_a?(Hash) ? args.pop : {}
gemfile(*args)
- opts = args.last.is_a?(Hash) ? args.last : {}
- opts[:retry] ||= 0
bundle :install, opts
end
def lock_gemfile(*args)
gemfile(*args)
opts = args.last.is_a?(Hash) ? args.last : {}
- opts[:retry] ||= 0
bundle :lock, opts
end
+ def base_system_gems(*names, **options)
+ system_gems names.map {|name| find_base_path(name) }, **options
+ end
+
def system_gems(*gems)
gems = gems.flatten
options = gems.last.is_a?(Hash) ? gems.pop : {}
- path = options.fetch(:path, system_gem_path)
- with_gem_path_as(path) do
- gem_repo = options.fetch(:gem_repo, gem_repo1)
- gems.each do |g|
- gem_name = g.to_s
- if gem_name.start_with?("bundler")
- version = gem_name.match(/\Abundler-(?<version>.*)\z/)[:version] if gem_name != "bundler"
- with_built_bundler(version) {|gem_path| install_gem(gem_path) }
- elsif gem_name =~ %r{\A(?:[a-zA-Z]:)?/.*\.gem\z}
- install_gem(gem_name)
- else
- install_gem("#{gem_repo}/gems/#{gem_name}.gem")
- end
+ install_dir = options.fetch(:path, system_gem_path)
+ default = options.fetch(:default, false)
+ gems.each do |g|
+ gem_name = g.to_s
+ bundler = gem_name.match(/\Abundler-(?<version>.*)\z/)
+
+ if bundler
+ with_built_bundler(bundler[:version], released: options.fetch(:released, false)) {|gem_path| install_gem(gem_path, install_dir, default) }
+ elsif %r{\A(?:[a-zA-Z]:)?/.*\.gem\z}.match?(gem_name)
+ install_gem(gem_name, install_dir, default)
+ else
+ gem_repo = options.fetch(:gem_repo, gem_repo1)
+ install_gem("#{gem_repo}/gems/#{gem_name}.gem", install_dir, default)
end
end
end
- def install_gem(path)
- raise "OMG `#{path}` does not exist!" unless File.exist?(path)
+ def self.install_dev_bundler
+ extend self
- gem_command "install --no-document --ignore-dependencies '#{path}'"
+ with_built_bundler(nil, build_path: tmp_root) {|gem_path| install_gem(gem_path, pristine_system_gem_path) }
end
- def with_built_bundler(version = nil)
- version ||= Bundler::VERSION
- full_name = "bundler-#{version}"
- build_path = tmp + full_name
- bundler_path = build_path + "#{full_name}.gem"
+ def install_gem(path, install_dir, default = false)
+ raise ArgumentError, "`#{path}` does not exist!" unless File.exist?(path)
- Dir.mkdir build_path
+ require "rubygems/installer"
- begin
- shipped_files.each do |shipped_file|
- target_shipped_file = build_path + shipped_file
- target_shipped_dir = File.dirname(target_shipped_file)
- FileUtils.mkdir_p target_shipped_dir unless File.directory?(target_shipped_dir)
- FileUtils.cp shipped_file, target_shipped_file, :preserve => true
- end
+ with_simulated_platform do
+ installer = Gem::Installer.at(
+ path.to_s,
+ install_dir: install_dir.to_s,
+ document: [],
+ ignore_dependencies: true,
+ wrappers: true,
+ env_shebang: true,
+ force: true
+ )
+ installer.install
+ end
+
+ if default
+ gem = Pathname.new(path).basename.to_s.match(/(.*)\.gem/)[1]
+
+ # Revert Gem::Installer#write_spec and apply Gem::Installer#write_default_spec
+ FileUtils.mkdir_p File.join(install_dir, "specifications", "default")
+ File.rename File.join(install_dir, "specifications", gem + ".gemspec"),
+ File.join(install_dir, "specifications", "default", gem + ".gemspec")
- replace_version_file(version, dir: build_path) # rubocop:disable Style/HashSyntax
+ # Revert Gem::Installer#write_cache_file
+ File.delete File.join(install_dir, "cache", gem + ".gem")
+ end
+ end
- build_metadata = {
- :built_at => loaded_gemspec.date.utc.strftime("%Y-%m-%d"),
- :git_commit_sha => git_commit_sha,
- }
+ def uninstall_gem(name, options = {})
+ require "rubygems/uninstaller"
- replace_build_metadata(build_metadata, dir: build_path) # rubocop:disable Style/HashSyntax
+ gem_home = options.dig(:env, "GEM_HOME") || system_gem_path.to_s
- gem_command "build #{relative_gemspec}", :dir => build_path
+ with_env_vars("GEM_HOME" => gem_home) do
+ Gem.clear_paths
- yield(bundler_path)
+ uninstaller = Gem::Uninstaller.new(
+ name,
+ ignore: true,
+ executables: true,
+ all: true
+ )
+ uninstaller.uninstall
ensure
- build_path.rmtree
+ Gem.clear_paths
end
end
+ def installed_gems_list(options = {})
+ gem_home = options.dig(:env, "GEM_HOME") || system_gem_path.to_s
+
+ # Temporarily set GEM_HOME for the command
+ old_gem_home = ENV["GEM_HOME"]
+ ENV["GEM_HOME"] = gem_home
+ Gem.clear_paths
+
+ begin
+ require "rubygems/commands/list_command"
+
+ # Capture output from the list command
+ require "stringio"
+ output_io = StringIO.new
+ cmd = Gem::Commands::ListCommand.new
+ cmd.ui = Gem::StreamUI.new(StringIO.new, output_io, StringIO.new, false)
+ cmd.invoke
+ output = output_io.string.strip
+ ensure
+ ENV["GEM_HOME"] = old_gem_home
+ Gem.clear_paths
+ end
+
+ # Create a fake command execution so `out` helper works
+ command_execution = Spec::CommandExecution.new("gem list", timeout: 60)
+ command_execution.original_stdout << output
+ command_execution.exitstatus = 0
+ command_executions << command_execution
+
+ output
+ end
+
+ def with_built_bundler(version = nil, opts = {}, &block)
+ require_relative "builders"
+
+ Builders::BundlerBuilder.new(self, "bundler", version)._build(opts, &block)
+ end
+
def with_gem_path_as(path)
without_env_side_effects do
ENV["GEM_HOME"] = path.to_s
ENV["GEM_PATH"] = path.to_s
+ ENV["BUNDLER_ORIG_GEM_HOME"] = nil
ENV["BUNDLER_ORIG_GEM_PATH"] = nil
yield
end
@@ -369,20 +430,40 @@ module Spec
ENV.replace(backup)
end
- def with_path_added(path)
- with_path_as([path.to_s, ENV["PATH"]].join(File::PATH_SEPARATOR)) do
- yield
+ # Simulate the platform set by BUNDLER_SPEC_PLATFORM for in-process
+ # operations, mirroring what hax.rb does for subprocesses.
+ def with_simulated_platform
+ spec_platform = ENV["BUNDLER_SPEC_PLATFORM"]
+ unless spec_platform
+ return yield
end
- end
- def opt_add(option, options)
- [option.strip, options].compact.reject(&:empty?).join(" ")
- end
+ old_arch = RbConfig::CONFIG["arch"]
+ old_host_os = RbConfig::CONFIG["host_os"]
- def opt_remove(option, options)
- return unless options
+ if /mingw|mswin/.match?(spec_platform)
+ Gem.class_variable_set(:@@win_platform, nil) # rubocop:disable Style/ClassVars
+ RbConfig::CONFIG["host_os"] = spec_platform.gsub(/^[^-]+-/, "").tr("-", "_")
+ end
- options.split(" ").reject {|opt| opt.strip == option.strip }.join(" ")
+ RbConfig::CONFIG["arch"] = spec_platform
+ Gem::Platform.instance_variable_set(:@local, nil)
+ Gem.instance_variable_set(:@platforms, [])
+
+ yield
+ ensure
+ if spec_platform
+ RbConfig::CONFIG["arch"] = old_arch
+ RbConfig::CONFIG["host_os"] = old_host_os
+ Gem::Platform.instance_variable_set(:@local, nil)
+ Gem.instance_variable_set(:@platforms, [])
+ end
+ end
+
+ def with_path_added(path)
+ with_path_as([path.to_s, ENV["PATH"]].join(File::PATH_SEPARATOR)) do
+ yield
+ end
end
def break_git!
@@ -395,102 +476,75 @@ module Spec
end
def with_fake_man
- skip "fake_man is not a Windows friendly binstub" if Gem.win_platform?
-
FileUtils.mkdir_p(tmp("fake_man"))
- File.open(tmp("fake_man/man"), "w", 0o755) do |f|
- f.puts "#!/usr/bin/env ruby\nputs ARGV.inspect\n"
- end
+ create_file(tmp("fake_man/man"), <<~SCRIPT)
+ #!/usr/bin/env ruby
+ puts ARGV.inspect
+ SCRIPT
with_path_added(tmp("fake_man")) { yield }
end
def pristine_system_gems(*gems)
- FileUtils.rm_rf(system_gem_path)
-
- system_gems(*gems)
- end
-
- def realworld_system_gems(*gems)
- gems = gems.flatten
- opts = gems.last.is_a?(Hash) ? gems.pop : {}
- path = opts.fetch(:path, system_gem_path)
+ FileUtils.rm_r(system_gem_path)
- with_gem_path_as(path) do
- gems.each do |gem|
- gem_command "install --no-document #{gem}"
- end
+ if gems.any?
+ system_gems(*gems)
+ else
+ default_system_gems
end
end
- def cache_gems(*gems)
+ def cache_gems(*gems, gem_repo: gem_repo1)
gems = gems.flatten
- FileUtils.rm_rf("#{bundled_app}/vendor/cache")
FileUtils.mkdir_p("#{bundled_app}/vendor/cache")
gems.each do |g|
- path = "#{gem_repo1}/gems/#{g}.gem"
- raise "OMG `#{path}` does not exist!" unless File.exist?(path)
+ path = "#{gem_repo}/gems/#{g}.gem"
+ raise ArgumentError, "`#{path}` does not exist!" unless File.exist?(path)
FileUtils.cp(path, "#{bundled_app}/vendor/cache")
end
end
def simulate_new_machine
- FileUtils.rm_rf bundled_app(".bundle")
- pristine_system_gems :bundler
+ FileUtils.rm_r bundled_app(".bundle")
+ pristine_system_gems
end
- def simulate_platform(platform)
- old = ENV["BUNDLER_SPEC_PLATFORM"]
- ENV["BUNDLER_SPEC_PLATFORM"] = platform.to_s
- yield if block_given?
- ensure
- ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given?
+ def default_system_gems
+ FileUtils.cp_r pristine_system_gem_path, system_gem_path
end
- def simulate_ruby_version(version)
- return if version == RUBY_VERSION
- old = ENV["BUNDLER_SPEC_RUBY_VERSION"]
- ENV["BUNDLER_SPEC_RUBY_VERSION"] = version
- yield if block_given?
+ def simulate_ruby_platform(ruby_platform)
+ old = ENV["BUNDLER_SPEC_RUBY_PLATFORM"]
+ ENV["BUNDLER_SPEC_RUBY_PLATFORM"] = ruby_platform.to_s
+ yield
ensure
- ENV["BUNDLER_SPEC_RUBY_VERSION"] = old if block_given?
+ ENV["BUNDLER_SPEC_RUBY_PLATFORM"] = old
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
+ def simulate_platform(platform)
+ old = ENV["BUNDLER_SPEC_PLATFORM"]
+ ENV["BUNDLER_SPEC_PLATFORM"] = platform.to_s
+ yield
ensure
- ENV["BUNDLER_SPEC_WINDOWS"] = old
+ ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given?
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")
+ def current_ruby_minor
+ Gem.ruby_version.segments.tap {|s| s.delete_at(2) }.join(".")
+ end
- old = ENV["BUNDLER_VERSION"]
- ENV["BUNDLER_VERSION"] = Bundler::VERSION
- yield
- ensure
- ENV["BUNDLER_VERSION"] = old
+ def next_ruby_minor
+ ruby_major_minor.map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".")
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")
- { "BUNDLER_VERSION" => Bundler::VERSION }
- else
- {}
- end
+ def ruby_major_minor
+ Gem.ruby_version.segments[0..1]
end
def revision_for(path)
- sys_exec("git rev-parse HEAD", :dir => path).strip
+ git("rev-parse HEAD", path).strip
end
def with_read_only(pattern)
@@ -542,35 +596,33 @@ module Spec
end
end
- def require_rack
- # need to hack, so we can require rack
+ def require_rack_test
+ # need to hack, so we can require rack for testing
old_gem_home = ENV["GEM_HOME"]
- ENV["GEM_HOME"] = Spec::Path.base_system_gems.to_s
- require "rack"
+ ENV["GEM_HOME"] = Spec::Path.scoped_base_system_gem_path.to_s
+ require "rack/test"
ENV["GEM_HOME"] = old_gem_home
end
- def wait_for_server(host, port, seconds = 15)
- tries = 0
- sleep 0.5
- TCPSocket.new(host, port)
- rescue StandardError => e
- raise(e) if tries > (seconds * 2)
- tries += 1
- retry
+ def exit_status_for_signal(signal_number)
+ # For details see: https://en.wikipedia.org/wiki/Exit_status#Shell_and_scripts
+ 128 + signal_number
end
- def find_unused_port
- port = 21_453
- begin
- port += 1 while TCPSocket.new("127.0.0.1", port)
- rescue StandardError
- false
- end
- port
+ def empty_repo4
+ FileUtils.rm_r gem_repo4
+
+ build_repo4 {}
end
- private
+ private
+
+ def match_source(contents)
+ match = /source ["']?(?<source>http[^"']+)["']?/.match(contents)
+ return unless match
+
+ @gemfile_source = match[:source]
+ end
def git_root_dir?
root.to_s == `git rev-parse --show-toplevel`.chomp
diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb
index 7440523fc9..1fbdd49abe 100644
--- a/spec/bundler/support/indexes.rb
+++ b/spec/bundler/support/indexes.rb
@@ -14,23 +14,25 @@ module Spec
alias_method :platforms, :platform
- def resolve(args = [])
+ def resolve(args = [], dependency_api_available: true)
@platforms ||= ["ruby"]
- deps = []
- default_source = instance_double("Bundler::Source::Rubygems", :specs => @index)
- source_requirements = { :default => default_source }
+ default_source = instance_double("Bundler::Source::Rubygems", specs: @index, to_s: "locally install gems", dependency_api_available?: dependency_api_available)
+ source_requirements = { default: default_source }
+ base = args[0] || Bundler::SpecSet.new([])
+ base.each {|ls| ls.source = default_source }
+ gem_version_promoter = args[1] || Bundler::GemVersionPromoter.new
+ originally_locked = args[2] || Bundler::SpecSet.new([])
+ unlock = args[3] || []
@deps.each do |d|
- @platforms.each do |p|
- source_requirements[d.name] = d.source = default_source
- deps << Bundler::DepProxy.new(d, p)
- end
+ name = d.name
+ source_requirements[name] = d.source = default_source
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)
+ packages = Bundler::Resolver::Base.new(source_requirements, @deps, base, @platforms, locked_specs: originally_locked, unlock: unlock)
+ Bundler::Resolver.new(packages, gem_version_promoter).start
+ end
+
+ def should_not_resolve
+ expect { resolve }.to raise_error(Bundler::GemNotFound)
end
def should_resolve_as(specs)
@@ -39,6 +41,12 @@ module Spec
expect(got).to eq(specs.sort)
end
+ def should_resolve_without_dependency_api(specs)
+ got = resolve(dependency_api_available: false)
+ got = got.map(&:full_name).sort
+ expect(got).to eq(specs.sort)
+ end
+
def should_resolve_and_include(specs, args = [])
got = resolve(args)
got = got.map(&:full_name).sort
@@ -47,13 +55,6 @@ module Spec
end
end
- def should_conflict_on(names)
- got = resolve
- raise "The resolve succeeded with: #{got.map(&:full_name).sort.inspect}"
- rescue Bundler::VersionConflict => e
- expect(Array(names).sort).to eq(e.conflicts.sort)
- end
-
def gem(*args, &blk)
build_spec(*args, &blk).first
end
@@ -65,20 +66,18 @@ module Spec
end
def should_conservative_resolve_and_include(opts, unlock, specs)
- # empty unlock means unlock all
opts = Array(opts)
- search = Bundler::GemVersionPromoter.new(@locked, unlock).tap do |s|
+ search = Bundler::GemVersionPromoter.new.tap do |s|
s.level = opts.first
s.strict = opts.include?(:strict)
- s.prerelease_specified = Hash[@deps.map {|d| [d.name, d.requirement.prerelease?] }]
end
- should_resolve_and_include specs, [@base, search]
+ should_resolve_and_include specs, [@base, search, @locked, unlock]
end
def an_awesome_index
build_index do
- gem "rack", %w[0.8 0.9 0.9.1 0.9.2 1.0 1.1]
- gem "rack-mount", %w[0.4 0.5 0.5.1 0.5.2 0.6]
+ gem "myrack", %w[0.8 0.9 0.9.1 0.9.2 1.0 1.1]
+ gem "myrack-mount", %w[0.4 0.5 0.5.1 0.5.2 0.6]
# --- Pre-release support
gem "RubyGems\0", ["1.3.2"]
@@ -89,10 +88,10 @@ module Spec
gem "actionpack", version do
dep "activesupport", version
if version >= v("3.0.0.beta")
- dep "rack", "~> 1.1"
- dep "rack-mount", ">= 0.5"
- elsif version > v("2.3") then dep "rack", "~> 1.0.0"
- elsif version > v("2.0.0") then dep "rack", "~> 0.9.0"
+ dep "myrack", "~> 1.1"
+ dep "myrack-mount", ">= 0.5"
+ elsif version > v("2.3") then dep "myrack", "~> 1.0.0"
+ elsif version > v("2.0.0") then dep "myrack", "~> 0.9.0"
end
end
gem "activerecord", version do
@@ -123,11 +122,11 @@ module Spec
end
versions "1.0 1.2 1.2.1 1.2.2 1.3 1.3.0.1 1.3.5 1.4.0 1.4.2 1.4.2.1" do |version|
- platforms "ruby java mswin32 mingw32 x64-mingw32" do |platform|
+ platforms "ruby java mswin32 mingw32 x64-mingw-ucrt" do |platform|
next if version == v("1.4.2.1") && platform != pl("x86-mswin32")
next if version == v("1.4.2") && platform == pl("x86-mswin32")
gem "nokogiri", version, platform do
- dep "weakling", ">= 0.0.3" if platform =~ pl("java")
+ dep "weakling", ">= 0.0.3" if platform =~ pl("java") # rubocop:disable Performance/RegexpMatch
end
end
end
@@ -304,7 +303,7 @@ module Spec
end
end
- def a_unresovable_child_index
+ def a_unresolvable_child_index
build_index do
gem "json", %w[1.8.0]
@@ -367,7 +366,7 @@ module Spec
def a_circular_index
build_index do
- gem "rack", "1.0.1"
+ gem "myrack", "1.0.1"
gem("foo", "0.2.6") do
dep "bar", ">= 0"
end
diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb
index 01e11eb94d..5a3c38a4db 100644
--- a/spec/bundler/support/matchers.rb
+++ b/spec/bundler/support/matchers.rb
@@ -52,7 +52,7 @@ module Spec
end
def self.define_compound_matcher(matcher, preconditions, &declarations)
- raise "Must have preconditions to define a compound matcher" if preconditions.empty?
+ raise ArgumentError, "Must have preconditions to define a compound matcher" if preconditions.empty?
define_method(matcher) do |*expected, &block_arg|
Precondition.new(
RSpec::Matchers::DSL::Matcher.new(matcher, declarations, self, *expected, &block_arg),
@@ -114,57 +114,87 @@ module Spec
match do
opts = names.last.is_a?(Hash) ? names.pop : {}
source = opts.delete(:source)
- groups = Array(opts[:groups])
- exclude_from_load_path = opts.delete(:exclude_from_load_path)
+ 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.filter_map do |full_name|
+ name, version, platform = full_name.split(/\s+/)
+ platform ||= "ruby"
+ require_path = name.tr("-", "/")
version_const = name == "bundler" ? "Bundler::VERSION" : Spec::Builders.constantize(name)
- code = []
- code << "$LOAD_PATH.delete '#{exclude_from_load_path}'" if exclude_from_load_path
- 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)
+ actual_platform ||= "ruby"
+ 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
- end.compact
+ next "Command to check for inclusion of gem #{full_name} failed"
+ end
@errors.empty?
end
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|
+ @errors = names.filter_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
- end.compact
+ next "expected #{name} (#{version}) not to be installed, but it was"
+ end
@errors.empty?
end
@@ -180,10 +210,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)
@@ -197,13 +223,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/options.rb b/spec/bundler/support/options.rb
new file mode 100644
index 0000000000..551fa1acd8
--- /dev/null
+++ b/spec/bundler/support/options.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Spec
+ module Options
+ def opt_add(option, options)
+ [option.strip, options].compact.reject(&:empty?).join(" ")
+ end
+
+ def opt_remove(option, options)
+ return unless options
+
+ options.split(" ").reject {|opt| opt.strip == option.strip }.join(" ")
+ end
+ end
+end
diff --git a/spec/bundler/support/path.rb b/spec/bundler/support/path.rb
index 042aae92fd..2e6486412f 100644
--- a/spec/bundler/support/path.rb
+++ b/spec/bundler/support/path.rb
@@ -1,12 +1,16 @@
# frozen_string_literal: true
-require "pathname"
+require "pathname" unless defined?(Pathname)
require "rbconfig"
+require_relative "env"
+
module Spec
module Path
+ include Spec::Env
+
def source_root
- @source_root ||= Pathname.new(ruby_core? ? "../../../.." : "../../..").expand_path(__FILE__)
+ @source_root ||= Pathname.new(ruby_core? ? "../../.." : "../../bundler").expand_path(__dir__)
end
def root
@@ -21,24 +25,36 @@ module Spec
@relative_gemspec ||= ruby_core? ? "lib/bundler/bundler.gemspec" : "bundler.gemspec"
end
- def gemspec_dir
- @gemspec_dir ||= gemspec.parent
- end
-
def loaded_gemspec
- @loaded_gemspec ||= Gem::Specification.load(gemspec.to_s)
+ @loaded_gemspec ||= Dir.chdir(source_root) { Gem::Specification.load(gemspec.to_s) }
end
def test_gemfile
- @test_gemfile ||= source_root.join(ruby_core? ? "tool/bundler/test_gems.rb" : "test_gems.rb")
+ @test_gemfile ||= tool_dir.join("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
- @dev_gemfile ||= source_root.join("dev_gems.rb")
+ @dev_gemfile ||= tool_dir.join("dev_gems.rb")
+ end
+
+ def dev_binstub
+ @dev_binstub ||= bindir.join("bundle")
end
def bindir
- @bindir ||= source_root.join(ruby_core? ? "libexec" : "exe")
+ @bindir ||= source_root.join(ruby_core? ? "spec/bin" : "../bin")
+ end
+
+ def exedir
+ @exedir ||= source_root.join(ruby_core? ? "libexec" : "exe")
end
def installed_bindir
@@ -50,17 +66,25 @@ module Spec
end
def gem_bin
- @gem_bin ||= ruby_core? ? ENV["GEM_COMMAND"] : "gem"
+ @gem_bin ||= ENV["GEM_COMMAND"] || "gem"
end
def path
env_path = ENV["PATH"]
- env_path = env_path.split(File::PATH_SEPARATOR).reject {|path| path == bindir.to_s }.join(File::PATH_SEPARATOR) if ruby_core?
+ env_path = env_path.split(File::PATH_SEPARATOR).reject {|path| path == exedir.to_s }.join(File::PATH_SEPARATOR) if ruby_core?
env_path
end
def spec_dir
- @spec_dir ||= source_root.join(ruby_core? ? "spec/bundler" : "spec")
+ @spec_dir ||= source_root.join(ruby_core? ? "spec/bundler" : "../spec")
+ end
+
+ def man_dir
+ @man_dir ||= lib_dir.join("bundler/man")
+ end
+
+ def hax
+ @hax ||= spec_dir.join("support/hax.rb")
end
def tracked_files
@@ -68,7 +92,13 @@ module Spec
end
def shipped_files
- @shipped_files ||= loaded_gemspec.files
+ @shipped_files ||= if ruby_core_tarball?
+ loaded_gemspec.files.map {|f| f.gsub(%r{^exe/}, "libexec/") }
+ elsif ruby_core?
+ tracked_files
+ else
+ loaded_gemspec.files
+ end
end
def lib_tracked_files
@@ -80,7 +110,28 @@ module Spec
end
def tmp(*path)
- source_root.join("tmp", scope, *path)
+ tmp_root.join("#{test_env_version}.#{scope}").join(*path)
+ end
+
+ def tmp_root
+ if ruby_core? && (tmpdir = ENV["TMPDIR"])
+ # Use realpath to resolve any symlinks in TMPDIR (e.g., on macOS /var -> /private/var)
+ real = begin
+ File.realpath(tmpdir)
+ rescue Errno::ENOENT, Errno::EACCES
+ tmpdir
+ end
+ Pathname(real)
+ else
+ (ruby_core? ? source_root : source_root.parent).join("tmp")
+ end
+ end
+
+ # Bump this version whenever you make a breaking change to the spec setup
+ # that requires regenerating tmp/.
+
+ def test_env_version
+ 2
end
def scope
@@ -91,31 +142,35 @@ module Spec
end
def home(*path)
- tmp.join("home", *path)
+ tmp("home", *path)
end
def default_bundle_path(*path)
- if Bundler.feature_flag.default_install_uses_path?
- local_gem_path(*path)
- else
- system_gem_path(*path)
- end
+ system_gem_path(*path)
+ end
+
+ def default_cache_path(*path)
+ default_bundle_path("cache/bundler", *path)
+ end
+
+ def compact_index_cache_path
+ home(".bundle/cache/compact_index")
end
def bundled_app(*path)
- root = tmp.join("bundled_app")
+ root = tmp("bundled_app")
FileUtils.mkdir_p(root)
root.join(*path)
end
def bundled_app2(*path)
- root = tmp.join("bundled_app2")
+ root = tmp("bundled_app2")
FileUtils.mkdir_p(root)
root.join(*path)
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)
@@ -130,8 +185,20 @@ module Spec
bundled_app("Gemfile.lock")
end
- def base_system_gems
- tmp.join("gems/base")
+ def scoped_base_system_gem_path
+ scoped_gem_path(base_system_gem_path)
+ end
+
+ def base_system_gem_path
+ tmp_root.join("gems/base")
+ end
+
+ def rubocop_gem_path
+ tmp_root.join("gems/rubocop")
+ end
+
+ def standard_gem_path
+ tmp_root.join("gems/standard")
end
def file_uri_for(path)
@@ -142,39 +209,47 @@ module Spec
end
def gem_repo1(*args)
- tmp("gems/remote1", *args)
+ gem_path("remote1", *args)
end
def gem_repo_missing(*args)
- tmp("gems/missing", *args)
+ gem_path("missing", *args)
end
def gem_repo2(*args)
- tmp("gems/remote2", *args)
+ gem_path("remote2", *args)
end
def gem_repo3(*args)
- tmp("gems/remote3", *args)
+ gem_path("remote3", *args)
end
def gem_repo4(*args)
- tmp("gems/remote4", *args)
+ gem_path("remote4", *args)
end
def security_repo(*args)
- tmp("gems/security_repo", *args)
+ gem_path("security_repo", *args)
end
def system_gem_path(*path)
- tmp("gems/system", *path)
+ gem_path("system", *path)
end
def pristine_system_gem_path
- tmp("gems/base_system")
+ tmp_root.join("gems/pristine_system")
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 gem_path(*args)
+ tmp("gems", *args)
end
def lib_path(*args)
@@ -204,47 +279,73 @@ module Spec
def replace_version_file(version, dir: source_root)
version_file = File.expand_path("lib/bundler/version.rb", dir)
contents = File.read(version_file)
- contents.sub!(/(^\s+VERSION\s*=\s*)"#{Gem::Version::VERSION_PATTERN}"/, %(\\1"#{version}"))
+ contents.sub!(/(^\s+VERSION\s*=\s*).*$/, %(\\1"#{version}"))
File.open(version_file, "w") {|f| f << contents }
end
- def replace_build_metadata(build_metadata, dir: source_root)
- build_metadata_file = File.expand_path("lib/bundler/build_metadata.rb", dir)
+ def replace_required_ruby_version(version, dir:)
+ gemspec_file = File.expand_path("bundler.gemspec", dir)
+ contents = File.read(gemspec_file)
+ contents.sub!(/(^\s+s\.required_ruby_version\s*=\s*)"[^"]+"/, %(\\1"#{version}"))
+ File.open(gemspec_file, "w") {|f| f << contents }
+ end
- ivars = build_metadata.sort.map do |k, v|
- " @#{k} = #{loaded_gemspec.send(:ruby_code, v)}"
- end.join("\n")
+ def replace_changelog(version, dir:)
+ changelog = File.expand_path("CHANGELOG.md", dir)
+ contents = File.readlines(changelog)
+ contents = [contents[0], contents[1], "## #{version} (2100-01-01)\n", *contents[3..-1]].join
+ File.open(changelog, "w") {|f| f << contents }
+ end
- contents = File.read(build_metadata_file)
- contents.sub!(/^(\s+# begin ivars).+(^\s+# end ivars)/m, "\\1\n#{ivars}\n\\2")
- File.open(build_metadata_file, "w") {|f| f << contents }
+ def git_root
+ ruby_core? ? source_root : source_root.parent
end
- def ruby_core?
- # avoid to warnings
- @ruby_core ||= nil
+ def rake_path
+ find_base_path("rake")
+ end
+
+ def rake_version
+ File.basename(rake_path).delete_prefix("rake-").delete_suffix(".gem")
+ end
- if @ruby_core.nil?
- @ruby_core = true & ENV["GEM_COMMAND"]
+ def sinatra_dependency_paths
+ deps = %w[
+ mustermann
+ rack
+ rack-protection
+ rack-session
+ tilt
+ sinatra
+ base64
+ logger
+ compact_index
+ ]
+ path = if deps.all? {|dep| !Dir[scoped_base_system_gem_path.join("gems/#{dep}-*")].empty? }
+ scoped_base_system_gem_path
+ elsif ruby_core? && deps.all? {|dep| !Dir[source_root.join(".bundle/gems/#{dep}-*")].empty? }
+ source_root.join(".bundle")
else
- @ruby_core
+ scoped_base_system_gem_path
end
- end
- def git_commit_sha
- ruby_core_tarball? ? "unknown" : sys_exec("git rev-parse --short HEAD", :dir => source_root).strip
+ Dir[path.join("gems/{#{deps.join(",")}}-*/lib")].map(&:to_s)
end
- private
+ private
+
+ def find_base_path(name)
+ Dir["#{scoped_base_system_gem_path}/**/#{name}-*.gem"].first
+ end
def git_ls_files(glob)
skip "Not running on a git context, since running tests from a tarball" if ruby_core_tarball?
- sys_exec("git ls-files -z -- #{glob}", :dir => source_root).split("\x0")
+ git("ls-files -z -- #{glob}", source_root).split("\x0")
end
def tracked_files_glob
- ruby_core? ? "lib/bundler lib/bundler.rb spec/bundler man/bundle*" : ""
+ ruby_core? ? "libexec/bundle* lib/bundler lib/bundler.rb spec/bundler man/bundle*" : "lib exe CHANGELOG.md LICENSE.md README.md bundler.gemspec"
end
def lib_tracked_files_glob
@@ -252,17 +353,29 @@ module Spec
end
def man_tracked_files_glob
- ruby_core? ? "man/bundle* man/gemfile*" : "man"
- end
-
- def git_root
- ruby_core? ? source_root : source_root.parent
+ "lib/bundler/man/bundle*.1.ronn lib/bundler/man/gemfile*.5.ronn"
end
def ruby_core_tarball?
!git_root.join(".git").directory?
end
+ def rubocop_gemfile_basename
+ tool_dir.join("rubocop_gems.rb")
+ end
+
+ def standard_gemfile_basename
+ tool_dir.join("standard_gems.rb")
+ end
+
+ def tool_dir
+ ruby_core? ? source_root.join("tool/bundler") : source_root.join("../tool/bundler")
+ end
+
+ def templates_dir
+ lib_dir.join("bundler", "templates")
+ end
+
extend self
end
end
diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb
index a6ebd7510f..56a0843005 100644
--- a/spec/bundler/support/platforms.rb
+++ b/spec/bundler/support/platforms.rb
@@ -2,66 +2,30 @@
module Spec
module Platforms
- include Bundler::GemHelpers
-
- def rb
- Gem::Platform::RUBY
- end
-
- def mac
- Gem::Platform.new("x86-darwin-10")
- end
-
- def x64_mac
- Gem::Platform.new("x86_64-darwin-15")
- end
-
- def java
- Gem::Platform.new([nil, "java", nil])
- end
-
- def linux
- Gem::Platform.new(["x86", "linux", nil])
- end
-
- def mswin
- Gem::Platform.new(["x86", "mswin32", nil])
- end
-
- def mingw
- Gem::Platform.new(["x86", "mingw32", nil])
- end
-
- def x64_mingw
- Gem::Platform.new(["x64", "mingw32", nil])
- end
-
- def all_platforms
- [rb, java, linux, mswin, mingw, x64_mingw]
- end
-
- def local
- generic_local_platform
+ def not_local
+ generic_local_platform == Gem::Platform::RUBY ? "java" : Gem::Platform::RUBY
end
- def specific_local_platform
+ def local_platform
Bundler.local_platform
end
- def not_local
- all_platforms.find {|p| p != generic_local_platform }
+ def generic_local_platform
+ Gem::Platform.generic(local_platform)
end
def local_tag
- if RUBY_PLATFORM == "java"
+ if Gem.java_platform?
:jruby
+ elsif Gem.win_platform?
+ :windows
else
:ruby
end
end
def not_local_tag
- [:ruby, :jruby].find {|tag| tag != local_tag }
+ [:jruby, :windows, :ruby].find {|tag| tag != local_tag }
end
def local_ruby_engine
@@ -69,12 +33,12 @@ module Spec
end
def local_engine_version
- RUBY_ENGINE_VERSION
+ RUBY_ENGINE == "ruby" ? Gem.ruby_version : RUBY_ENGINE_VERSION
end
def not_local_engine_version
case not_local_tag
- when :ruby
+ when :ruby, :windows
not_local_ruby_version
when :jruby
"1.6.1"
@@ -89,16 +53,23 @@ module Spec
9999
end
- def lockfile_platforms
- local_platforms.map(&:to_s).sort.join("\n ")
+ def default_platform_list(*extra, defaults: default_locked_platforms)
+ defaults.concat(extra).map(&:to_s).uniq
end
- def local_platforms
- if Bundler.feature_flag.specific_platform?
- [local, specific_local_platform]
- else
- [local]
- end
+ def lockfile_platforms(*extra, defaults: default_locked_platforms)
+ platforms = default_platform_list(*extra, defaults: defaults)
+ platforms.sort.join("\n ")
+ end
+
+ def default_locked_platforms
+ [local_platform, generic_default_locked_platform].compact
+ end
+
+ def generic_default_locked_platform
+ return unless Bundler::MatchPlatform.generic_local_platform_is_ruby?
+
+ Gem::Platform::RUBY
end
end
end
diff --git a/spec/bundler/support/rubygems_ext.rb b/spec/bundler/support/rubygems_ext.rb
index d13567d7af..812dc4deaa 100644
--- a/spec/bundler/support/rubygems_ext.rb
+++ b/spec/bundler/support/rubygems_ext.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+abort "RubyGems only supports Ruby 3.2 or higher" if RUBY_VERSION < "3.2.0"
+
require_relative "path"
$LOAD_PATH.unshift(Spec::Path.source_lib_dir.to_s)
@@ -8,22 +10,27 @@ module Spec
module Rubygems
extend self
- def dev_setup
- install_gems(dev_gemfile, dev_lockfile)
- end
-
def gem_load(gem_name, bin_container)
require_relative "switch_rubygems"
gem_load_and_activate(gem_name, bin_container)
end
- def gem_require(gem_name)
+ def gem_load_and_possibly_install(gem_name, bin_container)
+ require_relative "switch_rubygems"
+
+ gem_load_activate_and_possibly_install(gem_name, bin_container)
+ end
+
+ def gem_require(gem_name, entrypoint)
gem_activate(gem_name)
- require gem_name
+ require entrypoint
end
def test_setup
+ # Install test dependencies unless parallel-rspec is being used, since in that case they should be setup already
+ install_test_deps unless ENV["RSPEC_FORMATTER_OUTPUT_ID"]
+
setup_test_paths
require "fileutils"
@@ -32,89 +39,172 @@ module Spec
FileUtils.mkdir_p(Path.tmpdir)
ENV["HOME"] = Path.home.to_s
- ENV["TMPDIR"] = Path.tmpdir.to_s
+ # Remove "RUBY_CODESIGN", which is used by mkmf-generated Makefile to
+ # sign extension bundles on macOS, to avoid trying to find the specified key
+ # from the fake $HOME/Library/Keychains directory.
+ ENV.delete "RUBY_CODESIGN"
+ if Path.ruby_core?
+ if (tmpdir = ENV["TMPDIR"])
+ tmpdir_real = begin
+ File.realpath(tmpdir)
+ rescue Errno::ENOENT, Errno::EACCES
+ tmpdir
+ end
+ ENV["TMPDIR"] = tmpdir_real if tmpdir_real != tmpdir
+ end
+ else
+ ENV["TMPDIR"] = Path.tmpdir.to_s
+ end
require "rubygems/user_interaction"
Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
end
- def install_parallel_test_deps
- require "parallel"
+ def setup_test_paths
+ ENV["BUNDLE_PATH"] = nil
+ ENV["PATH"] = [Path.system_gem_path("bin"), ENV["PATH"]].join(File::PATH_SEPARATOR)
+ ENV["PATH"] = [Path.exedir, ENV["PATH"]].join(File::PATH_SEPARATOR) if Path.ruby_core?
+ end
+
+ def install_test_deps
+ dev_bundle("install", gemfile: test_gemfile, path: Path.base_system_gem_path.to_s)
+ dev_bundle("install", gemfile: rubocop_gemfile, path: Path.rubocop_gem_path.to_s)
+ dev_bundle("install", gemfile: standard_gemfile, path: Path.standard_gem_path.to_s)
- prev_env_test_number = ENV["TEST_ENV_NUMBER"]
+ require_relative "helpers"
+ Helpers.install_dev_bundler
- begin
- Parallel.processor_count.times do |n|
- ENV["TEST_ENV_NUMBER"] = (n + 1).to_s
+ install_vendored_compact_index
+ end
- install_test_deps
+ # Vendor `rubygems/rubygems.org#lib/compact_index/` under `tmp/compact_index/`
+ # so the artifice can serve compact-index responses without a runtime gem
+ # dependency. Pinned to a reviewed commit; override with COMPACT_INDEX_REF
+ # to refresh against another ref (the existing vendor copy is discarded).
+ def install_vendored_compact_index
+ target_root = Path.tmp_root.join("compact_index")
+ require "fileutils"
+ FileUtils.mkdir_p(Path.tmp_root)
+
+ files = %w[
+ lib/compact_index.rb
+ lib/compact_index/dependency.rb
+ lib/compact_index/gem.rb
+ lib/compact_index/gem_version.rb
+ lib/compact_index/versions_file.rb
+ ]
+
+ # Serialize installs so parallel test setups don't race on the same
+ # vendor tree, and only skip the download when every file is present so
+ # an interrupted run can't leave a partial copy behind.
+ File.open(Path.tmp_root.join("compact_index.lock"), File::CREAT | File::RDWR) do |lock|
+ lock.flock(File::LOCK_EX)
+
+ FileUtils.rm_rf(target_root) if ENV["COMPACT_INDEX_REF"]
+
+ next if files.all? {|path| File.exist?(target_root.join(path)) }
+
+ require "open-uri"
+ ref = ENV["COMPACT_INDEX_REF"] || "7c68a7b39761c61a66f9299f85b889ec39afc02c"
+ files.each do |path|
+ url = "https://raw.githubusercontent.com/rubygems/rubygems.org/#{ref}/#{path}"
+ target = target_root.join(path)
+ FileUtils.mkdir_p(File.dirname(target))
+ tmp = "#{target}.tmp"
+ File.write(tmp, URI.parse(url).open(&:read))
+ File.rename(tmp, target)
end
- ensure
- ENV["TEST_ENV_NUMBER"] = prev_env_test_number
end
end
- def setup_test_paths
- Gem.clear_paths
+ def check_source_control_changes(success_message:, error_message:)
+ require "open3"
- ENV["BUNDLE_PATH"] = nil
- ENV["GEM_HOME"] = ENV["GEM_PATH"] = Path.base_system_gems.to_s
- ENV["PATH"] = [Path.system_gem_path.join("bin"), ENV["PATH"]].join(File::PATH_SEPARATOR)
- ENV["PATH"] = [Path.bindir, ENV["PATH"]].join(File::PATH_SEPARATOR) if Path.ruby_core?
- end
+ output, status = Open3.capture2e("git status --porcelain")
- def install_test_deps
- setup_test_paths
+ if status.success? && output.empty?
+ puts
+ puts success_message
+ puts
+ else
+ system("git diff")
- workaround_loaded_specs_issue
+ puts
+ puts error_message
+ puts
- install_gems(test_gemfile, test_lockfile)
+ exit(1)
+ end
end
- private
+ def dev_bundle(*args, gemfile: dev_gemfile, path: nil)
+ old_gemfile = ENV["BUNDLE_GEMFILE"]
+ old_orig_gemfile = ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"]
+ ENV["BUNDLE_GEMFILE"] = gemfile.to_s
+ ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = nil
+
+ 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
- # 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)
+ require "shellwords"
+ # We don't use `Open3` here because it does not work on JRuby + Windows
+ output = `ruby #{Path.dev_binstub} #{args.shelljoin}`
+ raise output unless $?.success?
+ output
+ ensure
+ if path
+ ENV["BUNDLE_PATH"] = old_path
+ else
+ ENV["BUNDLE_PATH__SYSTEM"] = old_path__system
+ end
- 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")
+ ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = old_orig_gemfile
+ ENV["BUNDLE_GEMFILE"] = old_gemfile
end
+ private
+
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)
- require "bundler"
- gem_requirement = Bundler::LockfileParser.new(File.read(dev_lockfile)).dependencies[gem_name]&.requirement
- gem gem_name, gem_requirement
+ def gem_load_activate_and_possibly_install(gem_name, bin_container)
+ gem_activate_and_possibly_install(gem_name)
+ load Gem.bin_path(gem_name, bin_container)
end
- def install_gems(gemfile, lockfile)
- old_gemfile = ENV["BUNDLE_GEMFILE"]
- ENV["BUNDLE_GEMFILE"] = gemfile.to_s
+ def gem_activate_and_possibly_install(gem_name)
+ gem_activate(gem_name)
+ rescue Gem::LoadError => e
+ Gem.install(gem_name, e.requirement)
+ retry
+ end
+
+ def gem_activate(gem_name)
+ require_relative "activate"
require "bundler"
- definition = Bundler::Definition.build(gemfile, lockfile, nil)
- definition.validate_runtime!
- Bundler::Installer.install(Path.source_root, definition, :path => ENV["GEM_HOME"])
- ensure
- ENV["BUNDLE_GEMFILE"] = old_gemfile
+ gem_requirement = Bundler::LockfileParser.new(File.read(dev_lockfile)).specs.find {|spec| spec.name == gem_name }.version
+ gem gem_name, gem_requirement
end
def test_gemfile
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 66dcdc7ad3..c174c461f0 100644
--- a/spec/bundler/support/rubygems_version_manager.rb
+++ b/spec/bundler/support/rubygems_version_manager.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
-require "pathname"
-require_relative "helpers"
-require_relative "path"
+require_relative "options"
+require_relative "env"
+require_relative "subprocess"
class RubygemsVersionManager
- include Spec::Helpers
- include Spec::Path
+ include Spec::Options
+ include Spec::Env
+ include Spec::Subprocess
def initialize(source)
@source = source
@@ -24,34 +25,27 @@ 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"
rubygems_default_path = rubygems_path + "/defaults"
bundler_path = rubylibdir + "/bundler"
- bundler_exemptions = Gem.rubygems_version < Gem::Version.new("3.2.0") ? [bundler_path + "/errors.rb"] : []
bad_loaded_features = $LOADED_FEATURES.select do |loaded_feature|
(loaded_feature.start_with?(rubygems_path) && !loaded_feature.start_with?(rubygems_default_path)) ||
- (loaded_feature.start_with?(bundler_path) && !bundler_exemptions.any? {|bundler_exemption| loaded_feature.start_with?(bundler_exemption) })
+ loaded_feature.start_with?(bundler_path)
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
-private
+ private
def use_system?
@source.nil?
@@ -64,7 +58,7 @@ private
cmd = [RbConfig.ruby, $0, *ARGV].compact
- ENV["RUBYOPT"] = opt_add("-I#{local_copy_path.join("lib")}", opt_remove("--disable-gems", ENV["RUBYOPT"]))
+ ENV["RUBYOPT"] = opt_add("-I#{File.join(local_copy_path, "lib")}", opt_remove("--disable-gems", ENV["RUBYOPT"]))
exec(ENV, *cmd)
end
@@ -72,13 +66,14 @@ private
def switch_local_copy_if_needed
return unless local_copy_switch_needed?
- sys_exec("git checkout #{target_tag}", :dir => local_copy_path)
+ git("checkout #{target_tag}", local_copy_path)
- ENV["RGV"] = local_copy_path.to_s
+ ENV["RGV"] = local_copy_path
end
def rubygems_unrequire_needed?
- !$LOADED_FEATURES.include?(local_copy_path.join("lib/rubygems.rb").to_s)
+ require "rubygems"
+ !$LOADED_FEATURES.include?(File.join(local_copy_path, "lib/rubygems.rb"))
end
def local_copy_switch_needed?
@@ -90,7 +85,7 @@ private
end
def local_copy_tag
- sys_exec("git rev-parse --abbrev-ref HEAD", :dir => local_copy_path)
+ git("rev-parse --abbrev-ref HEAD", local_copy_path)
end
def local_copy_path
@@ -100,25 +95,29 @@ private
def resolve_local_copy_path
return expanded_source if source_is_path?
- rubygems_path = source_root.join("tmp/rubygems")
+ rubygems_path = File.join(source_root, "tmp/rubygems")
- unless rubygems_path.directory?
- sys_exec("git clone .. #{rubygems_path}", :dir => source_root)
+ unless File.directory?(rubygems_path)
+ git("clone .. #{rubygems_path}", source_root)
end
rubygems_path
end
def source_is_path?
- expanded_source.directory?
+ File.directory?(expanded_source)
end
def expanded_source
- @expanded_source ||= Pathname.new(@source).expand_path(source_root)
+ @expanded_source ||= File.expand_path(@source, source_root)
+ end
+
+ def source_root
+ @source_root ||= File.expand_path(ruby_core? ? "../../.." : "../..", __dir__)
end
def resolve_target_tag
- return "v#{@source}" if @source.match(/^\d/)
+ return "v#{@source}" if @source.match?(/^\d/)
@source
end
diff --git a/spec/bundler/support/setup.rb b/spec/bundler/support/setup.rb
new file mode 100644
index 0000000000..4ac2e5b472
--- /dev/null
+++ b/spec/bundler/support/setup.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require_relative "switch_rubygems"
+
+require_relative "rubygems_ext"
+Spec::Rubygems.install_test_deps
+
+require_relative "path"
+$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__)) if Spec::Path.ruby_core?
diff --git a/spec/bundler/support/shards.rb b/spec/bundler/support/shards.rb
new file mode 100644
index 0000000000..ce33896539
--- /dev/null
+++ b/spec/bundler/support/shards.rb
@@ -0,0 +1,200 @@
+# frozen_string_literal: true
+
+# This classifies test files into 4 shards by running `bin/rspec --profile 10000`
+# to ensure balanced execution times. When adding new test files, it is recommended to
+# re-aggregate and adjust the shards to keep them balanced.
+# For now, please add new files to shard 'shard_d'.
+
+module Spec
+ module Shards
+ EXAMPLE_MAPPINGS = {
+ shard_a: [
+ "spec/runtime/setup_spec.rb",
+ "spec/commands/install_spec.rb",
+ "spec/commands/add_spec.rb",
+ "spec/install/gems/compact_index_spec.rb",
+ "spec/commands/config_spec.rb",
+ "spec/commands/pristine_spec.rb",
+ "spec/install/gemfile/path_spec.rb",
+ "spec/update/git_spec.rb",
+ "spec/commands/open_spec.rb",
+ "spec/commands/remove_spec.rb",
+ "spec/commands/show_spec.rb",
+ "spec/plugins/source/example_spec.rb",
+ "spec/commands/console_spec.rb",
+ "spec/runtime/require_spec.rb",
+ "spec/runtime/env_helpers_spec.rb",
+ "spec/runtime/gem_tasks_spec.rb",
+ "spec/install/gemfile_spec.rb",
+ "spec/commands/fund_spec.rb",
+ "spec/commands/init_spec.rb",
+ "spec/bundler/ruby_dsl_spec.rb",
+ "spec/bundler/mirror_spec.rb",
+ "spec/bundler/source/git/git_proxy_spec.rb",
+ "spec/bundler/source_list_spec.rb",
+ "spec/bundler/plugin/installer_spec.rb",
+ "spec/bundler/errors_spec.rb",
+ "spec/bundler/friendly_errors_spec.rb",
+ "spec/resolver/platform_spec.rb",
+ "spec/bundler/fetcher/downloader_spec.rb",
+ "spec/update/force_spec.rb",
+ "spec/bundler/env_spec.rb",
+ "spec/install/gems/mirror_spec.rb",
+ "spec/install/failure_spec.rb",
+ "spec/bundler/yaml_serializer_spec.rb",
+ "spec/bundler/environment_preserver_spec.rb",
+ "spec/install/gemfile/install_if_spec.rb",
+ "spec/install/gems/gemfile_source_header_spec.rb",
+ "spec/bundler/fetcher/base_spec.rb",
+ "spec/bundler/rubygems_integration_spec.rb",
+ "spec/bundler/worker_spec.rb",
+ "spec/bundler/dependency_spec.rb",
+ "spec/bundler/ui_spec.rb",
+ "spec/bundler/plugin/source_list_spec.rb",
+ "spec/bundler/source/path_spec.rb",
+ ],
+ shard_b: [
+ "spec/install/gemfile/git_spec.rb",
+ "spec/install/gems/standalone_spec.rb",
+ "spec/commands/lock_spec.rb",
+ "spec/cache/gems_spec.rb",
+ "spec/other/major_deprecation_spec.rb",
+ "spec/install/gems/dependency_api_spec.rb",
+ "spec/install/gemfile/gemspec_spec.rb",
+ "spec/plugins/install_spec.rb",
+ "spec/commands/binstubs_spec.rb",
+ "spec/install/gems/flex_spec.rb",
+ "spec/runtime/inline_spec.rb",
+ "spec/commands/post_bundle_message_spec.rb",
+ "spec/runtime/executable_spec.rb",
+ "spec/lock/git_spec.rb",
+ "spec/plugins/hook_spec.rb",
+ "spec/install/allow_offline_install_spec.rb",
+ "spec/install/gems/post_install_spec.rb",
+ "spec/install/gemfile/ruby_spec.rb",
+ "spec/install/security_policy_spec.rb",
+ "spec/install/yanked_spec.rb",
+ "spec/update/gemfile_spec.rb",
+ "spec/runtime/load_spec.rb",
+ "spec/plugins/command_spec.rb",
+ "spec/commands/version_spec.rb",
+ "spec/install/prereleases_spec.rb",
+ "spec/bundler/uri_credentials_filter_spec.rb",
+ "spec/bundler/plugin_spec.rb",
+ "spec/install/gems/mirror_probe_spec.rb",
+ "spec/plugins/list_spec.rb",
+ "spec/bundler/compact_index_client/parser_spec.rb",
+ "spec/bundler/gem_version_promoter_spec.rb",
+ "spec/other/cli_dispatch_spec.rb",
+ "spec/bundler/source/rubygems_spec.rb",
+ "spec/cache/platform_spec.rb",
+ "spec/update/gems/fund_spec.rb",
+ "spec/bundler/stub_specification_spec.rb",
+ "spec/bundler/retry_spec.rb",
+ "spec/bundler/installer/spec_installation_spec.rb",
+ "spec/bundler/spec_set_spec.rb",
+ "spec/quality_es_spec.rb",
+ "spec/bundler/index_spec.rb",
+ "spec/other/cli_man_pages_spec.rb",
+ ],
+ shard_c: [
+ "spec/commands/newgem_spec.rb",
+ "spec/commands/exec_spec.rb",
+ "spec/commands/clean_spec.rb",
+ "spec/commands/platform_spec.rb",
+ "spec/cache/git_spec.rb",
+ "spec/install/gemfile/groups_spec.rb",
+ "spec/commands/cache_spec.rb",
+ "spec/commands/check_spec.rb",
+ "spec/commands/list_spec.rb",
+ "spec/install/path_spec.rb",
+ "spec/bundler/cli_spec.rb",
+ "spec/install/bundler_spec.rb",
+ "spec/install/git_spec.rb",
+ "spec/commands/doctor_spec.rb",
+ "spec/bundler/dsl_spec.rb",
+ "spec/install/gems/fund_spec.rb",
+ "spec/install/gems/env_spec.rb",
+ "spec/bundler/ruby_version_spec.rb",
+ "spec/bundler/definition_spec.rb",
+ "spec/install/gemfile/eval_gemfile_spec.rb",
+ "spec/plugins/source_spec.rb",
+ "spec/install/gems/dependency_api_fallback_spec.rb",
+ "spec/plugins/uninstall_spec.rb",
+ "spec/bundler/plugin/index_spec.rb",
+ "spec/bundler/bundler_spec.rb",
+ "spec/bundler/fetcher_spec.rb",
+ "spec/bundler/source/rubygems/remote_spec.rb",
+ "spec/bundler/lockfile_parser_spec.rb",
+ "spec/cache/cache_path_spec.rb",
+ "spec/bundler/source/git_spec.rb",
+ "spec/bundler/source_spec.rb",
+ "spec/commands/ssl_spec.rb",
+ "spec/bundler/fetcher/compact_index_spec.rb",
+ "spec/bundler/plugin/api_spec.rb",
+ "spec/bundler/endpoint_specification_spec.rb",
+ "spec/bundler/fetcher/index_spec.rb",
+ "spec/bundler/settings/validator_spec.rb",
+ "spec/bundler/build_metadata_spec.rb",
+ "spec/bundler/current_ruby_spec.rb",
+ "spec/bundler/installer/gem_installer_spec.rb",
+ "spec/bundler/installer/parallel_installer_spec.rb",
+ "spec/bundler/cli_common_spec.rb",
+ "spec/bundler/ci_detector_spec.rb",
+ ],
+ shard_d: [
+ "spec/bundler/rubygems_ext_spec.rb",
+ "spec/bundler/resolver/cooldown_spec.rb",
+ "spec/install/cooldown_spec.rb",
+ "spec/commands/outdated_spec.rb",
+ "spec/commands/update_spec.rb",
+ "spec/lock/lockfile_spec.rb",
+ "spec/install/deploy_spec.rb",
+ "spec/install/gemfile/sources_spec.rb",
+ "spec/runtime/self_management_spec.rb",
+ "spec/install/gemfile/specific_platform_spec.rb",
+ "spec/commands/info_spec.rb",
+ "spec/install/gems/resolving_spec.rb",
+ "spec/install/gemfile/platform_spec.rb",
+ "spec/bundler/gem_helper_spec.rb",
+ "spec/install/global_cache_spec.rb",
+ "spec/runtime/platform_spec.rb",
+ "spec/update/gems/post_install_spec.rb",
+ "spec/install/gems/native_extensions_spec.rb",
+ "spec/install/force_spec.rb",
+ "spec/cache/path_spec.rb",
+ "spec/install/gemspecs_spec.rb",
+ "spec/commands/help_spec.rb",
+ "spec/bundler/shared_helpers_spec.rb",
+ "spec/bundler/settings_spec.rb",
+ "spec/resolver/basic_spec.rb",
+ "spec/install/gemfile/force_ruby_platform_spec.rb",
+ "spec/commands/licenses_spec.rb",
+ "spec/install/gemfile/lockfile_spec.rb",
+ "spec/bundler/fetcher/dependency_spec.rb",
+ "spec/quality_spec.rb",
+ "spec/bundler/remote_specification_spec.rb",
+ "spec/install/process_lock_spec.rb",
+ "spec/install/binstubs_spec.rb",
+ "spec/bundler/compact_index_client/updater_spec.rb",
+ "spec/bundler/ui/shell_spec.rb",
+ "spec/other/ext_spec.rb",
+ "spec/commands/issue_spec.rb",
+ "spec/update/path_spec.rb",
+ "spec/bundler/plugin/api/source_spec.rb",
+ "spec/install/gems/win32_spec.rb",
+ "spec/bundler/plugin/dsl_spec.rb",
+ "spec/runtime/requiring_spec.rb",
+ "spec/bundler/plugin/events_spec.rb",
+ "spec/bundler/resolver/candidate_spec.rb",
+ "spec/bundler/digest_spec.rb",
+ "spec/bundler/fetcher/gem_remote_fetcher_spec.rb",
+ "spec/bundler/uri_normalizer_spec.rb",
+ "spec/install/gems/no_build_extension_spec.rb",
+ "spec/install/gems/no_install_plugin_spec.rb",
+ "spec/bundler/override_spec.rb",
+ "spec/install/gemfile/override_spec.rb",
+ ],
+ }.freeze
+ end
+end
diff --git a/spec/bundler/support/silent_logger.rb b/spec/bundler/support/silent_logger.rb
deleted file mode 100644
index 8665beb2c9..0000000000
--- a/spec/bundler/support/silent_logger.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-require "logger"
-module Spec
- class SilentLogger
- (::Logger.instance_methods - Object.instance_methods).each do |logger_instance_method|
- define_method(logger_instance_method) {|*args, &blk| }
- end
- end
-end
diff --git a/spec/bundler/support/sometimes.rb b/spec/bundler/support/sometimes.rb
deleted file mode 100644
index 65a95ed59c..0000000000
--- a/spec/bundler/support/sometimes.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-module Sometimes
- def run_with_retries(example_to_run, retries)
- example = RSpec.current_example
- example.metadata[:retries] ||= retries
-
- retries.times do |t|
- example.metadata[:retried] = t + 1
- example.instance_variable_set(:@exception, nil)
- example_to_run.run
- break unless example.exception
- end
-
- if e = example.exception
- new_exception = e.exception(e.message + "[Retried #{retries} times]")
- new_exception.set_backtrace e.backtrace
- example.instance_variable_set(:@exception, new_exception)
- end
- end
-end
-
-RSpec.configure do |config|
- config.include Sometimes
- config.alias_example_to :sometimes, :sometimes => true
- config.add_setting :sometimes_retry_count, :default => 5
-
- config.around(:each, :sometimes => true) do |example|
- retries = example.metadata[:retries] || RSpec.configuration.sometimes_retry_count
- run_with_retries(example, retries)
- end
-
- config.after(:suite) do
- message = proc do |color, text|
- colored = RSpec::Core::Formatters::ConsoleCodes.wrap(text, color)
- notification = RSpec::Core::Notifications::MessageNotification.new(colored)
- formatter = RSpec.configuration.formatters.first
- formatter.message(notification) if formatter.respond_to?(:message)
- end
-
- retried_examples = RSpec.world.example_groups.map do |g|
- g.descendants.map do |d|
- d.filtered_examples.select do |e|
- e.metadata[:sometimes] && e.metadata.fetch(:retried, 1) > 1
- end
- end
- end.flatten
-
- message.call(retried_examples.empty? ? :green : :yellow, "\n\nRetried examples: #{retried_examples.count}")
-
- retried_examples.each do |e|
- message.call(:cyan, " #{e.full_description}")
- path = RSpec::Core::Metadata.relative_path(e.location)
- message.call(:cyan, " [#{e.metadata[:retried]}/#{e.metadata[:retries]}] " + path)
- end
- end
-end
diff --git a/spec/bundler/support/streams.rb b/spec/bundler/support/streams.rb
deleted file mode 100644
index a947eebf6f..0000000000
--- a/spec/bundler/support/streams.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-require "stringio"
-
-def capture(*args)
- opts = args.pop if args.last.is_a?(Hash)
- opts ||= {}
-
- args.map!(&:to_s)
- begin
- result = StringIO.new
- result.close if opts[:closed]
- args.each {|stream| eval "$#{stream} = result" }
- yield
- ensure
- args.each {|stream| eval("$#{stream} = #{stream.upcase}") }
- end
- result.string
-end
diff --git a/spec/bundler/support/subprocess.rb b/spec/bundler/support/subprocess.rb
new file mode 100644
index 0000000000..91db80da48
--- /dev/null
+++ b/spec/bundler/support/subprocess.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+
+require_relative "command_execution"
+
+module Spec
+ module Subprocess
+ class TimeoutExceeded < StandardError; end
+
+ def command_executions
+ @command_executions ||= []
+ end
+
+ def last_command
+ command_executions.last || raise("There is no last command")
+ end
+
+ def out
+ last_command.stdout
+ end
+
+ def err
+ last_command.stderr
+ end
+
+ def stdboth
+ last_command.stdboth
+ end
+
+ def exitstatus
+ last_command.exitstatus
+ end
+
+ def git(cmd, path = Dir.pwd, options = {})
+ sh("git #{cmd}", options.merge(dir: path))
+ end
+
+ def sh(cmd, options = {})
+ dir = options[:dir]
+ env = options[:env] || {}
+
+ command_execution = CommandExecution.new(cmd.to_s, timeout: options[:timeout] || 60)
+
+ open3_opts = {}
+ open3_opts[:chdir] = dir if dir
+
+ require "open3"
+ require "shellwords"
+ Open3.popen3(env, *cmd.shellsplit, **open3_opts) do |stdin, stdout, stderr, wait_thr|
+ yield stdin, stdout, wait_thr if block_given?
+ stdin.close
+
+ stdout_handler = ->(data) { command_execution.original_stdout << data }
+ stderr_handler = ->(data) { command_execution.original_stderr << data }
+
+ stdout_thread = read_stream(stdout, stdout_handler, timeout: command_execution.timeout)
+ stderr_thread = read_stream(stderr, stderr_handler, timeout: command_execution.timeout)
+
+ stdout_thread.join
+ stderr_thread.join
+
+ status = wait_thr.value
+ command_execution.exitstatus = if status.exited?
+ status.exitstatus
+ elsif status.signaled?
+ exit_status_for_signal(status.termsig)
+ end
+ rescue TimeoutExceeded
+ command_execution.failure_reason = :timeout
+ command_execution.exitstatus = exit_status_for_signal(Signal.list["INT"])
+ end
+
+ unless options[:raise_on_error] == false || command_execution.success?
+ command_execution.raise_error!
+ end
+
+ command_executions << command_execution
+
+ command_execution.stdout
+ end
+
+ # Mostly copied from https://github.com/piotrmurach/tty-command/blob/49c37a895ccea107e8b78d20e4cb29de6a1a53c8/lib/tty/command/process_runner.rb#L165-L193
+ def read_stream(stream, handler, timeout:)
+ Thread.new do
+ Thread.current.report_on_exception = false
+ cmd_start = Time.now
+ readers = [stream]
+
+ while readers.any?
+ ready = IO.select(readers, nil, readers, timeout)
+ raise TimeoutExceeded if ready.nil?
+
+ ready[0].each do |reader|
+ chunk = reader.readpartial(16 * 1024)
+ handler.call(chunk)
+
+ # control total time spent reading
+ runtime = Time.now - cmd_start
+ time_left = timeout - runtime
+ raise TimeoutExceeded if time_left < 0.0
+ rescue Errno::EAGAIN, Errno::EINTR
+ rescue EOFError, Errno::EPIPE, Errno::EIO
+ readers.delete(reader)
+ reader.close
+ end
+ end
+ end
+ end
+
+ def all_commands_output
+ return "" if command_executions.empty?
+
+ "\n\nCommands:\n#{command_executions.map(&:to_s_verbose).join("\n\n")}"
+ end
+ end
+end
diff --git a/spec/bundler/support/sudo.rb b/spec/bundler/support/sudo.rb
deleted file mode 100644
index 04e9443945..0000000000
--- a/spec/bundler/support/sudo.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module Spec
- module Sudo
- def self.present?
- @which_sudo ||= Bundler.which("sudo")
- end
-
- def sudo(cmd)
- raise "sudo not present" unless Sudo.present?
- sys_exec("sudo #{cmd}")
- end
-
- def chown_system_gems_to_root
- sudo "chown -R root #{system_gem_path}"
- end
- end
-end
diff --git a/spec/bundler/support/switch_rubygems.rb b/spec/bundler/support/switch_rubygems.rb
index a138d22333..640b9f83b7 100644
--- a/spec/bundler/support/switch_rubygems.rb
+++ b/spec/bundler/support/switch_rubygems.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
require_relative "rubygems_version_manager"
+ENV["RGV"] ||= "."
RubygemsVersionManager.new(ENV["RGV"]).switch
diff --git a/spec/bundler/support/the_bundle.rb b/spec/bundler/support/the_bundle.rb
index f252a4515b..452abd7d41 100644
--- a/spec/bundler/support/the_bundle.rb
+++ b/spec/bundler/support/the_bundle.rb
@@ -8,10 +8,8 @@ module Spec
attr_accessor :bundle_dir
- def initialize(opts = {})
- opts = opts.dup
- @bundle_dir = Pathname.new(opts.delete(:bundle_dir) { bundled_app })
- raise "Too many options! #{opts}" unless opts.empty?
+ def initialize
+ @bundle_dir = Pathname.new(bundled_app)
end
def to_s
@@ -28,8 +26,16 @@ module Spec
end
def locked_gems
- raise "Cannot read lockfile if it doesn't exist" unless locked?
+ raise ArgumentError, "Cannot read lockfile if it doesn't exist" unless locked?
Bundler::LockfileParser.new(lockfile.read)
end
+
+ def locked_specs
+ locked_gems.specs.map(&:full_name)
+ end
+
+ def locked_platforms
+ locked_gems.platforms.map(&:to_s)
+ end
end
end
diff --git a/spec/bundler/support/vendored_net_http.rb b/spec/bundler/support/vendored_net_http.rb
new file mode 100644
index 0000000000..8ff2ccd1fe
--- /dev/null
+++ b/spec/bundler/support/vendored_net_http.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+# This defined? guard can be removed once RubyGems 3.4 support is dropped.
+#
+# Bundler specs load this code from `spec/support/vendored_net_http.rb` to avoid
+# activating the Bundler gem too early. Without this guard, we get redefinition
+# warnings once Bundler is actually activated and
+# `lib/bundler/vendored_net_http.rb` is required. This is not an issue in
+# RubyGems versions including `rubygems/vendored_net_http` since `require` takes
+# care of avoiding the double load.
+#
+unless defined?(Gem::Net)
+ begin
+ require "rubygems/vendored_net_http"
+ rescue LoadError
+ begin
+ require "rubygems/net/http"
+ rescue LoadError
+ require "net/http"
+ Gem::Net = Net
+ end
+ end
+end
diff --git a/spec/bundler/update/force_spec.rb b/spec/bundler/update/force_spec.rb
new file mode 100644
index 0000000000..325f58088a
--- /dev/null
+++ b/spec/bundler/update/force_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ before :each do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+ end
+
+ it "re-installs installed gems with --force" do
+ myrack_lib = default_bundle_path("gems/myrack-1.0.0/lib/myrack.rb")
+ myrack_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle :update, force: true
+
+ expect(out).to include "Installing myrack 1.0.0"
+ expect(myrack_lib.open(&:read)).to eq("MYRACK = '1.0.0'\n")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "re-installs installed gems with --redownload" do
+ myrack_lib = default_bundle_path("gems/myrack-1.0.0/lib/myrack.rb")
+ myrack_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle :update, redownload: true
+
+ expect(out).to include "Installing myrack 1.0.0"
+ expect(myrack_lib.open(&:read)).to eq("MYRACK = '1.0.0'\n")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+end
diff --git a/spec/bundler/update/gemfile_spec.rb b/spec/bundler/update/gemfile_spec.rb
index 1c5294101e..f8849640b6 100644
--- a/spec/bundler/update/gemfile_spec.rb
+++ b/spec/bundler/update/gemfile_spec.rb
@@ -4,44 +4,44 @@ RSpec.describe "bundle update" do
context "with --gemfile" do
it "finds the gemfile" do
gemfile bundled_app("NotGemfile"), <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
- bundle :install, :gemfile => bundled_app("NotGemfile")
- bundle :update, :gemfile => bundled_app("NotGemfile"), :all => true
+ bundle :install, gemfile: bundled_app("NotGemfile")
+ bundle :update, gemfile: bundled_app("NotGemfile"), all: true
# Specify BUNDLE_GEMFILE for `the_bundle`
# to retrieve the proper Gemfile
ENV["BUNDLE_GEMFILE"] = "NotGemfile"
- expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
context "with gemfile set via config" do
before do
gemfile bundled_app("NotGemfile"), <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
G
- bundle "config set --local gemfile #{bundled_app("NotGemfile")}"
+ bundle_config "gemfile #{bundled_app("NotGemfile")}"
bundle :install
end
it "uses the gemfile to update" do
- bundle "update", :all => true
+ bundle "update", all: true
bundle "list"
- expect(out).to include("rack (1.0.0)")
+ expect(out).to include("myrack (1.0.0)")
end
it "uses the gemfile while in a subdirectory" do
bundled_app("subdir").mkpath
- bundle "update", :all => true, :dir => bundled_app("subdir")
- bundle "list", :dir => bundled_app("subdir")
+ bundle "update", all: true, dir: bundled_app("subdir")
+ bundle "list", dir: bundled_app("subdir")
- expect(out).to include("rack (1.0.0)")
+ expect(out).to include("myrack (1.0.0)")
end
end
end
diff --git a/spec/bundler/update/gems/fund_spec.rb b/spec/bundler/update/gems/fund_spec.rb
new file mode 100644
index 0000000000..a5624d3e0a
--- /dev/null
+++ b/spec/bundler/update/gems/fund_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ before do
+ build_repo2 do
+ build_gem "has_funding_and_other_metadata" do |s|
+ s.metadata = {
+ "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
+ "homepage_uri" => "https://bestgemever.example.io",
+ "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding",
+ "source_code_uri" => "https://example.com/user/bestgemever",
+ "wiki_uri" => "https://example.com/user/bestgemever/wiki",
+ }
+ end
+
+ build_gem "has_funding", "1.2.3" do |s|
+ s.metadata = {
+ "funding_uri" => "https://example.com/has_funding/funding",
+ }
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem 'has_funding_and_other_metadata'
+ gem 'has_funding', '< 2.0'
+ G
+
+ bundle :install
+ end
+
+ context "when listed gems are updated" do
+ before do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem 'has_funding_and_other_metadata'
+ gem 'has_funding'
+ G
+
+ bundle :update, all: true
+ end
+
+ it "displays fund message" do
+ expect(out).to include("2 installed gems you directly depend on are looking for funding.")
+ end
+ end
+end
diff --git a/spec/bundler/update/gems/post_install_spec.rb b/spec/bundler/update/gems/post_install_spec.rb
index 3aaa659d57..9c71f6e0e3 100644
--- a/spec/bundler/update/gems/post_install_spec.rb
+++ b/spec/bundler/update/gems/post_install_spec.rb
@@ -5,8 +5,8 @@ RSpec.describe "bundle update" do
before do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack', "< 1.0"
+ source "https://gem.repo1"
+ gem 'myrack', "< 1.0"
gem 'thin'
G
@@ -17,10 +17,10 @@ RSpec.describe "bundle update" do
shared_examples "a config observer" do
context "when ignore post-install messages for gem is set" do
- let(:config) { "ignore_messages.rack true" }
+ let(:config) { "ignore_messages.myrack true" }
it "doesn't display gem's post-install message" do
- expect(out).not_to include("Rack's post install message")
+ expect(out).not_to include("Myrack's post install message")
end
end
@@ -35,8 +35,8 @@ RSpec.describe "bundle update" do
shared_examples "a post-install message outputter" do
it "should display post-install messages for updated gems" do
- expect(out).to include("Post-install message from rack:")
- expect(out).to include("Rack's post install message")
+ expect(out).to include("Post-install message from myrack:")
+ expect(out).to include("Myrack's post install message")
end
it "should not display the post-install message for non-updated gems" do
@@ -47,12 +47,12 @@ RSpec.describe "bundle update" do
context "when listed gem is updated" do
before do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack'
+ source "https://gem.repo1"
+ gem 'myrack'
gem 'thin'
G
- bundle :update, :all => true
+ bundle :update, all: true
end
it_behaves_like "a post-install message outputter"
@@ -62,12 +62,12 @@ RSpec.describe "bundle update" do
context "when dependency triggers update" do
before do
gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem 'rack-obama'
+ source "https://gem.repo1"
+ gem 'myrack-obama'
gem 'thin'
G
- bundle :update, :all => true
+ bundle :update, all: true
end
it_behaves_like "a post-install message outputter"
diff --git a/spec/bundler/update/git_spec.rb b/spec/bundler/update/git_spec.rb
index bf078fa576..526e988ab7 100644
--- a/spec/bundler/update/git_spec.rb
+++ b/spec/bundler/update/git_spec.rb
@@ -4,31 +4,33 @@ RSpec.describe "bundle update" do
describe "git sources" do
it "floats on a branch when :branch is used" do
build_git "foo", "1.0"
- update_git "foo", :branch => "omg"
+ update_git "foo", branch: "omg"
install_gemfile <<-G
+ source "https://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
- bundle "update", :all => true
+ bundle "update", all: true
expect(the_bundle).to include_gems "foo 1.1"
end
it "updates correctly when you have like craziness" do
- build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
- build_git "rails", "3.0", :path => lib_path("rails") do |s|
+ build_lib "activesupport", "3.0", path: lib_path("rails/activesupport")
+ build_git "rails", "3.0", path: lib_path("rails") do |s|
s.add_dependency "activesupport", "= 3.0"
end
install_gemfile <<-G
- gem "rails", :git => "#{file_uri_for(lib_path("rails"))}"
+ source "https://gem.repo1"
+ gem "rails", :git => "#{lib_path("rails")}"
G
bundle "update rails"
@@ -36,16 +38,17 @@ RSpec.describe "bundle update" do
end
it "floats on a branch when :branch is used and the source is specified in the update" do
- build_git "foo", "1.0", :path => lib_path("foo")
- update_git "foo", :branch => "omg", :path => lib_path("foo")
+ build_git "foo", "1.0", path: lib_path("foo")
+ update_git "foo", branch: "omg", path: lib_path("foo")
install_gemfile <<-G
+ source "https://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
@@ -54,18 +57,19 @@ RSpec.describe "bundle update" do
expect(the_bundle).to include_gems "foo 1.1"
end
- it "floats on master when updating all gems that are pinned to the source even if you have child dependencies" do
- build_git "foo", :path => lib_path("foo")
- build_gem "bar", :to_bundle => true do |s|
+ it "floats on main when updating all gems that are pinned to the source even if you have child dependencies" do
+ build_git "foo", path: lib_path("foo")
+ build_gem "bar", to_bundle: true do |s|
s.add_dependency "foo"
end
install_gemfile <<-G
- gem "foo", :git => "#{file_uri_for(lib_path("foo"))}"
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo")}"
gem "bar"
G
- update_git "foo", :path => lib_path("foo") do |s|
+ update_git "foo", path: lib_path("foo") do |s|
s.write "lib/foo.rb", "FOO = '1.1'"
end
@@ -75,47 +79,55 @@ RSpec.describe "bundle update" do
end
it "notices when you change the repo url in the Gemfile" do
- build_git "foo", :path => lib_path("foo_one")
- build_git "foo", :path => lib_path("foo_two")
+ build_git "foo", path: lib_path("foo_one")
+ build_git "foo", path: lib_path("foo_two")
install_gemfile <<-G
- gem "foo", "1.0", :git => "#{file_uri_for(lib_path("foo_one"))}"
+ source "https://gem.repo1"
+ gem "foo", "1.0", :git => "#{lib_path("foo_one")}"
G
- FileUtils.rm_rf lib_path("foo_one")
+ FileUtils.rm_r lib_path("foo_one")
install_gemfile <<-G
- gem "foo", "1.0", :git => "#{file_uri_for(lib_path("foo_two"))}"
+ source "https://gem.repo1"
+ gem "foo", "1.0", :git => "#{lib_path("foo_two")}"
G
expect(err).to be_empty
- expect(out).to include("Fetching #{file_uri_for(lib_path)}/foo_two")
+ expect(out).to include("Fetching #{lib_path}/foo_two")
expect(out).to include("Bundle complete!")
end
it "fetches tags from the remote" do
build_git "foo"
- @remote = build_git("bar", :bare => true)
- update_git "foo", :remote => file_uri_for(@remote.path)
- update_git "foo", :push => "master"
+ @remote = build_git("bar", bare: true)
+ update_git "foo", remote: @remote.path
+ update_git "foo", push: "main"
install_gemfile <<-G
+ source "https://gem.repo1"
gem 'foo', :git => "#{@remote.path}"
G
# Create a new tag on the remote that needs fetching
- update_git "foo", :tag => "fubar"
- update_git "foo", :push => "fubar"
+ update_git "foo", tag: "fubar"
+ update_git "foo", push: "fubar"
gemfile <<-G
+ source "https://gem.repo1"
gem 'foo', :git => "#{@remote.path}", :tag => "fubar"
G
- bundle "update", :all => true
+ bundle "update", all: true
+ expect(err).to be_empty
end
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'"
@@ -130,13 +142,13 @@ RSpec.describe "bundle update" do
s.add_dependency "submodule"
end
- sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0")
- sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0")
+ git "submodule add #{lib_path("submodule-1.0")} submodule-1.0", lib_path("has_submodule-1.0")
+ git "commit -m \"submodulator\"", lib_path("has_submodule-1.0")
end
it "it unlocks the source when submodules are added to a git source" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
git "#{lib_path("has_submodule-1.0")}" do
gem "has_submodule"
end
@@ -146,7 +158,7 @@ RSpec.describe "bundle update" do
expect(out).to eq("GEM")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
git "#{lib_path("has_submodule-1.0")}", :submodules => true do
gem "has_submodule"
end
@@ -156,9 +168,9 @@ RSpec.describe "bundle update" do
expect(out).to eq("GIT")
end
- it "unlocks the source when submodules are removed from git source", :git => ">= 2.9.0" do
+ it "unlocks the source when submodules are removed from git source", git: ">= 2.9.0" do
install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
git "#{lib_path("has_submodule-1.0")}", :submodules => true do
gem "has_submodule"
end
@@ -168,7 +180,7 @@ RSpec.describe "bundle update" do
expect(out).to eq("GIT")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo4)}"
+ source "https://gem.repo4"
git "#{lib_path("has_submodule-1.0")}" do
gem "has_submodule"
end
@@ -183,65 +195,67 @@ RSpec.describe "bundle update" do
build_git "foo", "1.0"
install_gemfile <<-G
- gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}"
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- lib_path("foo-1.0").join(".git").rmtree
+ FileUtils.rm_rf lib_path("foo-1.0").join(".git")
- bundle :update, :all => true, :raise_on_error => false
+ bundle :update, all: true, raise_on_error: false
expect(err).to include(lib_path("foo-1.0").to_s).
and match(/Git error: command `git fetch.+has failed/)
end
it "should not explode on invalid revision on update of gem by name" do
- build_git "rack", "0.8"
+ build_git "myrack", "0.8"
- build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
- s.write "lib/rack.rb", "puts :LOCAL"
+ build_git "myrack", "0.8", path: lib_path("local-myrack") do |s|
+ s.write "lib/myrack.rb", "puts :LOCAL"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack", :git => "#{file_uri_for(lib_path("rack-0.8"))}", :branch => "master"
+ source "https://gem.repo1"
+ gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main"
G
- bundle %(config set local.rack #{lib_path("local-rack")})
- bundle "update rack"
+ bundle %(config set local.myrack #{lib_path("local-myrack")})
+ bundle "update myrack"
expect(out).to include("Bundle updated!")
end
it "shows the previous version of the gem" do
- build_git "rails", "2.3.2", :path => lib_path("rails")
+ build_git "rails", "2.3.2", path: lib_path("rails")
install_gemfile <<-G
- gem "rails", :git => "#{file_uri_for(lib_path("rails"))}"
+ source "https://gem.repo1"
+ gem "rails", :git => "#{lib_path("rails")}"
G
- update_git "rails", "3.0", :path => lib_path("rails"), :gemspec => true
+ update_git "rails", "3.0", path: lib_path("rails"), gemspec: true
- bundle "update", :all => true
- expect(out).to include("Using rails 3.0 (was 2.3.2) from #{file_uri_for(lib_path("rails"))} (at master@#{revision_for(lib_path("rails"))[0..6]})")
+ bundle "update", all: true
+ expect(out).to include("Using rails 3.0 (was 2.3.2) from #{lib_path("rails")} (at main@#{revision_for(lib_path("rails"))[0..6]})")
end
end
describe "with --source flag" do
before :each do
build_repo2
- @git = build_git "foo", :path => lib_path("foo") do |s|
+ @git = build_git "foo", path: lib_path("foo") do |s|
s.executables = "foobar"
end
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
git "#{lib_path("foo")}" do
gem 'foo'
end
- gem 'rack'
+ gem 'myrack'
G
end
it "updates the source" do
- update_git "foo", :path => @git.path
+ update_git "foo", path: @git.path
bundle "update --source foo"
@@ -254,7 +268,7 @@ RSpec.describe "bundle update" do
end
it "unlocks gems that were originally pulled in by the source" do
- update_git "foo", "2.0", :path => @git.path
+ update_git "foo", "2.0", path: @git.path
bundle "update --source foo"
expect(the_bundle).to include_gems "foo 2.0"
@@ -262,76 +276,45 @@ RSpec.describe "bundle update" do
it "leaves all other gems frozen" do
update_repo2
- update_git "foo", :path => @git.path
+ update_git "foo", path: @git.path
bundle "update --source foo"
- expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).to include_gems "myrack 1.0"
end
end
context "when the gem and the repository have different names" do
before :each do
build_repo2
- @git = build_git "foo", :path => lib_path("bar")
+ @git = build_git "foo", path: lib_path("bar")
install_gemfile <<-G
- source "#{file_uri_for(gem_repo2)}"
+ source "https://gem.repo2"
git "#{lib_path("bar")}" do
gem 'foo'
end
- gem 'rack'
+ gem 'myrack'
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'"
- update_git "foo", "2.0", :path => @git.path do |s|
+ update_git "foo", "2.0", path: @git.path do |s|
s.write "foo.gemspec", spec_lines.join("\n")
end
- ref = @git.ref_for "master"
+ ref = @git.ref_for "main"
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
- 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")
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "foo", "2.0"
+ c.checksum gem_repo2, "myrack", "1.0.0"
end
- ref = @git.ref_for "master"
-
- bundle "update --source bar"
-
- lockfile_should_be <<-G
+ expect(lockfile).to eq <<~G
GIT
remote: #{@git.path}
revision: #{ref}
@@ -339,19 +322,19 @@ RSpec.describe "bundle update" do
foo (2.0)
GEM
- remote: #{file_uri_for(gem_repo2)}/
+ remote: https://gem.repo2/
specs:
- rack (1.0.0)
+ myrack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
foo!
- rack
-
+ myrack
+ #{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
end
diff --git a/spec/bundler/update/path_spec.rb b/spec/bundler/update/path_spec.rb
index 38c125e04b..8c76c94e1a 100644
--- a/spec/bundler/update/path_spec.rb
+++ b/spec/bundler/update/path_spec.rb
@@ -3,13 +3,14 @@
RSpec.describe "path sources" do
describe "bundle update --source" do
it "shows the previous version of the gem when updated from path source" do
- build_lib "activesupport", "2.3.5", :path => lib_path("rails/activesupport")
+ build_lib "activesupport", "2.3.5", path: lib_path("rails/activesupport")
install_gemfile <<-G
+ source "https://gem.repo1"
gem "activesupport", :path => "#{lib_path("rails/activesupport")}"
G
- build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport")
+ build_lib "activesupport", "3.0", path: lib_path("rails/activesupport")
bundle "update --source activesupport"
expect(out).to include("Using activesupport 3.0 (was 2.3.5) from source at `#{lib_path("rails/activesupport")}`")
diff --git a/spec/bundler/update/redownload_spec.rb b/spec/bundler/update/redownload_spec.rb
deleted file mode 100644
index 147be823f5..0000000000
--- a/spec/bundler/update/redownload_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "bundle update" do
- before :each do
- install_gemfile <<-G
- source "#{file_uri_for(gem_repo1)}"
- gem "rack"
- G
- end
-
- describe "with --force" do
- it "shows a deprecation when single flag passed", :bundler => 2 do
- bundle "update rack --force"
- expect(err).to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
-
- it "shows a deprecation when multiple flags passed", :bundler => 2 do
- bundle "update rack --no-color --force"
- expect(err).to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
- end
-
- describe "with --redownload" do
- it "does not show a deprecation when single flag passed" do
- bundle "update rack --redownload"
- expect(err).not_to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
-
- it "does not show a deprecation when single multiple flags passed" do
- bundle "update rack --no-color --redownload"
- expect(err).not_to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
- end
-end
diff --git a/spec/default.mspec b/spec/default.mspec
index 8791058f95..d756dc31ff 100644
--- a/spec/default.mspec
+++ b/spec/default.mspec
@@ -3,60 +3,58 @@ $VERBOSE = false
if (opt = ENV["RUBYOPT"]) and (opt = opt.dup).sub!(/(?:\A|\s)-w(?=\z|\s)/, '')
ENV["RUBYOPT"] = opt
end
+
+# Enable constant leak checks by ruby/mspec
+ENV["CHECK_CONSTANT_LEAKS"] ||= "true"
+
require "./rbconfig" unless defined?(RbConfig)
+require_relative "../tool/test-coverage" if ENV.key?("COVERAGE")
+require_relative "../tool/lib/test/jobserver"
load File.dirname(__FILE__) + '/ruby/default.mspec'
-OBJDIR = File.expand_path("spec/ruby/optional/capi/ext")
+OBJDIR = File.expand_path("spec/ruby/optional/capi/ext") unless defined?(OBJDIR)
class MSpecScript
+ @testing_ruby = true
+
builddir = Dir.pwd
srcdir = ENV['SRCDIR']
- if !srcdir and File.exist?("#{builddir}/Makefile") then
- File.open("#{builddir}/Makefile", "r:US-ASCII") {|f|
- f.read[/^\s*srcdir\s*=\s*(.+)/i] and srcdir = $1
- }
- end
- srcdir = File.expand_path(srcdir)
+ srcdir ||= File.read("Makefile", encoding: "US-ASCII")[/^\s*srcdir\s*=\s*(.+)/i, 1] rescue nil
config = RbConfig::CONFIG
# The default implementation to run the specs.
set :target, File.join(builddir, "miniruby#{config['exeext']}")
set :prefix, File.expand_path('ruby', File.dirname(__FILE__))
- set :flags, %W[
- -I#{srcdir}/lib
- #{srcdir}/tool/runruby.rb --archdir=#{Dir.pwd} --extout=#{config['EXTOUT']}
- --
- ]
+ if srcdir
+ srcdir = File.expand_path(srcdir)
+ set :flags, %W[
+ -I#{srcdir}/lib
+ #{srcdir}/tool/runruby.rb --archdir=#{builddir} --extout=#{config['EXTOUT']}
+ --
+ ]
+ end
+
+ # Disable to run for bundled gems in test-spec
+ set :bundled_gems, (File.readlines("#{srcdir}/gems/bundled_gems").map do |line|
+ next if /^\s*(?:#|$)/ =~ line
+ gem = line.split.first
+ gem = "openstruct" if gem == "ostruct"
+ "#{srcdir}/spec/ruby/library/#{gem}"
+ end.compact)
+ set :stdlibs, Dir.glob("#{srcdir}/spec/ruby/library/*")
+ set :library, get(:stdlibs).to_a - get(:bundled_gems).to_a
+
+ set :files, get(:command_line) + get(:language) + get(:core) + get(:library) + get(:security) + get(:optional)
+
+ if ENV.key?("COVERAGE")
+ set :excludes, ["Coverage"]
+ end
end
module MSpecScript::JobServer
def cores(max = 1)
- if max > 1 and /(?:\A|\s)--jobserver-(?:auth|fds)=(\d+),(\d+)/ =~ ENV["MAKEFLAGS"]
- cores = 1
- begin
- r = IO.for_fd($1.to_i(10), "rb", autoclose: false)
- w = IO.for_fd($2.to_i(10), "wb", autoclose: false)
- jobtokens = r.read_nonblock(max - 1)
- cores = jobtokens.size
- if cores > 0
- cores += 1
- jobserver = w
- w = nil
- at_exit {
- jobserver.print(jobtokens)
- jobserver.close
- }
- MSpecScript::JobServer.module_eval do
- remove_method :cores
- define_method(:cores) do
- cores
- end
- end
- return cores
- end
- rescue Errno::EBADF
- ensure
- r&.close
- w&.close
- end
+ MSpecScript::JobServer.remove_method :cores
+ if cores = Test::JobServer.max_jobs(max)
+ MSpecScript::JobServer.define_method(:cores) { cores }
+ return cores
end
super
end
@@ -65,3 +63,76 @@ end
class MSpecScript
prepend JobServer
end
+
+require 'mspec/runner/formatters/dotted'
+
+class DottedFormatter
+ prepend Module.new {
+ BASE = __dir__ + "/ruby/" unless defined?(BASE)
+ COUNT_WIDTH = 6 unless defined?(COUNT_WIDTH)
+
+ def initialize(out = nil)
+ super
+ if out
+ @columns = nil
+ else
+ columns = ENV["COLUMNS"]&.to_i
+ columns = 80 unless columns&.nonzero?
+ w = COUNT_WIDTH + 1
+ round = 20
+ @columns = (columns - w) / round * round + w
+ end
+ @dotted = 0
+ @loaded = false
+ @count = 0
+ end
+
+ def register
+ super
+ MSpec.register :load, self
+ MSpec.register :unload, self
+ end
+
+ def after(*)
+ if @columns
+ if @dotted == 0
+ s = sprintf("%*d ", COUNT_WIDTH, @count)
+ print(s)
+ @dotted += s.size
+ end
+ @count +=1
+ end
+ super
+ if @columns and (@dotted += 1) >= @columns
+ print "\n"
+ @dotted = 0
+ end
+ end
+
+ def load(*)
+ file = MSpec.file || MSpec.files_array.first
+ @loaded = true
+ s = "#{file.delete_prefix(BASE)}:"
+ print s
+ if @columns
+ if (@dotted += s.size) >= @columns
+ print "\n"
+ @dotted = 0
+ else
+ print " "
+ @dotted += 1
+ end
+ end
+ @count = 0
+ end
+
+ def unload
+ super
+ if @loaded
+ print "\n" if @dotted > 0
+ @dotted = 0
+ @loaded = nil
+ end
+ end
+ }
+end
diff --git a/spec/lib/formatter_overrides.rb b/spec/lib/formatter_overrides.rb
new file mode 100644
index 0000000000..4a61d425d1
--- /dev/null
+++ b/spec/lib/formatter_overrides.rb
@@ -0,0 +1,6 @@
+module FormatterOverrides
+ def example_pending(_); end
+ def dump_pending(_); end
+end
+
+RSpec::Core::Formatters::ProgressFormatter.prepend FormatterOverrides
diff --git a/spec/lib/spec_coverage.rb b/spec/lib/spec_coverage.rb
new file mode 100644
index 0000000000..34aed32f72
--- /dev/null
+++ b/spec/lib/spec_coverage.rb
@@ -0,0 +1 @@
+require_relative "../../tool/test-coverage" if ENV.key?('COVERAGE')
diff --git a/spec/mmtk.mspec b/spec/mmtk.mspec
new file mode 100644
index 0000000000..ac0998708c
--- /dev/null
+++ b/spec/mmtk.mspec
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+# Testing behaviour specific to default GC
+MSpec.register(:exclude, "GC.stat increases major_gc_count after GC is run")
+MSpec.register(:exclude, "GC.stat provides some number for heap_free_slots")
+# GC::Profiler is not implemented
+MSpec.register(:exclude, "GC::Profiler.disable disables the profiler")
+MSpec.register(:exclude, "GC::Profiler.enable enables the profiler")
+MSpec.register(:exclude, "GC::Profiler.enabled? reports as disabled when disabled")
+MSpec.register(:exclude, "GC::Profiler.enabled? reports as enabled when enabled")
+MSpec.register(:exclude, "GC::Profiler.result returns a string")
+MSpec.register(:exclude, "GC::Profiler.total_time returns an float")
diff --git a/spec/mspec/.rspec b/spec/mspec/.rspec
new file mode 100644
index 0000000000..4e1e0d2f72
--- /dev/null
+++ b/spec/mspec/.rspec
@@ -0,0 +1 @@
+--color
diff --git a/spec/mspec/Gemfile b/spec/mspec/Gemfile
index 8e463cd71d..617a995cad 100644
--- a/spec/mspec/Gemfile
+++ b/spec/mspec/Gemfile
@@ -1,4 +1,4 @@
source 'https://rubygems.org'
gem "rake", "~> 12.3"
-gem "rspec", "~> 2.14.1"
+gem "rspec", "~> 3.0"
diff --git a/spec/mspec/Gemfile.lock b/spec/mspec/Gemfile.lock
index 74489c5aca..cd39906044 100644
--- a/spec/mspec/Gemfile.lock
+++ b/spec/mspec/Gemfile.lock
@@ -1,16 +1,21 @@
GEM
remote: https://rubygems.org/
specs:
- diff-lcs (1.3)
+ diff-lcs (1.4.4)
rake (12.3.3)
- rspec (2.14.1)
- rspec-core (~> 2.14.0)
- rspec-expectations (~> 2.14.0)
- rspec-mocks (~> 2.14.0)
- rspec-core (2.14.8)
- rspec-expectations (2.14.5)
- diff-lcs (>= 1.1.3, < 2.0)
- rspec-mocks (2.14.6)
+ 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.2)
PLATFORMS
java
@@ -18,4 +23,4 @@ PLATFORMS
DEPENDENCIES
rake (~> 12.3)
- rspec (~> 2.14.1)
+ rspec (~> 3.0)
diff --git a/spec/mspec/README.md b/spec/mspec/README.md
index d4054bcf0f..94ab608031 100644
--- a/spec/mspec/README.md
+++ b/spec/mspec/README.md
@@ -36,7 +36,7 @@ specs in a manner compatible with multiple Ruby implementations.
## Requirements
-MSpec requires Ruby 2.5 or more recent.
+MSpec requires Ruby 2.6 or more recent.
## Bundler
@@ -53,7 +53,7 @@ To install the gem dependencies with Bundler, run the following:
ruby -S bundle install
```
-## Running Specs
+## Development
Use RSpec to run the MSpec specs. There are no plans currently to make the
MSpec specs runnable by MSpec: https://github.com/ruby/mspec/issues/19.
diff --git a/spec/mspec/bin/mspec b/spec/mspec/bin/mspec
index f833257bb0..5bd753c06d 100755
--- a/spec/mspec/bin/mspec
+++ b/spec/mspec/bin/mspec
@@ -4,4 +4,4 @@ $:.unshift File.expand_path('../../lib', __FILE__)
require 'mspec/commands/mspec'
-MSpecMain.main
+MSpecMain.main(false)
diff --git a/spec/mspec/lib/mspec/commands/mkspec.rb b/spec/mspec/lib/mspec/commands/mkspec.rb
index d10cc35d18..f75e683b19 100755..100644
--- a/spec/mspec/lib/mspec/commands/mkspec.rb
+++ b/spec/mspec/lib/mspec/commands/mkspec.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/env ruby
-
require 'rbconfig'
require 'mspec/version'
require 'mspec/utils/options'
@@ -95,7 +93,9 @@ class MkSpec
def write_spec(file, meth, exists)
if exists
- out = `#{ruby} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e '#{meth}' #{file}`
+ command = "#{RbConfig.ruby} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e '#{meth}' #{file}"
+ puts "$ #{command}" if $DEBUG
+ out = `#{command}`
return if out.include?(meth)
end
@@ -133,18 +133,6 @@ EOS
end
end
- ##
- # Determine and return the path of the ruby executable.
-
- def ruby
- ruby = File.join(RbConfig::CONFIG['bindir'],
- RbConfig::CONFIG['ruby_install_name'])
-
- ruby.gsub! File::SEPARATOR, File::ALT_SEPARATOR if File::ALT_SEPARATOR
-
- return ruby
- end
-
def self.main
ENV['MSPEC_RUNNER'] = '1'
diff --git a/spec/mspec/lib/mspec/commands/mspec-ci.rb b/spec/mspec/lib/mspec/commands/mspec-ci.rb
index a31db1d7dc..8951572f69 100644
--- a/spec/mspec/lib/mspec/commands/mspec-ci.rb
+++ b/spec/mspec/lib/mspec/commands/mspec-ci.rb
@@ -1,7 +1,3 @@
-#!/usr/bin/env ruby
-
-$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
-
require 'mspec/version'
require 'mspec/utils/options'
require 'mspec/utils/script'
@@ -22,6 +18,7 @@ class MSpecCI < MSpecScript
options.chdir
options.prefix
options.configure { |f| load f }
+ options.repeat
options.pretend
options.interrupt
options.timeout
diff --git a/spec/mspec/lib/mspec/commands/mspec-run.rb b/spec/mspec/lib/mspec/commands/mspec-run.rb
index 4d8f4d9984..0fb338fa23 100644
--- a/spec/mspec/lib/mspec/commands/mspec-run.rb
+++ b/spec/mspec/lib/mspec/commands/mspec-run.rb
@@ -1,7 +1,3 @@
-#!/usr/bin/env ruby
-
-$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
-
require 'mspec/version'
require 'mspec/utils/options'
require 'mspec/utils/script'
@@ -32,6 +28,7 @@ class MSpecRun < MSpecScript
options.chdir
options.prefix
options.configure { |f| load f }
+ options.env
options.randomize
options.repeat
options.pretend
@@ -52,6 +49,9 @@ class MSpecRun < MSpecScript
options.doc "\n When to perform it"
options.action_filters
+ options.doc "\n Launchable"
+ options.launchable
+
options.doc "\n Help!"
options.debug
options.version MSpec::VERSION
diff --git a/spec/mspec/lib/mspec/commands/mspec-tag.rb b/spec/mspec/lib/mspec/commands/mspec-tag.rb
index e1d04d1446..9ce9f048c6 100644
--- a/spec/mspec/lib/mspec/commands/mspec-tag.rb
+++ b/spec/mspec/lib/mspec/commands/mspec-tag.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/env ruby
-
require 'mspec/version'
require 'mspec/utils/options'
require 'mspec/utils/script'
@@ -114,6 +112,7 @@ class MSpecTag < MSpecScript
MSpec.register_mode :pretend
MSpec.register_mode :unguarded
config[:formatter] = false
+ config[:xtags] = []
else
raise ArgumentError, "No recognized action given"
end
diff --git a/spec/mspec/lib/mspec/commands/mspec.rb b/spec/mspec/lib/mspec/commands/mspec.rb
index 9d82949ff1..a9d94ca354 100755..100644
--- a/spec/mspec/lib/mspec/commands/mspec.rb
+++ b/spec/mspec/lib/mspec/commands/mspec.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/env ruby
-
require 'mspec/version'
require 'mspec/utils/options'
require 'mspec/utils/script'
@@ -25,6 +23,7 @@ class MSpecMain < MSpecScript
config[:command] = argv.shift if ["ci", "run", "tag"].include?(argv[0])
options = MSpecOptions.new "mspec [COMMAND] [options] (FILE|DIRECTORY|GLOB)+", 30, config
+ @options = options
options.doc " The mspec command sets up and invokes the sub-commands"
options.doc " (see below) to enable, for instance, running the specs"
@@ -37,11 +36,6 @@ class MSpecMain < MSpecScript
options.targets
- options.on("--warnings", "Don't suppress warnings") do
- config[:flags] << '-w'
- ENV['OUTPUT_WARNINGS'] = '1'
- end
-
options.on("-j", "--multi", "Run multiple (possibly parallel) subprocesses") do
config[:multi] = true
end
@@ -110,8 +104,9 @@ class MSpecMain < MSpecScript
if config[:multi]
exit multi_exec(argv)
else
- $stderr.puts "$ #{argv.join(' ')}"
- $stderr.flush
+ log = config[:options].include?('--error-output') ? $stdout : $stderr
+ log.puts "$ #{argv.join(' ')}"
+ log.flush
exec(*argv, close_others: false)
end
end
diff --git a/spec/mspec/lib/mspec/expectations/expectations.rb b/spec/mspec/lib/mspec/expectations/expectations.rb
index 8d01dc22ab..09852ab557 100644
--- a/spec/mspec/lib/mspec/expectations/expectations.rb
+++ b/spec/mspec/lib/mspec/expectations/expectations.rb
@@ -32,4 +32,8 @@ class SpecExpectation
result_to_s = MSpec.format(result)
raise SpecExpectationNotMetError, "Expected #{receiver_to_s}#{predicate_to_s}#{args_to_s}\n#{expectation} but was #{result_to_s}"
end
+
+ def self.fail_single_arg_predicate(receiver, predicate, arg, result, expectation)
+ fail_predicate(receiver, predicate, [arg], nil, result, expectation)
+ end
end
diff --git a/spec/mspec/lib/mspec/expectations/should.rb b/spec/mspec/lib/mspec/expectations/should.rb
index ca0617484c..c1790e0ac8 100644
--- a/spec/mspec/lib/mspec/expectations/should.rb
+++ b/spec/mspec/lib/mspec/expectations/should.rb
@@ -1,7 +1,7 @@
class Object
NO_MATCHER_GIVEN = Object.new
- def should(matcher = NO_MATCHER_GIVEN)
+ def should(matcher = NO_MATCHER_GIVEN, &block)
MSpec.expectation
state = MSpec.current.state
raise "should outside example" unless state
@@ -10,6 +10,9 @@ class Object
if NO_MATCHER_GIVEN.equal?(matcher)
SpecPositiveOperatorMatcher.new(self)
else
+ # The block was given to #should syntactically, but it was intended for a matcher like #raise_error
+ matcher.block = block if block
+
unless matcher.matches? self
expected, actual = matcher.failure_message
SpecExpectation.fail_with(expected, actual)
@@ -17,7 +20,7 @@ class Object
end
end
- def should_not(matcher = NO_MATCHER_GIVEN)
+ def should_not(matcher = NO_MATCHER_GIVEN, &block)
MSpec.expectation
state = MSpec.current.state
raise "should_not outside example" unless state
@@ -26,6 +29,9 @@ class Object
if NO_MATCHER_GIVEN.equal?(matcher)
SpecNegativeOperatorMatcher.new(self)
else
+ # The block was given to #should_not syntactically, but it was intended for the matcher
+ matcher.block = block if block
+
if matcher.matches? self
expected, actual = matcher.negative_failure_message
SpecExpectation.fail_with(expected, actual)
diff --git a/spec/mspec/lib/mspec/guards/platform.rb b/spec/mspec/lib/mspec/guards/platform.rb
index 2d5c2de6b6..fadd8d75ef 100644
--- a/spec/mspec/lib/mspec/guards/platform.rb
+++ b/spec/mspec/lib/mspec/guards/platform.rb
@@ -41,6 +41,10 @@ class PlatformGuard < SpecGuard
os?(:windows)
end
+ def self.wasi?
+ os?(:wasi)
+ end
+
def self.wsl?
if defined?(@wsl_p)
@wsl_p
@@ -49,16 +53,27 @@ class PlatformGuard < SpecGuard
end
end
+ # In bits
WORD_SIZE = 1.size * 8
+ deprecate_constant :WORD_SIZE
+ # In bits
POINTER_SIZE = begin
require 'rbconfig/sizeof'
RbConfig::SIZEOF["void*"] * 8
rescue LoadError
- WORD_SIZE
+ [0].pack('j').size * 8
+ end
+
+ # In bits
+ C_LONG_SIZE = if defined?(RbConfig::SIZEOF[])
+ RbConfig::SIZEOF["long"] * 8
+ else
+ [0].pack('l!').size * 8
end
def self.wordsize?(size)
+ warn "#wordsize? is deprecated, use #c_long_size?"
size == WORD_SIZE
end
@@ -66,6 +81,10 @@ class PlatformGuard < SpecGuard
size == POINTER_SIZE
end
+ def self.c_long_size?(size)
+ size == C_LONG_SIZE
+ end
+
def initialize(*args)
if args.last.is_a?(Hash)
@options, @platforms = args.last, args[0..-2]
@@ -81,10 +100,13 @@ class PlatformGuard < SpecGuard
case key
when :os
match &&= PlatformGuard.os?(*value)
- when :wordsize
- match &&= PlatformGuard.wordsize? value
when :pointer_size
match &&= PlatformGuard.pointer_size? value
+ when :wordsize
+ warn ":wordsize is deprecated, use :c_long_size"
+ match &&= PlatformGuard.wordsize? value
+ when :c_long_size
+ match &&= PlatformGuard::c_long_size? value
end
end
match
diff --git a/spec/mspec/lib/mspec/guards/superuser.rb b/spec/mspec/lib/mspec/guards/superuser.rb
index e92ea7e862..24daf9b26c 100644
--- a/spec/mspec/lib/mspec/guards/superuser.rb
+++ b/spec/mspec/lib/mspec/guards/superuser.rb
@@ -6,10 +6,20 @@ class SuperUserGuard < SpecGuard
end
end
+class RealSuperUserGuard < SpecGuard
+ def match?
+ Process.uid == 0
+ end
+end
+
def as_superuser(&block)
SuperUserGuard.new.run_if(:as_superuser, &block)
end
+def as_real_superuser(&block)
+ RealSuperUserGuard.new.run_if(:as_real_superuser, &block)
+end
+
def as_user(&block)
SuperUserGuard.new.run_unless(:as_user, &block)
end
diff --git a/spec/mspec/lib/mspec/guards/version.rb b/spec/mspec/lib/mspec/guards/version.rb
index 20f8c06d38..f5ea1988ae 100644
--- a/spec/mspec/lib/mspec/guards/version.rb
+++ b/spec/mspec/lib/mspec/guards/version.rb
@@ -33,6 +33,30 @@ class VersionGuard < SpecGuard
@version >= @requirement
end
end
+
+ @kernel_version = nil
+ def self.kernel_version
+ if @kernel_version
+ @kernel_version
+ else
+ if v = RUBY_PLATFORM[/darwin(\d+)/, 1] # build time version
+ uname = v
+ else
+ begin
+ require 'etc'
+ etc = true
+ rescue LoadError
+ etc = false
+ end
+ if etc and Etc.respond_to?(:uname)
+ uname = Etc.uname.fetch(:release)
+ else
+ uname = `uname -r`.chomp
+ end
+ end
+ @kernel_version = uname
+ end
+ end
end
def version_is(base_version, requirement, &block)
@@ -42,3 +66,7 @@ end
def ruby_version_is(requirement, &block)
VersionGuard.new(VersionGuard::FULL_RUBY_VERSION, requirement).run_if(:ruby_version_is, &block)
end
+
+def kernel_version_is(requirement, &block)
+ VersionGuard.new(VersionGuard.kernel_version, requirement).run_if(:kernel_version_is, &block)
+end
diff --git a/spec/mspec/lib/mspec/helpers/datetime.rb b/spec/mspec/lib/mspec/helpers/datetime.rb
index 3a40cc0902..84ac86b686 100644
--- a/spec/mspec/lib/mspec/helpers/datetime.rb
+++ b/spec/mspec/lib/mspec/helpers/datetime.rb
@@ -25,6 +25,7 @@ def new_datetime(opts = {})
end
def with_timezone(name, offset = nil, daylight_saving_zone = "")
+ skip "WASI doesn't have TZ concept" if PlatformGuard.wasi?
zone = name.dup
if offset
diff --git a/spec/mspec/lib/mspec/helpers/io.rb b/spec/mspec/lib/mspec/helpers/io.rb
index 1938255d1a..2ad14f47a1 100644
--- a/spec/mspec/lib/mspec/helpers/io.rb
+++ b/spec/mspec/lib/mspec/helpers/io.rb
@@ -7,7 +7,7 @@ class IOStub
end
def write(*str)
- self << str.join
+ self << str.join('')
end
def << str
@@ -16,7 +16,7 @@ class IOStub
end
def print(*str)
- write(str.join + $\.to_s)
+ write(str.join('') + $\.to_s)
end
def method_missing(name, *args, &block)
@@ -85,7 +85,3 @@ def new_io(name, mode = "w:utf-8")
File.new(name, mode)
end
end
-
-def find_unused_fd
- Dir.entries("/dev/fd").map(&:to_i).max + 1
-end
diff --git a/spec/mspec/lib/mspec/helpers/numeric.rb b/spec/mspec/lib/mspec/helpers/numeric.rb
index db1fde64d8..0b47855cd2 100644
--- a/spec/mspec/lib/mspec/helpers/numeric.rb
+++ b/spec/mspec/lib/mspec/helpers/numeric.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
require 'mspec/guards/platform'
def nan_value
@@ -9,15 +10,17 @@ def infinity_value
end
def bignum_value(plus = 0)
- 0x8000_0000_0000_0000 + plus
+ # Must be >= fixnum_max + 2, so -bignum_value is < fixnum_min
+ # A fixed value has the advantage to be the same numeric value for all Rubies and is much easier to spec
+ (2**64) + plus
end
def max_long
- 2**(0.size * 8 - 1) - 1
+ 2**(PlatformGuard::C_LONG_SIZE - 1) - 1
end
def min_long
- -(2**(0.size * 8 - 1))
+ -(2**(PlatformGuard::C_LONG_SIZE - 1))
end
# This is a bit hairy, but we need to be able to write specs that cover the
@@ -26,7 +29,24 @@ end
# specs based on the relationship between values rather than specific
# values.
if PlatformGuard.standard? or PlatformGuard.implementation? :topaz
- if PlatformGuard.wordsize? 32
+ limits_available = begin
+ require 'rbconfig/sizeof'
+ defined?(RbConfig::LIMITS.[]) && ['FIXNUM_MAX', 'FIXNUM_MIN'].all? do |key|
+ Integer === RbConfig::LIMITS[key]
+ end
+ rescue LoadError
+ false
+ end
+
+ if limits_available
+ def fixnum_max
+ RbConfig::LIMITS['FIXNUM_MAX']
+ end
+
+ def fixnum_min
+ RbConfig::LIMITS['FIXNUM_MIN']
+ end
+ elsif PlatformGuard.c_long_size? 32
def fixnum_max
(2**30) - 1
end
@@ -34,7 +54,7 @@ if PlatformGuard.standard? or PlatformGuard.implementation? :topaz
def fixnum_min
-(2**30)
end
- elsif PlatformGuard.wordsize? 64
+ elsif PlatformGuard.c_long_size? 64
def fixnum_max
(2**62) - 1
end
diff --git a/spec/mspec/lib/mspec/helpers/ruby_exe.rb b/spec/mspec/lib/mspec/helpers/ruby_exe.rb
index 075a8aaef5..2e499d6f9a 100644
--- a/spec/mspec/lib/mspec/helpers/ruby_exe.rb
+++ b/spec/mspec/lib/mspec/helpers/ruby_exe.rb
@@ -15,8 +15,10 @@ require 'mspec/helpers/tmp'
#
# `#{RUBY_EXE} 'path/to/some/file.rb'`
#
-# The ruby_exe helper also accepts an options hash with three
-# keys: :options, :args and :env. For example:
+# The ruby_exe helper also accepts an options hash with four
+# keys: :options, :args :env and :exception.
+#
+# For example:
#
# ruby_exe('file.rb', :options => "-w",
# :args => "arg1 arg2",
@@ -28,6 +30,9 @@ require 'mspec/helpers/tmp'
#
# with access to ENV["FOO"] with value "bar".
#
+# When `exception: false` and Ruby command fails then exception will not be
+# raised.
+#
# If +nil+ is passed for the first argument, the command line
# will be built only from the options hash.
#
@@ -52,6 +57,8 @@ require 'mspec/helpers/tmp'
# (with -T on the command line or in the config with set :flags)
# will be appended to RUBY_EXE so that the interpreter
# is always called with those flags.
+#
+# Failure of a Ruby command leads to raising exception by default.
def ruby_exe_options(option)
case option
@@ -105,6 +112,8 @@ unless Object.const_defined?(:RUBY_EXE) and RUBY_EXE
end
def ruby_exe(code = :not_given, opts = {})
+ skip "WASI doesn't provide subprocess" if PlatformGuard.wasi?
+
if opts[:dir]
raise "ruby_exe(..., dir: dir) is no longer supported, use Dir.chdir"
end
@@ -128,10 +137,47 @@ def ruby_exe(code = :not_given, opts = {})
code = tmpfile
end
+ expected_status = opts.fetch(:exit_status, 0)
+
begin
- platform_is_not :opal do
- `#{ruby_cmd(code, opts)}`
+ command = ruby_cmd(code, opts)
+
+ # Try to avoid the extra shell for 2>&1
+ # This is notably useful for TimeoutAction which can then signal the ruby subprocess and not the shell
+ popen_options = []
+ if command.end_with?(' 2>&1')
+ command = command[0...-5]
+ popen_options = [{ err: [:child, :out] }]
end
+
+ output = IO.popen(command, *popen_options) do |io|
+ pid = io.pid
+ MSpec.subprocesses << pid
+ begin
+ io.read
+ ensure
+ MSpec.subprocesses.delete(pid)
+ end
+ end
+
+ status = Process.last_status
+
+ exit_status = if status.exited?
+ status.exitstatus
+ elsif status.signaled?
+ signame = Signal.signame status.termsig
+ raise "No signal name?" unless signame
+ :"SIG#{signame}"
+ else
+ raise SpecExpectationNotMetError, "#{exit_status.inspect} is neither exited? nor signaled?"
+ end
+ if exit_status != expected_status
+ formatted_output = output.lines.map { |line| " #{line}" }.join
+ raise SpecExpectationNotMetError,
+ "Expected exit status is #{expected_status.inspect} but actual is #{exit_status.inspect} for command ruby_exe(#{command.inspect})\nOutput:\n#{formatted_output}"
+ end
+
+ output
ensure
saved_env.each { |key, value| ENV[key] = value }
env.keys.each do |key|
diff --git a/spec/mspec/lib/mspec/helpers/tmp.rb b/spec/mspec/lib/mspec/helpers/tmp.rb
index b2a38ee983..e903dd9f50 100644
--- a/spec/mspec/lib/mspec/helpers/tmp.rb
+++ b/spec/mspec/lib/mspec/helpers/tmp.rb
@@ -12,7 +12,7 @@ else
end
SPEC_TEMP_DIR = spec_temp_dir
-SPEC_TEMP_UNIQUIFIER = "0"
+SPEC_TEMP_UNIQUIFIER = +"0"
at_exit do
begin
@@ -36,11 +36,25 @@ all specs are cleaning up temporary files:
end
def tmp(name, uniquify = true)
- mkdir_p SPEC_TEMP_DIR unless Dir.exist? SPEC_TEMP_DIR
+ if Dir.exist? SPEC_TEMP_DIR
+ stat = File.stat(SPEC_TEMP_DIR)
+ if stat.world_writable? and !stat.sticky?
+ raise ArgumentError, "SPEC_TEMP_DIR (#{SPEC_TEMP_DIR}) is world writable but not sticky"
+ end
+ else
+ platform_is_not :windows do
+ umask = File.umask
+ if (umask & 0002) == 0 # o+w
+ raise ArgumentError, "File.umask #=> #{umask.to_s(8)} (world-writable)"
+ end
+ end
+ mkdir_p SPEC_TEMP_DIR
+ end
if uniquify and !name.empty?
slash = name.rindex "/"
index = slash ? slash + 1 : 0
+ name = +name
name.insert index, "#{SPEC_TEMP_UNIQUIFIER.succ!}-"
end
diff --git a/spec/mspec/lib/mspec/helpers/warning.rb b/spec/mspec/lib/mspec/helpers/warning.rb
index f94551c185..e3d72b78bd 100644
--- a/spec/mspec/lib/mspec/helpers/warning.rb
+++ b/spec/mspec/lib/mspec/helpers/warning.rb
@@ -1,5 +1,7 @@
require 'mspec/guards/version'
+# You might be looking for #silence_warnings, use #suppress_warning instead.
+# MSpec calls it #suppress_warning for consistency with EnvUtil.suppress_warning in CRuby test/.
def suppress_warning
verbose = $VERBOSE
$VERBOSE = nil
diff --git a/spec/mspec/lib/mspec/matchers/base.rb b/spec/mspec/lib/mspec/matchers/base.rb
index 94d3b71e55..3534520d88 100644
--- a/spec/mspec/lib/mspec/matchers/base.rb
+++ b/spec/mspec/lib/mspec/matchers/base.rb
@@ -16,15 +16,32 @@ class SpecPositiveOperatorMatcher < BasicObject
end
def ==(expected)
- method_missing(:==, expected)
+ result = @actual == expected
+ unless result
+ ::SpecExpectation.fail_single_arg_predicate(@actual, :==, expected, result, "to be truthy")
+ end
end
def !=(expected)
- method_missing(:!=, expected)
+ result = @actual != expected
+ unless result
+ ::SpecExpectation.fail_single_arg_predicate(@actual, :!=, expected, result, "to be truthy")
+ end
end
def equal?(expected)
- method_missing(:equal?, expected)
+ result = @actual.equal?(expected)
+ unless result
+ ::SpecExpectation.fail_single_arg_predicate(@actual, :equal?, expected, result, "to be truthy")
+ end
+ end
+
+ def raise(exception = ::Exception, message = nil, options = nil, &block)
+ matcher = ::RaiseErrorMatcher.new(exception, message, options, &block)
+ unless matcher.matches? @actual
+ expected, actual = matcher.failure_message
+ ::SpecExpectation.fail_with(expected, actual)
+ end
end
def method_missing(name, *args, &block)
@@ -41,15 +58,32 @@ class SpecNegativeOperatorMatcher < BasicObject
end
def ==(expected)
- method_missing(:==, expected)
+ result = @actual == expected
+ if result
+ ::SpecExpectation.fail_single_arg_predicate(@actual, :==, expected, result, "to be falsy")
+ end
end
def !=(expected)
- method_missing(:!=, expected)
+ result = @actual != expected
+ if result
+ ::SpecExpectation.fail_single_arg_predicate(@actual, :!=, expected, result, "to be falsy")
+ end
end
def equal?(expected)
- method_missing(:equal?, expected)
+ result = @actual.equal?(expected)
+ if result
+ ::SpecExpectation.fail_single_arg_predicate(@actual, :equal?, expected, result, "to be falsy")
+ end
+ end
+
+ def raise(exception = ::Exception, message = nil, options = nil, &block)
+ matcher = ::RaiseErrorMatcher.new(exception, message, options, &block)
+ if matcher.matches? @actual
+ expected, actual = matcher.negative_failure_message
+ ::SpecExpectation.fail_with(expected, actual)
+ end
end
def method_missing(name, *args, &block)
diff --git a/spec/mspec/lib/mspec/matchers/complain.rb b/spec/mspec/lib/mspec/matchers/complain.rb
index 887e72b4b0..19310c0bbb 100644
--- a/spec/mspec/lib/mspec/matchers/complain.rb
+++ b/spec/mspec/lib/mspec/matchers/complain.rb
@@ -19,7 +19,6 @@ class ComplainMatcher
@verbose = $VERBOSE
err = IOStub.new
- Thread.current[:in_mspec_complain_matcher] = true
$stderr = err
$VERBOSE = @options.key?(:verbose) ? @options[:verbose] : false
begin
@@ -27,7 +26,6 @@ class ComplainMatcher
ensure
$VERBOSE = @verbose
$stderr = @saved_err
- Thread.current[:in_mspec_complain_matcher] = false
end
@warning = err.to_s
diff --git a/spec/mspec/lib/mspec/matchers/include.rb b/spec/mspec/lib/mspec/matchers/include.rb
index 0b7eaf3ce2..3f07f35548 100644
--- a/spec/mspec/lib/mspec/matchers/include.rb
+++ b/spec/mspec/lib/mspec/matchers/include.rb
@@ -15,11 +15,11 @@ class IncludeMatcher
end
def failure_message
- ["Expected #{@actual.inspect}", "to include #{@element.inspect}"]
+ ["Expected #{MSpec.format(@actual)}", "to include #{MSpec.format(@element)}"]
end
def negative_failure_message
- ["Expected #{@actual.inspect}", "not to include #{@element.inspect}"]
+ ["Expected #{MSpec.format(@actual)}", "not to include #{MSpec.format(@element)}"]
end
end
diff --git a/spec/mspec/lib/mspec/matchers/match_yaml.rb b/spec/mspec/lib/mspec/matchers/match_yaml.rb
index 920d85a14f..30561627c3 100644
--- a/spec/mspec/lib/mspec/matchers/match_yaml.rb
+++ b/spec/mspec/lib/mspec/matchers/match_yaml.rb
@@ -30,7 +30,11 @@ class MatchYAMLMatcher
def valid_yaml?(obj)
require 'yaml'
begin
- YAML.load(obj)
+ if YAML.respond_to?(:unsafe_load)
+ YAML.unsafe_load(obj)
+ else
+ YAML.load(obj)
+ end
rescue
false
else
diff --git a/spec/mspec/lib/mspec/matchers/output.rb b/spec/mspec/lib/mspec/matchers/output.rb
index 20721df743..5bb5d55027 100644
--- a/spec/mspec/lib/mspec/matchers/output.rb
+++ b/spec/mspec/lib/mspec/matchers/output.rb
@@ -42,12 +42,12 @@ class OutputMatcher
expected_out = "\n"
actual_out = "\n"
unless @out.nil?
- expected_out += " $stdout: #{@out.inspect}\n"
- actual_out += " $stdout: #{@stdout.inspect}\n"
+ expected_out += " $stdout: #{MSpec.format(@out)}\n"
+ actual_out += " $stdout: #{MSpec.format(@stdout.to_s)}\n"
end
unless @err.nil?
- expected_out += " $stderr: #{@err.inspect}\n"
- actual_out += " $stderr: #{@stderr.inspect}\n"
+ expected_out += " $stderr: #{MSpec.format(@err)}\n"
+ actual_out += " $stderr: #{MSpec.format(@stderr.to_s)}\n"
end
["Expected:#{expected_out}", " got:#{actual_out}"]
end
diff --git a/spec/mspec/lib/mspec/matchers/raise_error.rb b/spec/mspec/lib/mspec/matchers/raise_error.rb
index 878428d43a..8cba842ce3 100644
--- a/spec/mspec/lib/mspec/matchers/raise_error.rb
+++ b/spec/mspec/lib/mspec/matchers/raise_error.rb
@@ -1,7 +1,18 @@
class RaiseErrorMatcher
- def initialize(exception, message, &block)
+ FAILURE_MESSAGE_FOR_EXCEPTION = {}.compare_by_identity
+ UNDEF_CAUSE = Object.new
+
+ attr_writer :block
+
+ def initialize(exception, message = nil, options = nil, &block)
+ if message.is_a? Hash
+ @message = nil
+ options = message
+ else
+ @message = message
+ end
+ @cause = options ? options.fetch(:cause, UNDEF_CAUSE) : UNDEF_CAUSE
@exception = exception
- @message = message
@block = block
@actual = nil
end
@@ -13,7 +24,7 @@ class RaiseErrorMatcher
def matches?(proc)
@result = proc.call
return false
- rescue Exception => actual
+ rescue Object => actual
@actual = actual
if matching_exception?(actual)
@@ -21,7 +32,7 @@ class RaiseErrorMatcher
@block[actual] if @block
return true
else
- actual.instance_variable_set(:@mspec_raise_error_message, failure_message)
+ FAILURE_MESSAGE_FOR_EXCEPTION[actual] = failure_message
raise actual
end
end
@@ -41,24 +52,45 @@ class RaiseErrorMatcher
end
end
+ def matching_cause?(exc)
+ case @cause
+ when UNDEF_CAUSE
+ true
+ else
+ @cause == exc.cause
+ end
+ end
+
def matching_exception?(exc)
- matching_class?(exc) and matching_message?(exc)
+ matching_class?(exc) and matching_message?(exc) and matching_cause?(exc)
end
- def exception_class_and_message(exception_class, message)
- if message
- "#{exception_class} (#{message})"
- else
- "#{exception_class}"
+ def exception_class_and_message_and_cause(exception_class, message, cause)
+ string = "#{exception_class}"
+ prefixed = false
+ prefix = -> { prefixed ? ", " : prefixed = "(" }
+
+ if message != nil
+ string << "#{prefix.()}#{message.inspect}"
end
+
+ if cause != UNDEF_CAUSE
+ string << "#{prefix.()}cause: #{cause.inspect}"
+ end
+
+ string << ")" if prefixed
+
+ string
end
def format_expected_exception
- exception_class_and_message(@exception, @message)
+ exception_class_and_message_and_cause(@exception, @message, @cause)
end
def format_exception(exception)
- exception_class_and_message(exception.class, exception.message)
+ exception_class_and_message_and_cause(exception.class,
+ @message == nil ? nil : exception.message,
+ @cause == UNDEF_CAUSE ? UNDEF_CAUSE : exception.cause)
end
def failure_message
@@ -83,7 +115,18 @@ class RaiseErrorMatcher
end
module MSpecMatchers
- private def raise_error(exception = Exception, message = nil, &block)
- RaiseErrorMatcher.new(exception, message, &block)
+ private def raise_error(exception = Exception, message = nil, options = nil, &block)
+ RaiseErrorMatcher.new(exception, message, options, &block)
+ end
+
+ # CRuby < 4.1 has inconsistent coercion errors:
+ # https://bugs.ruby-lang.org/issues/21864
+ # This matcher ignores the message on CRuby < 4.1
+ # and checks the message for all other cases, including other Rubies
+ private def raise_consistent_error(exception = Exception, message = nil, options = nil, &block)
+ if RUBY_ENGINE == "ruby" and ruby_version_is ""..."4.1"
+ message = nil
+ end
+ RaiseErrorMatcher.new(exception, message, options, &block)
end
end
diff --git a/spec/mspec/lib/mspec/mocks/mock.rb b/spec/mspec/lib/mspec/mocks/mock.rb
index bce41b630f..c61ba35ea7 100644
--- a/spec/mspec/lib/mspec/mocks/mock.rb
+++ b/spec/mspec/lib/mspec/mocks/mock.rb
@@ -18,20 +18,16 @@ module Mock
@stubs ||= Hash.new { |h,k| h[k] = [] }
end
- def self.replaced_name(obj, sym)
- :"__mspec_#{obj.__id__}_#{sym}__"
+ def self.replaced_name(key)
+ :"__mspec_#{key.last}__"
end
def self.replaced_key(obj, sym)
- [replaced_name(obj, sym), sym]
+ [obj.__id__, sym]
end
- def self.has_key?(keys, sym)
- !!keys.find { |k| k.first == sym }
- end
-
- def self.replaced?(sym)
- has_key?(mocks.keys, sym) or has_key?(stubs.keys, sym)
+ def self.replaced?(key)
+ mocks.include?(key) or stubs.include?(key)
end
def self.clear_replaced(key)
@@ -40,8 +36,9 @@ module Mock
end
def self.mock_respond_to?(obj, sym, include_private = false)
- name = replaced_name(obj, :respond_to?)
- if replaced? name
+ key = replaced_key(obj, :respond_to?)
+ if replaced? key
+ name = replaced_name(key)
obj.__send__ name, sym, include_private
else
obj.respond_to? sym, include_private
@@ -59,8 +56,8 @@ module Mock
return
end
- if (sym == :respond_to? or mock_respond_to?(obj, sym, true)) and !replaced?(key.first)
- meta.__send__ :alias_method, key.first, sym
+ if (sym == :respond_to? or mock_respond_to?(obj, sym, true)) and !replaced?(key)
+ meta.__send__ :alias_method, replaced_name(key), sym
end
suppress_warning {
@@ -180,7 +177,7 @@ module Mock
mock_respond_to? obj, *args
else
SpecExpectation.fail_with("Mock '#{name_or_inspect obj}': method #{sym}\n",
- "called with unexpected arguments #{inspect_args compare}")
+ "called with unexpected arguments #{inspect_args args}")
end
end
@@ -191,7 +188,7 @@ module Mock
next
end
- replaced = key.first
+ replaced = replaced_name(key)
sym = key.last
meta = obj.singleton_class
diff --git a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
index 9efabc79b4..0a8c9c3252 100644
--- a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
+++ b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb
@@ -36,6 +36,7 @@ class LeakChecker
@thread_info = find_threads
@env_info = find_env
@argv_info = find_argv
+ @globals_info = find_globals
@encoding_info = find_encodings
end
@@ -48,9 +49,10 @@ class LeakChecker
check_process_leak
check_env
check_argv
+ check_globals
check_encodings
check_tracepoints
- GC.start if !@leaks.empty?
+ GC.start unless @leaks.empty?
@leaks.empty?
end
@@ -130,14 +132,14 @@ class LeakChecker
attr_accessor :count
end
- def new(data)
+ def new(...)
LeakChecker::TempfileCounter.count += 1
- super(data)
+ super
end
}
LeakChecker.const_set(:TempfileCounter, m)
- class << Tempfile::Remover
+ class << Tempfile
prepend LeakChecker::TempfileCounter
end
end
@@ -171,7 +173,8 @@ class LeakChecker
def find_threads
Thread.list.find_all {|t|
- t != Thread.current && t.alive?
+ t != Thread.current && t.alive? &&
+ !(t.thread_variable?(:"\0__detached_thread__") && t.thread_variable_get(:"\0__detached_thread__"))
}
end
@@ -244,6 +247,19 @@ class LeakChecker
end
end
+ def find_globals
+ { verbose: $VERBOSE, debug: $DEBUG }
+ end
+
+ def check_globals
+ old_globals = @globals_info
+ new_globals = find_globals
+ if new_globals != old_globals
+ leak "Globals changed: #{old_globals.inspect} to #{new_globals.inspect}"
+ @globals_info = new_globals
+ end
+ end
+
def find_encodings
[Encoding.default_internal, Encoding.default_external]
end
@@ -285,6 +301,7 @@ class LeakCheckerAction
end
def start
+ disable_nss_modules
@checker = LeakChecker.new
end
@@ -300,4 +317,61 @@ class LeakCheckerAction
end
end
end
+
+ private
+
+ # This function is intended to disable all NSS modules when ruby is compiled
+ # against glibc. NSS modules allow the system administrator to load custom
+ # shared objects into all processes using glibc, and use them to customise
+ # the behaviour of username, groupname, hostname, etc lookups. This is
+ # normally configured in the file /etc/nsswitch.conf.
+ # These modules often do things like open cache files or connect to system
+ # daemons like sssd or dbus, which of course means they have open file
+ # descriptors of their own. This can cause the leak-checking functionality
+ # in this file to report that such descriptors have been leaked, and fail
+ # the test suite.
+ # This function uses glibc's __nss_configure_lookup function to override any
+ # configuration in /etc/nsswitch.conf, and just use the built in files/dns
+ # name lookup functionality (which is of course perfectly sufficient for
+ # running ruby/spec).
+ def disable_nss_modules
+ begin
+ require 'fiddle'
+ rescue LoadError
+ # Make sure it's possible to run the test suite on a ruby implementation
+ # which does not (yet?) have Fiddle.
+ return
+ end
+
+ begin
+ libc = Fiddle.dlopen(nil)
+ # Older versions of fiddle don't have Fiddle::Type (and instead rely on Fiddle::TYPE_)
+ # Even older versions of fiddle don't have CONST_STRING,
+ string_type = defined?(Fiddle::TYPE_CONST_STRING) ? Fiddle::TYPE_CONST_STRING : Fiddle::TYPE_VOIDP
+ nss_configure_lookup = Fiddle::Function.new(
+ libc['__nss_configure_lookup'],
+ [string_type, string_type],
+ Fiddle::TYPE_INT
+ )
+ rescue Fiddle::DLError
+ # We're not running with glibc - no need to do this.
+ return
+ end
+
+ nss_configure_lookup.call 'passwd', 'files'
+ nss_configure_lookup.call 'shadow', 'files'
+ nss_configure_lookup.call 'group', 'files'
+ nss_configure_lookup.call 'hosts', 'files dns'
+ nss_configure_lookup.call 'services', 'files'
+ nss_configure_lookup.call 'netgroup', 'files'
+ nss_configure_lookup.call 'automount', 'files'
+ nss_configure_lookup.call 'aliases', 'files'
+ nss_configure_lookup.call 'ethers', 'files'
+ nss_configure_lookup.call 'gshadow', 'files'
+ nss_configure_lookup.call 'initgroups', 'files'
+ nss_configure_lookup.call 'networks', 'files dns'
+ nss_configure_lookup.call 'protocols', 'files'
+ nss_configure_lookup.call 'publickey', 'files'
+ nss_configure_lookup.call 'rpc', 'files'
+ end
end
diff --git a/spec/mspec/lib/mspec/runner/actions/timeout.rb b/spec/mspec/lib/mspec/runner/actions/timeout.rb
index c85bf49ad3..1200926872 100644
--- a/spec/mspec/lib/mspec/runner/actions/timeout.rb
+++ b/spec/mspec/lib/mspec/runner/actions/timeout.rb
@@ -3,11 +3,14 @@ class TimeoutAction
@timeout = timeout
@queue = Queue.new
@started = now
+ @fail = false
+ @error_message = "took longer than the configured timeout of #{@timeout}s"
end
def register
MSpec.register :start, self
MSpec.register :before, self
+ MSpec.register :after, self
MSpec.register :finish, self
end
@@ -35,9 +38,27 @@ class TimeoutAction
if @queue.empty?
elapsed = now - @started
if elapsed > @timeout
- STDERR.puts "\n#{@current_state.description}"
+ if @current_state
+ STDERR.puts "\nExample #{@error_message}:"
+ STDERR.puts "#{@current_state.description}"
+ else
+ STDERR.puts "\nSome code outside an example #{@error_message}"
+ end
STDERR.flush
- abort "Example took longer than the configured timeout of #{@timeout}s"
+
+ show_backtraces
+ if MSpec.subprocesses.empty?
+ exit! 2
+ else
+ # Do not exit but signal the subprocess so we can get their output
+ MSpec.subprocesses.each do |pid|
+ kill_wait_one_second :SIGTERM, pid
+ hard_kill :SIGKILL, pid
+ end
+ @fail = true
+ @current_state = nil
+ break # stop this thread, will fail in #after
+ end
end
end
end
@@ -53,8 +74,72 @@ class TimeoutAction
end
end
+ def after(state = nil)
+ @queue << -> do
+ @current_state = nil
+ end
+
+ if @fail
+ STDERR.puts "\n\nThe last example #{@error_message}. See above for the subprocess stacktrace."
+ exit! 2
+ end
+ end
+
def finish
@thread.kill
@thread.join
end
+
+ private def hard_kill(signal, pid)
+ begin
+ Process.kill signal, pid
+ rescue Errno::ESRCH
+ # Process already terminated
+ end
+ end
+
+ private def kill_wait_one_second(signal, pid)
+ begin
+ Process.kill signal, pid
+ sleep 1
+ rescue Errno::ESRCH
+ # Process already terminated
+ end
+ end
+
+ private def show_backtraces
+ java_stacktraces = -> pid {
+ if RUBY_ENGINE == 'truffleruby' || RUBY_ENGINE == 'jruby'
+ STDERR.puts 'Java stacktraces:'
+ kill_wait_one_second :SIGQUIT, pid
+ end
+ }
+
+ if MSpec.subprocesses.empty?
+ java_stacktraces.call Process.pid
+
+ STDERR.puts "\nRuby backtraces:"
+ if defined?(Truffle::Debug.show_backtraces)
+ Truffle::Debug.show_backtraces
+ else
+ Thread.list.each do |thread|
+ unless thread == Thread.current
+ STDERR.puts thread.inspect, thread.backtrace, ''
+ end
+ end
+ end
+ else
+ MSpec.subprocesses.each do |pid|
+ STDERR.puts "\nFor subprocess #{pid}"
+ java_stacktraces.call pid
+
+ if RUBY_ENGINE == 'truffleruby'
+ STDERR.puts "\nRuby backtraces:"
+ kill_wait_one_second :SIGALRM, pid
+ else
+ STDERR.puts "Don't know how to print backtraces of a subprocess on #{RUBY_ENGINE}"
+ end
+ end
+ end
+ end
end
diff --git a/spec/mspec/lib/mspec/runner/context.rb b/spec/mspec/lib/mspec/runner/context.rb
index 62483590bb..bcd83b2465 100644
--- a/spec/mspec/lib/mspec/runner/context.rb
+++ b/spec/mspec/lib/mspec/runner/context.rb
@@ -210,6 +210,7 @@ class ContextState
MSpec.clear_expectations
if example
passed = protect nil, example
+ passed = protect nil, -> { MSpec.actions :passed, state, example } if passed
MSpec.actions :example, state, example
protect nil, EXPECTATION_MISSING if !MSpec.expectation? and passed
end
diff --git a/spec/mspec/lib/mspec/runner/exception.rb b/spec/mspec/lib/mspec/runner/exception.rb
index aea6610cd3..23375733e6 100644
--- a/spec/mspec/lib/mspec/runner/exception.rb
+++ b/spec/mspec/lib/mspec/runner/exception.rb
@@ -29,7 +29,7 @@ class ExceptionState
if @failure
message
- elsif raise_error_message = @exception.instance_variable_get(:@mspec_raise_error_message)
+ elsif raise_error_message = RaiseErrorMatcher::FAILURE_MESSAGE_FOR_EXCEPTION[@exception]
raise_error_message.join("\n")
else
"#{@exception.class}: #{message}"
@@ -40,6 +40,15 @@ class ExceptionState
@backtrace_filter ||= MSpecScript.config[:backtrace_filter] || %r{(?:/bin/mspec|/lib/mspec/)}
bt = @exception.backtrace || []
- bt.select { |line| $MSPEC_DEBUG or @backtrace_filter !~ line }.join("\n")
+ unless $MSPEC_DEBUG
+ # Exclude <internal: entries inside MSpec code, so only after the first ignored entry
+ first_excluded_line = bt.index { |line| @backtrace_filter =~ line }
+ if first_excluded_line
+ bt = bt[0...first_excluded_line] + bt[first_excluded_line..-1].reject { |line|
+ @backtrace_filter =~ line || /^<internal:/ =~ line
+ }
+ end
+ end
+ bt.join("\n")
end
end
diff --git a/spec/mspec/lib/mspec/runner/formatters/base.rb b/spec/mspec/lib/mspec/runner/formatters/base.rb
index 6c075c4d0a..882e15c8c2 100644
--- a/spec/mspec/lib/mspec/runner/formatters/base.rb
+++ b/spec/mspec/lib/mspec/runner/formatters/base.rb
@@ -1,9 +1,13 @@
require 'mspec/expectations/expectations'
require 'mspec/runner/actions/timer'
require 'mspec/runner/actions/tally'
+require 'mspec/utils/options'
if ENV['CHECK_LEAKS']
require 'mspec/runner/actions/leakchecker'
+end
+
+if ENV['CHECK_LEAKS'] || ENV['CHECK_CONSTANT_LEAKS']
require 'mspec/runner/actions/constants_leak_checker'
end
@@ -18,10 +22,17 @@ class BaseFormatter
@count = 0 # For subclasses
- if out.nil?
+ if out
+ @out = File.open out, "w"
+ else
@out = $stdout
+ end
+
+ err = MSpecOptions.latest && MSpecOptions.latest.config[:error_output]
+ if err
+ @err = (err == 'stderr') ? $stderr : File.open(err, "w")
else
- @out = File.open out, "w"
+ @err = @out
end
end
@@ -32,8 +43,11 @@ class BaseFormatter
@counter = @tally.counter
if ENV['CHECK_LEAKS']
- save = ENV['CHECK_LEAKS'] == 'save'
LeakCheckerAction.new.register
+ end
+
+ if (ENV['CHECK_LEAKS'] || ENV['CHECK_CONSTANT_LEAKS']) && ENV['CHECK_CONSTANT_LEAKS'] != 'false'
+ save = ENV['CHECK_LEAKS'] == 'save' || ENV['CHECK_CONSTANT_LEAKS'] == 'save'
ConstantsLeakCheckerAction.new(save).register
end
@@ -105,6 +119,14 @@ class BaseFormatter
# evaluating the examples.
def finish
print "\n"
+
+ if MSpecOptions.latest && MSpecOptions.latest.config[:print_skips]
+ print "\nSkips:\n" unless MSpec.skips.empty?
+ MSpec.skips.each do |skip, block|
+ print "#{skip.message} in #{(block.source_location || ['?']).join(':')}\n"
+ end
+ end
+
count = 0
@exceptions.each do |exc|
count += 1
@@ -115,9 +137,9 @@ class BaseFormatter
def print_exception(exc, count)
outcome = exc.failure? ? "FAILED" : "ERROR"
- print "\n#{count})\n#{exc.description} #{outcome}\n"
- print exc.message, "\n"
- print exc.backtrace, "\n"
+ @err.print "\n#{count})\n#{exc.description} #{outcome}\n"
+ @err.print exc.message, "\n"
+ @err.print exc.backtrace, "\n"
end
# A convenience method to allow printing to different outputs.
diff --git a/spec/mspec/lib/mspec/runner/formatters/junit.rb b/spec/mspec/lib/mspec/runner/formatters/junit.rb
index 12e43a3263..6351ccbce9 100644
--- a/spec/mspec/lib/mspec/runner/formatters/junit.rb
+++ b/spec/mspec/lib/mspec/runner/formatters/junit.rb
@@ -24,7 +24,7 @@ class JUnitFormatter < YamlFormatter
errors = @tally.counter.errors
failures = @tally.counter.failures
- printf <<-XML
+ print <<-XML
<?xml version="1.0" encoding="UTF-8" ?>
<testsuites
@@ -42,8 +42,8 @@ class JUnitFormatter < YamlFormatter
@tests.each do |h|
description = encode_for_xml h[:test].description
- printf <<-XML, "Spec", description, 0.0
- <testcase classname="%s" name="%s" time="%f">
+ print <<-XML
+ <testcase classname="Spec" name="#{description}" time="0.0">
XML
if h[:exception]
outcome = h[:test].failure? ? "failure" : "error"
diff --git a/spec/mspec/lib/mspec/runner/formatters/launchable.rb b/spec/mspec/lib/mspec/runner/formatters/launchable.rb
new file mode 100644
index 0000000000..f738781c71
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/formatters/launchable.rb
@@ -0,0 +1,88 @@
+module LaunchableFormatter
+ def self.extend_object(obj)
+ super
+ obj.init
+ end
+
+ def self.setDir(dir)
+ @@path = File.join(dir, "#{rand.to_s}.json")
+ self
+ end
+
+ def init
+ @timer = nil
+ @tests = []
+ end
+
+ def before(state = nil)
+ super
+ @timer = TimerAction.new
+ @timer.start
+ end
+
+ def after(state = nil)
+ super
+ @timer.finish
+ file = MSpec.file
+ return if file.nil? || state&.example.nil? || exception?
+
+ @tests << {:test => state, :file => file, :exception => false, duration: @timer.elapsed}
+ end
+
+ def exception(exception)
+ super
+ @timer.finish
+ file = MSpec.file
+ return if file.nil?
+
+ @tests << {:test => exception, :file => file, :exception => true, duration: @timer.elapsed}
+ end
+
+ def finish
+ super
+
+ require_relative '../../../../../../tool/lib/launchable'
+
+ @writer = writer = Launchable::JsonStreamWriter.new(@@path)
+ @writer.write_array('testCases')
+ at_exit {
+ @writer.close
+ }
+
+ repo_path = File.expand_path("#{__dir__}/../../../../../../")
+
+ @tests.each do |t|
+ testcase = t[:test].description
+ relative_path = t[:file].delete_prefix("#{repo_path}/")
+ # The test path is a URL-encoded representation.
+ # https://github.com/launchableinc/cli/blob/v1.81.0/launchable/testpath.py#L18
+ test_path = {file: relative_path, testcase: testcase}.map{|key, val|
+ "#{encode_test_path_component(key)}=#{encode_test_path_component(val)}"
+ }.join('#')
+
+ status = 'TEST_PASSED'
+ if t[:exception]
+ message = t[:test].message
+ backtrace = t[:test].backtrace
+ e = "#{message}\n#{backtrace}"
+ status = 'TEST_FAILED'
+ end
+
+ @writer.write_object(
+ {
+ testPath: test_path,
+ status: status,
+ duration: t[:duration],
+ createdAt: Time.now.to_s,
+ stderr: e,
+ stdout: nil
+ }
+ )
+ end
+ end
+
+ private
+ def encode_test_path_component component
+ component.to_s.gsub('%', '%25').gsub('=', '%3D').gsub('#', '%23').gsub('&', '%26').tr("\x00-\x08", "")
+ end
+end
diff --git a/spec/mspec/lib/mspec/runner/formatters/multi.rb b/spec/mspec/lib/mspec/runner/formatters/multi.rb
index a723ae8eb9..fa1da3766b 100644
--- a/spec/mspec/lib/mspec/runner/formatters/multi.rb
+++ b/spec/mspec/lib/mspec/runner/formatters/multi.rb
@@ -42,6 +42,6 @@ module MultiFormatter
end
def print_exception(exc, count)
- print "\n#{count})\n#{exc}\n"
+ @err.print "\n#{count})\n#{exc}\n"
end
end
diff --git a/spec/mspec/lib/mspec/runner/mspec.rb b/spec/mspec/lib/mspec/runner/mspec.rb
index 0e2496d3d7..0e016c67a7 100644
--- a/spec/mspec/lib/mspec/runner/mspec.rb
+++ b/spec/mspec/lib/mspec/runner/mspec.rb
@@ -26,6 +26,7 @@ module MSpec
@unload = nil
@tagged = nil
@current = nil
+ @passed = nil
@example = nil
@modes = []
@shared = {}
@@ -36,9 +37,11 @@ module MSpec
@repeat = 1
@expectation = nil
@expectations = false
+ @skips = []
+ @subprocesses = []
class << self
- attr_reader :file, :include, :exclude
+ attr_reader :file, :include, :exclude, :skips, :subprocesses
attr_writer :repeat, :randomize
attr_accessor :formatter
end
@@ -116,8 +119,9 @@ module MSpec
rescue SystemExit => e
raise e
rescue SkippedSpecError => e
+ @skips << [e, block]
return false
- rescue Exception => exc
+ rescue Object => exc
register_exit 1
actions :exception, ExceptionState.new(current && current.state, location, exc)
return false
@@ -155,7 +159,9 @@ module MSpec
# Stores the shared ContextState keyed by description.
def self.register_shared(state)
- @shared[state.to_s] = state
+ name = state.to_s
+ raise "duplicated shared #describe: #{name}" if @shared.key?(name)
+ @shared[name] = state
end
# Returns the shared ContextState matching description.
@@ -239,6 +245,7 @@ module MSpec
# :before before a single spec is run
# :add while a describe block is adding examples to run later
# :expectation before a 'should', 'should_receive', etc.
+ # :passed after an example block is run and passes, passed the block, run before :example action
# :example after an example block is run, passed the block
# :exception after an exception is rescued
# :after after a single spec is run
@@ -358,6 +365,7 @@ module MSpec
# Writes each tag in +tags+ to the tag file. Overwrites the
# tag file if it exists.
def self.write_tags(tags)
+ return delete_tags if tags.empty?
file = tags_file
make_tag_dir(file)
File.open(file, "w:utf-8") do |f|
@@ -389,7 +397,7 @@ module MSpec
desc = tag.escape(tag.description)
file = tags_file
if File.exist? file
- lines = IO.readlines(file)
+ lines = File.readlines(file)
File.open(file, "w:utf-8") do |f|
lines.each do |line|
line = line.chomp
diff --git a/spec/mspec/lib/mspec/runner/shared.rb b/spec/mspec/lib/mspec/runner/shared.rb
index 1d68365474..283711c1d7 100644
--- a/spec/mspec/lib/mspec/runner/shared.rb
+++ b/spec/mspec/lib/mspec/runner/shared.rb
@@ -1,10 +1,14 @@
require 'mspec/runner/mspec'
def it_behaves_like(desc, meth, obj = nil)
- send :before, :all do
+ before :all do
@method = meth
@object = obj
end
+ after :all do
+ @method = nil
+ @object = nil
+ end
- send :it_should_behave_like, desc.to_s
+ it_should_behave_like desc.to_s
end
diff --git a/spec/mspec/lib/mspec/utils/name_map.rb b/spec/mspec/lib/mspec/utils/name_map.rb
index a389b9d1de..9b04112e2e 100644
--- a/spec/mspec/lib/mspec/utils/name_map.rb
+++ b/spec/mspec/lib/mspec/utils/name_map.rb
@@ -51,6 +51,10 @@ class NameMap
SpecVersion
]
+ ALWAYS_PRIVATE = %w[
+ initialize initialize_copy initialize_clone initialize_dup respond_to_missing?
+ ].map(&:to_sym)
+
def initialize(filter = false)
@seen = {}
@filter = filter
@@ -62,10 +66,17 @@ class NameMap
end
def class_or_module(c)
- const = Object.const_get(c, false)
+ begin
+ const = Object.const_get(c, false)
+ rescue NameError, RuntimeError
+ # Either the constant doesn't exist or it is
+ # explicitly raising an error, like `SortedSet`.
+ return nil
+ end
+ return nil unless Module === const
+
filtered = @filter && EXCLUDED.include?(const.name)
- return const if Module === const and !filtered
- rescue NameError
+ return const unless filtered
end
def namespace(mod, const)
@@ -86,7 +97,8 @@ class NameMap
hash["#{name}."] = ms.sort unless ms.empty?
ms = m.public_instance_methods(false) +
- m.protected_instance_methods(false)
+ m.protected_instance_methods(false) +
+ (m.private_instance_methods(false) & ALWAYS_PRIVATE)
ms.map! { |x| x.to_s }
hash["#{name}#"] = ms.sort unless ms.empty?
diff --git a/spec/mspec/lib/mspec/utils/options.rb b/spec/mspec/lib/mspec/utils/options.rb
index bef1dbdd2e..adeafa1f81 100644
--- a/spec/mspec/lib/mspec/utils/options.rb
+++ b/spec/mspec/lib/mspec/utils/options.rb
@@ -32,6 +32,10 @@ class MSpecOptions
# Raised if an unrecognized option is encountered.
class ParseError < Exception; end
+ class << self
+ attr_accessor :latest
+ end
+
attr_accessor :config, :banner, :width, :options
def initialize(banner = "", width = 30, config = nil)
@@ -46,7 +50,7 @@ class MSpecOptions
@extra << x
}
- yield self if block_given?
+ MSpecOptions.latest = self
end
# Registers an option. Acceptable formats for arguments are:
@@ -200,6 +204,13 @@ class MSpecOptions
"Load FILE containing configuration options", &block)
end
+ def env
+ on("--env", "KEY=VALUE", "Set environment variable") do |env|
+ key, value = env.split('=', 2)
+ ENV[key] = value
+ end
+ end
+
def targets
on("-t", "--target", "TARGET",
"Implementation to run the specs, where TARGET is:") do |t|
@@ -311,6 +322,11 @@ class MSpecOptions
"Write formatter output to FILE") do |f|
config[:output] = f
end
+
+ on("--error-output", "FILE",
+ "Write error output of failing specs to FILE, or $stderr if value is 'stderr'.") do |f|
+ config[:error_output] = f
+ end
end
def filters
@@ -414,6 +430,10 @@ class MSpecOptions
end
MSpec.register :load, obj
end
+
+ on("--print-skips", "Print skips") do
+ config[:print_skips] = true
+ end
end
def interrupt
@@ -464,13 +484,22 @@ class MSpecOptions
def debug
on("-d", "--debug",
- "Set MSpec debugging flag for more verbose output") do
+ "Disable MSpec backtrace filtering") do
$MSPEC_DEBUG = true
end
end
+ def launchable
+ on("--launchable-test-reports", "DIR",
+ "DIR The directory for reporting test results in Launchable JSON format") do |o|
+ require 'mspec/runner/formatters/launchable'
+ config[:launchable] = LaunchableFormatter.setDir(o)
+ end
+ end
+
def all
configure {}
+ env
targets
formatters
filters
@@ -487,5 +516,6 @@ class MSpecOptions
action_filters
actions
debug
+ launchable
end
end
diff --git a/spec/mspec/lib/mspec/utils/script.rb b/spec/mspec/lib/mspec/utils/script.rb
index e3478ef7b0..15fd23fabf 100644
--- a/spec/mspec/lib/mspec/utils/script.rb
+++ b/spec/mspec/lib/mspec/utils/script.rb
@@ -37,10 +37,19 @@ class MSpecScript
config[key]
end
+ class << self
+ attr_accessor :child_process
+ end
+
+ # True if the current process is the one going to run the specs with `MSpec.process`.
+ # False for e.g. `mspec` which exec's to `mspec-run`.
+ # This is useful in .mspec config files.
+ def self.child_process?
+ MSpecScript.child_process
+ end
+
def initialize
- ruby_version_is ""..."2.5" do
- abort "MSpec needs Ruby 2.5 or more recent"
- end
+ check_version!
config[:formatter] = nil
config[:includes] = []
@@ -150,8 +159,14 @@ class MSpecScript
end
if config[:formatter]
- config[:formatter].new(config[:output])
+ config[:formatter] = config[:formatter].new(config[:output])
+ end
+
+ if config[:launchable]
+ config[:formatter].extend config[:launchable]
end
+
+ config[:formatter]
end
# Callback for enabling custom actions, etc. This version is a
@@ -269,10 +284,11 @@ class MSpecScript
# Instantiates an instance and calls the series of methods to
# invoke the script.
- def self.main
+ def self.main(child_process = true)
+ MSpecScript.child_process = child_process
+
script = new
script.load_default
- script.try_load '~/.mspecrc'
script.options
script.signals
script.register
@@ -280,4 +296,10 @@ class MSpecScript
require 'mspec'
script.run
end
+
+ private def check_version!
+ ruby_version_is ""..."2.6" do
+ warn "MSpec is supported for Ruby 2.6 and above only"
+ end
+ end
end
diff --git a/spec/mspec/lib/mspec/utils/version.rb b/spec/mspec/lib/mspec/utils/version.rb
index 787a76b053..9c1c58b8df 100644
--- a/spec/mspec/lib/mspec/utils/version.rb
+++ b/spec/mspec/lib/mspec/utils/version.rb
@@ -42,7 +42,7 @@ class SpecVersion
def <=>(other)
if other.respond_to? :to_int
- other = Integer other
+ other = Integer(other.to_int)
else
other = SpecVersion.new(String(other)).to_i
end
diff --git a/spec/mspec/lib/mspec/utils/warnings.rb b/spec/mspec/lib/mspec/utils/warnings.rb
index 01fca00b8d..23efc696a5 100644
--- a/spec/mspec/lib/mspec/utils/warnings.rb
+++ b/spec/mspec/lib/mspec/utils/warnings.rb
@@ -1,54 +1,10 @@
require 'mspec/guards/version'
-if RUBY_ENGINE == "ruby"
- def Warning.warn(message)
- # Suppress any warning inside the method to prevent recursion
- verbose = $VERBOSE
- $VERBOSE = nil
-
- if Thread.current[:in_mspec_complain_matcher]
- return $stderr.write(message)
- end
-
- case message
- # $VERBOSE = true warnings
- when /(.+\.rb):(\d+):.+possibly useless use of (<|<=|==|>=|>) in void context/
- # Make sure there is a .should otherwise it is missing
- line_nb = Integer($2)
- unless File.exist?($1) and /\.should(_not)? (<|<=|==|>=|>)/ === File.readlines($1)[line_nb-1]
- $stderr.write message
- end
- when /possibly useless use of (\+|-) in void context/
- when /assigned but unused variable/
- when /method redefined/
- when /previous definition of/
- when /instance variable @.+ not initialized/
- when /statement not reached/
- when /shadowing outer local variable/
- when /setting Encoding.default_(in|ex)ternal/
- when /unknown (un)?pack directive/
- when /(un)?trust(ed\?)? is deprecated/
- when /\.exists\? is a deprecated name/
- when /Float .+ out of range/
- when /passing a block to String#(bytes|chars|codepoints|lines) is deprecated/
- when /core\/string\/modulo_spec\.rb:\d+: warning: too many arguments for format string/
- when /regexp\/shared\/new_ascii(_8bit)?\.rb:\d+: warning: Unknown escape .+ is ignored/
-
- # $VERBOSE = false warnings
- when /constant ::(Fixnum|Bignum) is deprecated/
- when /\/(argf|io|stringio)\/.+(ARGF|IO)#(lines|chars|bytes|codepoints) is deprecated/
- when /Thread\.exclusive is deprecated.+\n.+thread\/exclusive_spec\.rb/
- when /hash\/shared\/index\.rb:\d+: warning: Hash#index is deprecated; use Hash#key/
- when /env\/shared\/key\.rb:\d+: warning: ENV\.index is deprecated; use ENV\.key/
- when /exponent(_spec)?\.rb:\d+: warning: in a\*\*b, b may be too big/
- when /enumerator\/(new_spec|initialize_spec)\.rb:\d+: warning: Enumerator\.new without a block is deprecated/
- when /Pattern matching is experimental, and the behavior may change in future versions of Ruby!/
- else
- $stderr.write message
- end
- ensure
- $VERBOSE = verbose
- end
-else
- $VERBOSE = nil unless ENV['OUTPUT_WARNINGS']
+# Always enable deprecation warnings when running MSpec, as ruby/spec tests for them,
+# and like in most test frameworks, deprecation warnings should be enabled by default,
+# so that deprecations are noticed before the breaking change.
+# Disable experimental warnings, we want to test new experimental features in ruby/spec.
+if Object.const_defined?(:Warning) and Warning.respond_to?(:[]=)
+ Warning[:deprecated] = true
+ Warning[:experimental] = false
end
diff --git a/spec/mspec/spec/commands/mkspec_spec.rb b/spec/mspec/spec/commands/mkspec_spec.rb
index 1b959dcb78..32262723de 100644
--- a/spec/mspec/spec/commands/mkspec_spec.rb
+++ b/spec/mspec/spec/commands/mkspec_spec.rb
@@ -1,18 +1,18 @@
require 'spec_helper'
require 'mspec/commands/mkspec'
+require 'fileutils'
-
-describe "The -c, --constant CONSTANT option" do
+RSpec.describe "The -c, --constant CONSTANT option" do
before :each do
@options = MSpecOptions.new
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MkSpec.new
@config = @script.config
end
it "is enabled by #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-c", "--constant", "CONSTANT",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-c", "--constant", "CONSTANT",
an_instance_of(String))
@script.options []
end
@@ -21,22 +21,22 @@ describe "The -c, --constant CONSTANT option" do
["-c", "--constant"].each do |opt|
@config[:constants] = []
@script.options [opt, "Object"]
- @config[:constants].should include("Object")
+ expect(@config[:constants]).to include("Object")
end
end
end
-describe "The -b, --base DIR option" do
+RSpec.describe "The -b, --base DIR option" do
before :each do
@options = MSpecOptions.new
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MkSpec.new
@config = @script.config
end
it "is enabled by #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-b", "--base", "DIR",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-b", "--base", "DIR",
an_instance_of(String))
@script.options []
end
@@ -45,22 +45,22 @@ describe "The -b, --base DIR option" do
["-b", "--base"].each do |opt|
@config[:base] = nil
@script.options [opt, "superspec"]
- @config[:base].should == File.expand_path("superspec")
+ expect(@config[:base]).to eq(File.expand_path("superspec"))
end
end
end
-describe "The -r, --require LIBRARY option" do
+RSpec.describe "The -r, --require LIBRARY option" do
before :each do
@options = MSpecOptions.new
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MkSpec.new
@config = @script.config
end
it "is enabled by #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-r", "--require", "LIBRARY",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-r", "--require", "LIBRARY",
an_instance_of(String))
@script.options []
end
@@ -69,22 +69,22 @@ describe "The -r, --require LIBRARY option" do
["-r", "--require"].each do |opt|
@config[:requires] = []
@script.options [opt, "libspec"]
- @config[:requires].should include("libspec")
+ expect(@config[:requires]).to include("libspec")
end
end
end
-describe "The -V, --version-guard VERSION option" do
+RSpec.describe "The -V, --version-guard VERSION option" do
before :each do
@options = MSpecOptions.new
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MkSpec.new
@config = @script.config
end
it "is enabled by #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-V", "--version-guard", "VERSION",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-V", "--version-guard", "VERSION",
an_instance_of(String))
@script.options []
end
@@ -93,150 +93,150 @@ describe "The -V, --version-guard VERSION option" do
["-r", "--require"].each do |opt|
@config[:requires] = []
@script.options [opt, "libspec"]
- @config[:requires].should include("libspec")
+ expect(@config[:requires]).to include("libspec")
end
end
end
-describe MkSpec, "#options" do
+RSpec.describe MkSpec, "#options" do
before :each do
@options = MSpecOptions.new
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MkSpec.new
end
it "parses the command line options" do
- @options.should_receive(:parse).with(["--this", "and", "--that"])
+ expect(@options).to receive(:parse).with(["--this", "and", "--that"])
@script.options ["--this", "and", "--that"]
end
it "parses ARGV unless passed other options" do
- @options.should_receive(:parse).with(ARGV)
+ expect(@options).to receive(:parse).with(ARGV)
@script.options
end
it "prints help and exits if passed an unrecognized option" do
- @options.should_receive(:raise).with(MSpecOptions::ParseError, an_instance_of(String))
- @options.stub(:puts)
- @options.stub(:exit)
+ expect(@options).to receive(:raise).with(MSpecOptions::ParseError, an_instance_of(String))
+ allow(@options).to receive(:puts)
+ allow(@options).to receive(:exit)
@script.options ["--iunknown"]
end
end
-describe MkSpec, "#create_directory" do
+RSpec.describe MkSpec, "#create_directory" do
before :each do
@script = MkSpec.new
@script.config[:base] = "spec"
end
it "prints a warning if a file with the directory name exists" do
- File.should_receive(:exist?).and_return(true)
- File.should_receive(:directory?).and_return(false)
- FileUtils.should_not_receive(:mkdir_p)
- @script.should_receive(:puts).with("spec/class already exists and is not a directory.")
- @script.create_directory("Class").should == nil
+ expect(File).to receive(:exist?).and_return(true)
+ expect(File).to receive(:directory?).and_return(false)
+ expect(FileUtils).not_to receive(:mkdir_p)
+ expect(@script).to receive(:puts).with("spec/class already exists and is not a directory.")
+ expect(@script.create_directory("Class")).to eq(nil)
end
it "does nothing if the directory already exists" do
- File.should_receive(:exist?).and_return(true)
- File.should_receive(:directory?).and_return(true)
- FileUtils.should_not_receive(:mkdir_p)
- @script.create_directory("Class").should == "spec/class"
+ expect(File).to receive(:exist?).and_return(true)
+ expect(File).to receive(:directory?).and_return(true)
+ expect(FileUtils).not_to receive(:mkdir_p)
+ expect(@script.create_directory("Class")).to eq("spec/class")
end
it "creates the directory if it does not exist" do
- File.should_receive(:exist?).and_return(false)
- @script.should_receive(:mkdir_p).with("spec/class")
- @script.create_directory("Class").should == "spec/class"
+ expect(File).to receive(:exist?).and_return(false)
+ expect(@script).to receive(:mkdir_p).with("spec/class")
+ expect(@script.create_directory("Class")).to eq("spec/class")
end
it "creates the directory for a namespaced module if it does not exist" do
- File.should_receive(:exist?).and_return(false)
- @script.should_receive(:mkdir_p).with("spec/struct/tms")
- @script.create_directory("Struct::Tms").should == "spec/struct/tms"
+ expect(File).to receive(:exist?).and_return(false)
+ expect(@script).to receive(:mkdir_p).with("spec/struct/tms")
+ expect(@script.create_directory("Struct::Tms")).to eq("spec/struct/tms")
end
end
-describe MkSpec, "#write_requires" do
+RSpec.describe MkSpec, "#write_requires" do
before :each do
@script = MkSpec.new
@script.config[:base] = "spec"
@file = double("file")
- File.stub(:open).and_yield(@file)
+ allow(File).to receive(:open).and_yield(@file)
end
it "writes the spec_helper require line" do
- @file.should_receive(:puts).with("require_relative '../../../spec_helper'")
+ expect(@file).to receive(:puts).with("require_relative '../../../spec_helper'")
@script.write_requires("spec/core/tcejbo", "spec/core/tcejbo/inspect_spec.rb")
end
it "writes require lines for each library specified on the command line" do
- @file.stub(:puts)
- @file.should_receive(:puts).with("require_relative '../../../spec_helper'")
- @file.should_receive(:puts).with("require 'complex'")
+ allow(@file).to receive(:puts)
+ expect(@file).to receive(:puts).with("require_relative '../../../spec_helper'")
+ expect(@file).to receive(:puts).with("require 'complex'")
@script.config[:requires] << 'complex'
@script.write_requires("spec/core/tcejbo", "spec/core/tcejbo/inspect_spec.rb")
end
end
-describe MkSpec, "#write_spec" do
+RSpec.describe MkSpec, "#write_spec" do
before :each do
@file = IOStub.new
- File.stub(:open).and_yield(@file)
+ allow(File).to receive(:open).and_yield(@file)
@script = MkSpec.new
- @script.stub(:puts)
+ allow(@script).to receive(:puts)
@response = double("system command response")
- @response.stub(:include?).and_return(false)
- @script.stub(:`).and_return(@response)
+ allow(@response).to receive(:include?).and_return(false)
+ allow(@script).to receive(:`).and_return(@response)
end
it "checks if specs exist for the method if the spec file exists" do
- name = Regexp.escape(@script.ruby)
- @script.should_receive(:`).with(
+ name = Regexp.escape(RbConfig.ruby)
+ expect(@script).to receive(:`).with(
%r"#{name} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e 'Object#inspect' spec/core/tcejbo/inspect_spec.rb")
@script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
end
it "checks for the method name in the spec file output" do
- @response.should_receive(:include?).with("Array#[]=")
+ expect(@response).to receive(:include?).with("Array#[]=")
@script.write_spec("spec/core/yarra/element_set_spec.rb", "Array#[]=", true)
end
it "returns nil if the spec file exists and contains a spec for the method" do
- @response.stub(:include?).and_return(true)
- @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true).should == nil
+ allow(@response).to receive(:include?).and_return(true)
+ expect(@script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)).to eq(nil)
end
it "does not print the spec file name if it exists and contains a spec for the method" do
- @response.stub(:include?).and_return(true)
- @script.should_not_receive(:puts)
+ allow(@response).to receive(:include?).and_return(true)
+ expect(@script).not_to receive(:puts)
@script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
end
it "prints the spec file name if a template spec is written" do
- @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
+ expect(@script).to receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
@script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
end
it "writes a template spec to the file if the spec file does not exist" do
- @file.should_receive(:puts).twice
- @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
+ expect(@file).to receive(:puts).twice
+ expect(@script).to receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
@script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", false)
end
it "writes a template spec to the file if it exists but contains no spec for the method" do
- @response.should_receive(:include?).and_return(false)
- @file.should_receive(:puts).twice
- @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
+ expect(@response).to receive(:include?).and_return(false)
+ expect(@file).to receive(:puts).twice
+ expect(@script).to receive(:puts).with("spec/core/tcejbo/inspect_spec.rb")
@script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
end
it "writes a template spec" do
@script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
- @file.should == <<EOS
+ expect(@file).to eq <<EOS
describe "Object#inspect" do
it "needs to be reviewed for spec completeness"
@@ -247,7 +247,7 @@ EOS
it "writes a template spec with version guard" do
@script.config[:version] = '""..."1.9"'
@script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true)
- @file.should == <<EOS
+ expect(@file).to eq <<EOS
ruby_version_is ""..."1.9" do
describe "Object#inspect" do
@@ -259,105 +259,105 @@ EOS
end
end
-describe MkSpec, "#create_file" do
+RSpec.describe MkSpec, "#create_file" do
before :each do
@script = MkSpec.new
- @script.stub(:write_requires)
- @script.stub(:write_spec)
+ allow(@script).to receive(:write_requires)
+ allow(@script).to receive(:write_spec)
- File.stub(:exist?).and_return(false)
+ allow(File).to receive(:exist?).and_return(false)
end
it "generates a file name based on the directory, class/module, and method" do
- File.should_receive(:join).with("spec/tcejbo", "inspect_spec.rb"
+ expect(File).to receive(:join).with("spec/tcejbo", "inspect_spec.rb"
).and_return("spec/tcejbo/inspect_spec.rb")
@script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
end
it "does not call #write_requires if the spec file already exists" do
- File.should_receive(:exist?).and_return(true)
- @script.should_not_receive(:write_requires)
+ expect(File).to receive(:exist?).and_return(true)
+ expect(@script).not_to receive(:write_requires)
@script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
end
it "calls #write_requires if the spec file does not exist" do
- File.should_receive(:exist?).and_return(false)
- @script.should_receive(:write_requires).with(
+ expect(File).to receive(:exist?).and_return(false)
+ expect(@script).to receive(:write_requires).with(
"spec/tcejbo", "spec/tcejbo/inspect_spec.rb")
@script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
end
it "calls #write_spec with the file, method name" do
- @script.should_receive(:write_spec).with(
+ expect(@script).to receive(:write_spec).with(
"spec/tcejbo/inspect_spec.rb", "Object#inspect", false)
@script.create_file("spec/tcejbo", "Object", "inspect", "Object#inspect")
end
end
-describe MkSpec, "#run" do
+RSpec.describe MkSpec, "#run" do
before :each do
@options = MSpecOptions.new
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@map = NameMap.new
- NameMap.stub(:new).and_return(@map)
+ allow(NameMap).to receive(:new).and_return(@map)
@script = MkSpec.new
- @script.stub(:create_directory).and_return("spec/mkspec")
- @script.stub(:create_file)
+ allow(@script).to receive(:create_directory).and_return("spec/mkspec")
+ allow(@script).to receive(:create_file)
@script.config[:constants] = [MkSpec]
end
it "loads files in the requires list" do
- @script.stub(:require)
- @script.should_receive(:require).with("alib")
- @script.should_receive(:require).with("blib")
+ allow(@script).to receive(:require)
+ expect(@script).to receive(:require).with("alib")
+ expect(@script).to receive(:require).with("blib")
@script.config[:requires] = ["alib", "blib"]
@script.run
end
it "creates a map of constants to methods" do
- @map.should_receive(:map).with({}, @script.config[:constants]).and_return({})
+ expect(@map).to receive(:map).with({}, @script.config[:constants]).and_return({})
@script.run
end
it "calls #create_directory for each class/module in the map" do
- @script.should_receive(:create_directory).with("MkSpec").twice
+ expect(@script).to receive(:create_directory).with("MkSpec").twice
@script.run
end
it "calls #create_file for each method on each class/module in the map" do
- @map.should_receive(:map).with({}, @script.config[:constants]
+ expect(@map).to receive(:map).with({}, @script.config[:constants]
).and_return({"MkSpec#" => ["run"]})
- @script.should_receive(:create_file).with("spec/mkspec", "MkSpec", "run", "MkSpec#run")
+ expect(@script).to receive(:create_file).with("spec/mkspec", "MkSpec", "run", "MkSpec#run")
@script.run
end
end
-describe MkSpec, ".main" do
+RSpec.describe MkSpec, ".main" do
before :each do
@script = double("MkSpec").as_null_object
- MkSpec.stub(:new).and_return(@script)
+ allow(MkSpec).to receive(:new).and_return(@script)
end
it "sets MSPEC_RUNNER = '1' in the environment" do
ENV["MSPEC_RUNNER"] = "0"
MkSpec.main
- ENV["MSPEC_RUNNER"].should == "1"
+ expect(ENV["MSPEC_RUNNER"]).to eq("1")
end
it "creates an instance of MSpecScript" do
- MkSpec.should_receive(:new).and_return(@script)
+ expect(MkSpec).to receive(:new).and_return(@script)
MkSpec.main
end
it "calls the #options method on the script" do
- @script.should_receive(:options)
+ expect(@script).to receive(:options)
MkSpec.main
end
it "calls the #run method on the script" do
- @script.should_receive(:run)
+ expect(@script).to receive(:run)
MkSpec.main
end
end
diff --git a/spec/mspec/spec/commands/mspec_ci_spec.rb b/spec/mspec/spec/commands/mspec_ci_spec.rb
index a90cbd8d0d..b8dc9d062f 100644
--- a/spec/mspec/spec/commands/mspec_ci_spec.rb
+++ b/spec/mspec/spec/commands/mspec_ci_spec.rb
@@ -3,148 +3,153 @@ require 'mspec/runner/mspec'
require 'mspec/runner/filters/tag'
require 'mspec/commands/mspec-ci'
-describe MSpecCI, "#options" do
+RSpec.describe MSpecCI, "#options" do
before :each do
@options, @config = new_option
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MSpecCI.new
- @script.stub(:config).and_return(@config)
- @script.stub(:files).and_return([])
+ allow(@script).to receive(:config).and_return(@config)
+ allow(@script).to receive(:files).and_return([])
end
it "enables the chdir option" do
- @options.should_receive(:chdir)
+ expect(@options).to receive(:chdir)
@script.options []
end
it "enables the prefix option" do
- @options.should_receive(:prefix)
+ expect(@options).to receive(:prefix)
@script.options []
end
it "enables the config option" do
- @options.should_receive(:configure)
+ expect(@options).to receive(:configure)
@script.options []
end
it "provides a custom action (block) to the config option" do
- @script.should_receive(:load).with("cfg.mspec")
+ expect(@script).to receive(:load).with("cfg.mspec")
@script.options ["-B", "cfg.mspec"]
end
it "enables the dry run option" do
- @options.should_receive(:pretend)
+ expect(@options).to receive(:pretend)
@script.options []
end
it "enables the unguarded option" do
- @options.should_receive(:unguarded)
+ expect(@options).to receive(:unguarded)
@script.options []
end
it "enables the interrupt single specs option" do
- @options.should_receive(:interrupt)
+ expect(@options).to receive(:interrupt)
@script.options []
end
it "enables the formatter options" do
- @options.should_receive(:formatters)
+ expect(@options).to receive(:formatters)
@script.options []
end
it "enables the verbose option" do
- @options.should_receive(:verbose)
+ expect(@options).to receive(:verbose)
@script.options []
end
it "enables the action options" do
- @options.should_receive(:actions)
+ expect(@options).to receive(:actions)
@script.options []
end
it "enables the action filter options" do
- @options.should_receive(:action_filters)
+ expect(@options).to receive(:action_filters)
@script.options []
end
it "enables the version option" do
- @options.should_receive(:version)
+ expect(@options).to receive(:version)
@script.options []
end
it "enables the help option" do
- @options.should_receive(:help)
+ expect(@options).to receive(:help)
@script.options []
end
+ it "enables the repeat option" do
+ expect(@options).to receive(:repeat)
+ @script.options @argv
+ end
+
it "calls #custom_options" do
- @script.should_receive(:custom_options).with(@options)
+ expect(@script).to receive(:custom_options).with(@options)
@script.options []
end
end
-describe MSpecCI, "#run" do
+RSpec.describe MSpecCI, "#run" do
before :each do
- MSpec.stub(:process)
+ allow(MSpec).to receive(:process)
@filter = double("TagFilter")
- TagFilter.stub(:new).and_return(@filter)
- @filter.stub(:register)
+ allow(TagFilter).to receive(:new).and_return(@filter)
+ allow(@filter).to receive(:register)
@tags = ["fails", "critical", "unstable", "incomplete", "unsupported"]
@config = { :ci_files => ["one", "two"] }
@script = MSpecCI.new
- @script.stub(:exit)
- @script.stub(:config).and_return(@config)
- @script.stub(:files).and_return(["one", "two"])
+ allow(@script).to receive(:exit)
+ allow(@script).to receive(:config).and_return(@config)
+ allow(@script).to receive(:files).and_return(["one", "two"])
@script.options []
end
it "registers the tags patterns" do
@config[:tags_patterns] = [/spec/, "tags"]
- MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"])
+ expect(MSpec).to receive(:register_tags_patterns).with([/spec/, "tags"])
@script.run
end
it "registers the files to process" do
- MSpec.should_receive(:register_files).with(["one", "two"])
+ expect(MSpec).to receive(:register_files).with(["one", "two"])
@script.run
end
it "registers a tag filter for 'fails', 'unstable', 'incomplete', 'critical', 'unsupported'" do
filter = double("fails filter")
- TagFilter.should_receive(:new).with(:exclude, *@tags).and_return(filter)
- filter.should_receive(:register)
+ expect(TagFilter).to receive(:new).with(:exclude, *@tags).and_return(filter)
+ expect(filter).to receive(:register)
@script.run
end
it "registers an additional exclude tag specified by :ci_xtags" do
@config[:ci_xtags] = "windows"
filter = double("fails filter")
- TagFilter.should_receive(:new).with(:exclude, *(@tags + ["windows"])).and_return(filter)
- filter.should_receive(:register)
+ expect(TagFilter).to receive(:new).with(:exclude, *(@tags + ["windows"])).and_return(filter)
+ expect(filter).to receive(:register)
@script.run
end
it "registers additional exclude tags specified by a :ci_xtags array" do
@config[:ci_xtags] = ["windows", "windoze"]
filter = double("fails filter")
- TagFilter.should_receive(:new).with(:exclude,
+ expect(TagFilter).to receive(:new).with(:exclude,
*(@tags + ["windows", "windoze"])).and_return(filter)
- filter.should_receive(:register)
+ expect(filter).to receive(:register)
@script.run
end
it "processes the files" do
- MSpec.should_receive(:process)
+ expect(MSpec).to receive(:process)
@script.run
end
it "exits with the exit code registered with MSpec" do
- MSpec.stub(:exit_code).and_return(7)
- @script.should_receive(:exit).with(7)
+ allow(MSpec).to receive(:exit_code).and_return(7)
+ expect(@script).to receive(:exit).with(7)
@script.run
end
end
diff --git a/spec/mspec/spec/commands/mspec_run_spec.rb b/spec/mspec/spec/commands/mspec_run_spec.rb
index fcb44ad5a9..f96be2b43e 100644
--- a/spec/mspec/spec/commands/mspec_run_spec.rb
+++ b/spec/mspec/spec/commands/mspec_run_spec.rb
@@ -5,130 +5,135 @@ require 'mspec/commands/mspec-run'
one_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/one_spec.rb'
two_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/two_spec.rb'
-describe MSpecRun, ".new" do
+RSpec.describe MSpecRun, ".new" do
before :each do
@script = MSpecRun.new
end
it "sets config[:files] to an empty list" do
- @script.config[:files].should == []
+ expect(@script.config[:files]).to eq([])
end
end
-describe MSpecRun, "#options" do
+RSpec.describe MSpecRun, "#options" do
before :each do
@argv = [one_spec, two_spec]
@options, @config = new_option
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MSpecRun.new
- @script.stub(:config).and_return(@config)
+ allow(@script).to receive(:config).and_return(@config)
end
it "enables the filter options" do
- @options.should_receive(:filters)
+ expect(@options).to receive(:filters)
@script.options @argv
end
it "enables the chdir option" do
- @options.should_receive(:chdir)
+ expect(@options).to receive(:chdir)
@script.options @argv
end
it "enables the prefix option" do
- @options.should_receive(:prefix)
+ expect(@options).to receive(:prefix)
@script.options @argv
end
it "enables the configure option" do
- @options.should_receive(:configure)
+ expect(@options).to receive(:configure)
@script.options @argv
end
it "provides a custom action (block) to the config option" do
- @script.should_receive(:load).with("cfg.mspec")
+ expect(@script).to receive(:load).with("cfg.mspec")
@script.options ["-B", "cfg.mspec", one_spec]
end
it "enables the randomize option to runs specs in random order" do
- @options.should_receive(:randomize)
+ expect(@options).to receive(:randomize)
@script.options @argv
end
it "enables the dry run option" do
- @options.should_receive(:pretend)
+ expect(@options).to receive(:pretend)
@script.options @argv
end
it "enables the unguarded option" do
- @options.should_receive(:unguarded)
+ expect(@options).to receive(:unguarded)
@script.options @argv
end
it "enables the interrupt single specs option" do
- @options.should_receive(:interrupt)
+ expect(@options).to receive(:interrupt)
@script.options @argv
end
it "enables the formatter options" do
- @options.should_receive(:formatters)
+ expect(@options).to receive(:formatters)
@script.options @argv
end
it "enables the verbose option" do
- @options.should_receive(:verbose)
+ expect(@options).to receive(:verbose)
@script.options @argv
end
it "enables the verify options" do
- @options.should_receive(:verify)
+ expect(@options).to receive(:verify)
@script.options @argv
end
it "enables the action options" do
- @options.should_receive(:actions)
+ expect(@options).to receive(:actions)
@script.options @argv
end
it "enables the action filter options" do
- @options.should_receive(:action_filters)
+ expect(@options).to receive(:action_filters)
@script.options @argv
end
it "enables the version option" do
- @options.should_receive(:version)
+ expect(@options).to receive(:version)
@script.options @argv
end
it "enables the help option" do
- @options.should_receive(:help)
+ expect(@options).to receive(:help)
+ @script.options @argv
+ end
+
+ it "enables the repeat option" do
+ expect(@options).to receive(:repeat)
@script.options @argv
end
it "exits if there are no files to process and './spec' is not a directory" do
- File.should_receive(:directory?).with("./spec").and_return(false)
- @options.should_receive(:parse).and_return([])
- @script.should_receive(:abort).with("No files specified.")
+ expect(File).to receive(:directory?).with("./spec").and_return(false)
+ expect(@options).to receive(:parse).and_return([])
+ expect(@script).to receive(:abort).with("No files specified.")
@script.options
end
it "process 'spec/' if it is a directory and no files were specified" do
- File.should_receive(:directory?).with("./spec").and_return(true)
- @options.should_receive(:parse).and_return([])
- @script.should_receive(:files).with(["spec/"]).and_return(["spec/a_spec.rb"])
+ expect(File).to receive(:directory?).with("./spec").and_return(true)
+ expect(@options).to receive(:parse).and_return([])
+ expect(@script).to receive(:files).with(["spec/"]).and_return(["spec/a_spec.rb"])
@script.options
end
it "calls #custom_options" do
- @script.should_receive(:custom_options).with(@options)
+ expect(@script).to receive(:custom_options).with(@options)
@script.options @argv
end
end
-describe MSpecRun, "#run" do
+RSpec.describe MSpecRun, "#run" do
before :each do
@script = MSpecRun.new
- @script.stub(:exit)
+ allow(@script).to receive(:exit)
@spec_dir = File.expand_path(File.dirname(__FILE__)+"/fixtures")
@file_patterns = [
@spec_dir+"/level2",
@@ -139,35 +144,35 @@ describe MSpecRun, "#run" do
@spec_dir+"/one_spec.rb",
@spec_dir+"/two_spec.rb"]
@script.options @file_patterns
- MSpec.stub :process
+ allow(MSpec).to receive :process
end
it "registers the tags patterns" do
@script.config[:tags_patterns] = [/spec/, "tags"]
- MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"])
+ expect(MSpec).to receive(:register_tags_patterns).with([/spec/, "tags"])
@script.run
end
it "registers the files to process" do
- MSpec.should_receive(:register_files).with(@files)
+ expect(MSpec).to receive(:register_files).with(@files)
@script.run
end
it "uses config[:files] if no files are given on the command line" do
@script.config[:files] = @file_patterns
- MSpec.should_receive(:register_files).with(@files)
+ expect(MSpec).to receive(:register_files).with(@files)
@script.options []
@script.run
end
it "processes the files" do
- MSpec.should_receive(:process)
+ expect(MSpec).to receive(:process)
@script.run
end
it "exits with the exit code registered with MSpec" do
- MSpec.stub(:exit_code).and_return(7)
- @script.should_receive(:exit).with(7)
+ allow(MSpec).to receive(:exit_code).and_return(7)
+ expect(@script).to receive(:exit).with(7)
@script.run
end
end
diff --git a/spec/mspec/spec/commands/mspec_spec.rb b/spec/mspec/spec/commands/mspec_spec.rb
index b01af6b41b..d19bebb2d6 100644
--- a/spec/mspec/spec/commands/mspec_spec.rb
+++ b/spec/mspec/spec/commands/mspec_spec.rb
@@ -2,55 +2,55 @@ require 'spec_helper'
require 'yaml'
require 'mspec/commands/mspec'
-describe MSpecMain, "#options" do
+RSpec.describe MSpecMain, "#options" do
before :each do
@options, @config = new_option
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MSpecMain.new
- @script.stub(:config).and_return(@config)
- @script.stub(:load)
+ allow(@script).to receive(:config).and_return(@config)
+ allow(@script).to receive(:load)
end
it "enables the configure option" do
- @options.should_receive(:configure)
+ expect(@options).to receive(:configure)
@script.options
end
it "provides a custom action (block) to the config option" do
@script.options ["-B", "config"]
- @config[:options].should include("-B", "config")
+ expect(@config[:options]).to include("-B", "config")
end
it "loads the file specified by the config option" do
- @script.should_receive(:load).with("config")
+ expect(@script).to receive(:load).with("config")
@script.options ["-B", "config"]
end
it "enables the target options" do
- @options.should_receive(:targets)
+ expect(@options).to receive(:targets)
@script.options
end
it "sets config[:options] to all argv entries that are not registered options" do
@options.on "-X", "--exclude", "ARG", "description"
@script.options [".", "-G", "fail", "-X", "ARG", "--list", "unstable", "some/file.rb"]
- @config[:options].should == [".", "-G", "fail", "--list", "unstable", "some/file.rb"]
+ expect(@config[:options]).to eq([".", "-G", "fail", "--list", "unstable", "some/file.rb"])
end
it "calls #custom_options" do
- @script.should_receive(:custom_options).with(@options)
+ expect(@script).to receive(:custom_options).with(@options)
@script.options
end
end
-describe MSpecMain, "#run" do
+RSpec.describe MSpecMain, "#run" do
before :each do
@options, @config = new_option
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MSpecMain.new
- @script.stub(:config).and_return(@config)
- @script.stub(:exec)
+ allow(@script).to receive(:config).and_return(@config)
+ allow(@script).to receive(:exec)
@err = $stderr
$stderr = IOStub.new
end
@@ -60,76 +60,49 @@ describe MSpecMain, "#run" do
end
it "uses exec to invoke the runner script" do
- @script.should_receive(:exec).with("ruby", "#{MSPEC_HOME}/bin/mspec-run", close_others: false)
+ expect(@script).to receive(:exec).with("ruby", "#{MSPEC_HOME}/bin/mspec-run", close_others: false)
@script.options []
@script.run
end
it "shows the command line on stderr" do
- @script.should_receive(:exec).with("ruby", "#{MSPEC_HOME}/bin/mspec-run", close_others: false)
+ expect(@script).to receive(:exec).with("ruby", "#{MSPEC_HOME}/bin/mspec-run", close_others: false)
@script.options []
@script.run
- $stderr.to_s.should == "$ ruby #{Dir.pwd}/bin/mspec-run\n"
+ expect($stderr.to_s).to eq("$ ruby #{Dir.pwd}/bin/mspec-run\n")
end
it "adds config[:launch] to the exec options" do
- @script.should_receive(:exec).with("ruby",
+ expect(@script).to receive(:exec).with("ruby",
"-Xlaunch.option", "#{MSPEC_HOME}/bin/mspec-run", close_others: false)
@config[:launch] << "-Xlaunch.option"
@script.options []
@script.run
- $stderr.to_s.should == "$ ruby -Xlaunch.option #{Dir.pwd}/bin/mspec-run\n"
+ expect($stderr.to_s).to eq("$ ruby -Xlaunch.option #{Dir.pwd}/bin/mspec-run\n")
end
it "calls #multi_exec if the command is 'ci' and the multi option is passed" do
- @script.should_receive(:multi_exec).and_return do |argv|
- argv.should == ["ruby", "#{MSPEC_HOME}/bin/mspec-ci"]
+ expect(@script).to receive(:multi_exec) do |argv|
+ expect(argv).to eq(["ruby", "#{MSPEC_HOME}/bin/mspec-ci"])
end
@script.options ["ci", "-j"]
- lambda do
+ expect do
@script.run
- end.should raise_error(SystemExit)
+ end.to raise_error(SystemExit)
end
end
-describe "The --warnings option" do
+RSpec.describe "The -j, --multi option" do
before :each do
@options, @config = new_option
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MSpecMain.new
- @script.stub(:config).and_return(@config)
+ allow(@script).to receive(:config).and_return(@config)
end
it "is enabled by #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("--warnings", an_instance_of(String))
- @script.options
- end
-
- it "sets flags to -w" do
- @config[:flags] = []
- @script.options ["--warnings"]
- @config[:flags].should include("-w")
- end
-
- it "set OUTPUT_WARNINGS = '1' in the environment" do
- ENV['OUTPUT_WARNINGS'] = '0'
- @script.options ["--warnings"]
- ENV['OUTPUT_WARNINGS'].should == '1'
- end
-end
-
-describe "The -j, --multi option" do
- before :each do
- @options, @config = new_option
- MSpecOptions.stub(:new).and_return(@options)
- @script = MSpecMain.new
- @script.stub(:config).and_return(@config)
- end
-
- it "is enabled by #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-j", "--multi", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-j", "--multi", an_instance_of(String))
@script.options
end
@@ -137,22 +110,22 @@ describe "The -j, --multi option" do
["-j", "--multi"].each do |opt|
@config[:multi] = nil
@script.options [opt]
- @config[:multi].should == true
+ expect(@config[:multi]).to eq(true)
end
end
end
-describe "The -h, --help option" do
+RSpec.describe "The -h, --help option" do
before :each do
@options, @config = new_option
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MSpecMain.new
- @script.stub(:config).and_return(@config)
+ allow(@script).to receive(:config).and_return(@config)
end
it "is enabled by #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-h", "--help", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-h", "--help", an_instance_of(String))
@script.options
end
@@ -160,30 +133,30 @@ describe "The -h, --help option" do
["-h", "--help"].each do |opt|
@config[:options] = []
@script.options ["ci", opt]
- @config[:options].sort.should == ["-h"]
+ expect(@config[:options].sort).to eq(["-h"])
end
end
it "prints help and exits" do
- @script.should_receive(:puts).twice
- @script.should_receive(:exit).twice
+ expect(@script).to receive(:puts).twice
+ expect(@script).to receive(:exit).twice
["-h", "--help"].each do |opt|
@script.options [opt]
end
end
end
-describe "The -v, --version option" do
+RSpec.describe "The -v, --version option" do
before :each do
@options, @config = new_option
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MSpecMain.new
- @script.stub(:config).and_return(@config)
+ allow(@script).to receive(:config).and_return(@config)
end
it "is enabled by #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-v", "--version", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-v", "--version", an_instance_of(String))
@script.options
end
@@ -191,15 +164,15 @@ describe "The -v, --version option" do
["-v", "--version"].each do |opt|
@config[:options] = []
@script.options ["ci", opt]
- @config[:options].sort.should == ["-v"]
+ expect(@config[:options].sort).to eq(["-v"])
end
end
it "prints the version and exits if no subscript is invoked" do
@config[:command] = nil
- File.stub(:basename).and_return("mspec")
- @script.should_receive(:puts).twice.with("mspec #{MSpec::VERSION}")
- @script.should_receive(:exit).twice
+ allow(File).to receive(:basename).and_return("mspec")
+ expect(@script).to receive(:puts).twice.with("mspec #{MSpec::VERSION}")
+ expect(@script).to receive(:exit).twice
["-v", "--version"].each do |opt|
@script.options [opt]
end
diff --git a/spec/mspec/spec/commands/mspec_tag_spec.rb b/spec/mspec/spec/commands/mspec_tag_spec.rb
index cdb3ac1a60..1ab5f6ea58 100644
--- a/spec/mspec/spec/commands/mspec_tag_spec.rb
+++ b/spec/mspec/spec/commands/mspec_tag_spec.rb
@@ -8,13 +8,13 @@ require 'mspec/runner/actions/tagpurge'
one_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/one_spec.rb'
two_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/two_spec.rb'
-describe MSpecTag, ".new" do
+RSpec.describe MSpecTag, ".new" do
before :each do
@script = MSpecTag.new
end
it "sets config[:ltags] to an empty list" do
- @script.config[:ltags].should == []
+ expect(@script.config[:ltags]).to eq([])
end
it "sets config[:tagger] to :add" do
@@ -30,16 +30,16 @@ describe MSpecTag, ".new" do
end
end
-describe MSpecTag, "#options" do
+RSpec.describe MSpecTag, "#options" do
before :each do
@stdout, $stdout = $stdout, IOStub.new
@argv = [one_spec, two_spec]
@options, @config = new_option
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MSpecTag.new
- @script.stub(:config).and_return(@config)
+ allow(@script).to receive(:config).and_return(@config)
end
after :each do
@@ -47,80 +47,80 @@ describe MSpecTag, "#options" do
end
it "enables the filter options" do
- @options.should_receive(:filters)
+ expect(@options).to receive(:filters)
@script.options @argv
end
it "enables the configure option" do
- @options.should_receive(:configure)
+ expect(@options).to receive(:configure)
@script.options @argv
end
it "provides a custom action (block) to the config option" do
- @script.should_receive(:load).with("cfg.mspec")
+ expect(@script).to receive(:load).with("cfg.mspec")
@script.options ["-B", "cfg.mspec", one_spec]
end
it "enables the dry run option" do
- @options.should_receive(:pretend)
+ expect(@options).to receive(:pretend)
@script.options @argv
end
it "enables the unguarded option" do
- @options.should_receive(:unguarded)
+ expect(@options).to receive(:unguarded)
@script.options @argv
end
it "enables the interrupt single specs option" do
- @options.should_receive(:interrupt)
+ expect(@options).to receive(:interrupt)
@script.options @argv
end
it "enables the formatter options" do
- @options.should_receive(:formatters)
+ expect(@options).to receive(:formatters)
@script.options @argv
end
it "enables the verbose option" do
- @options.should_receive(:verbose)
+ expect(@options).to receive(:verbose)
@script.options @argv
end
it "enables the version option" do
- @options.should_receive(:version)
+ expect(@options).to receive(:version)
@script.options @argv
end
it "enables the help option" do
- @options.should_receive(:help)
+ expect(@options).to receive(:help)
@script.options @argv
end
it "calls #custom_options" do
- @script.should_receive(:custom_options).with(@options)
+ expect(@script).to receive(:custom_options).with(@options)
@script.options @argv
end
it "exits if there are no files to process" do
- @options.should_receive(:parse).and_return([])
- @script.should_receive(:exit)
+ expect(@options).to receive(:parse).and_return([])
+ expect(@script).to receive(:exit)
@script.options
- $stdout.should include "No files specified"
+ expect($stdout.to_s).to include "No files specified"
end
end
-describe MSpecTag, "options" do
+RSpec.describe MSpecTag, "options" do
before :each do
@options, @config = new_option
- MSpecOptions.stub(:new).and_return(@options)
+ allow(MSpecOptions).to receive(:new).and_return(@options)
@script = MSpecTag.new
- @script.stub(:config).and_return(@config)
+ allow(@script).to receive(:config).and_return(@config)
end
describe "-N, --add TAG" do
it "is enabled with #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-N", "--add", "TAG", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-N", "--add", "TAG", an_instance_of(String))
@script.options [one_spec]
end
@@ -129,16 +129,16 @@ describe MSpecTag, "options" do
@config[:tagger] = nil
@config[:tag] = nil
@script.options [opt, "taggit", one_spec]
- @config[:tagger].should == :add
- @config[:tag].should == "taggit:"
+ expect(@config[:tagger]).to eq(:add)
+ expect(@config[:tag]).to eq("taggit:")
end
end
end
describe "-R, --del TAG" do
it "is enabled with #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-R", "--del", "TAG",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-R", "--del", "TAG",
an_instance_of(String))
@script.options [one_spec]
end
@@ -149,17 +149,17 @@ describe MSpecTag, "options" do
@config[:tag] = nil
@config[:outcome] = nil
@script.options [opt, "taggit", one_spec]
- @config[:tagger].should == :del
- @config[:tag].should == "taggit:"
- @config[:outcome].should == :pass
+ expect(@config[:tagger]).to eq(:del)
+ expect(@config[:tag]).to eq("taggit:")
+ expect(@config[:outcome]).to eq(:pass)
end
end
end
describe "-Q, --pass" do
it "is enabled with #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-Q", "--pass", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-Q", "--pass", an_instance_of(String))
@script.options [one_spec]
end
@@ -167,15 +167,15 @@ describe MSpecTag, "options" do
["-Q", "--pass"].each do |opt|
@config[:outcome] = nil
@script.options [opt, one_spec]
- @config[:outcome].should == :pass
+ expect(@config[:outcome]).to eq(:pass)
end
end
end
describe "-F, --fail" do
it "is enabled with #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-F", "--fail", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-F", "--fail", an_instance_of(String))
@script.options [one_spec]
end
@@ -183,15 +183,15 @@ describe MSpecTag, "options" do
["-F", "--fail"].each do |opt|
@config[:outcome] = nil
@script.options [opt, one_spec]
- @config[:outcome].should == :fail
+ expect(@config[:outcome]).to eq(:fail)
end
end
end
describe "-L, --all" do
it "is enabled with #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("-L", "--all", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-L", "--all", an_instance_of(String))
@script.options [one_spec]
end
@@ -199,100 +199,100 @@ describe MSpecTag, "options" do
["-L", "--all"].each do |opt|
@config[:outcome] = nil
@script.options [opt, one_spec]
- @config[:outcome].should == :all
+ expect(@config[:outcome]).to eq(:all)
end
end
end
describe "--list TAG" do
it "is enabled with #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("--list", "TAG", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("--list", "TAG", an_instance_of(String))
@script.options [one_spec]
end
it "sets the mode to :list" do
@config[:tagger] = nil
@script.options ["--list", "TAG", one_spec]
- @config[:tagger].should == :list
+ expect(@config[:tagger]).to eq(:list)
end
it "sets ltags to include TAG" do
@config[:tag] = nil
@script.options ["--list", "TAG", one_spec]
- @config[:ltags].should == ["TAG"]
+ expect(@config[:ltags]).to eq(["TAG"])
end
end
describe "--list-all" do
it "is enabled with #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("--list-all", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("--list-all", an_instance_of(String))
@script.options [one_spec]
end
it "sets the mode to :list_all" do
@config[:tagger] = nil
@script.options ["--list-all", one_spec]
- @config[:tagger].should == :list_all
+ expect(@config[:tagger]).to eq(:list_all)
end
end
describe "--purge" do
it "is enabled with #options" do
- @options.stub(:on)
- @options.should_receive(:on).with("--purge", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("--purge", an_instance_of(String))
@script.options [one_spec]
end
it "sets the mode to :purge" do
@config[:tagger] = nil
@script.options ["--purge", one_spec]
- @config[:tagger].should == :purge
+ expect(@config[:tagger]).to eq(:purge)
end
end
end
-describe MSpecTag, "#run" do
+RSpec.describe MSpecTag, "#run" do
before :each do
- MSpec.stub(:process)
+ allow(MSpec).to receive(:process)
options = double("MSpecOptions").as_null_object
- options.stub(:parse).and_return(["one", "two"])
- MSpecOptions.stub(:new).and_return(options)
+ allow(options).to receive(:parse).and_return(["one", "two"])
+ allow(MSpecOptions).to receive(:new).and_return(options)
@config = { }
@script = MSpecTag.new
- @script.stub(:exit)
- @script.stub(:config).and_return(@config)
- @script.stub(:files).and_return(["one", "two"])
+ allow(@script).to receive(:exit)
+ allow(@script).to receive(:config).and_return(@config)
+ allow(@script).to receive(:files).and_return(["one", "two"])
@script.options
end
it "registers the tags patterns" do
@config[:tags_patterns] = [/spec/, "tags"]
- MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"])
+ expect(MSpec).to receive(:register_tags_patterns).with([/spec/, "tags"])
@script.run
end
it "registers the files to process" do
- MSpec.should_receive(:register_files).with(["one", "two"])
+ expect(MSpec).to receive(:register_files).with(["one", "two"])
@script.run
end
it "processes the files" do
- MSpec.should_receive(:process)
+ expect(MSpec).to receive(:process)
@script.run
end
it "exits with the exit code registered with MSpec" do
- MSpec.stub(:exit_code).and_return(7)
- @script.should_receive(:exit).with(7)
+ allow(MSpec).to receive(:exit_code).and_return(7)
+ expect(@script).to receive(:exit).with(7)
@script.run
end
end
-describe MSpecTag, "#register" do
+RSpec.describe MSpecTag, "#register" do
before :each do
@script = MSpecTag.new
@config = @script.config
@@ -301,19 +301,19 @@ describe MSpecTag, "#register" do
@config[:astrings] = []
@config[:ltags] = ["fails", "unstable"]
- @script.stub(:files).and_return([])
+ allow(@script).to receive(:files).and_return([])
@script.options "fake"
@t = double("TagAction")
- @t.stub(:register)
+ allow(@t).to receive(:register)
@tl = double("TagListAction")
- @tl.stub(:register)
+ allow(@tl).to receive(:register)
end
it "raises an ArgumentError if no recognized action is given" do
@config[:tagger] = :totally_whack
- lambda { @script.register }.should raise_error(ArgumentError)
+ expect { @script.register }.to raise_error(ArgumentError)
end
describe "when config[:tagger] is the default (:add)" do
@@ -322,93 +322,93 @@ describe MSpecTag, "#register" do
end
it "creates a TagAction" do
- TagAction.should_receive(:new).and_return(@t)
+ expect(TagAction).to receive(:new).and_return(@t)
@script.register
end
it "creates a TagAction if config[:tagger] is :del" do
@config[:tagger] = :del
@config[:outcome] = :pass
- TagAction.should_receive(:new).with(:del, :pass, "fake", nil, [], []).and_return(@t)
+ expect(TagAction).to receive(:new).with(:del, :pass, "fake", nil, [], []).and_return(@t)
@script.register
end
it "calls #register on the TagAction instance" do
- TagAction.should_receive(:new).and_return(@t)
- @t.should_receive(:register)
+ expect(TagAction).to receive(:new).and_return(@t)
+ expect(@t).to receive(:register)
@script.register
end
end
describe "when config[:tagger] is :list" do
before :each do
- TagListAction.should_receive(:new).with(@config[:ltags]).and_return(@tl)
+ expect(TagListAction).to receive(:new).with(@config[:ltags]).and_return(@tl)
@config[:tagger] = :list
end
it "creates a TagListAction" do
- @tl.should_receive(:register)
+ expect(@tl).to receive(:register)
@script.register
end
it "registers MSpec pretend mode" do
- MSpec.should_receive(:register_mode).with(:pretend)
+ expect(MSpec).to receive(:register_mode).with(:pretend)
@script.register
end
it "sets config[:formatter] to false" do
@script.register
- @config[:formatter].should be_false
+ expect(@config[:formatter]).to be_falsey
end
end
describe "when config[:tagger] is :list_all" do
before :each do
- TagListAction.should_receive(:new).with(nil).and_return(@tl)
+ expect(TagListAction).to receive(:new).with(nil).and_return(@tl)
@config[:tagger] = :list_all
end
it "creates a TagListAction" do
- @tl.should_receive(:register)
+ expect(@tl).to receive(:register)
@script.register
end
it "registers MSpec pretend mode" do
- MSpec.should_receive(:register_mode).with(:pretend)
+ expect(MSpec).to receive(:register_mode).with(:pretend)
@script.register
end
it "sets config[:formatter] to false" do
@script.register
- @config[:formatter].should be_false
+ expect(@config[:formatter]).to be_falsey
end
end
describe "when config[:tagger] is :purge" do
before :each do
- TagPurgeAction.should_receive(:new).and_return(@tl)
- MSpec.stub(:register_mode)
+ expect(TagPurgeAction).to receive(:new).and_return(@tl)
+ allow(MSpec).to receive(:register_mode)
@config[:tagger] = :purge
end
it "creates a TagPurgeAction" do
- @tl.should_receive(:register)
+ expect(@tl).to receive(:register)
@script.register
end
it "registers MSpec in pretend mode" do
- MSpec.should_receive(:register_mode).with(:pretend)
+ expect(MSpec).to receive(:register_mode).with(:pretend)
@script.register
end
it "registers MSpec in unguarded mode" do
- MSpec.should_receive(:register_mode).with(:unguarded)
+ expect(MSpec).to receive(:register_mode).with(:unguarded)
@script.register
end
it "sets config[:formatter] to false" do
@script.register
- @config[:formatter].should be_false
+ expect(@config[:formatter]).to be_falsey
end
end
end
diff --git a/spec/mspec/spec/expectations/expectations_spec.rb b/spec/mspec/spec/expectations/expectations_spec.rb
index fea692f3e3..371829d4f9 100644
--- a/spec/mspec/spec/expectations/expectations_spec.rb
+++ b/spec/mspec/spec/expectations/expectations_spec.rb
@@ -1,29 +1,29 @@
require 'spec_helper'
require 'mspec/expectations/expectations'
-describe SpecExpectationNotMetError do
+RSpec.describe SpecExpectationNotMetError do
it "is a subclass of StandardError" do
- SpecExpectationNotMetError.ancestors.should include(StandardError)
+ expect(SpecExpectationNotMetError.ancestors).to include(StandardError)
end
end
-describe SpecExpectationNotFoundError do
+RSpec.describe SpecExpectationNotFoundError do
it "is a subclass of StandardError" do
- SpecExpectationNotFoundError.ancestors.should include(StandardError)
+ expect(SpecExpectationNotFoundError.ancestors).to include(StandardError)
end
end
-describe SpecExpectationNotFoundError, "#message" do
+RSpec.describe SpecExpectationNotFoundError, "#message" do
it "returns 'No behavior expectation was found in the example'" do
m = SpecExpectationNotFoundError.new.message
- m.should == "No behavior expectation was found in the example"
+ expect(m).to eq("No behavior expectation was found in the example")
end
end
-describe SpecExpectation, "#fail_with" do
+RSpec.describe SpecExpectation, "#fail_with" do
it "raises an SpecExpectationNotMetError" do
- lambda {
+ expect {
SpecExpectation.fail_with "expected this", "to equal that"
- }.should raise_error(SpecExpectationNotMetError, "expected this to equal that")
+ }.to raise_error(SpecExpectationNotMetError, "expected this to equal that")
end
end
diff --git a/spec/mspec/spec/expectations/should.rb b/spec/mspec/spec/expectations/should.rb
deleted file mode 100644
index 48503b1631..0000000000
--- a/spec/mspec/spec/expectations/should.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-$: << File.dirname(__FILE__) + '/../../lib'
-require 'mspec'
-require 'mspec/utils/script'
-
-# The purpose of these specs is to confirm that the #should
-# and #should_not methods are functioning appropriately. We
-# use a separate spec file that is invoked from the MSpec
-# specs but is run by MSpec. This avoids conflicting with
-# RSpec's #should and #should_not methods.
-
-class ShouldSpecsMonitor
- def initialize
- @called = 0
- end
-
- def expectation(state)
- @called += 1
- end
-
- def finish
- puts "I was called #{@called} times"
- end
-end
-
-# Simplistic runner
-formatter = DottedFormatter.new
-formatter.register
-
-monitor = ShouldSpecsMonitor.new
-MSpec.register :expectation, monitor
-MSpec.register :finish, monitor
-
-at_exit { MSpec.actions :finish }
-
-MSpec.actions :start
-MSpec.setup_env
-
-# Specs
-describe "MSpec expectation method #should" do
- it "accepts a matcher" do
- :sym.should be_kind_of(Symbol)
- end
-
- it "causes a failure to be recorded" do
- 1.should == 2
- end
-
- it "registers that an expectation has been encountered" do
- # an empty example block causes an exception because
- # no expectation was encountered
- end
-
- it "invokes the MSpec :expectation actions" do
- 1.should == 1
- end
-end
-
-describe "MSpec expectation method #should_not" do
- it "accepts a matcher" do
- "sym".should_not be_kind_of(Symbol)
- end
-
- it "causes a failure to be recorded" do
- 1.should_not == 1
- end
-
- it "registers that an expectation has been encountered" do
- end
-
- it "invokes the MSpec :expectation actions" do
- 1.should_not == 2
- end
-end
diff --git a/spec/mspec/spec/expectations/should_spec.rb b/spec/mspec/spec/expectations/should_spec.rb
index b8bda8f86f..472890979d 100644
--- a/spec/mspec/spec/expectations/should_spec.rb
+++ b/spec/mspec/spec/expectations/should_spec.rb
@@ -1,17 +1,17 @@
require 'spec_helper'
require 'rbconfig'
-describe "MSpec" do
+RSpec.describe "MSpec" do
before :all do
path = RbConfig::CONFIG['bindir']
exe = RbConfig::CONFIG['ruby_install_name']
- file = File.dirname(__FILE__) + '/should.rb'
+ file = File.expand_path('../../fixtures/should.rb', __FILE__)
@out = `#{path}/#{exe} #{file}`
end
describe "#should" do
it "records failures" do
- @out.should include <<-EOS
+ expect(@out).to include <<-EOS
1)
MSpec expectation method #should causes a failure to be recorded FAILED
Expected 1 == 2
@@ -20,7 +20,7 @@ EOS
end
it "raises exceptions for examples with no expectations" do
- @out.should include <<-EOS
+ expect(@out).to include <<-EOS
2)
MSpec expectation method #should registers that an expectation has been encountered FAILED
No behavior expectation was found in the example
@@ -30,7 +30,7 @@ EOS
describe "#should_not" do
it "records failures" do
- @out.should include <<-EOS
+ expect(@out).to include <<-EOS
3)
MSpec expectation method #should_not causes a failure to be recorded FAILED
Expected 1 == 1
@@ -39,7 +39,7 @@ EOS
end
it "raises exceptions for examples with no expectations" do
- @out.should include <<-EOS
+ expect(@out).to include <<-EOS
4)
MSpec expectation method #should_not registers that an expectation has been encountered FAILED
No behavior expectation was found in the example
@@ -48,14 +48,14 @@ EOS
end
it "prints status information" do
- @out.should include ".FF..FF."
+ expect(@out).to include ".FF..FF."
end
it "prints out a summary" do
- @out.should include "0 files, 8 examples, 6 expectations, 4 failures, 0 errors"
+ expect(@out).to include "0 files, 8 examples, 6 expectations, 4 failures, 0 errors"
end
it "records expectations" do
- @out.should include "I was called 6 times"
+ expect(@out).to include "I was called 6 times"
end
end
diff --git a/spec/mspec/spec/fixtures/config.mspec b/spec/mspec/spec/fixtures/config.mspec
index 4a069e2eb0..01654c5094 100644
--- a/spec/mspec/spec/fixtures/config.mspec
+++ b/spec/mspec/spec/fixtures/config.mspec
@@ -1,8 +1,6 @@
class MSpecScript
set :target, 'ruby'
- set :backtrace_filter, /lib\/mspec\//
-
set :tags_patterns, [
[%r(spec/fixtures/), 'spec/fixtures/tags/'],
[/_spec.rb$/, '_tags.txt']
diff --git a/spec/mspec/spec/fixtures/should.rb b/spec/mspec/spec/fixtures/should.rb
new file mode 100644
index 0000000000..f494775c5f
--- /dev/null
+++ b/spec/mspec/spec/fixtures/should.rb
@@ -0,0 +1,75 @@
+$: << File.dirname(__FILE__) + '/../../lib'
+require 'mspec'
+require 'mspec/utils/script'
+
+# The purpose of these specs is to confirm that the #should
+# and #should_not methods are functioning appropriately. We
+# use a separate spec file that is invoked from the MSpec
+# specs but is run by MSpec. This avoids conflicting with
+# RSpec's #should and #should_not methods.
+
+raise "RSpec should not be loaded" if defined?(RSpec)
+
+class ShouldSpecsMonitor
+ def initialize
+ @called = 0
+ end
+
+ def expectation(state)
+ @called += 1
+ end
+
+ def finish
+ puts "I was called #{@called} times"
+ end
+end
+
+# Simplistic runner
+formatter = DottedFormatter.new
+formatter.register
+
+monitor = ShouldSpecsMonitor.new
+MSpec.register :expectation, monitor
+MSpec.register :finish, monitor
+
+at_exit { MSpec.actions :finish }
+
+MSpec.actions :start
+MSpec.setup_env
+
+# Specs
+describe "MSpec expectation method #should" do
+ it "accepts a matcher" do
+ :sym.should be_kind_of(Symbol)
+ end
+
+ it "causes a failure to be recorded" do
+ 1.should == 2
+ end
+
+ it "registers that an expectation has been encountered" do
+ # an empty example block causes an exception because
+ # no expectation was encountered
+ end
+
+ it "invokes the MSpec :expectation actions" do
+ 1.should == 1
+ end
+end
+
+describe "MSpec expectation method #should_not" do
+ it "accepts a matcher" do
+ "sym".should_not be_kind_of(Symbol)
+ end
+
+ it "causes a failure to be recorded" do
+ 1.should_not == 1
+ end
+
+ it "registers that an expectation has been encountered" do
+ end
+
+ it "invokes the MSpec :expectation actions" do
+ 1.should_not == 2
+ end
+end
diff --git a/spec/mspec/spec/guards/block_device_spec.rb b/spec/mspec/spec/guards/block_device_spec.rb
index 3b437b6d74..dd420d4a81 100644
--- a/spec/mspec/spec/guards/block_device_spec.rb
+++ b/spec/mspec/spec/guards/block_device_spec.rb
@@ -1,46 +1,46 @@
require 'spec_helper'
require 'mspec/guards'
-describe Object, "#with_block_device" do
+RSpec.describe Object, "#with_block_device" do
before :each do
ScratchPad.clear
@guard = BlockDeviceGuard.new
- BlockDeviceGuard.stub(:new).and_return(@guard)
+ allow(BlockDeviceGuard).to receive(:new).and_return(@guard)
end
platform_is_not :freebsd, :windows do
it "yields if block device is available" do
- @guard.should_receive(:`).and_return("block devices")
+ expect(@guard).to receive(:`).and_return("block devices")
with_block_device { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield if block device is not available" do
- @guard.should_receive(:`).and_return(nil)
+ expect(@guard).to receive(:`).and_return(nil)
with_block_device { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
end
platform_is :freebsd, :windows do
it "does not yield, since platform does not support block devices" do
- @guard.should_not_receive(:`)
+ expect(@guard).not_to receive(:`)
with_block_device { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
end
it "sets the name of the guard to :with_block_device" do
with_block_device { }
- @guard.name.should == :with_block_device
+ expect(@guard.name).to eq(:with_block_device)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:match?).and_return(true)
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:match?).and_return(true)
+ expect(@guard).to receive(:unregister)
+ expect do
with_block_device { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
diff --git a/spec/mspec/spec/guards/bug_spec.rb b/spec/mspec/spec/guards/bug_spec.rb
index c8529a49f7..72a3405dbc 100644
--- a/spec/mspec/spec/guards/bug_spec.rb
+++ b/spec/mspec/spec/guards/bug_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require 'mspec/guards'
-describe BugGuard, "#match? when #implementation? is 'ruby'" do
+RSpec.describe BugGuard, "#match? when #implementation? is 'ruby'" do
before :all do
@verbose = $VERBOSE
$VERBOSE = nil
@@ -23,47 +23,47 @@ describe BugGuard, "#match? when #implementation? is 'ruby'" do
end
it "returns false when version argument is less than RUBY_VERSION" do
- BugGuard.new("#1", "1.8.5").match?.should == false
+ expect(BugGuard.new("#1", "1.8.5").match?).to eq(false)
end
it "returns true when version argument is equal to RUBY_VERSION" do
- BugGuard.new("#1", "1.8.6").match?.should == true
+ expect(BugGuard.new("#1", "1.8.6").match?).to eq(true)
end
it "returns true when version argument is greater than RUBY_VERSION" do
- BugGuard.new("#1", "1.8.7").match?.should == true
+ expect(BugGuard.new("#1", "1.8.7").match?).to eq(true)
end
it "returns true when version argument implicitly includes RUBY_VERSION" do
- BugGuard.new("#1", "1.8").match?.should == true
- BugGuard.new("#1", "1.8.6").match?.should == true
+ expect(BugGuard.new("#1", "1.8").match?).to eq(true)
+ expect(BugGuard.new("#1", "1.8.6").match?).to eq(true)
end
it "returns true when the argument range includes RUBY_VERSION" do
- BugGuard.new("#1", '1.8.5'..'1.8.7').match?.should == true
- BugGuard.new("#1", '1.8'..'1.9').match?.should == true
- BugGuard.new("#1", '1.8'...'1.9').match?.should == true
- BugGuard.new("#1", '1.8'..'1.8.6').match?.should == true
- BugGuard.new("#1", '1.8.5'..'1.8.6').match?.should == true
- BugGuard.new("#1", ''...'1.8.7').match?.should == true
+ expect(BugGuard.new("#1", '1.8.5'..'1.8.7').match?).to eq(true)
+ expect(BugGuard.new("#1", '1.8'..'1.9').match?).to eq(true)
+ expect(BugGuard.new("#1", '1.8'...'1.9').match?).to eq(true)
+ expect(BugGuard.new("#1", '1.8'..'1.8.6').match?).to eq(true)
+ expect(BugGuard.new("#1", '1.8.5'..'1.8.6').match?).to eq(true)
+ expect(BugGuard.new("#1", ''...'1.8.7').match?).to eq(true)
end
it "returns false when the argument range does not include RUBY_VERSION" do
- BugGuard.new("#1", '1.8.7'..'1.8.9').match?.should == false
- BugGuard.new("#1", '1.8.4'..'1.8.5').match?.should == false
- BugGuard.new("#1", '1.8.4'...'1.8.6').match?.should == false
- BugGuard.new("#1", '1.8.5'...'1.8.6').match?.should == false
- BugGuard.new("#1", ''...'1.8.6').match?.should == false
+ expect(BugGuard.new("#1", '1.8.7'..'1.8.9').match?).to eq(false)
+ expect(BugGuard.new("#1", '1.8.4'..'1.8.5').match?).to eq(false)
+ expect(BugGuard.new("#1", '1.8.4'...'1.8.6').match?).to eq(false)
+ expect(BugGuard.new("#1", '1.8.5'...'1.8.6').match?).to eq(false)
+ expect(BugGuard.new("#1", ''...'1.8.6').match?).to eq(false)
end
it "returns false when MSpec.mode?(:no_ruby_bug) is true" do
- MSpec.should_receive(:mode?).with(:no_ruby_bug).twice.and_return(:true)
- BugGuard.new("#1", "1.8.5").match?.should == false
- BugGuard.new("#1", "1.8").match?.should == false
+ expect(MSpec).to receive(:mode?).with(:no_ruby_bug).twice.and_return(:true)
+ expect(BugGuard.new("#1", "1.8.5").match?).to eq(false)
+ expect(BugGuard.new("#1", "1.8").match?).to eq(false)
end
end
-describe BugGuard, "#match? when #implementation? is not 'ruby'" do
+RSpec.describe BugGuard, "#match? when #implementation? is not 'ruby'" do
before :all do
@verbose = $VERBOSE
$VERBOSE = nil
@@ -88,64 +88,64 @@ describe BugGuard, "#match? when #implementation? is not 'ruby'" do
end
it "returns false when version argument is less than RUBY_VERSION" do
- BugGuard.new("#1", "1.8").match?.should == false
- BugGuard.new("#1", "1.8.6").match?.should == false
+ expect(BugGuard.new("#1", "1.8").match?).to eq(false)
+ expect(BugGuard.new("#1", "1.8.6").match?).to eq(false)
end
it "returns false when version argument is equal to RUBY_VERSION" do
- BugGuard.new("#1", "1.8.6").match?.should == false
+ expect(BugGuard.new("#1", "1.8.6").match?).to eq(false)
end
it "returns false when version argument is greater than RUBY_VERSION" do
- BugGuard.new("#1", "1.8.7").match?.should == false
+ expect(BugGuard.new("#1", "1.8.7").match?).to eq(false)
end
it "returns false no matter if the argument range includes RUBY_VERSION" do
- BugGuard.new("#1", '1.8'...'1.9').match?.should == false
- BugGuard.new("#1", '1.8.5'...'1.8.7').match?.should == false
- BugGuard.new("#1", '1.8.4'...'1.8.6').match?.should == false
+ expect(BugGuard.new("#1", '1.8'...'1.9').match?).to eq(false)
+ expect(BugGuard.new("#1", '1.8.5'...'1.8.7').match?).to eq(false)
+ expect(BugGuard.new("#1", '1.8.4'...'1.8.6').match?).to eq(false)
end
it "returns false when MSpec.mode?(:no_ruby_bug) is true" do
- MSpec.stub(:mode?).and_return(:true)
- BugGuard.new("#1", "1.8.6").match?.should == false
+ allow(MSpec).to receive(:mode?).and_return(:true)
+ expect(BugGuard.new("#1", "1.8.6").match?).to eq(false)
end
end
-describe Object, "#ruby_bug" do
+RSpec.describe Object, "#ruby_bug" do
before :each do
hide_deprecation_warnings
@guard = BugGuard.new "#1234", "x.x.x"
- BugGuard.stub(:new).and_return(@guard)
+ allow(BugGuard).to receive(:new).and_return(@guard)
ScratchPad.clear
end
it "yields when #match? returns false" do
- @guard.stub(:match?).and_return(false)
+ allow(@guard).to receive(:match?).and_return(false)
ruby_bug("#1234", "1.8.6") { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield when #match? returns true" do
- @guard.stub(:match?).and_return(true)
+ allow(@guard).to receive(:match?).and_return(true)
ruby_bug("#1234", "1.8.6") { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "requires a bug tracker number and a version number" do
- lambda { ruby_bug { } }.should raise_error(ArgumentError)
- lambda { ruby_bug("#1234") { } }.should raise_error(ArgumentError)
+ expect { ruby_bug { } }.to raise_error(ArgumentError)
+ expect { ruby_bug("#1234") { } }.to raise_error(ArgumentError)
end
it "sets the name of the guard to :ruby_bug" do
ruby_bug("#1234", "1.8.6") { }
- @guard.name.should == :ruby_bug
+ expect(@guard.name).to eq(:ruby_bug)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:unregister)
+ expect do
ruby_bug("", "") { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
diff --git a/spec/mspec/spec/guards/conflict_spec.rb b/spec/mspec/spec/guards/conflict_spec.rb
index deada96821..7dbe83153d 100644
--- a/spec/mspec/spec/guards/conflict_spec.rb
+++ b/spec/mspec/spec/guards/conflict_spec.rb
@@ -1,53 +1,53 @@
require 'spec_helper'
require 'mspec/guards'
-describe Object, "#conflicts_with" do
+RSpec.describe Object, "#conflicts_with" do
before :each do
hide_deprecation_warnings
ScratchPad.clear
end
it "does not yield if Object.constants includes any of the arguments" do
- Object.stub(:constants).and_return(["SomeClass", "OtherClass"])
+ allow(Object).to receive(:constants).and_return(["SomeClass", "OtherClass"])
conflicts_with(:SomeClass, :AClass, :BClass) { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "does not yield if Object.constants (as Symbols) includes any of the arguments" do
- Object.stub(:constants).and_return([:SomeClass, :OtherClass])
+ allow(Object).to receive(:constants).and_return([:SomeClass, :OtherClass])
conflicts_with(:SomeClass, :AClass, :BClass) { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "yields if Object.constants does not include any of the arguments" do
- Object.stub(:constants).and_return(["SomeClass", "OtherClass"])
+ allow(Object).to receive(:constants).and_return(["SomeClass", "OtherClass"])
conflicts_with(:AClass, :BClass) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "yields if Object.constants (as Symbols) does not include any of the arguments" do
- Object.stub(:constants).and_return([:SomeClass, :OtherClass])
+ allow(Object).to receive(:constants).and_return([:SomeClass, :OtherClass])
conflicts_with(:AClass, :BClass) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
end
-describe Object, "#conflicts_with" do
+RSpec.describe Object, "#conflicts_with" do
before :each do
hide_deprecation_warnings
@guard = ConflictsGuard.new
- ConflictsGuard.stub(:new).and_return(@guard)
+ allow(ConflictsGuard).to receive(:new).and_return(@guard)
end
it "sets the name of the guard to :conflicts_with" do
conflicts_with(:AClass, :BClass) { }
- @guard.name.should == :conflicts_with
+ expect(@guard.name).to eq(:conflicts_with)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:unregister)
+ expect do
conflicts_with(:AClass, :BClass) { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
diff --git a/spec/mspec/spec/guards/endian_spec.rb b/spec/mspec/spec/guards/endian_spec.rb
index 5b40c203ab..943b558ed3 100644
--- a/spec/mspec/spec/guards/endian_spec.rb
+++ b/spec/mspec/spec/guards/endian_spec.rb
@@ -1,55 +1,55 @@
require 'spec_helper'
require 'mspec/guards'
-describe Object, "#big_endian" do
+RSpec.describe Object, "#big_endian" do
before :each do
@guard = BigEndianGuard.new
- BigEndianGuard.stub(:new).and_return(@guard)
+ allow(BigEndianGuard).to receive(:new).and_return(@guard)
ScratchPad.clear
end
it "yields on big-endian platforms" do
- @guard.stub(:pattern).and_return([?\001])
+ allow(@guard).to receive(:pattern).and_return([?\001])
big_endian { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield on little-endian platforms" do
- @guard.stub(:pattern).and_return([?\000])
+ allow(@guard).to receive(:pattern).and_return([?\000])
big_endian { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "sets the name of the guard to :big_endian" do
big_endian { }
- @guard.name.should == :big_endian
+ expect(@guard.name).to eq(:big_endian)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.stub(:pattern).and_return([?\001])
- @guard.should_receive(:unregister)
- lambda do
+ allow(@guard).to receive(:pattern).and_return([?\001])
+ expect(@guard).to receive(:unregister)
+ expect do
big_endian { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
-describe Object, "#little_endian" do
+RSpec.describe Object, "#little_endian" do
before :each do
@guard = BigEndianGuard.new
- BigEndianGuard.stub(:new).and_return(@guard)
+ allow(BigEndianGuard).to receive(:new).and_return(@guard)
ScratchPad.clear
end
it "yields on little-endian platforms" do
- @guard.stub(:pattern).and_return([?\000])
+ allow(@guard).to receive(:pattern).and_return([?\000])
little_endian { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield on big-endian platforms" do
- @guard.stub(:pattern).and_return([?\001])
+ allow(@guard).to receive(:pattern).and_return([?\001])
little_endian { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
end
diff --git a/spec/mspec/spec/guards/feature_spec.rb b/spec/mspec/spec/guards/feature_spec.rb
index 8761cb2fbb..fcb8997591 100644
--- a/spec/mspec/spec/guards/feature_spec.rb
+++ b/spec/mspec/spec/guards/feature_spec.rb
@@ -1,120 +1,120 @@
require 'spec_helper'
require 'mspec/guards'
-describe FeatureGuard, ".enabled?" do
+RSpec.describe FeatureGuard, ".enabled?" do
it "returns true if the feature is enabled" do
- MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(true)
- FeatureGuard.enabled?(:encoding).should be_true
+ expect(MSpec).to receive(:feature_enabled?).with(:encoding).and_return(true)
+ expect(FeatureGuard.enabled?(:encoding)).to be_truthy
end
it "returns false if the feature is not enabled" do
- MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(false)
- FeatureGuard.enabled?(:encoding).should be_false
+ expect(MSpec).to receive(:feature_enabled?).with(:encoding).and_return(false)
+ expect(FeatureGuard.enabled?(:encoding)).to be_falsey
end
it "returns true if all the features are enabled" do
- MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
- MSpec.should_receive(:feature_enabled?).with(:two).and_return(true)
- FeatureGuard.enabled?(:one, :two).should be_true
+ expect(MSpec).to receive(:feature_enabled?).with(:one).and_return(true)
+ expect(MSpec).to receive(:feature_enabled?).with(:two).and_return(true)
+ expect(FeatureGuard.enabled?(:one, :two)).to be_truthy
end
it "returns false if any of the features are not enabled" do
- MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
- MSpec.should_receive(:feature_enabled?).with(:two).and_return(false)
- FeatureGuard.enabled?(:one, :two).should be_false
+ expect(MSpec).to receive(:feature_enabled?).with(:one).and_return(true)
+ expect(MSpec).to receive(:feature_enabled?).with(:two).and_return(false)
+ expect(FeatureGuard.enabled?(:one, :two)).to be_falsey
end
end
-describe Object, "#with_feature" do
+RSpec.describe Object, "#with_feature" do
before :each do
ScratchPad.clear
@guard = FeatureGuard.new :encoding
- FeatureGuard.stub(:new).and_return(@guard)
+ allow(FeatureGuard).to receive(:new).and_return(@guard)
end
it "sets the name of the guard to :with_feature" do
with_feature(:encoding) { }
- @guard.name.should == :with_feature
+ expect(@guard.name).to eq(:with_feature)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:match?).and_return(true)
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:match?).and_return(true)
+ expect(@guard).to receive(:unregister)
+ expect do
with_feature { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
-describe Object, "#with_feature" do
+RSpec.describe Object, "#with_feature" do
before :each do
ScratchPad.clear
end
it "yields if the feature is enabled" do
- MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(true)
+ expect(MSpec).to receive(:feature_enabled?).with(:encoding).and_return(true)
with_feature(:encoding) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "yields if all the features are enabled" do
- MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
- MSpec.should_receive(:feature_enabled?).with(:two).and_return(true)
+ expect(MSpec).to receive(:feature_enabled?).with(:one).and_return(true)
+ expect(MSpec).to receive(:feature_enabled?).with(:two).and_return(true)
with_feature(:one, :two) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield if the feature is not enabled" do
- MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(false)
+ expect(MSpec).to receive(:feature_enabled?).with(:encoding).and_return(false)
with_feature(:encoding) { ScratchPad.record :yield }
- ScratchPad.recorded.should be_nil
+ expect(ScratchPad.recorded).to be_nil
end
it "does not yield if any of the features are not enabled" do
- MSpec.should_receive(:feature_enabled?).with(:one).and_return(true)
- MSpec.should_receive(:feature_enabled?).with(:two).and_return(false)
+ expect(MSpec).to receive(:feature_enabled?).with(:one).and_return(true)
+ expect(MSpec).to receive(:feature_enabled?).with(:two).and_return(false)
with_feature(:one, :two) { ScratchPad.record :yield }
- ScratchPad.recorded.should be_nil
+ expect(ScratchPad.recorded).to be_nil
end
end
-describe Object, "#without_feature" do
+RSpec.describe Object, "#without_feature" do
before :each do
ScratchPad.clear
@guard = FeatureGuard.new :encoding
- FeatureGuard.stub(:new).and_return(@guard)
+ allow(FeatureGuard).to receive(:new).and_return(@guard)
end
it "sets the name of the guard to :without_feature" do
without_feature(:encoding) { }
- @guard.name.should == :without_feature
+ expect(@guard.name).to eq(:without_feature)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:match?).and_return(false)
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:match?).and_return(false)
+ expect(@guard).to receive(:unregister)
+ expect do
without_feature { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
-describe Object, "#without_feature" do
+RSpec.describe Object, "#without_feature" do
before :each do
ScratchPad.clear
end
it "does not yield if the feature is enabled" do
- MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(true)
+ expect(MSpec).to receive(:feature_enabled?).with(:encoding).and_return(true)
without_feature(:encoding) { ScratchPad.record :yield }
- ScratchPad.recorded.should be_nil
+ expect(ScratchPad.recorded).to be_nil
end
it "yields if the feature is disabled" do
- MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(false)
+ expect(MSpec).to receive(:feature_enabled?).with(:encoding).and_return(false)
without_feature(:encoding) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
end
diff --git a/spec/mspec/spec/guards/guard_spec.rb b/spec/mspec/spec/guards/guard_spec.rb
index 2c3317afe6..e29d235747 100644
--- a/spec/mspec/spec/guards/guard_spec.rb
+++ b/spec/mspec/spec/guards/guard_spec.rb
@@ -2,41 +2,41 @@ require 'spec_helper'
require 'mspec/guards'
require 'rbconfig'
-describe SpecGuard, ".ruby_version" do
+RSpec.describe SpecGuard, ".ruby_version" do
before :each do
stub_const "RUBY_VERSION", "8.2.3"
end
it "returns the full version for :full" do
- SpecGuard.ruby_version(:full).should == "8.2.3"
+ expect(SpecGuard.ruby_version(:full)).to eq("8.2.3")
end
it "returns major.minor.tiny for :tiny" do
- SpecGuard.ruby_version(:tiny).should == "8.2.3"
+ expect(SpecGuard.ruby_version(:tiny)).to eq("8.2.3")
end
it "returns major.minor.tiny for :teeny" do
- SpecGuard.ruby_version(:tiny).should == "8.2.3"
+ expect(SpecGuard.ruby_version(:tiny)).to eq("8.2.3")
end
it "returns major.minor for :minor" do
- SpecGuard.ruby_version(:minor).should == "8.2"
+ expect(SpecGuard.ruby_version(:minor)).to eq("8.2")
end
it "defaults to :minor" do
- SpecGuard.ruby_version.should == "8.2"
+ expect(SpecGuard.ruby_version).to eq("8.2")
end
it "returns major for :major" do
- SpecGuard.ruby_version(:major).should == "8"
+ expect(SpecGuard.ruby_version(:major)).to eq("8")
end
end
-describe SpecGuard, "#yield?" do
+RSpec.describe SpecGuard, "#yield?" do
before :each do
MSpec.clear_modes
@guard = SpecGuard.new
- @guard.stub(:match?).and_return(false)
+ allow(@guard).to receive(:match?).and_return(false)
end
after :each do
@@ -47,77 +47,77 @@ describe SpecGuard, "#yield?" do
it "returns true if MSpec.mode?(:unguarded) is true" do
MSpec.register_mode :unguarded
- @guard.yield?.should == true
+ expect(@guard.yield?).to eq(true)
end
it "returns true if MSpec.mode?(:verify) is true" do
MSpec.register_mode :verify
- @guard.yield?.should == true
+ expect(@guard.yield?).to eq(true)
end
it "returns true if MSpec.mode?(:verify) is true regardless of invert being true" do
MSpec.register_mode :verify
- @guard.yield?(true).should == true
+ expect(@guard.yield?(true)).to eq(true)
end
it "returns true if MSpec.mode?(:report) is true" do
MSpec.register_mode :report
- @guard.yield?.should == true
+ expect(@guard.yield?).to eq(true)
end
it "returns true if MSpec.mode?(:report) is true regardless of invert being true" do
MSpec.register_mode :report
- @guard.yield?(true).should == true
+ expect(@guard.yield?(true)).to eq(true)
end
it "returns true if MSpec.mode?(:report_on) is true and SpecGuards.guards contains the named guard" do
MSpec.register_mode :report_on
SpecGuard.guards << :guard_name
- @guard.yield?.should == false
+ expect(@guard.yield?).to eq(false)
@guard.name = :guard_name
- @guard.yield?.should == true
+ expect(@guard.yield?).to eq(true)
end
it "returns #match? if neither report nor verify mode are true" do
- @guard.stub(:match?).and_return(false)
- @guard.yield?.should == false
- @guard.stub(:match?).and_return(true)
- @guard.yield?.should == true
+ allow(@guard).to receive(:match?).and_return(false)
+ expect(@guard.yield?).to eq(false)
+ allow(@guard).to receive(:match?).and_return(true)
+ expect(@guard.yield?).to eq(true)
end
it "returns #match? if invert is true and neither report nor verify mode are true" do
- @guard.stub(:match?).and_return(false)
- @guard.yield?(true).should == true
- @guard.stub(:match?).and_return(true)
- @guard.yield?(true).should == false
+ allow(@guard).to receive(:match?).and_return(false)
+ expect(@guard.yield?(true)).to eq(true)
+ allow(@guard).to receive(:match?).and_return(true)
+ expect(@guard.yield?(true)).to eq(false)
end
end
-describe SpecGuard, "#match?" do
+RSpec.describe SpecGuard, "#match?" do
before :each do
@guard = SpecGuard.new
end
it "must be implemented in subclasses" do
- lambda {
+ expect {
@guard.match?
- }.should raise_error("must be implemented by the subclass")
+ }.to raise_error("must be implemented by the subclass")
end
end
-describe SpecGuard, "#unregister" do
+RSpec.describe SpecGuard, "#unregister" do
before :each do
- MSpec.stub(:unregister)
+ allow(MSpec).to receive(:unregister)
@guard = SpecGuard.new
end
it "unregisters from MSpec :add actions" do
- MSpec.should_receive(:unregister).with(:add, @guard)
+ expect(MSpec).to receive(:unregister).with(:add, @guard)
@guard.unregister
end
end
-describe SpecGuard, "#record" do
+RSpec.describe SpecGuard, "#record" do
after :each do
SpecGuard.clear
end
@@ -126,28 +126,28 @@ describe SpecGuard, "#record" do
guard = SpecGuard.new "a", "1.8"..."1.9"
guard.name = :named_guard
guard.record "SomeClass#action returns true"
- SpecGuard.report.should == {
+ expect(SpecGuard.report).to eq({
'named_guard a, 1.8...1.9' => ["SomeClass#action returns true"]
- }
+ })
end
end
-describe SpecGuard, ".guards" do
+RSpec.describe SpecGuard, ".guards" do
it "returns an Array" do
- SpecGuard.guards.should be_kind_of(Array)
+ expect(SpecGuard.guards).to be_kind_of(Array)
end
end
-describe SpecGuard, ".clear_guards" do
+RSpec.describe SpecGuard, ".clear_guards" do
it "resets the array to empty" do
SpecGuard.guards << :guard
- SpecGuard.guards.should == [:guard]
+ expect(SpecGuard.guards).to eq([:guard])
SpecGuard.clear_guards
- SpecGuard.guards.should == []
+ expect(SpecGuard.guards).to eq([])
end
end
-describe SpecGuard, ".finish" do
+RSpec.describe SpecGuard, ".finish" do
before :each do
$stdout = @out = IOStub.new
end
@@ -163,88 +163,88 @@ describe SpecGuard, ".finish" do
guard.record "SomeClass#action returns true"
guard.record "SomeClass#reverse returns false"
SpecGuard.finish
- $stdout.should == %[
+ expect($stdout).to eq(%[
2 specs omitted by guard: named_guard a, 1.8...1.9:
SomeClass#action returns true
SomeClass#reverse returns false
-]
+])
end
end
-describe SpecGuard, ".run_if" do
+RSpec.describe SpecGuard, ".run_if" do
before :each do
@guard = SpecGuard.new
ScratchPad.clear
end
it "yields if match? returns true" do
- @guard.stub(:match?).and_return(true)
+ allow(@guard).to receive(:match?).and_return(true)
@guard.run_if(:name) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield if match? returns false" do
- @guard.stub(:match?).and_return(false)
+ allow(@guard).to receive(:match?).and_return(false)
@guard.run_if(:name) { fail }
end
it "returns the result of the block if match? is true" do
- @guard.stub(:match?).and_return(true)
- @guard.run_if(:name) { 42 }.should == 42
+ allow(@guard).to receive(:match?).and_return(true)
+ expect(@guard.run_if(:name) { 42 }).to eq(42)
end
it "returns nil if given a block and match? is false" do
- @guard.stub(:match?).and_return(false)
- @guard.run_if(:name) { 42 }.should == nil
+ allow(@guard).to receive(:match?).and_return(false)
+ expect(@guard.run_if(:name) { 42 }).to eq(nil)
end
it "returns what #match? returns when no block is given" do
- @guard.stub(:match?).and_return(true)
- @guard.run_if(:name).should == true
- @guard.stub(:match?).and_return(false)
- @guard.run_if(:name).should == false
+ allow(@guard).to receive(:match?).and_return(true)
+ expect(@guard.run_if(:name)).to eq(true)
+ allow(@guard).to receive(:match?).and_return(false)
+ expect(@guard.run_if(:name)).to eq(false)
end
end
-describe SpecGuard, ".run_unless" do
+RSpec.describe SpecGuard, ".run_unless" do
before :each do
@guard = SpecGuard.new
ScratchPad.clear
end
it "yields if match? returns false" do
- @guard.stub(:match?).and_return(false)
+ allow(@guard).to receive(:match?).and_return(false)
@guard.run_unless(:name) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield if match? returns true" do
- @guard.stub(:match?).and_return(true)
+ allow(@guard).to receive(:match?).and_return(true)
@guard.run_unless(:name) { fail }
end
it "returns the result of the block if match? is false" do
- @guard.stub(:match?).and_return(false)
- @guard.run_unless(:name) { 42 }.should == 42
+ allow(@guard).to receive(:match?).and_return(false)
+ expect(@guard.run_unless(:name) { 42 }).to eq(42)
end
it "returns nil if given a block and match? is true" do
- @guard.stub(:match?).and_return(true)
- @guard.run_unless(:name) { 42 }.should == nil
+ allow(@guard).to receive(:match?).and_return(true)
+ expect(@guard.run_unless(:name) { 42 }).to eq(nil)
end
it "returns the opposite of what #match? returns when no block is given" do
- @guard.stub(:match?).and_return(true)
- @guard.run_unless(:name).should == false
- @guard.stub(:match?).and_return(false)
- @guard.run_unless(:name).should == true
+ allow(@guard).to receive(:match?).and_return(true)
+ expect(@guard.run_unless(:name)).to eq(false)
+ allow(@guard).to receive(:match?).and_return(false)
+ expect(@guard.run_unless(:name)).to eq(true)
end
end
-describe Object, "#guard" do
+RSpec.describe Object, "#guard" do
before :each do
ScratchPad.clear
end
@@ -255,31 +255,31 @@ describe Object, "#guard" do
it "allows to combine guards" do
guard1 = VersionGuard.new '1.2.3', 'x.x.x'
- VersionGuard.stub(:new).and_return(guard1)
+ allow(VersionGuard).to receive(:new).and_return(guard1)
guard2 = PlatformGuard.new :dummy
- PlatformGuard.stub(:new).and_return(guard2)
+ allow(PlatformGuard).to receive(:new).and_return(guard2)
- guard1.stub(:match?).and_return(true)
- guard2.stub(:match?).and_return(true)
+ allow(guard1).to receive(:match?).and_return(true)
+ allow(guard2).to receive(:match?).and_return(true)
guard -> { ruby_version_is "2.4" and platform_is :linux } do
ScratchPad.record :yield
end
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
- guard1.stub(:match?).and_return(false)
- guard2.stub(:match?).and_return(true)
+ allow(guard1).to receive(:match?).and_return(false)
+ allow(guard2).to receive(:match?).and_return(true)
guard -> { ruby_version_is "2.4" and platform_is :linux } do
fail
end
- guard1.stub(:match?).and_return(true)
- guard2.stub(:match?).and_return(false)
+ allow(guard1).to receive(:match?).and_return(true)
+ allow(guard2).to receive(:match?).and_return(false)
guard -> { ruby_version_is "2.4" and platform_is :linux } do
fail
end
- guard1.stub(:match?).and_return(false)
- guard2.stub(:match?).and_return(false)
+ allow(guard1).to receive(:match?).and_return(false)
+ allow(guard2).to receive(:match?).and_return(false)
guard -> { ruby_version_is "2.4" and platform_is :linux } do
fail
end
@@ -289,7 +289,7 @@ describe Object, "#guard" do
guard -> { true } do
ScratchPad.record :yield
end
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield when the Proc returns false" do
@@ -304,12 +304,12 @@ describe Object, "#guard" do
guard -> { false } do
ScratchPad.record :yield1
end
- ScratchPad.recorded.should == :yield1
+ expect(ScratchPad.recorded).to eq(:yield1)
guard -> { true } do
ScratchPad.record :yield2
end
- ScratchPad.recorded.should == :yield2
+ expect(ScratchPad.recorded).to eq(:yield2)
end
it "yields if MSpec.mode?(:verify) is true" do
@@ -318,12 +318,12 @@ describe Object, "#guard" do
guard -> { false } do
ScratchPad.record :yield1
end
- ScratchPad.recorded.should == :yield1
+ expect(ScratchPad.recorded).to eq(:yield1)
guard -> { true } do
ScratchPad.record :yield2
end
- ScratchPad.recorded.should == :yield2
+ expect(ScratchPad.recorded).to eq(:yield2)
end
it "yields if MSpec.mode?(:report) is true" do
@@ -332,72 +332,72 @@ describe Object, "#guard" do
guard -> { false } do
ScratchPad.record :yield1
end
- ScratchPad.recorded.should == :yield1
+ expect(ScratchPad.recorded).to eq(:yield1)
guard -> { true } do
ScratchPad.record :yield2
end
- ScratchPad.recorded.should == :yield2
+ expect(ScratchPad.recorded).to eq(:yield2)
end
it "raises an error if no Proc is given" do
- -> { guard :foo }.should raise_error(RuntimeError)
+ expect { guard :foo }.to raise_error(RuntimeError)
end
it "requires a block" do
- -> {
+ expect {
guard(-> { true })
- }.should raise_error(LocalJumpError)
- -> {
+ }.to raise_error(LocalJumpError)
+ expect {
guard(-> { false })
- }.should raise_error(LocalJumpError)
+ }.to raise_error(LocalJumpError)
end
end
-describe Object, "#guard_not" do
+RSpec.describe Object, "#guard_not" do
before :each do
ScratchPad.clear
end
it "allows to combine guards" do
guard1 = VersionGuard.new '1.2.3', 'x.x.x'
- VersionGuard.stub(:new).and_return(guard1)
+ allow(VersionGuard).to receive(:new).and_return(guard1)
guard2 = PlatformGuard.new :dummy
- PlatformGuard.stub(:new).and_return(guard2)
+ allow(PlatformGuard).to receive(:new).and_return(guard2)
- guard1.stub(:match?).and_return(true)
- guard2.stub(:match?).and_return(true)
+ allow(guard1).to receive(:match?).and_return(true)
+ allow(guard2).to receive(:match?).and_return(true)
guard_not -> { ruby_version_is "2.4" and platform_is :linux } do
fail
end
- guard1.stub(:match?).and_return(false)
- guard2.stub(:match?).and_return(true)
+ allow(guard1).to receive(:match?).and_return(false)
+ allow(guard2).to receive(:match?).and_return(true)
guard_not -> { ruby_version_is "2.4" and platform_is :linux } do
ScratchPad.record :yield1
end
- ScratchPad.recorded.should == :yield1
+ expect(ScratchPad.recorded).to eq(:yield1)
- guard1.stub(:match?).and_return(true)
- guard2.stub(:match?).and_return(false)
+ allow(guard1).to receive(:match?).and_return(true)
+ allow(guard2).to receive(:match?).and_return(false)
guard_not -> { ruby_version_is "2.4" and platform_is :linux } do
ScratchPad.record :yield2
end
- ScratchPad.recorded.should == :yield2
+ expect(ScratchPad.recorded).to eq(:yield2)
- guard1.stub(:match?).and_return(false)
- guard2.stub(:match?).and_return(false)
+ allow(guard1).to receive(:match?).and_return(false)
+ allow(guard2).to receive(:match?).and_return(false)
guard_not -> { ruby_version_is "2.4" and platform_is :linux } do
ScratchPad.record :yield3
end
- ScratchPad.recorded.should == :yield3
+ expect(ScratchPad.recorded).to eq(:yield3)
end
it "yields when the Proc returns false" do
guard_not -> { false } do
ScratchPad.record :yield
end
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield when the Proc returns true" do
@@ -407,15 +407,15 @@ describe Object, "#guard_not" do
end
it "raises an error if no Proc is given" do
- -> { guard_not :foo }.should raise_error(RuntimeError)
+ expect { guard_not :foo }.to raise_error(RuntimeError)
end
it "requires a block" do
- -> {
+ expect {
guard_not(-> { true })
- }.should raise_error(LocalJumpError)
- -> {
+ }.to raise_error(LocalJumpError)
+ expect {
guard_not(-> { false })
- }.should raise_error(LocalJumpError)
+ }.to raise_error(LocalJumpError)
end
end
diff --git a/spec/mspec/spec/guards/platform_spec.rb b/spec/mspec/spec/guards/platform_spec.rb
index 6088fb2ba6..bd37432800 100644
--- a/spec/mspec/spec/guards/platform_spec.rb
+++ b/spec/mspec/spec/guards/platform_spec.rb
@@ -1,252 +1,252 @@
require 'spec_helper'
require 'mspec/guards'
-describe Object, "#platform_is" do
+RSpec.describe Object, "#platform_is" do
before :each do
@guard = PlatformGuard.new :dummy
- PlatformGuard.stub(:new).and_return(@guard)
+ allow(PlatformGuard).to receive(:new).and_return(@guard)
ScratchPad.clear
end
it "does not yield when #os? returns false" do
- PlatformGuard.stub(:os?).and_return(false)
+ allow(PlatformGuard).to receive(:os?).and_return(false)
platform_is(:ruby) { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "yields when #os? returns true" do
- PlatformGuard.stub(:os?).and_return(true)
+ allow(PlatformGuard).to receive(:os?).and_return(true)
platform_is(:solarce) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "returns what #os? returns when no block is given" do
- PlatformGuard.stub(:os?).and_return(true)
- platform_is(:solarce).should == true
- PlatformGuard.stub(:os?).and_return(false)
- platform_is(:solarce).should == false
+ allow(PlatformGuard).to receive(:os?).and_return(true)
+ expect(platform_is(:solarce)).to eq(true)
+ allow(PlatformGuard).to receive(:os?).and_return(false)
+ expect(platform_is(:solarce)).to eq(false)
end
it "sets the name of the guard to :platform_is" do
platform_is(:solarce) { }
- @guard.name.should == :platform_is
+ expect(@guard.name).to eq(:platform_is)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:match?).and_return(true)
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:match?).and_return(true)
+ expect(@guard).to receive(:unregister)
+ expect do
platform_is(:solarce) { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
-describe Object, "#platform_is_not" do
+RSpec.describe Object, "#platform_is_not" do
before :each do
@guard = PlatformGuard.new :dummy
- PlatformGuard.stub(:new).and_return(@guard)
+ allow(PlatformGuard).to receive(:new).and_return(@guard)
ScratchPad.clear
end
it "does not yield when #os? returns true" do
- PlatformGuard.stub(:os?).and_return(true)
+ allow(PlatformGuard).to receive(:os?).and_return(true)
platform_is_not(:ruby) { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "yields when #os? returns false" do
- PlatformGuard.stub(:os?).and_return(false)
+ allow(PlatformGuard).to receive(:os?).and_return(false)
platform_is_not(:solarce) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "returns the opposite of what #os? returns when no block is given" do
- PlatformGuard.stub(:os?).and_return(true)
- platform_is_not(:solarce).should == false
- PlatformGuard.stub(:os?).and_return(false)
- platform_is_not(:solarce).should == true
+ allow(PlatformGuard).to receive(:os?).and_return(true)
+ expect(platform_is_not(:solarce)).to eq(false)
+ allow(PlatformGuard).to receive(:os?).and_return(false)
+ expect(platform_is_not(:solarce)).to eq(true)
end
it "sets the name of the guard to :platform_is_not" do
platform_is_not(:solarce) { }
- @guard.name.should == :platform_is_not
+ expect(@guard.name).to eq(:platform_is_not)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:match?).and_return(false)
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:match?).and_return(false)
+ expect(@guard).to receive(:unregister)
+ expect do
platform_is_not(:solarce) { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
-describe Object, "#platform_is :wordsize => SIZE_SPEC" do
+RSpec.describe Object, "#platform_is :c_long_size => SIZE_SPEC" do
before :each do
- @guard = PlatformGuard.new :darwin, :wordsize => 32
- PlatformGuard.stub(:os?).and_return(true)
- PlatformGuard.stub(:new).and_return(@guard)
+ @guard = PlatformGuard.new :darwin, :c_long_size => 32
+ allow(PlatformGuard).to receive(:os?).and_return(true)
+ allow(PlatformGuard).to receive(:new).and_return(@guard)
ScratchPad.clear
end
- it "yields when #wordsize? returns true" do
- PlatformGuard.stub(:wordsize?).and_return(true)
- platform_is(:wordsize => 32) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ it "yields when #c_long_size? returns true" do
+ allow(PlatformGuard).to receive(:c_long_size?).and_return(true)
+ platform_is(:c_long_size => 32) { ScratchPad.record :yield }
+ expect(ScratchPad.recorded).to eq(:yield)
end
- it "doesn not yield when #wordsize? returns false" do
- PlatformGuard.stub(:wordsize?).and_return(false)
- platform_is(:wordsize => 32) { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ it "doesn not yield when #c_long_size? returns false" do
+ allow(PlatformGuard).to receive(:c_long_size?).and_return(false)
+ platform_is(:c_long_size => 32) { ScratchPad.record :yield }
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
end
-describe Object, "#platform_is_not :wordsize => SIZE_SPEC" do
+RSpec.describe Object, "#platform_is_not :c_long_size => SIZE_SPEC" do
before :each do
- @guard = PlatformGuard.new :darwin, :wordsize => 32
- PlatformGuard.stub(:os?).and_return(true)
- PlatformGuard.stub(:new).and_return(@guard)
+ @guard = PlatformGuard.new :darwin, :c_long_size => 32
+ allow(PlatformGuard).to receive(:os?).and_return(true)
+ allow(PlatformGuard).to receive(:new).and_return(@guard)
ScratchPad.clear
end
- it "yields when #wordsize? returns false" do
- PlatformGuard.stub(:wordsize?).and_return(false)
- platform_is_not(:wordsize => 32) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ it "yields when #c_long_size? returns false" do
+ allow(PlatformGuard).to receive(:c_long_size?).and_return(false)
+ platform_is_not(:c_long_size => 32) { ScratchPad.record :yield }
+ expect(ScratchPad.recorded).to eq(:yield)
end
- it "doesn not yield when #wordsize? returns true" do
- PlatformGuard.stub(:wordsize?).and_return(true)
- platform_is_not(:wordsize => 32) { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ it "doesn not yield when #c_long_size? returns true" do
+ allow(PlatformGuard).to receive(:c_long_size?).and_return(true)
+ platform_is_not(:c_long_size => 32) { ScratchPad.record :yield }
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
end
-describe PlatformGuard, ".implementation?" do
+RSpec.describe PlatformGuard, ".implementation?" do
it "returns true if passed :ruby and RUBY_ENGINE == 'ruby'" do
stub_const 'RUBY_ENGINE', 'ruby'
- PlatformGuard.implementation?(:ruby).should == true
+ expect(PlatformGuard.implementation?(:ruby)).to eq(true)
end
it "returns true if passed :rubinius and RUBY_ENGINE == 'rbx'" do
stub_const 'RUBY_ENGINE', 'rbx'
- PlatformGuard.implementation?(:rubinius).should == true
+ expect(PlatformGuard.implementation?(:rubinius)).to eq(true)
end
it "returns true if passed :jruby and RUBY_ENGINE == 'jruby'" do
stub_const 'RUBY_ENGINE', 'jruby'
- PlatformGuard.implementation?(:jruby).should == true
+ expect(PlatformGuard.implementation?(:jruby)).to eq(true)
end
it "returns true if passed :ironruby and RUBY_ENGINE == 'ironruby'" do
stub_const 'RUBY_ENGINE', 'ironruby'
- PlatformGuard.implementation?(:ironruby).should == true
+ expect(PlatformGuard.implementation?(:ironruby)).to eq(true)
end
it "returns true if passed :maglev and RUBY_ENGINE == 'maglev'" do
stub_const 'RUBY_ENGINE', 'maglev'
- PlatformGuard.implementation?(:maglev).should == true
+ expect(PlatformGuard.implementation?(:maglev)).to eq(true)
end
it "returns true if passed :topaz and RUBY_ENGINE == 'topaz'" do
stub_const 'RUBY_ENGINE', 'topaz'
- PlatformGuard.implementation?(:topaz).should == true
+ expect(PlatformGuard.implementation?(:topaz)).to eq(true)
end
it "returns true if passed :ruby and RUBY_ENGINE matches /^ruby/" do
stub_const 'RUBY_ENGINE', 'ruby'
- PlatformGuard.implementation?(:ruby).should == true
+ expect(PlatformGuard.implementation?(:ruby)).to eq(true)
stub_const 'RUBY_ENGINE', 'ruby1.8'
- PlatformGuard.implementation?(:ruby).should == true
+ expect(PlatformGuard.implementation?(:ruby)).to eq(true)
stub_const 'RUBY_ENGINE', 'ruby1.9'
- PlatformGuard.implementation?(:ruby).should == true
+ expect(PlatformGuard.implementation?(:ruby)).to eq(true)
end
it "works for an unrecognized name" do
stub_const 'RUBY_ENGINE', 'myrubyimplementation'
- PlatformGuard.implementation?(:myrubyimplementation).should == true
- PlatformGuard.implementation?(:other).should == false
+ expect(PlatformGuard.implementation?(:myrubyimplementation)).to eq(true)
+ expect(PlatformGuard.implementation?(:other)).to eq(false)
end
end
-describe PlatformGuard, ".standard?" do
+RSpec.describe PlatformGuard, ".standard?" do
it "returns true if implementation? returns true" do
- PlatformGuard.should_receive(:implementation?).with(:ruby).and_return(true)
- PlatformGuard.standard?.should be_true
+ expect(PlatformGuard).to receive(:implementation?).with(:ruby).and_return(true)
+ expect(PlatformGuard.standard?).to be_truthy
end
it "returns false if implementation? returns false" do
- PlatformGuard.should_receive(:implementation?).with(:ruby).and_return(false)
- PlatformGuard.standard?.should be_false
+ expect(PlatformGuard).to receive(:implementation?).with(:ruby).and_return(false)
+ expect(PlatformGuard.standard?).to be_falsey
end
end
-describe PlatformGuard, ".wordsize?" do
+RSpec.describe PlatformGuard, ".c_long_size?" do
it "returns true when arg is 32 and 1.size is 4" do
- PlatformGuard.wordsize?(32).should == (1.size == 4)
+ expect(PlatformGuard.c_long_size?(32)).to eq(1.size == 4)
end
it "returns true when arg is 64 and 1.size is 8" do
- PlatformGuard.wordsize?(64).should == (1.size == 8)
+ expect(PlatformGuard.c_long_size?(64)).to eq(1.size == 8)
end
end
-describe PlatformGuard, ".os?" do
+RSpec.describe PlatformGuard, ".os?" do
before :each do
stub_const 'PlatformGuard::PLATFORM', 'solarce'
end
it "returns false when arg does not match the platform" do
- PlatformGuard.os?(:ruby).should == false
+ expect(PlatformGuard.os?(:ruby)).to eq(false)
end
it "returns false when no arg matches the platform" do
- PlatformGuard.os?(:ruby, :jruby, :rubinius, :maglev).should == false
+ expect(PlatformGuard.os?(:ruby, :jruby, :rubinius, :maglev)).to eq(false)
end
it "returns true when arg matches the platform" do
- PlatformGuard.os?(:solarce).should == true
+ expect(PlatformGuard.os?(:solarce)).to eq(true)
end
it "returns true when any arg matches the platform" do
- PlatformGuard.os?(:ruby, :jruby, :solarce, :rubinius, :maglev).should == true
+ expect(PlatformGuard.os?(:ruby, :jruby, :solarce, :rubinius, :maglev)).to eq(true)
end
it "returns true when arg is :windows and the platform contains 'mswin'" do
stub_const 'PlatformGuard::PLATFORM', 'mswin32'
- PlatformGuard.os?(:windows).should == true
+ expect(PlatformGuard.os?(:windows)).to eq(true)
end
it "returns true when arg is :windows and the platform contains 'mingw'" do
stub_const 'PlatformGuard::PLATFORM', 'i386-mingw32'
- PlatformGuard.os?(:windows).should == true
+ expect(PlatformGuard.os?(:windows)).to eq(true)
end
it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do
stub_const 'PlatformGuard::PLATFORM', 'i386-mswin32'
- PlatformGuard.os?(:linux).should == false
+ expect(PlatformGuard.os?(:linux)).to eq(false)
end
it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do
stub_const 'PlatformGuard::PLATFORM', 'i386-mingw32'
- PlatformGuard.os?(:linux).should == false
+ expect(PlatformGuard.os?(:linux)).to eq(false)
end
end
-describe PlatformGuard, ".os?" do
+RSpec.describe PlatformGuard, ".os?" do
it "returns true if called with the current OS or architecture" do
os = RbConfig::CONFIG["host_os"].sub("-gnu", "")
arch = RbConfig::CONFIG["host_arch"]
- PlatformGuard.os?(os).should == true
- PlatformGuard.os?(arch).should == true
- PlatformGuard.os?("#{arch}-#{os}").should == true
+ expect(PlatformGuard.os?(os)).to eq(true)
+ expect(PlatformGuard.os?(arch)).to eq(true)
+ expect(PlatformGuard.os?("#{arch}-#{os}")).to eq(true)
end
end
-describe PlatformGuard, ".os? on JRuby" do
+RSpec.describe PlatformGuard, ".os? on JRuby" do
before :all do
@verbose = $VERBOSE
$VERBOSE = nil
@@ -266,72 +266,72 @@ describe PlatformGuard, ".os? on JRuby" do
end
it "raises an error when testing for a :java platform" do
- lambda {
+ expect {
PlatformGuard.os?(:java)
- }.should raise_error(":java is not a valid OS")
+ }.to raise_error(":java is not a valid OS")
end
it "returns true when arg is :windows and RUBY_PLATFORM contains 'java' and os?(:windows) is true" do
stub_const 'PlatformGuard::PLATFORM', 'mswin32'
- PlatformGuard.os?(:windows).should == true
+ expect(PlatformGuard.os?(:windows)).to eq(true)
end
it "returns true when RUBY_PLATFORM contains 'java' and os?(argument) is true" do
stub_const 'PlatformGuard::PLATFORM', 'amiga'
- PlatformGuard.os?(:amiga).should == true
+ expect(PlatformGuard.os?(:amiga)).to eq(true)
end
end
-describe PlatformGuard, ".os?" do
+RSpec.describe PlatformGuard, ".os?" do
before :each do
stub_const 'PlatformGuard::PLATFORM', 'unreal'
end
it "returns true if argument matches RbConfig::CONFIG['host_os']" do
- PlatformGuard.os?(:unreal).should == true
+ expect(PlatformGuard.os?(:unreal)).to eq(true)
end
it "returns true if any argument matches RbConfig::CONFIG['host_os']" do
- PlatformGuard.os?(:bsd, :unreal, :amiga).should == true
+ expect(PlatformGuard.os?(:bsd, :unreal, :amiga)).to eq(true)
end
it "returns false if no argument matches RbConfig::CONFIG['host_os']" do
- PlatformGuard.os?(:bsd, :netbsd, :amiga, :msdos).should == false
+ expect(PlatformGuard.os?(:bsd, :netbsd, :amiga, :msdos)).to eq(false)
end
it "returns false if argument does not match RbConfig::CONFIG['host_os']" do
- PlatformGuard.os?(:amiga).should == false
+ expect(PlatformGuard.os?(:amiga)).to eq(false)
end
it "returns true when arg is :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do
stub_const 'PlatformGuard::PLATFORM', 'i386-mswin32'
- PlatformGuard.os?(:windows).should == true
+ expect(PlatformGuard.os?(:windows)).to eq(true)
end
it "returns true when arg is :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do
stub_const 'PlatformGuard::PLATFORM', 'i386-mingw32'
- PlatformGuard.os?(:windows).should == true
+ expect(PlatformGuard.os?(:windows)).to eq(true)
end
it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do
stub_const 'PlatformGuard::PLATFORM', 'i386-mingw32'
- PlatformGuard.os?(:linux).should == false
+ expect(PlatformGuard.os?(:linux)).to eq(false)
end
it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do
stub_const 'PlatformGuard::PLATFORM', 'i386-mingw32'
- PlatformGuard.os?(:linux).should == false
+ expect(PlatformGuard.os?(:linux)).to eq(false)
end
end
-describe PlatformGuard, ".windows?" do
+RSpec.describe PlatformGuard, ".windows?" do
it "returns true on windows" do
stub_const 'PlatformGuard::PLATFORM', 'i386-mingw32'
- PlatformGuard.windows?.should == true
+ expect(PlatformGuard.windows?).to eq(true)
end
it "returns false on non-windows" do
stub_const 'PlatformGuard::PLATFORM', 'i586-linux'
- PlatformGuard.windows?.should == false
+ expect(PlatformGuard.windows?).to eq(false)
end
end
diff --git a/spec/mspec/spec/guards/quarantine_spec.rb b/spec/mspec/spec/guards/quarantine_spec.rb
index e5c7da7939..eb5ff1da27 100644
--- a/spec/mspec/spec/guards/quarantine_spec.rb
+++ b/spec/mspec/spec/guards/quarantine_spec.rb
@@ -1,35 +1,35 @@
require 'spec_helper'
require 'mspec/guards'
-describe QuarantineGuard, "#match?" do
+RSpec.describe QuarantineGuard, "#match?" do
it "returns true" do
- QuarantineGuard.new.match?.should == true
+ expect(QuarantineGuard.new.match?).to eq(true)
end
end
-describe Object, "#quarantine!" do
+RSpec.describe Object, "#quarantine!" do
before :each do
ScratchPad.clear
@guard = QuarantineGuard.new
- QuarantineGuard.stub(:new).and_return(@guard)
+ allow(QuarantineGuard).to receive(:new).and_return(@guard)
end
it "does not yield" do
quarantine! { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "sets the name of the guard to :quarantine!" do
quarantine! { }
- @guard.name.should == :quarantine!
+ expect(@guard.name).to eq(:quarantine!)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:match?).and_return(false)
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:match?).and_return(false)
+ expect(@guard).to receive(:unregister)
+ expect do
quarantine! { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
diff --git a/spec/mspec/spec/guards/superuser_spec.rb b/spec/mspec/spec/guards/superuser_spec.rb
index f8815057e1..aba2cc2bb0 100644
--- a/spec/mspec/spec/guards/superuser_spec.rb
+++ b/spec/mspec/spec/guards/superuser_spec.rb
@@ -1,35 +1,35 @@
require 'spec_helper'
require 'mspec/guards'
-describe Object, "#as_superuser" do
+RSpec.describe Object, "#as_superuser" do
before :each do
@guard = SuperUserGuard.new
- SuperUserGuard.stub(:new).and_return(@guard)
+ allow(SuperUserGuard).to receive(:new).and_return(@guard)
ScratchPad.clear
end
it "does not yield when Process.euid is not 0" do
- Process.stub(:euid).and_return(501)
+ allow(Process).to receive(:euid).and_return(501)
as_superuser { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "yields when Process.euid is 0" do
- Process.stub(:euid).and_return(0)
+ allow(Process).to receive(:euid).and_return(0)
as_superuser { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "sets the name of the guard to :as_superuser" do
as_superuser { }
- @guard.name.should == :as_superuser
+ expect(@guard.name).to eq(:as_superuser)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:match?).and_return(true)
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:match?).and_return(true)
+ expect(@guard).to receive(:unregister)
+ expect do
as_superuser { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
diff --git a/spec/mspec/spec/guards/support_spec.rb b/spec/mspec/spec/guards/support_spec.rb
index 38414abebd..a61d003d6c 100644
--- a/spec/mspec/spec/guards/support_spec.rb
+++ b/spec/mspec/spec/guards/support_spec.rb
@@ -1,54 +1,54 @@
require 'spec_helper'
require 'mspec/guards'
-describe Object, "#not_supported_on" do
+RSpec.describe Object, "#not_supported_on" do
before :each do
ScratchPad.clear
end
it "raises an Exception when passed :ruby" do
stub_const "RUBY_ENGINE", "jruby"
- lambda {
+ expect {
not_supported_on(:ruby) { ScratchPad.record :yield }
- }.should raise_error(Exception)
- ScratchPad.recorded.should_not == :yield
+ }.to raise_error(Exception)
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "does not yield when #implementation? returns true" do
stub_const "RUBY_ENGINE", "jruby"
not_supported_on(:jruby) { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "yields when #standard? returns true" do
stub_const "RUBY_ENGINE", "ruby"
not_supported_on(:rubinius) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "yields when #implementation? returns false" do
stub_const "RUBY_ENGINE", "jruby"
not_supported_on(:rubinius) { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
end
-describe Object, "#not_supported_on" do
+RSpec.describe Object, "#not_supported_on" do
before :each do
@guard = SupportedGuard.new
- SupportedGuard.stub(:new).and_return(@guard)
+ allow(SupportedGuard).to receive(:new).and_return(@guard)
end
it "sets the name of the guard to :not_supported_on" do
not_supported_on(:rubinius) { }
- @guard.name.should == :not_supported_on
+ expect(@guard.name).to eq(:not_supported_on)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:match?).and_return(false)
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:match?).and_return(false)
+ expect(@guard).to receive(:unregister)
+ expect do
not_supported_on(:rubinius) { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
diff --git a/spec/mspec/spec/guards/user_spec.rb b/spec/mspec/spec/guards/user_spec.rb
index 2de4db7390..2526504656 100644
--- a/spec/mspec/spec/guards/user_spec.rb
+++ b/spec/mspec/spec/guards/user_spec.rb
@@ -1,20 +1,20 @@
require 'spec_helper'
require 'mspec/guards'
-describe Object, "#as_user" do
+RSpec.describe Object, "#as_user" do
before :each do
ScratchPad.clear
end
it "yields when the Process.euid is not 0" do
- Process.stub(:euid).and_return(501)
+ allow(Process).to receive(:euid).and_return(501)
as_user { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield when the Process.euid is 0" do
- Process.stub(:euid).and_return(0)
+ allow(Process).to receive(:euid).and_return(0)
as_user { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
end
diff --git a/spec/mspec/spec/guards/version_spec.rb b/spec/mspec/spec/guards/version_spec.rb
index b0025efcfb..5a5f4ddc3b 100644
--- a/spec/mspec/spec/guards/version_spec.rb
+++ b/spec/mspec/spec/guards/version_spec.rb
@@ -11,102 +11,102 @@ require 'mspec/guards'
# is as typically understood: a..b means v >= a and v <= b;
# a...b means v >= a and v < b.
-describe VersionGuard, "#match?" do
+RSpec.describe VersionGuard, "#match?" do
before :each do
hide_deprecation_warnings
@current = '1.8.6'
end
it "returns true when the argument is equal to RUBY_VERSION" do
- VersionGuard.new(@current, '1.8.6').match?.should == true
+ expect(VersionGuard.new(@current, '1.8.6').match?).to eq(true)
end
it "returns true when the argument is less than RUBY_VERSION" do
- VersionGuard.new(@current, '1.8').match?.should == true
- VersionGuard.new(@current, '1.8.5').match?.should == true
+ expect(VersionGuard.new(@current, '1.8').match?).to eq(true)
+ expect(VersionGuard.new(@current, '1.8.5').match?).to eq(true)
end
it "returns false when the argument is greater than RUBY_VERSION" do
- VersionGuard.new(@current, '1.8.7').match?.should == false
- VersionGuard.new(@current, '1.9.2').match?.should == false
+ expect(VersionGuard.new(@current, '1.8.7').match?).to eq(false)
+ expect(VersionGuard.new(@current, '1.9.2').match?).to eq(false)
end
it "returns true when the argument range includes RUBY_VERSION" do
- VersionGuard.new(@current, '1.8.5'..'1.8.7').match?.should == true
- VersionGuard.new(@current, '1.8'..'1.9').match?.should == true
- VersionGuard.new(@current, '1.8'...'1.9').match?.should == true
- VersionGuard.new(@current, '1.8'..'1.8.6').match?.should == true
- VersionGuard.new(@current, '1.8.5'..'1.8.6').match?.should == true
- VersionGuard.new(@current, ''...'1.8.7').match?.should == true
+ expect(VersionGuard.new(@current, '1.8.5'..'1.8.7').match?).to eq(true)
+ expect(VersionGuard.new(@current, '1.8'..'1.9').match?).to eq(true)
+ expect(VersionGuard.new(@current, '1.8'...'1.9').match?).to eq(true)
+ expect(VersionGuard.new(@current, '1.8'..'1.8.6').match?).to eq(true)
+ expect(VersionGuard.new(@current, '1.8.5'..'1.8.6').match?).to eq(true)
+ expect(VersionGuard.new(@current, ''...'1.8.7').match?).to eq(true)
end
it "returns false when the argument range does not include RUBY_VERSION" do
- VersionGuard.new(@current, '1.8.7'..'1.8.9').match?.should == false
- VersionGuard.new(@current, '1.8.4'..'1.8.5').match?.should == false
- VersionGuard.new(@current, '1.8.4'...'1.8.6').match?.should == false
- VersionGuard.new(@current, '1.8.5'...'1.8.6').match?.should == false
- VersionGuard.new(@current, ''...'1.8.6').match?.should == false
+ expect(VersionGuard.new(@current, '1.8.7'..'1.8.9').match?).to eq(false)
+ expect(VersionGuard.new(@current, '1.8.4'..'1.8.5').match?).to eq(false)
+ expect(VersionGuard.new(@current, '1.8.4'...'1.8.6').match?).to eq(false)
+ expect(VersionGuard.new(@current, '1.8.5'...'1.8.6').match?).to eq(false)
+ expect(VersionGuard.new(@current, ''...'1.8.6').match?).to eq(false)
end
end
-describe Object, "#ruby_version_is" do
+RSpec.describe Object, "#ruby_version_is" do
before :each do
@guard = VersionGuard.new '1.2.3', 'x.x.x'
- VersionGuard.stub(:new).and_return(@guard)
+ allow(VersionGuard).to receive(:new).and_return(@guard)
ScratchPad.clear
end
it "yields when #match? returns true" do
- @guard.stub(:match?).and_return(true)
+ allow(@guard).to receive(:match?).and_return(true)
ruby_version_is('x.x.x') { ScratchPad.record :yield }
- ScratchPad.recorded.should == :yield
+ expect(ScratchPad.recorded).to eq(:yield)
end
it "does not yield when #match? returns false" do
- @guard.stub(:match?).and_return(false)
+ allow(@guard).to receive(:match?).and_return(false)
ruby_version_is('x.x.x') { ScratchPad.record :yield }
- ScratchPad.recorded.should_not == :yield
+ expect(ScratchPad.recorded).not_to eq(:yield)
end
it "returns what #match? returns when no block is given" do
- @guard.stub(:match?).and_return(true)
- ruby_version_is('x.x.x').should == true
- @guard.stub(:match?).and_return(false)
- ruby_version_is('x.x.x').should == false
+ allow(@guard).to receive(:match?).and_return(true)
+ expect(ruby_version_is('x.x.x')).to eq(true)
+ allow(@guard).to receive(:match?).and_return(false)
+ expect(ruby_version_is('x.x.x')).to eq(false)
end
it "sets the name of the guard to :ruby_version_is" do
ruby_version_is("") { }
- @guard.name.should == :ruby_version_is
+ expect(@guard.name).to eq(:ruby_version_is)
end
it "calls #unregister even when an exception is raised in the guard block" do
- @guard.should_receive(:match?).and_return(true)
- @guard.should_receive(:unregister)
- lambda do
+ expect(@guard).to receive(:match?).and_return(true)
+ expect(@guard).to receive(:unregister)
+ expect do
ruby_version_is("") { raise Exception }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
end
end
-describe Object, "#version_is" do
+RSpec.describe Object, "#version_is" do
before :each do
hide_deprecation_warnings
end
it "returns the expected values" do
- version_is('1.2.3', '1.2.2').should == true
- version_is('1.2.3', '1.2.3').should == true
- version_is('1.2.3', '1.2.4').should == false
-
- version_is('1.2.3', '1').should == true
- version_is('1.2.3', '1.0').should == true
- version_is('1.2.3', '2').should == false
- version_is('1.2.3', '2.0').should == false
-
- version_is('1.2.3', '1.2.2'..'1.2.4').should == true
- version_is('1.2.3', '1.2.2'..'1.2.3').should == true
- version_is('1.2.3', '1.2.2'...'1.2.3').should == false
- version_is('1.2.3', '1.2.3'..'1.2.4').should == true
+ expect(version_is('1.2.3', '1.2.2')).to eq(true)
+ expect(version_is('1.2.3', '1.2.3')).to eq(true)
+ expect(version_is('1.2.3', '1.2.4')).to eq(false)
+
+ expect(version_is('1.2.3', '1')).to eq(true)
+ expect(version_is('1.2.3', '1.0')).to eq(true)
+ expect(version_is('1.2.3', '2')).to eq(false)
+ expect(version_is('1.2.3', '2.0')).to eq(false)
+
+ expect(version_is('1.2.3', '1.2.2'..'1.2.4')).to eq(true)
+ expect(version_is('1.2.3', '1.2.2'..'1.2.3')).to eq(true)
+ expect(version_is('1.2.3', '1.2.2'...'1.2.3')).to eq(false)
+ expect(version_is('1.2.3', '1.2.3'..'1.2.4')).to eq(true)
end
end
diff --git a/spec/mspec/spec/helpers/argf_spec.rb b/spec/mspec/spec/helpers/argf_spec.rb
index cf5eb0fe88..1412d71f84 100644
--- a/spec/mspec/spec/helpers/argf_spec.rb
+++ b/spec/mspec/spec/helpers/argf_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
-describe Object, "#argf" do
+RSpec.describe Object, "#argf" do
before :each do
@saved_argv = ARGV.dup
@argv = [__FILE__]
@@ -10,28 +10,28 @@ describe Object, "#argf" do
it "sets @argf to an instance of ARGF.class with the given argv" do
argf @argv do
- @argf.should be_an_instance_of ARGF.class
- @argf.filename.should == @argv.first
+ expect(@argf).to be_an_instance_of ARGF.class
+ expect(@argf.filename).to eq(@argv.first)
end
- @argf.should be_nil
+ expect(@argf).to be_nil
end
it "does not alter ARGV nor ARGF" do
argf @argv do
end
- ARGV.should == @saved_argv
- ARGF.argv.should == @saved_argv
+ expect(ARGV).to eq(@saved_argv)
+ expect(ARGF.argv).to eq(@saved_argv)
end
it "does not close STDIN" do
argf ['-'] do
end
- STDIN.should_not be_closed
+ expect(STDIN).not_to be_closed
end
it "disallows nested calls" do
argf @argv do
- lambda { argf @argv }.should raise_error
+ expect { argf @argv }.to raise_error
end
end
end
diff --git a/spec/mspec/spec/helpers/argv_spec.rb b/spec/mspec/spec/helpers/argv_spec.rb
index c3b21c7639..1db7e38650 100644
--- a/spec/mspec/spec/helpers/argv_spec.rb
+++ b/spec/mspec/spec/helpers/argv_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
-describe Object, "#argv" do
+RSpec.describe Object, "#argv" do
before :each do
ScratchPad.clear
@@ -12,16 +12,16 @@ describe Object, "#argv" do
it "replaces and restores the value of ARGV" do
argv @argv
- ARGV.should == @argv
+ expect(ARGV).to eq(@argv)
argv :restore
- ARGV.should == @saved_argv
+ expect(ARGV).to eq(@saved_argv)
end
it "yields to the block after setting ARGV" do
argv @argv do
ScratchPad.record ARGV.dup
end
- ScratchPad.recorded.should == @argv
- ARGV.should == @saved_argv
+ expect(ScratchPad.recorded).to eq(@argv)
+ expect(ARGV).to eq(@saved_argv)
end
end
diff --git a/spec/mspec/spec/helpers/datetime_spec.rb b/spec/mspec/spec/helpers/datetime_spec.rb
index 8696c0c9c7..af4f557376 100644
--- a/spec/mspec/spec/helpers/datetime_spec.rb
+++ b/spec/mspec/spec/helpers/datetime_spec.rb
@@ -2,43 +2,43 @@ require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
-describe Object, "#new_datetime" do
+RSpec.describe Object, "#new_datetime" do
it "returns a default DateTime instance" do
- new_datetime.should == DateTime.new
+ expect(new_datetime).to eq(DateTime.new)
end
it "returns a DateTime instance with the specified year value" do
d = new_datetime :year => 1970
- d.year.should == 1970
+ expect(d.year).to eq(1970)
end
it "returns a DateTime instance with the specified month value" do
d = new_datetime :month => 11
- d.mon.should == 11
+ expect(d.mon).to eq(11)
end
it "returns a DateTime instance with the specified day value" do
d = new_datetime :day => 23
- d.day.should == 23
+ expect(d.day).to eq(23)
end
it "returns a DateTime instance with the specified hour value" do
d = new_datetime :hour => 10
- d.hour.should == 10
+ expect(d.hour).to eq(10)
end
it "returns a DateTime instance with the specified minute value" do
d = new_datetime :minute => 10
- d.min.should == 10
+ expect(d.min).to eq(10)
end
it "returns a DateTime instance with the specified second value" do
d = new_datetime :second => 2
- d.sec.should == 2
+ expect(d.sec).to eq(2)
end
it "returns a DateTime instance with the specified offset value" do
d = new_datetime :offset => Rational(3,24)
- d.offset.should == Rational(3,24)
+ expect(d.offset).to eq(Rational(3,24))
end
end
diff --git a/spec/mspec/spec/helpers/fixture_spec.rb b/spec/mspec/spec/helpers/fixture_spec.rb
index 4dbdd092f1..d8e2ae7be4 100644
--- a/spec/mspec/spec/helpers/fixture_spec.rb
+++ b/spec/mspec/spec/helpers/fixture_spec.rb
@@ -2,24 +2,24 @@ require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
-describe Object, "#fixture" do
+RSpec.describe Object, "#fixture" do
before :each do
@dir = File.realpath("..", __FILE__)
end
it "returns the expanded path to a fixture file" do
name = fixture(__FILE__, "subdir", "file.txt")
- name.should == "#{@dir}/fixtures/subdir/file.txt"
+ expect(name).to eq("#{@dir}/fixtures/subdir/file.txt")
end
it "omits '/shared' if it is the suffix of the directory string" do
name = fixture("#{@dir}/shared/file.rb", "subdir", "file.txt")
- name.should == "#{@dir}/fixtures/subdir/file.txt"
+ expect(name).to eq("#{@dir}/fixtures/subdir/file.txt")
end
it "does not append '/fixtures' if it is the suffix of the directory string" do
commands_dir = "#{File.dirname(@dir)}/commands"
name = fixture("#{commands_dir}/fixtures/file.rb", "subdir", "file.txt")
- name.should == "#{commands_dir}/fixtures/subdir/file.txt"
+ expect(name).to eq("#{commands_dir}/fixtures/subdir/file.txt")
end
end
diff --git a/spec/mspec/spec/helpers/flunk_spec.rb b/spec/mspec/spec/helpers/flunk_spec.rb
index 7b1216d3f7..b6a1f21c12 100644
--- a/spec/mspec/spec/helpers/flunk_spec.rb
+++ b/spec/mspec/spec/helpers/flunk_spec.rb
@@ -4,17 +4,17 @@ require 'mspec/runner/mspec'
require 'mspec/guards'
require 'mspec/helpers'
-describe Object, "#flunk" do
+RSpec.describe Object, "#flunk" do
before :each do
- MSpec.stub(:actions)
- MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ allow(MSpec).to receive(:actions)
+ allow(MSpec).to receive(:current).and_return(double("spec state").as_null_object)
end
it "raises an SpecExpectationNotMetError unconditionally" do
- lambda { flunk }.should raise_error(SpecExpectationNotMetError)
+ expect { flunk }.to raise_error(SpecExpectationNotMetError)
end
it "accepts on argument for an optional message" do
- lambda {flunk "test"}.should raise_error(SpecExpectationNotMetError)
+ expect {flunk "test"}.to raise_error(SpecExpectationNotMetError)
end
end
diff --git a/spec/mspec/spec/helpers/fs_spec.rb b/spec/mspec/spec/helpers/fs_spec.rb
index e40c6c5607..15bb43903a 100644
--- a/spec/mspec/spec/helpers/fs_spec.rb
+++ b/spec/mspec/spec/helpers/fs_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
-describe Object, "#cp" do
+RSpec.describe Object, "#cp" do
before :each do
@source = tmp("source.txt")
@copy = tmp("copied.txt")
@@ -19,12 +19,12 @@ describe Object, "#cp" do
it "copies a file" do
cp @source, @copy
data = IO.read(@copy)
- data.should == @contents
- data.should == IO.read(@source)
+ expect(data).to eq(@contents)
+ expect(data).to eq(IO.read(@source))
end
end
-describe Object, "#touch" do
+RSpec.describe Object, "#touch" do
before :all do
@name = tmp("touched.txt")
end
@@ -35,29 +35,29 @@ describe Object, "#touch" do
it "creates a file" do
touch @name
- File.exist?(@name).should be_true
+ expect(File.exist?(@name)).to be_truthy
end
it "accepts an optional mode argument" do
touch @name, "wb"
- File.exist?(@name).should be_true
+ expect(File.exist?(@name)).to be_truthy
end
it "overwrites an existing file" do
File.open(@name, "w") { |f| f.puts "used" }
- File.size(@name).should > 0
+ expect(File.size(@name)).to be > 0
touch @name
- File.size(@name).should == 0
+ expect(File.size(@name)).to eq(0)
end
it "yields the open file if passed a block" do
touch(@name) { |f| f.write "touching" }
- IO.read(@name).should == "touching"
+ expect(IO.read(@name)).to eq("touching")
end
end
-describe Object, "#touch" do
+RSpec.describe Object, "#touch" do
before :all do
@name = tmp("subdir/touched.txt")
end
@@ -68,11 +68,11 @@ describe Object, "#touch" do
it "creates all the directories in the path to the file" do
touch @name
- File.exist?(@name).should be_true
+ expect(File.exist?(@name)).to be_truthy
end
end
-describe Object, "#mkdir_p" do
+RSpec.describe Object, "#mkdir_p" do
before :all do
@dir1 = tmp("/nested")
@dir2 = @dir1 + "/directory"
@@ -86,17 +86,17 @@ describe Object, "#mkdir_p" do
it "creates all the directories in a path" do
mkdir_p @dir2
- File.directory?(@dir2).should be_true
+ expect(File.directory?(@dir2)).to be_truthy
end
it "raises an ArgumentError if a path component is a file" do
File.open(@dir1, "w") { |f| }
- lambda { mkdir_p @dir2 }.should raise_error(ArgumentError)
+ expect { mkdir_p @dir2 }.to raise_error(ArgumentError)
end
it "works if multiple processes try to create the same directory concurrently" do
original = File.method(:directory?)
- File.should_receive(:directory?).at_least(:once) { |dir|
+ expect(File).to receive(:directory?).at_least(:once) { |dir|
ret = original.call(dir)
if !ret and dir == @dir1
Dir.mkdir(dir) # Simulate race
@@ -104,11 +104,11 @@ describe Object, "#mkdir_p" do
ret
}
mkdir_p @dir1
- original.call(@dir1).should be_true
+ expect(original.call(@dir1)).to be_truthy
end
end
-describe Object, "#rm_r" do
+RSpec.describe Object, "#rm_r" do
before :all do
@topdir = tmp("rm_r_tree")
@topfile = @topdir + "/file.txt"
@@ -137,59 +137,59 @@ describe Object, "#rm_r" do
end
it "raises an ArgumentError if the path is not prefixed by MSPEC_RM_PREFIX" do
- lambda { rm_r "some_file.txt" }.should raise_error(ArgumentError)
+ expect { rm_r "some_file.txt" }.to raise_error(ArgumentError)
end
it "removes a single file" do
rm_r @subfile
- File.exist?(@subfile).should be_false
+ expect(File.exist?(@subfile)).to be_falsey
end
it "removes multiple files" do
rm_r @topfile, @subfile
- File.exist?(@topfile).should be_false
- File.exist?(@subfile).should be_false
+ expect(File.exist?(@topfile)).to be_falsey
+ expect(File.exist?(@subfile)).to be_falsey
end
platform_is_not :windows do
it "removes a symlink to a file" do
File.symlink @topfile, @link
rm_r @link
- File.exist?(@link).should be_false
+ expect(File.exist?(@link)).to be_falsey
end
it "removes a symlink to a directory" do
File.symlink @subdir1, @link
rm_r @link
- lambda do
+ expect do
File.lstat(@link)
- end.should raise_error(Errno::ENOENT)
- File.exist?(@subdir1).should be_true
+ end.to raise_error(Errno::ENOENT)
+ expect(File.exist?(@subdir1)).to be_truthy
end
it "removes a dangling symlink" do
File.symlink "non_existent_file", @link
rm_r @link
- lambda do
+ expect do
File.lstat(@link)
- end.should raise_error(Errno::ENOENT)
+ end.to raise_error(Errno::ENOENT)
end
it "removes a socket" do
require 'socket'
UNIXServer.new(@socket).close
rm_r @socket
- File.exist?(@socket).should be_false
+ expect(File.exist?(@socket)).to be_falsey
end
end
it "removes a single directory" do
rm_r @subdir2
- File.directory?(@subdir2).should be_false
+ expect(File.directory?(@subdir2)).to be_falsey
end
it "recursively removes a directory tree" do
rm_r @topdir
- File.directory?(@topdir).should be_false
+ expect(File.directory?(@topdir)).to be_falsey
end
end
diff --git a/spec/mspec/spec/helpers/io_spec.rb b/spec/mspec/spec/helpers/io_spec.rb
index 19f8384912..14c1a2d6b5 100644
--- a/spec/mspec/spec/helpers/io_spec.rb
+++ b/spec/mspec/spec/helpers/io_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
-describe IOStub do
+RSpec.describe IOStub do
before :each do
@out = IOStub.new
@sep = $\
@@ -14,42 +14,42 @@ describe IOStub do
it "provides a write method" do
@out.write "this"
- @out.should == "this"
+ expect(@out).to eq("this")
end
it "concatenates the arguments sent to write" do
@out.write "flim ", "flam"
- @out.should == "flim flam"
+ expect(@out).to eq("flim flam")
end
it "provides a print method that appends the default separator" do
$\ = " [newline] "
@out.print "hello"
@out.print "world"
- @out.should == "hello [newline] world [newline] "
+ expect(@out).to eq("hello [newline] world [newline] ")
end
it "provides a puts method that appends the default separator" do
@out.puts "hello", 1, 2, 3
- @out.should == "hello\n1\n2\n3\n"
+ expect(@out).to eq("hello\n1\n2\n3\n")
end
it "provides a puts method that appends separator if argument not given" do
@out.puts
- @out.should == "\n"
+ expect(@out).to eq("\n")
end
it "provides a printf method" do
@out.printf "%-10s, %03d, %2.1f", "test", 42, 4.2
- @out.should == "test , 042, 4.2"
+ expect(@out).to eq("test , 042, 4.2")
end
it "provides a flush method that does nothing and returns self" do
- @out.flush.should == @out
+ expect(@out.flush).to eq(@out)
end
end
-describe Object, "#new_fd" do
+RSpec.describe Object, "#new_fd" do
before :each do
@name = tmp("io_specs")
@io = nil
@@ -60,36 +60,36 @@ describe Object, "#new_fd" do
rm_r @name
end
- it "returns a Integer that can be used to create an IO instance" do
+ it "returns an Integer that can be used to create an IO instance" do
fd = new_fd @name
- fd.should be_kind_of(Integer)
+ expect(fd).to be_kind_of(Integer)
@io = IO.new fd, 'w:utf-8'
@io.sync = true
@io.print "io data"
- IO.read(@name).should == "io data"
+ expect(IO.read(@name)).to eq("io data")
end
it "accepts an options Hash" do
- FeatureGuard.stub(:enabled?).and_return(true)
+ allow(FeatureGuard).to receive(:enabled?).and_return(true)
fd = new_fd @name, { :mode => 'w:utf-8' }
- fd.should be_kind_of(Integer)
+ expect(fd).to be_kind_of(Integer)
@io = IO.new fd, 'w:utf-8'
@io.sync = true
@io.print "io data"
- IO.read(@name).should == "io data"
+ expect(IO.read(@name)).to eq("io data")
end
it "raises an ArgumentError if the options Hash does not include :mode" do
- FeatureGuard.stub(:enabled?).and_return(true)
- lambda { new_fd @name, { :encoding => "utf-8" } }.should raise_error(ArgumentError)
+ allow(FeatureGuard).to receive(:enabled?).and_return(true)
+ expect { new_fd @name, { :encoding => "utf-8" } }.to raise_error(ArgumentError)
end
end
-describe Object, "#new_io" do
+RSpec.describe Object, "#new_io" do
before :each do
@name = tmp("io_specs.txt")
end
@@ -101,14 +101,14 @@ describe Object, "#new_io" do
it "returns a File instance" do
@io = new_io @name
- @io.should be_an_instance_of(File)
+ expect(@io).to be_an_instance_of(File)
end
it "opens the IO for reading if passed 'r'" do
touch(@name) { |f| f.print "io data" }
@io = new_io @name, "r"
- @io.read.should == "io data"
- lambda { @io.puts "more data" }.should raise_error(IOError)
+ expect(@io.read).to eq("io data")
+ expect { @io.puts "more data" }.to raise_error(IOError)
end
it "opens the IO for writing if passed 'w'" do
@@ -116,14 +116,14 @@ describe Object, "#new_io" do
@io.sync = true
@io.print "io data"
- IO.read(@name).should == "io data"
+ expect(IO.read(@name)).to eq("io data")
end
it "opens the IO for reading if passed { :mode => 'r' }" do
touch(@name) { |f| f.print "io data" }
@io = new_io @name, { :mode => "r" }
- @io.read.should == "io data"
- lambda { @io.puts "more data" }.should raise_error(IOError)
+ expect(@io.read).to eq("io data")
+ expect { @io.puts "more data" }.to raise_error(IOError)
end
it "opens the IO for writing if passed { :mode => 'w' }" do
@@ -131,6 +131,6 @@ describe Object, "#new_io" do
@io.sync = true
@io.print "io data"
- IO.read(@name).should == "io data"
+ expect(IO.read(@name)).to eq("io data")
end
end
diff --git a/spec/mspec/spec/helpers/mock_to_path_spec.rb b/spec/mspec/spec/helpers/mock_to_path_spec.rb
index 464e7e5440..c2ce985190 100644
--- a/spec/mspec/spec/helpers/mock_to_path_spec.rb
+++ b/spec/mspec/spec/helpers/mock_to_path_spec.rb
@@ -1,17 +1,23 @@
require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
+require 'mspec/mocks'
+
+RSpec.describe Object, "#mock_to_path" do
+ before :each do
+ state = double("run state").as_null_object
+ expect(MSpec).to receive(:current).and_return(state)
+ end
-describe Object, "#mock_to_path" do
it "returns an object that responds to #to_path" do
obj = mock_to_path("foo")
- obj.should be_a(MockObject)
- obj.should respond_to(:to_path)
+ expect(obj).to be_a(MockObject)
+ expect(obj).to respond_to(:to_path)
obj.to_path
end
it "returns the provided path when #to_path is called" do
obj = mock_to_path("/tmp/foo")
- obj.to_path.should == "/tmp/foo"
+ expect(obj.to_path).to eq("/tmp/foo")
end
end
diff --git a/spec/mspec/spec/helpers/numeric_spec.rb b/spec/mspec/spec/helpers/numeric_spec.rb
index 2ea56e5961..64495b7276 100644
--- a/spec/mspec/spec/helpers/numeric_spec.rb
+++ b/spec/mspec/spec/helpers/numeric_spec.rb
@@ -2,24 +2,30 @@ require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
-describe Object, "#bignum_value" do
+RSpec.describe Object, "#bignum_value" do
it "returns a value that is an instance of Bignum on any platform" do
- bignum_value.should == 0x8000_0000_0000_0000
+ expect(bignum_value).to be > fixnum_max
end
it "returns the default value incremented by the argument" do
- bignum_value(42).should == 0x8000_0000_0000_002a
+ expect(bignum_value(42)).to eq(bignum_value + 42)
end
end
-describe Object, "#nan_value" do
+RSpec.describe Object, "-bignum_value" do
+ it "returns a value that is an instance of Bignum on any platform" do
+ expect(-bignum_value).to be < fixnum_min
+ end
+end
+
+RSpec.describe Object, "#nan_value" do
it "returns NaN" do
- nan_value.nan?.should be_true
+ expect(nan_value.nan?).to be_truthy
end
end
-describe Object, "#infinity_value" do
+RSpec.describe Object, "#infinity_value" do
it "returns Infinity" do
- infinity_value.infinite?.should == 1
+ expect(infinity_value.infinite?).to eq(1)
end
end
diff --git a/spec/mspec/spec/helpers/ruby_exe_spec.rb b/spec/mspec/spec/helpers/ruby_exe_spec.rb
index 8036043578..56bade1ba9 100644
--- a/spec/mspec/spec/helpers/ruby_exe_spec.rb
+++ b/spec/mspec/spec/helpers/ruby_exe_spec.rb
@@ -10,7 +10,7 @@ class RubyExeSpecs
public :ruby_exe
end
-describe "#ruby_exe_options" do
+RSpec.describe "#ruby_exe_options" do
before :each do
@ruby_exe_env = ENV['RUBY_EXE']
@script = RubyExeSpecs.new
@@ -22,89 +22,89 @@ describe "#ruby_exe_options" do
it "returns ENV['RUBY_EXE'] when passed :env" do
ENV['RUBY_EXE'] = "kowabunga"
- @script.ruby_exe_options(:env).should == "kowabunga"
+ expect(@script.ruby_exe_options(:env)).to eq("kowabunga")
end
it "returns 'bin/jruby' when passed :engine and RUBY_ENGINE is 'jruby'" do
stub_const "RUBY_ENGINE", 'jruby'
- @script.ruby_exe_options(:engine).should == 'bin/jruby'
+ expect(@script.ruby_exe_options(:engine)).to eq('bin/jruby')
end
it "returns 'bin/rbx' when passed :engine, RUBY_ENGINE is 'rbx'" do
stub_const "RUBY_ENGINE", 'rbx'
- @script.ruby_exe_options(:engine).should == 'bin/rbx'
+ expect(@script.ruby_exe_options(:engine)).to eq('bin/rbx')
end
it "returns 'ir' when passed :engine and RUBY_ENGINE is 'ironruby'" do
stub_const "RUBY_ENGINE", 'ironruby'
- @script.ruby_exe_options(:engine).should == 'ir'
+ expect(@script.ruby_exe_options(:engine)).to eq('ir')
end
it "returns 'maglev-ruby' when passed :engine and RUBY_ENGINE is 'maglev'" do
stub_const "RUBY_ENGINE", 'maglev'
- @script.ruby_exe_options(:engine).should == 'maglev-ruby'
+ expect(@script.ruby_exe_options(:engine)).to eq('maglev-ruby')
end
it "returns 'topaz' when passed :engine and RUBY_ENGINE is 'topaz'" do
stub_const "RUBY_ENGINE", 'topaz'
- @script.ruby_exe_options(:engine).should == 'topaz'
+ expect(@script.ruby_exe_options(:engine)).to eq('topaz')
end
it "returns RUBY_ENGINE + $(EXEEXT) when passed :name" do
bin = RUBY_ENGINE + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
name = File.join ".", bin
- @script.ruby_exe_options(:name).should == name
+ expect(@script.ruby_exe_options(:name)).to eq(name)
end
it "returns $(bindir)/$(RUBY_INSTALL_NAME) + $(EXEEXT) when passed :install_name" do
bin = RbConfig::CONFIG['RUBY_INSTALL_NAME'] + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
name = File.join RbConfig::CONFIG['bindir'], bin
- @script.ruby_exe_options(:install_name).should == name
+ expect(@script.ruby_exe_options(:install_name)).to eq(name)
end
end
-describe "#resolve_ruby_exe" do
+RSpec.describe "#resolve_ruby_exe" do
before :each do
@name = "ruby_spec_exe"
@script = RubyExeSpecs.new
end
it "returns the value returned by #ruby_exe_options if it exists and is executable" do
- @script.should_receive(:ruby_exe_options).and_return(@name)
- File.should_receive(:file?).with(@name).and_return(true)
- File.should_receive(:executable?).with(@name).and_return(true)
- File.should_receive(:expand_path).with(@name).and_return(@name)
- @script.resolve_ruby_exe.should == @name
+ expect(@script).to receive(:ruby_exe_options).and_return(@name)
+ expect(File).to receive(:file?).with(@name).and_return(true)
+ expect(File).to receive(:executable?).with(@name).and_return(true)
+ expect(File).to receive(:expand_path).with(@name).and_return(@name)
+ expect(@script.resolve_ruby_exe).to eq(@name)
end
it "expands the path portion of the result of #ruby_exe_options" do
- @script.should_receive(:ruby_exe_options).and_return("#{@name}")
- File.should_receive(:file?).with(@name).and_return(true)
- File.should_receive(:executable?).with(@name).and_return(true)
- File.should_receive(:expand_path).with(@name).and_return("/usr/bin/#{@name}")
- @script.resolve_ruby_exe.should == "/usr/bin/#{@name}"
+ expect(@script).to receive(:ruby_exe_options).and_return("#{@name}")
+ expect(File).to receive(:file?).with(@name).and_return(true)
+ expect(File).to receive(:executable?).with(@name).and_return(true)
+ expect(File).to receive(:expand_path).with(@name).and_return("/usr/bin/#{@name}")
+ expect(@script.resolve_ruby_exe).to eq("/usr/bin/#{@name}")
end
it "adds the flags after the executable" do
@name = 'bin/rbx'
- @script.should_receive(:ruby_exe_options).and_return(@name)
- File.should_receive(:file?).with(@name).and_return(true)
- File.should_receive(:executable?).with(@name).and_return(true)
- File.should_receive(:expand_path).with(@name).and_return(@name)
+ expect(@script).to receive(:ruby_exe_options).and_return(@name)
+ expect(File).to receive(:file?).with(@name).and_return(true)
+ expect(File).to receive(:executable?).with(@name).and_return(true)
+ expect(File).to receive(:expand_path).with(@name).and_return(@name)
- ENV.should_receive(:[]).with("RUBY_FLAGS").and_return('-X19')
- @script.resolve_ruby_exe.should == 'bin/rbx -X19'
+ expect(ENV).to receive(:[]).with("RUBY_FLAGS").and_return('-X19')
+ expect(@script.resolve_ruby_exe).to eq('bin/rbx -X19')
end
it "raises an exception if no exe is found" do
- File.should_receive(:file?).at_least(:once).and_return(false)
- lambda {
+ expect(File).to receive(:file?).at_least(:once).and_return(false)
+ expect {
@script.resolve_ruby_exe
- }.should raise_error(Exception)
+ }.to raise_error(Exception)
end
end
-describe Object, "#ruby_cmd" do
+RSpec.describe Object, "#ruby_cmd" do
before :each do
stub_const 'RUBY_EXE', 'ruby_spec_exe -w -Q'
@@ -115,83 +115,142 @@ describe Object, "#ruby_cmd" do
end
it "returns a command that runs the given file if it is a file that exists" do
- File.should_receive(:exist?).with(@file).and_return(true)
- @script.ruby_cmd(@file).should == "ruby_spec_exe -w -Q some/ruby/file.rb"
+ expect(File).to receive(:exist?).with(@file).and_return(true)
+ expect(@script.ruby_cmd(@file)).to eq("ruby_spec_exe -w -Q some/ruby/file.rb")
end
it "includes the given options and arguments with a file" do
- File.should_receive(:exist?).with(@file).and_return(true)
- @script.ruby_cmd(@file, :options => "-w -Cdir", :args => "< file.txt").should ==
+ expect(File).to receive(:exist?).with(@file).and_return(true)
+ expect(@script.ruby_cmd(@file, :options => "-w -Cdir", :args => "< file.txt")).to eq(
"ruby_spec_exe -w -Q -w -Cdir some/ruby/file.rb < file.txt"
+ )
end
it "includes the given options and arguments with -e" do
- File.should_receive(:exist?).with(@code).and_return(false)
- @script.ruby_cmd(@code, :options => "-W0 -Cdir", :args => "< file.txt").should ==
+ expect(File).to receive(:exist?).with(@code).and_return(false)
+ expect(@script.ruby_cmd(@code, :options => "-W0 -Cdir", :args => "< file.txt")).to eq(
%(ruby_spec_exe -w -Q -W0 -Cdir -e "some \\"real\\" 'ruby' code" < file.txt)
+ )
end
it "returns a command with options and arguments but without code or file" do
- @script.ruby_cmd(nil, :options => "-c", :args => "> file.txt").should ==
+ expect(@script.ruby_cmd(nil, :options => "-c", :args => "> file.txt")).to eq(
"ruby_spec_exe -w -Q -c > file.txt"
+ )
end
end
-describe Object, "#ruby_exe" do
+RSpec.describe Object, "#ruby_exe" do
before :each do
stub_const 'RUBY_EXE', 'ruby_spec_exe -w -Q'
@script = RubyExeSpecs.new
- @script.stub(:`)
+ allow(IO).to receive(:popen).and_return('OUTPUT')
+
+ status_successful = double(Process::Status, exited?: true, exitstatus: 0)
+ allow(Process).to receive(:last_status).and_return(status_successful)
+ end
+
+ it "returns command STDOUT when given command" do
+ code = "code"
+ options = {}
+ output = "output"
+ expect(IO).to receive(:popen).and_return(output)
+
+ expect(@script.ruby_exe(code, options)).to eq output
end
it "returns an Array containing the interpreter executable and flags when given no arguments" do
- @script.ruby_exe.should == ['ruby_spec_exe', '-w', '-Q']
+ expect(@script.ruby_exe).to eq(['ruby_spec_exe', '-w', '-Q'])
end
it "executes (using `) the result of calling #ruby_cmd with the given arguments" do
code = "code"
options = {}
- @script.should_receive(:ruby_cmd).and_return("ruby_cmd")
- @script.should_receive(:`).with("ruby_cmd")
+ expect(@script).to receive(:ruby_cmd).and_return("ruby_cmd")
+ expect(IO).to receive(:popen).with("ruby_cmd")
@script.ruby_exe(code, options)
end
+ it "raises exception when command exit status is not successful" do
+ code = "code"
+ options = {}
+
+ status_failed = double(Process::Status, exited?: true, exitstatus: 4)
+ allow(Process).to receive(:last_status).and_return(status_failed)
+
+ expect {
+ @script.ruby_exe(code, options)
+ }.to raise_error(%r{Expected exit status is 0 but actual is 4 for command ruby_exe\(.+\)})
+ end
+
+ it "shows in the exception message if a signal killed the process" do
+ code = "code"
+ options = {}
+
+ status_failed = double(Process::Status, exited?: false, signaled?: true, termsig: Signal.list.fetch('TERM'))
+ allow(Process).to receive(:last_status).and_return(status_failed)
+
+ expect {
+ @script.ruby_exe(code, options)
+ }.to raise_error(%r{Expected exit status is 0 but actual is :SIGTERM for command ruby_exe\(.+\)})
+ end
+
describe "with :dir option" do
it "is deprecated" do
- lambda {
+ expect {
@script.ruby_exe nil, :dir => "tmp"
- }.should raise_error(/no longer supported, use Dir\.chdir/)
+ }.to raise_error(/no longer supported, use Dir\.chdir/)
end
end
describe "with :env option" do
it "preserves the values of existing ENV keys" do
ENV["ABC"] = "123"
- ENV.stub(:[])
- ENV.should_receive(:[]).with("ABC")
+ allow(ENV).to receive(:[])
+ expect(ENV).to receive(:[]).with("ABC")
@script.ruby_exe nil, :env => { :ABC => "xyz" }
end
it "adds the :env entries to ENV" do
- ENV.should_receive(:[]=).with("ABC", "xyz")
+ expect(ENV).to receive(:[]=).with("ABC", "xyz")
@script.ruby_exe nil, :env => { :ABC => "xyz" }
end
it "deletes the :env entries in ENV when an exception is raised" do
- ENV.should_receive(:delete).with("XYZ")
+ expect(ENV).to receive(:delete).with("XYZ")
@script.ruby_exe nil, :env => { :XYZ => "xyz" }
end
it "resets the values of existing ENV keys when an exception is raised" do
ENV["ABC"] = "123"
- ENV.should_receive(:[]=).with("ABC", "xyz")
- ENV.should_receive(:[]=).with("ABC", "123")
+ expect(ENV).to receive(:[]=).with("ABC", "xyz")
+ expect(ENV).to receive(:[]=).with("ABC", "123")
- @script.should_receive(:`).and_raise(Exception)
- lambda do
+ expect(IO).to receive(:popen).and_raise(Exception)
+ expect do
@script.ruby_exe nil, :env => { :ABC => "xyz" }
- end.should raise_error(Exception)
+ end.to raise_error(Exception)
+ end
+ end
+
+ describe "with :exit_status option" do
+ before do
+ status_failed = double(Process::Status, exited?: true, exitstatus: 4)
+ allow(Process).to receive(:last_status).and_return(status_failed)
+ end
+
+ it "raises exception when command ends with not expected status" do
+ expect {
+ @script.ruby_exe("path", exit_status: 1)
+ }.to raise_error(%r{Expected exit status is 1 but actual is 4 for command ruby_exe\(.+\)})
+ end
+
+ it "does not raise exception when command ends with expected status" do
+ output = "output"
+ expect(IO).to receive(:popen).and_return(output)
+
+ expect(@script.ruby_exe("path", exit_status: 4)).to eq output
end
end
end
diff --git a/spec/mspec/spec/helpers/scratch_spec.rb b/spec/mspec/spec/helpers/scratch_spec.rb
index 6a9eb2cf73..9dbef94aa3 100644
--- a/spec/mspec/spec/helpers/scratch_spec.rb
+++ b/spec/mspec/spec/helpers/scratch_spec.rb
@@ -2,23 +2,23 @@ require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
-describe ScratchPad do
+RSpec.describe ScratchPad do
it "records an object and returns a previously recorded object" do
ScratchPad.record :this
- ScratchPad.recorded.should == :this
+ expect(ScratchPad.recorded).to eq(:this)
end
it "clears the recorded object" do
ScratchPad.record :that
- ScratchPad.recorded.should == :that
+ expect(ScratchPad.recorded).to eq(:that)
ScratchPad.clear
- ScratchPad.recorded.should == nil
+ expect(ScratchPad.recorded).to eq(nil)
end
it "provides a convenience shortcut to append to a previously recorded object" do
ScratchPad.record []
ScratchPad << :new
ScratchPad << :another
- ScratchPad.recorded.should == [:new, :another]
+ expect(ScratchPad.recorded).to eq([:new, :another])
end
end
diff --git a/spec/mspec/spec/helpers/suppress_warning_spec.rb b/spec/mspec/spec/helpers/suppress_warning_spec.rb
index d16361fa55..4cae189bd3 100644
--- a/spec/mspec/spec/helpers/suppress_warning_spec.rb
+++ b/spec/mspec/spec/helpers/suppress_warning_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
-describe Object, "#suppress_warning" do
+RSpec.describe Object, "#suppress_warning" do
it "hides warnings" do
suppress_warning do
warn "should not be shown"
@@ -14,6 +14,6 @@ describe Object, "#suppress_warning" do
suppress_warning do
a = 1
end
- a.should == 1
+ expect(a).to eq(1)
end
end
diff --git a/spec/mspec/spec/helpers/tmp_spec.rb b/spec/mspec/spec/helpers/tmp_spec.rb
index 4dcf629d94..c41dcd57b3 100644
--- a/spec/mspec/spec/helpers/tmp_spec.rb
+++ b/spec/mspec/spec/helpers/tmp_spec.rb
@@ -2,26 +2,26 @@ require 'spec_helper'
require 'mspec/guards'
require 'mspec/helpers'
-describe Object, "#tmp" do
+RSpec.describe Object, "#tmp" do
before :all do
@dir = SPEC_TEMP_DIR
end
it "returns a name relative to the current working directory" do
- tmp("test.txt").should == "#{@dir}/#{SPEC_TEMP_UNIQUIFIER}-test.txt"
+ expect(tmp("test.txt")).to eq("#{@dir}/#{SPEC_TEMP_UNIQUIFIER}-test.txt")
end
it "returns a 'unique' name on repeated calls" do
a = tmp("text.txt")
b = tmp("text.txt")
- a.should_not == b
+ expect(a).not_to eq(b)
end
it "does not 'uniquify' the name if requested not to" do
- tmp("test.txt", false).should == "#{@dir}/test.txt"
+ expect(tmp("test.txt", false)).to eq("#{@dir}/test.txt")
end
it "returns the name of the temporary directory when passed an empty string" do
- tmp("").should == "#{@dir}/"
+ expect(tmp("")).to eq("#{@dir}/")
end
end
diff --git a/spec/mspec/spec/integration/interpreter_spec.rb b/spec/mspec/spec/integration/interpreter_spec.rb
index b6fa6859d1..dbf3987a08 100644
--- a/spec/mspec/spec/integration/interpreter_spec.rb
+++ b/spec/mspec/spec/integration/interpreter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe "The interpreter passed with -t" do
+RSpec.describe "The interpreter passed with -t" do
it "is used in subprocess" do
fixtures = "spec/fixtures"
interpreter = "#{fixtures}/my_ruby"
@@ -8,11 +8,11 @@ describe "The interpreter passed with -t" do
out = out.lines.map(&:chomp).reject { |line|
line == 'RUBY_DESCRIPTION'
}.take(3)
- out.should == [
+ expect(out).to eq([
interpreter,
interpreter,
"CWD/#{interpreter}"
- ]
- ret.success?.should == true
+ ])
+ expect(ret.success?).to eq(true)
end
end
diff --git a/spec/mspec/spec/integration/object_methods_spec.rb b/spec/mspec/spec/integration/object_methods_spec.rb
index 3be704beb6..697fbd10fa 100644
--- a/spec/mspec/spec/integration/object_methods_spec.rb
+++ b/spec/mspec/spec/integration/object_methods_spec.rb
@@ -9,10 +9,10 @@ Finished in D.DDDDDD seconds
1 file, 1 example, 1 expectation, 0 failures, 0 errors, 0 tagged
EOS
-describe "MSpec" do
+RSpec.describe "MSpec" do
it "does not define public methods on Object" do
out, ret = run_mspec("run", "spec/fixtures/object_methods_spec.rb")
- out.should == expected_output
- ret.success?.should == true
+ expect(out).to eq(expected_output)
+ expect(ret.success?).to eq(true)
end
end
diff --git a/spec/mspec/spec/integration/run_spec.rb b/spec/mspec/spec/integration/run_spec.rb
index df10f77094..ea0735e9b2 100644
--- a/spec/mspec/spec/integration/run_spec.rb
+++ b/spec/mspec/spec/integration/run_spec.rb
@@ -1,22 +1,21 @@
require 'spec_helper'
-describe "Running mspec" do
+RSpec.describe "Running mspec" do
+ q = BACKTRACE_QUOTE
a_spec_output = <<EOS
1)
Foo#bar errors FAILED
Expected 1 == 2
to be truthy but was false
-CWD/spec/fixtures/a_spec.rb:8:in `block (2 levels) in <top (required)>'
-CWD/spec/fixtures/a_spec.rb:2:in `<top (required)>'
-CWD/bin/mspec-run:7:in `<main>'
+CWD/spec/fixtures/a_spec.rb:8:in #{q}block (2 levels) in <top (required)>'
+CWD/spec/fixtures/a_spec.rb:2:in #{q}<top (required)>'
2)
Foo#bar fails ERROR
RuntimeError: failure
-CWD/spec/fixtures/a_spec.rb:12:in `block (2 levels) in <top (required)>'
-CWD/spec/fixtures/a_spec.rb:2:in `<top (required)>'
-CWD/bin/mspec-run:7:in `<main>'
+CWD/spec/fixtures/a_spec.rb:12:in #{q}block (2 levels) in <top (required)>'
+CWD/spec/fixtures/a_spec.rb:2:in #{q}<top (required)>'
Finished in D.DDDDDD seconds
EOS
@@ -27,20 +26,20 @@ EOS
it "runs the specs" do
out, ret = run_mspec("run", "#{fixtures}/a_spec.rb")
- out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}"
- ret.success?.should == false
+ expect(out).to eq("RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}")
+ expect(ret.success?).to eq(false)
end
it "directly with mspec-run runs the specs" do
out, ret = run_mspec("-run", "#{fixtures}/a_spec.rb")
- out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}"
- ret.success?.should == false
+ expect(out).to eq("RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}")
+ expect(ret.success?).to eq(false)
end
it "runs the specs in parallel with -j using the dotted formatter" do
out, ret = run_mspec("run", "-j #{fixtures}/a_spec.rb #{fixtures}/b_spec.rb")
- out.should == "RUBY_DESCRIPTION\n...\n#{a_spec_output}\n#{ab_stats}"
- ret.success?.should == false
+ expect(out).to eq("RUBY_DESCRIPTION\n...\n#{a_spec_output}\n#{ab_stats}")
+ expect(ret.success?).to eq(false)
end
it "runs the specs in parallel with -j -fa" do
@@ -49,25 +48,25 @@ EOS
"\r[/ | 0% | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " +
"\r[- | ==================50% | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " +
"\r[\\ | ==================100%================== | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m "
- out.should == "RUBY_DESCRIPTION\n#{progress_bar}\n#{a_spec_output}\n#{ab_stats}"
- ret.success?.should == false
+ expect(out).to eq("RUBY_DESCRIPTION\n#{progress_bar}\n#{a_spec_output}\n#{ab_stats}")
+ expect(ret.success?).to eq(false)
end
it "gives a useful error message when a subprocess dies in parallel mode" do
out, ret = run_mspec("run", "-j #{fixtures}/b_spec.rb #{fixtures}/die_spec.rb")
lines = out.lines
- lines.should include "A child mspec-run process died unexpectedly while running CWD/spec/fixtures/die_spec.rb\n"
- lines.should include "Finished in D.DDDDDD seconds\n"
- lines.last.should =~ /^\d files?, \d examples?, \d expectations?, 0 failures, 0 errors, 0 tagged$/
- ret.success?.should == false
+ expect(lines).to include "A child mspec-run process died unexpectedly while running CWD/spec/fixtures/die_spec.rb\n"
+ expect(lines).to include "Finished in D.DDDDDD seconds\n"
+ expect(lines.last).to match(/^\d files?, \d examples?, \d expectations?, 0 failures, 0 errors, 0 tagged$/)
+ expect(ret.success?).to eq(false)
end
it "gives a useful error message when a subprocess prints unexpected output on STDOUT in parallel mode" do
out, ret = run_mspec("run", "-j #{fixtures}/b_spec.rb #{fixtures}/chatty_spec.rb")
lines = out.lines
- lines.should include "A child mspec-run process printed unexpected output on STDOUT: #{'"Hello\nIt\'s me!\n"'} while running CWD/spec/fixtures/chatty_spec.rb\n"
- lines.should include "Finished in D.DDDDDD seconds\n"
- lines.last.should == "2 files, 2 examples, 2 expectations, 0 failures, 0 errors, 0 tagged\n"
- ret.success?.should == false
+ expect(lines).to include "A child mspec-run process printed unexpected output on STDOUT: #{'"Hello\nIt\'s me!\n"'} while running CWD/spec/fixtures/chatty_spec.rb\n"
+ expect(lines).to include "Finished in D.DDDDDD seconds\n"
+ expect(lines.last).to eq("2 files, 2 examples, 2 expectations, 0 failures, 0 errors, 0 tagged\n")
+ expect(ret.success?).to eq(false)
end
end
diff --git a/spec/mspec/spec/integration/tag_spec.rb b/spec/mspec/spec/integration/tag_spec.rb
index 1882d71e32..ae08e9d45f 100644
--- a/spec/mspec/spec/integration/tag_spec.rb
+++ b/spec/mspec/spec/integration/tag_spec.rb
@@ -1,7 +1,7 @@
# encoding: utf-8
require 'spec_helper'
-describe "Running mspec tag" do
+RSpec.describe "Running mspec tag" do
before :all do
FileUtils.rm_rf 'spec/fixtures/tags'
end
@@ -13,7 +13,8 @@ describe "Running mspec tag" do
it "tags the failing specs" do
fixtures = "spec/fixtures"
out, ret = run_mspec("tag", "--add fails --fail #{fixtures}/tagging_spec.rb")
- out.should == <<EOS
+ q = BACKTRACE_QUOTE
+ expect(out).to eq <<EOS
RUBY_DESCRIPTION
.FF
TagAction: specs tagged with 'fails':
@@ -26,29 +27,27 @@ Tag#me érròrs in unicode
Tag#me errors FAILED
Expected 1 == 2
to be truthy but was false
-CWD/spec/fixtures/tagging_spec.rb:9:in `block (2 levels) in <top (required)>'
-CWD/spec/fixtures/tagging_spec.rb:3:in `<top (required)>'
-CWD/bin/mspec-tag:7:in `<main>'
+CWD/spec/fixtures/tagging_spec.rb:9:in #{q}block (2 levels) in <top (required)>'
+CWD/spec/fixtures/tagging_spec.rb:3:in #{q}<top (required)>'
2)
Tag#me érròrs in unicode FAILED
Expected 1 == 2
to be truthy but was false
-CWD/spec/fixtures/tagging_spec.rb:13:in `block (2 levels) in <top (required)>'
-CWD/spec/fixtures/tagging_spec.rb:3:in `<top (required)>'
-CWD/bin/mspec-tag:7:in `<main>'
+CWD/spec/fixtures/tagging_spec.rb:13:in #{q}block (2 levels) in <top (required)>'
+CWD/spec/fixtures/tagging_spec.rb:3:in #{q}<top (required)>'
Finished in D.DDDDDD seconds
1 file, 3 examples, 3 expectations, 2 failures, 0 errors, 0 tagged
EOS
- ret.success?.should == false
+ expect(ret.success?).to eq(false)
end
it "does not run already tagged specs" do
fixtures = "spec/fixtures"
out, ret = run_mspec("run", "--excl-tag fails #{fixtures}/tagging_spec.rb")
- out.should == <<EOS
+ expect(out).to eq <<EOS
RUBY_DESCRIPTION
.
@@ -56,6 +55,6 @@ Finished in D.DDDDDD seconds
1 file, 3 examples, 1 expectation, 0 failures, 0 errors, 2 tagged
EOS
- ret.success?.should == true
+ expect(ret.success?).to eq(true)
end
end
diff --git a/spec/mspec/spec/matchers/base_spec.rb b/spec/mspec/spec/matchers/base_spec.rb
index 762822bf09..6b5a3fbd72 100644
--- a/spec/mspec/spec/matchers/base_spec.rb
+++ b/spec/mspec/spec/matchers/base_spec.rb
@@ -3,11 +3,11 @@ require 'mspec/expectations/expectations'
require 'mspec/matchers'
require 'time'
-describe SpecPositiveOperatorMatcher, "== operator" do
+RSpec.describe SpecPositiveOperatorMatcher, "== operator" do
it "provides a failure message that 'Expected x to equal y'" do
- lambda {
+ expect {
SpecPositiveOperatorMatcher.new(1) == 2
- }.should raise_error(SpecExpectationNotMetError, "Expected 1 == 2\nto be truthy but was false")
+ }.to raise_error(SpecExpectationNotMetError, "Expected 1 == 2\nto be truthy but was false")
end
it "does not raise an exception when == returns true" do
@@ -15,11 +15,11 @@ describe SpecPositiveOperatorMatcher, "== operator" do
end
end
-describe SpecPositiveOperatorMatcher, "=~ operator" do
+RSpec.describe SpecPositiveOperatorMatcher, "=~ operator" do
it "provides a failure message that 'Expected \"x\" to match y'" do
- lambda {
+ expect {
SpecPositiveOperatorMatcher.new('real') =~ /fake/
- }.should raise_error(SpecExpectationNotMetError, "Expected \"real\" =~ /fake/\nto be truthy but was nil")
+ }.to raise_error(SpecExpectationNotMetError, "Expected \"real\" =~ /fake/\nto be truthy but was nil")
end
it "does not raise an exception when =~ returns true" do
@@ -27,11 +27,11 @@ describe SpecPositiveOperatorMatcher, "=~ operator" do
end
end
-describe SpecPositiveOperatorMatcher, "> operator" do
+RSpec.describe SpecPositiveOperatorMatcher, "> operator" do
it "provides a failure message that 'Expected x to be greater than y'" do
- lambda {
+ expect {
SpecPositiveOperatorMatcher.new(4) > 5
- }.should raise_error(SpecExpectationNotMetError, "Expected 4 > 5\nto be truthy but was false")
+ }.to raise_error(SpecExpectationNotMetError, "Expected 4 > 5\nto be truthy but was false")
end
it "does not raise an exception when > returns true" do
@@ -39,11 +39,11 @@ describe SpecPositiveOperatorMatcher, "> operator" do
end
end
-describe SpecPositiveOperatorMatcher, ">= operator" do
+RSpec.describe SpecPositiveOperatorMatcher, ">= operator" do
it "provides a failure message that 'Expected x to be greater than or equal to y'" do
- lambda {
+ expect {
SpecPositiveOperatorMatcher.new(4) >= 5
- }.should raise_error(SpecExpectationNotMetError, "Expected 4 >= 5\nto be truthy but was false")
+ }.to raise_error(SpecExpectationNotMetError, "Expected 4 >= 5\nto be truthy but was false")
end
it "does not raise an exception when > returns true" do
@@ -52,11 +52,11 @@ describe SpecPositiveOperatorMatcher, ">= operator" do
end
end
-describe SpecPositiveOperatorMatcher, "< operator" do
+RSpec.describe SpecPositiveOperatorMatcher, "< operator" do
it "provides a failure message that 'Expected x to be less than y'" do
- lambda {
+ expect {
SpecPositiveOperatorMatcher.new(5) < 4
- }.should raise_error(SpecExpectationNotMetError, "Expected 5 < 4\nto be truthy but was false")
+ }.to raise_error(SpecExpectationNotMetError, "Expected 5 < 4\nto be truthy but was false")
end
it "does not raise an exception when < returns true" do
@@ -64,11 +64,11 @@ describe SpecPositiveOperatorMatcher, "< operator" do
end
end
-describe SpecPositiveOperatorMatcher, "<= operator" do
+RSpec.describe SpecPositiveOperatorMatcher, "<= operator" do
it "provides a failure message that 'Expected x to be less than or equal to y'" do
- lambda {
+ expect {
SpecPositiveOperatorMatcher.new(5) <= 4
- }.should raise_error(SpecExpectationNotMetError, "Expected 5 <= 4\nto be truthy but was false")
+ }.to raise_error(SpecExpectationNotMetError, "Expected 5 <= 4\nto be truthy but was false")
end
it "does not raise an exception when < returns true" do
@@ -77,7 +77,7 @@ describe SpecPositiveOperatorMatcher, "<= operator" do
end
end
-describe SpecPositiveOperatorMatcher, "arbitrary predicates" do
+RSpec.describe SpecPositiveOperatorMatcher, "arbitrary predicates" do
it "do not raise an exception when the predicate is truthy" do
SpecPositiveOperatorMatcher.new(2).eql?(2)
SpecPositiveOperatorMatcher.new(2).equal?(2)
@@ -89,31 +89,31 @@ describe SpecPositiveOperatorMatcher, "arbitrary predicates" do
end
it "provide a failure message when the predicate returns a falsy value" do
- lambda {
+ expect {
SpecPositiveOperatorMatcher.new(2).eql?(3)
- }.should raise_error(SpecExpectationNotMetError, "Expected 2.eql? 3\nto be truthy but was false")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected 2.eql? 3\nto be truthy but was false")
+ expect {
SpecPositiveOperatorMatcher.new(2).equal?(3)
- }.should raise_error(SpecExpectationNotMetError, "Expected 2.equal? 3\nto be truthy but was false")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected 2.equal? 3\nto be truthy but was false")
+ expect {
SpecPositiveOperatorMatcher.new([1, 2, 3]).include?(4)
- }.should raise_error(SpecExpectationNotMetError, "Expected [1, 2, 3].include? 4\nto be truthy but was false")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected [1, 2, 3].include? 4\nto be truthy but was false")
+ expect {
SpecPositiveOperatorMatcher.new("abc").start_with?("de")
- }.should raise_error(SpecExpectationNotMetError, "Expected \"abc\".start_with? \"de\"\nto be truthy but was false")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected \"abc\".start_with? \"de\"\nto be truthy but was false")
+ expect {
SpecPositiveOperatorMatcher.new("abc").start_with?("d", "e")
- }.should raise_error(SpecExpectationNotMetError, "Expected \"abc\".start_with? \"d\", \"e\"\nto be truthy but was false")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected \"abc\".start_with? \"d\", \"e\"\nto be truthy but was false")
+ expect {
SpecPositiveOperatorMatcher.new(2).odd?
- }.should raise_error(SpecExpectationNotMetError, "Expected 2.odd?\nto be truthy but was false")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected 2.odd?\nto be truthy but was false")
+ expect {
SpecPositiveOperatorMatcher.new([1, 3]).any? { |e| e.even? }
- }.should raise_error(SpecExpectationNotMetError, "Expected [1, 3].any? { ... }\nto be truthy but was false")
+ }.to raise_error(SpecExpectationNotMetError, "Expected [1, 3].any? { ... }\nto be truthy but was false")
end
end
-describe SpecNegativeOperatorMatcher, "arbitrary predicates" do
+RSpec.describe SpecNegativeOperatorMatcher, "arbitrary predicates" do
it "do not raise an exception when the predicate returns a falsy value" do
SpecNegativeOperatorMatcher.new(2).eql?(3)
SpecNegativeOperatorMatcher.new(2).equal?(3)
@@ -125,35 +125,35 @@ describe SpecNegativeOperatorMatcher, "arbitrary predicates" do
end
it "provide a failure message when the predicate returns a truthy value" do
- lambda {
+ expect {
SpecNegativeOperatorMatcher.new(2).eql?(2)
- }.should raise_error(SpecExpectationNotMetError, "Expected 2.eql? 2\nto be falsy but was true")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected 2.eql? 2\nto be falsy but was true")
+ expect {
SpecNegativeOperatorMatcher.new(2).equal?(2)
- }.should raise_error(SpecExpectationNotMetError, "Expected 2.equal? 2\nto be falsy but was true")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected 2.equal? 2\nto be falsy but was true")
+ expect {
SpecNegativeOperatorMatcher.new([1, 2, 3]).include?(2)
- }.should raise_error(SpecExpectationNotMetError, "Expected [1, 2, 3].include? 2\nto be falsy but was true")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected [1, 2, 3].include? 2\nto be falsy but was true")
+ expect {
SpecNegativeOperatorMatcher.new("abc").start_with?("ab")
- }.should raise_error(SpecExpectationNotMetError, "Expected \"abc\".start_with? \"ab\"\nto be falsy but was true")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected \"abc\".start_with? \"ab\"\nto be falsy but was true")
+ expect {
SpecNegativeOperatorMatcher.new("abc").start_with?("d", "a")
- }.should raise_error(SpecExpectationNotMetError, "Expected \"abc\".start_with? \"d\", \"a\"\nto be falsy but was true")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected \"abc\".start_with? \"d\", \"a\"\nto be falsy but was true")
+ expect {
SpecNegativeOperatorMatcher.new(3).odd?
- }.should raise_error(SpecExpectationNotMetError, "Expected 3.odd?\nto be falsy but was true")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected 3.odd?\nto be falsy but was true")
+ expect {
SpecNegativeOperatorMatcher.new([1, 2]).any? { |e| e.even? }
- }.should raise_error(SpecExpectationNotMetError, "Expected [1, 2].any? { ... }\nto be falsy but was true")
+ }.to raise_error(SpecExpectationNotMetError, "Expected [1, 2].any? { ... }\nto be falsy but was true")
end
end
-describe SpecNegativeOperatorMatcher, "== operator" do
+RSpec.describe SpecNegativeOperatorMatcher, "== operator" do
it "provides a failure message that 'Expected x not to equal y'" do
- lambda {
+ expect {
SpecNegativeOperatorMatcher.new(1) == 1
- }.should raise_error(SpecExpectationNotMetError, "Expected 1 == 1\nto be falsy but was true")
+ }.to raise_error(SpecExpectationNotMetError, "Expected 1 == 1\nto be falsy but was true")
end
it "does not raise an exception when == returns false" do
@@ -161,11 +161,11 @@ describe SpecNegativeOperatorMatcher, "== operator" do
end
end
-describe SpecNegativeOperatorMatcher, "=~ operator" do
+RSpec.describe SpecNegativeOperatorMatcher, "=~ operator" do
it "provides a failure message that 'Expected \"x\" not to match /y/'" do
- lambda {
+ expect {
SpecNegativeOperatorMatcher.new('real') =~ /real/
- }.should raise_error(SpecExpectationNotMetError, "Expected \"real\" =~ /real/\nto be falsy but was 0")
+ }.to raise_error(SpecExpectationNotMetError, "Expected \"real\" =~ /real/\nto be falsy but was 0")
end
it "does not raise an exception when =~ returns false" do
@@ -173,11 +173,11 @@ describe SpecNegativeOperatorMatcher, "=~ operator" do
end
end
-describe SpecNegativeOperatorMatcher, "< operator" do
+RSpec.describe SpecNegativeOperatorMatcher, "< operator" do
it "provides a failure message that 'Expected x not to be less than y'" do
- lambda {
+ expect {
SpecNegativeOperatorMatcher.new(4) < 5
- }.should raise_error(SpecExpectationNotMetError, "Expected 4 < 5\nto be falsy but was true")
+ }.to raise_error(SpecExpectationNotMetError, "Expected 4 < 5\nto be falsy but was true")
end
it "does not raise an exception when < returns false" do
@@ -185,14 +185,14 @@ describe SpecNegativeOperatorMatcher, "< operator" do
end
end
-describe SpecNegativeOperatorMatcher, "<= operator" do
+RSpec.describe SpecNegativeOperatorMatcher, "<= operator" do
it "provides a failure message that 'Expected x not to be less than or equal to y'" do
- lambda {
+ expect {
SpecNegativeOperatorMatcher.new(4) <= 5
- }.should raise_error(SpecExpectationNotMetError, "Expected 4 <= 5\nto be falsy but was true")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected 4 <= 5\nto be falsy but was true")
+ expect {
SpecNegativeOperatorMatcher.new(5) <= 5
- }.should raise_error(SpecExpectationNotMetError, "Expected 5 <= 5\nto be falsy but was true")
+ }.to raise_error(SpecExpectationNotMetError, "Expected 5 <= 5\nto be falsy but was true")
end
it "does not raise an exception when <= returns false" do
@@ -200,11 +200,11 @@ describe SpecNegativeOperatorMatcher, "<= operator" do
end
end
-describe SpecNegativeOperatorMatcher, "> operator" do
+RSpec.describe SpecNegativeOperatorMatcher, "> operator" do
it "provides a failure message that 'Expected x not to be greater than y'" do
- lambda {
+ expect {
SpecNegativeOperatorMatcher.new(5) > 4
- }.should raise_error(SpecExpectationNotMetError, "Expected 5 > 4\nto be falsy but was true")
+ }.to raise_error(SpecExpectationNotMetError, "Expected 5 > 4\nto be falsy but was true")
end
it "does not raise an exception when > returns false" do
@@ -212,14 +212,14 @@ describe SpecNegativeOperatorMatcher, "> operator" do
end
end
-describe SpecNegativeOperatorMatcher, ">= operator" do
+RSpec.describe SpecNegativeOperatorMatcher, ">= operator" do
it "provides a failure message that 'Expected x not to be greater than or equal to y'" do
- lambda {
+ expect {
SpecNegativeOperatorMatcher.new(5) >= 4
- }.should raise_error(SpecExpectationNotMetError, "Expected 5 >= 4\nto be falsy but was true")
- lambda {
+ }.to raise_error(SpecExpectationNotMetError, "Expected 5 >= 4\nto be falsy but was true")
+ expect {
SpecNegativeOperatorMatcher.new(5) >= 5
- }.should raise_error(SpecExpectationNotMetError, "Expected 5 >= 5\nto be falsy but was true")
+ }.to raise_error(SpecExpectationNotMetError, "Expected 5 >= 5\nto be falsy but was true")
end
it "does not raise an exception when >= returns false" do
diff --git a/spec/mspec/spec/matchers/be_an_instance_of_spec.rb b/spec/mspec/spec/matchers/be_an_instance_of_spec.rb
index 7f2126df7d..7c74249d24 100644
--- a/spec/mspec/spec/matchers/be_an_instance_of_spec.rb
+++ b/spec/mspec/spec/matchers/be_an_instance_of_spec.rb
@@ -13,38 +13,38 @@ module BeAnInOfSpecs
end
end
-describe BeAnInstanceOfMatcher do
+RSpec.describe BeAnInstanceOfMatcher do
it "matches when actual is an instance_of? expected" do
a = BeAnInOfSpecs::A.new
- BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(a).should be_true
+ expect(BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(a)).to be_truthy
b = BeAnInOfSpecs::B.new
- BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(b).should be_true
+ expect(BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(b)).to be_truthy
end
it "does not match when actual is not an instance_of? expected" do
a = BeAnInOfSpecs::A.new
- BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(a).should be_false
+ expect(BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(a)).to be_falsey
b = BeAnInOfSpecs::B.new
- BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(b).should be_false
+ expect(BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(b)).to be_falsey
c = BeAnInOfSpecs::C.new
- BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(c).should be_false
- BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(c).should be_false
+ expect(BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(c)).to be_falsey
+ expect(BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(c)).to be_falsey
end
it "provides a useful failure message" do
matcher = BeAnInstanceOfMatcher.new(Numeric)
matcher.matches?("string")
- matcher.failure_message.should == [
- "Expected \"string\" (String)", "to be an instance of Numeric"]
+ expect(matcher.failure_message).to eq([
+ "Expected \"string\" (String)", "to be an instance of Numeric"])
end
it "provides a useful negative failure message" do
matcher = BeAnInstanceOfMatcher.new(Numeric)
matcher.matches?(4.0)
- matcher.negative_failure_message.should == [
- "Expected 4.0 (Float)", "not to be an instance of Numeric"]
+ expect(matcher.negative_failure_message).to eq([
+ "Expected 4.0 (Float)", "not to be an instance of Numeric"])
end
end
diff --git a/spec/mspec/spec/matchers/be_ancestor_of_spec.rb b/spec/mspec/spec/matchers/be_ancestor_of_spec.rb
index c6bd1a26c7..abc05e0f7a 100644
--- a/spec/mspec/spec/matchers/be_ancestor_of_spec.rb
+++ b/spec/mspec/spec/matchers/be_ancestor_of_spec.rb
@@ -5,24 +5,24 @@ require 'mspec/matchers'
class Parent; end
class Child < Parent; end
-describe BeAncestorOfMatcher do
+RSpec.describe BeAncestorOfMatcher do
it "matches when actual is an ancestor of expected" do
- BeAncestorOfMatcher.new(Child).matches?(Parent).should == true
+ expect(BeAncestorOfMatcher.new(Child).matches?(Parent)).to eq(true)
end
it "does not match when actual is not an ancestor of expected" do
- BeAncestorOfMatcher.new(Parent).matches?(Child).should == false
+ expect(BeAncestorOfMatcher.new(Parent).matches?(Child)).to eq(false)
end
it "provides a useful failure message" do
matcher = BeAncestorOfMatcher.new(Parent)
matcher.matches?(Child)
- matcher.failure_message.should == ["Expected Child", "to be an ancestor of Parent"]
+ expect(matcher.failure_message).to eq(["Expected Child", "to be an ancestor of Parent"])
end
it "provides a useful negative failure message" do
matcher = BeAncestorOfMatcher.new(Child)
matcher.matches?(Parent)
- matcher.negative_failure_message.should == ["Expected Parent", "not to be an ancestor of Child"]
+ expect(matcher.negative_failure_message).to eq(["Expected Parent", "not to be an ancestor of Child"])
end
end
diff --git a/spec/mspec/spec/matchers/be_close_spec.rb b/spec/mspec/spec/matchers/be_close_spec.rb
index 6edff98e4a..dfd4f4ddbb 100644
--- a/spec/mspec/spec/matchers/be_close_spec.rb
+++ b/spec/mspec/spec/matchers/be_close_spec.rb
@@ -3,46 +3,46 @@ require 'mspec/expectations/expectations'
require 'mspec/matchers'
# Adapted from RSpec 1.0.8
-describe BeCloseMatcher do
+RSpec.describe BeCloseMatcher do
it "matches when actual == expected" do
- BeCloseMatcher.new(5.0, 0.5).matches?(5.0).should == true
+ expect(BeCloseMatcher.new(5.0, 0.5).matches?(5.0)).to eq(true)
end
it "matches when actual < (expected + tolerance)" do
- BeCloseMatcher.new(5.0, 0.5).matches?(5.49).should == true
+ expect(BeCloseMatcher.new(5.0, 0.5).matches?(5.49)).to eq(true)
end
it "matches when actual > (expected - tolerance)" do
- BeCloseMatcher.new(5.0, 0.5).matches?(4.51).should == true
+ expect(BeCloseMatcher.new(5.0, 0.5).matches?(4.51)).to eq(true)
end
it "matches when actual == (expected + tolerance)" do
- BeCloseMatcher.new(5.0, 0.5).matches?(5.5).should == true
- BeCloseMatcher.new(3, 2).matches?(5).should == true
+ expect(BeCloseMatcher.new(5.0, 0.5).matches?(5.5)).to eq(true)
+ expect(BeCloseMatcher.new(3, 2).matches?(5)).to eq(true)
end
it "matches when actual == (expected - tolerance)" do
- BeCloseMatcher.new(5.0, 0.5).matches?(4.5).should == true
- BeCloseMatcher.new(3, 2).matches?(1).should == true
+ expect(BeCloseMatcher.new(5.0, 0.5).matches?(4.5)).to eq(true)
+ expect(BeCloseMatcher.new(3, 2).matches?(1)).to eq(true)
end
it "does not match when actual < (expected - tolerance)" do
- BeCloseMatcher.new(5.0, 0.5).matches?(4.49).should == false
+ expect(BeCloseMatcher.new(5.0, 0.5).matches?(4.49)).to eq(false)
end
it "does not match when actual > (expected + tolerance)" do
- BeCloseMatcher.new(5.0, 0.5).matches?(5.51).should == false
+ expect(BeCloseMatcher.new(5.0, 0.5).matches?(5.51)).to eq(false)
end
it "provides a useful failure message" do
matcher = BeCloseMatcher.new(5.0, 0.5)
matcher.matches?(6.5)
- matcher.failure_message.should == ["Expected 6.5", "to be within 5.0 +/- 0.5"]
+ expect(matcher.failure_message).to eq(["Expected 6.5", "to be within 5.0 +/- 0.5"])
end
it "provides a useful negative failure message" do
matcher = BeCloseMatcher.new(5.0, 0.5)
matcher.matches?(4.9)
- matcher.negative_failure_message.should == ["Expected 4.9", "not to be within 5.0 +/- 0.5"]
+ expect(matcher.negative_failure_message).to eq(["Expected 4.9", "not to be within 5.0 +/- 0.5"])
end
end
diff --git a/spec/mspec/spec/matchers/be_computed_by_spec.rb b/spec/mspec/spec/matchers/be_computed_by_spec.rb
index 9833e211a4..f73861a576 100644
--- a/spec/mspec/spec/matchers/be_computed_by_spec.rb
+++ b/spec/mspec/spec/matchers/be_computed_by_spec.rb
@@ -1,35 +1,35 @@
require 'spec_helper'
require 'mspec/matchers'
-describe BeComputedByMatcher do
+RSpec.describe BeComputedByMatcher do
it "matches when all entries in the Array compute" do
array = [ [65, "A"],
[90, "Z"] ]
- BeComputedByMatcher.new(:chr).matches?(array).should be_true
+ expect(BeComputedByMatcher.new(:chr).matches?(array)).to be_truthy
end
it "matches when all entries in the Array with arguments compute" do
array = [ [1, 2, 3],
[2, 4, 6] ]
- BeComputedByMatcher.new(:+).matches?(array).should be_true
+ expect(BeComputedByMatcher.new(:+).matches?(array)).to be_truthy
end
it "does not match when any entry in the Array does not compute" do
array = [ [65, "A" ],
[91, "Z" ] ]
- BeComputedByMatcher.new(:chr).matches?(array).should be_false
+ expect(BeComputedByMatcher.new(:chr).matches?(array)).to be_falsey
end
it "accepts an argument list to apply to each method call" do
array = [ [65, "1000001" ],
[90, "1011010" ] ]
- BeComputedByMatcher.new(:to_s, 2).matches?(array).should be_true
+ expect(BeComputedByMatcher.new(:to_s, 2).matches?(array)).to be_truthy
end
it "does not match when any entry in the Array with arguments does not compute" do
array = [ [1, 2, 3],
[2, 4, 7] ]
- BeComputedByMatcher.new(:+).matches?(array).should be_false
+ expect(BeComputedByMatcher.new(:+).matches?(array)).to be_falsey
end
it "provides a useful failure message" do
@@ -37,6 +37,6 @@ describe BeComputedByMatcher do
[91, "Z" ] ]
matcher = BeComputedByMatcher.new(:chr)
matcher.matches?(array)
- matcher.failure_message.should == ["Expected \"Z\"", "to be computed by 91.chr (computed \"[\" instead)"]
+ expect(matcher.failure_message).to eq(["Expected \"Z\"", "to be computed by 91.chr (computed \"[\" instead)"])
end
end
diff --git a/spec/mspec/spec/matchers/be_empty_spec.rb b/spec/mspec/spec/matchers/be_empty_spec.rb
index cb8663f5ee..30678fe85f 100644
--- a/spec/mspec/spec/matchers/be_empty_spec.rb
+++ b/spec/mspec/spec/matchers/be_empty_spec.rb
@@ -2,25 +2,25 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe BeEmptyMatcher do
+RSpec.describe BeEmptyMatcher do
it "matches when actual is empty" do
- BeEmptyMatcher.new.matches?("").should == true
+ expect(BeEmptyMatcher.new.matches?("")).to eq(true)
end
it "does not match when actual is not empty" do
- BeEmptyMatcher.new.matches?([10]).should == false
+ expect(BeEmptyMatcher.new.matches?([10])).to eq(false)
end
it "provides a useful failure message" do
matcher = BeEmptyMatcher.new
matcher.matches?("not empty string")
- matcher.failure_message.should == ["Expected \"not empty string\"", "to be empty"]
+ expect(matcher.failure_message).to eq(["Expected \"not empty string\"", "to be empty"])
end
it "provides a useful negative failure message" do
matcher = BeEmptyMatcher.new
matcher.matches?("")
- matcher.negative_failure_message.should == ["Expected \"\"", "not to be empty"]
+ expect(matcher.negative_failure_message).to eq(["Expected \"\"", "not to be empty"])
end
end
diff --git a/spec/mspec/spec/matchers/be_false_spec.rb b/spec/mspec/spec/matchers/be_false_spec.rb
index 31afd24ebc..46d7253220 100644
--- a/spec/mspec/spec/matchers/be_false_spec.rb
+++ b/spec/mspec/spec/matchers/be_false_spec.rb
@@ -2,27 +2,27 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe BeFalseMatcher do
+RSpec.describe BeFalseMatcher do
it "matches when actual is false" do
- BeFalseMatcher.new.matches?(false).should == true
+ expect(BeFalseMatcher.new.matches?(false)).to eq(true)
end
it "does not match when actual is not false" do
- BeFalseMatcher.new.matches?("").should == false
- BeFalseMatcher.new.matches?(true).should == false
- BeFalseMatcher.new.matches?(nil).should == false
- BeFalseMatcher.new.matches?(0).should == false
+ expect(BeFalseMatcher.new.matches?("")).to eq(false)
+ expect(BeFalseMatcher.new.matches?(true)).to eq(false)
+ expect(BeFalseMatcher.new.matches?(nil)).to eq(false)
+ expect(BeFalseMatcher.new.matches?(0)).to eq(false)
end
it "provides a useful failure message" do
matcher = BeFalseMatcher.new
matcher.matches?("some string")
- matcher.failure_message.should == ["Expected \"some string\"", "to be false"]
+ expect(matcher.failure_message).to eq(["Expected \"some string\"", "to be false"])
end
it "provides a useful negative failure message" do
matcher = BeFalseMatcher.new
matcher.matches?(false)
- matcher.negative_failure_message.should == ["Expected false", "not to be false"]
+ expect(matcher.negative_failure_message).to eq(["Expected false", "not to be false"])
end
end
diff --git a/spec/mspec/spec/matchers/be_kind_of_spec.rb b/spec/mspec/spec/matchers/be_kind_of_spec.rb
index 7c4a59f7b9..1e19058411 100644
--- a/spec/mspec/spec/matchers/be_kind_of_spec.rb
+++ b/spec/mspec/spec/matchers/be_kind_of_spec.rb
@@ -2,30 +2,30 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe BeKindOfMatcher do
+RSpec.describe BeKindOfMatcher do
it "matches when actual is a kind_of? expected" do
- BeKindOfMatcher.new(Numeric).matches?(1).should == true
- BeKindOfMatcher.new(Integer).matches?(2).should == true
- BeKindOfMatcher.new(Regexp).matches?(/m/).should == true
+ expect(BeKindOfMatcher.new(Numeric).matches?(1)).to eq(true)
+ expect(BeKindOfMatcher.new(Integer).matches?(2)).to eq(true)
+ expect(BeKindOfMatcher.new(Regexp).matches?(/m/)).to eq(true)
end
it "does not match when actual is not a kind_of? expected" do
- BeKindOfMatcher.new(Integer).matches?(1.5).should == false
- BeKindOfMatcher.new(String).matches?(:a).should == false
- BeKindOfMatcher.new(Hash).matches?([]).should == false
+ expect(BeKindOfMatcher.new(Integer).matches?(1.5)).to eq(false)
+ expect(BeKindOfMatcher.new(String).matches?(:a)).to eq(false)
+ expect(BeKindOfMatcher.new(Hash).matches?([])).to eq(false)
end
it "provides a useful failure message" do
matcher = BeKindOfMatcher.new(Numeric)
matcher.matches?('string')
- matcher.failure_message.should == [
- "Expected \"string\" (String)", "to be kind of Numeric"]
+ expect(matcher.failure_message).to eq([
+ "Expected \"string\" (String)", "to be kind of Numeric"])
end
it "provides a useful negative failure message" do
matcher = BeKindOfMatcher.new(Numeric)
matcher.matches?(4.0)
- matcher.negative_failure_message.should == [
- "Expected 4.0 (Float)", "not to be kind of Numeric"]
+ expect(matcher.negative_failure_message).to eq([
+ "Expected 4.0 (Float)", "not to be kind of Numeric"])
end
end
diff --git a/spec/mspec/spec/matchers/be_nan_spec.rb b/spec/mspec/spec/matchers/be_nan_spec.rb
index 2062763a92..baa7447943 100644
--- a/spec/mspec/spec/matchers/be_nan_spec.rb
+++ b/spec/mspec/spec/matchers/be_nan_spec.rb
@@ -4,25 +4,25 @@ require 'mspec/guards'
require 'mspec/helpers'
require 'mspec/matchers'
-describe BeNaNMatcher do
+RSpec.describe BeNaNMatcher do
it "matches when actual is NaN" do
- BeNaNMatcher.new.matches?(nan_value).should == true
+ expect(BeNaNMatcher.new.matches?(nan_value)).to eq(true)
end
it "does not match when actual is not NaN" do
- BeNaNMatcher.new.matches?(1.0).should == false
- BeNaNMatcher.new.matches?(0).should == false
+ expect(BeNaNMatcher.new.matches?(1.0)).to eq(false)
+ expect(BeNaNMatcher.new.matches?(0)).to eq(false)
end
it "provides a useful failure message" do
matcher = BeNaNMatcher.new
matcher.matches?(0)
- matcher.failure_message.should == ["Expected 0", "to be NaN"]
+ expect(matcher.failure_message).to eq(["Expected 0", "to be NaN"])
end
it "provides a useful negative failure message" do
matcher = BeNaNMatcher.new
matcher.matches?(nan_value)
- matcher.negative_failure_message.should == ["Expected NaN", "not to be NaN"]
+ expect(matcher.negative_failure_message).to eq(["Expected NaN", "not to be NaN"])
end
end
diff --git a/spec/mspec/spec/matchers/be_nil_spec.rb b/spec/mspec/spec/matchers/be_nil_spec.rb
index 6551feb5de..e2768acf83 100644
--- a/spec/mspec/spec/matchers/be_nil_spec.rb
+++ b/spec/mspec/spec/matchers/be_nil_spec.rb
@@ -2,26 +2,26 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe BeNilMatcher do
+RSpec.describe BeNilMatcher do
it "matches when actual is nil" do
- BeNilMatcher.new.matches?(nil).should == true
+ expect(BeNilMatcher.new.matches?(nil)).to eq(true)
end
it "does not match when actual is not nil" do
- BeNilMatcher.new.matches?("").should == false
- BeNilMatcher.new.matches?(false).should == false
- BeNilMatcher.new.matches?(0).should == false
+ expect(BeNilMatcher.new.matches?("")).to eq(false)
+ expect(BeNilMatcher.new.matches?(false)).to eq(false)
+ expect(BeNilMatcher.new.matches?(0)).to eq(false)
end
it "provides a useful failure message" do
matcher = BeNilMatcher.new
matcher.matches?("some string")
- matcher.failure_message.should == ["Expected \"some string\"", "to be nil"]
+ expect(matcher.failure_message).to eq(["Expected \"some string\"", "to be nil"])
end
it "provides a useful negative failure message" do
matcher = BeNilMatcher.new
matcher.matches?(nil)
- matcher.negative_failure_message.should == ["Expected nil", "not to be nil"]
+ expect(matcher.negative_failure_message).to eq(["Expected nil", "not to be nil"])
end
end
diff --git a/spec/mspec/spec/matchers/be_true_or_false_spec.rb b/spec/mspec/spec/matchers/be_true_or_false_spec.rb
index 3edffcb1b1..e4b456eafc 100644
--- a/spec/mspec/spec/matchers/be_true_or_false_spec.rb
+++ b/spec/mspec/spec/matchers/be_true_or_false_spec.rb
@@ -2,18 +2,18 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe BeTrueOrFalseMatcher do
+RSpec.describe BeTrueOrFalseMatcher do
it "matches when actual is true" do
- BeTrueOrFalseMatcher.new.matches?(true).should == true
+ expect(BeTrueOrFalseMatcher.new.matches?(true)).to eq(true)
end
it "matches when actual is false" do
- BeTrueOrFalseMatcher.new.matches?(false).should == true
+ expect(BeTrueOrFalseMatcher.new.matches?(false)).to eq(true)
end
it "provides a useful failure message" do
matcher = BeTrueOrFalseMatcher.new
matcher.matches?("some string")
- matcher.failure_message.should == ["Expected \"some string\"", "to be true or false"]
+ expect(matcher.failure_message).to eq(["Expected \"some string\"", "to be true or false"])
end
end
diff --git a/spec/mspec/spec/matchers/be_true_spec.rb b/spec/mspec/spec/matchers/be_true_spec.rb
index 90c89b3911..39ef05a0f8 100644
--- a/spec/mspec/spec/matchers/be_true_spec.rb
+++ b/spec/mspec/spec/matchers/be_true_spec.rb
@@ -2,27 +2,27 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe BeTrueMatcher do
+RSpec.describe BeTrueMatcher do
it "matches when actual is true" do
- BeTrueMatcher.new.matches?(true).should == true
+ expect(BeTrueMatcher.new.matches?(true)).to eq(true)
end
it "does not match when actual is not true" do
- BeTrueMatcher.new.matches?("").should == false
- BeTrueMatcher.new.matches?(false).should == false
- BeTrueMatcher.new.matches?(nil).should == false
- BeTrueMatcher.new.matches?(0).should == false
+ expect(BeTrueMatcher.new.matches?("")).to eq(false)
+ expect(BeTrueMatcher.new.matches?(false)).to eq(false)
+ expect(BeTrueMatcher.new.matches?(nil)).to eq(false)
+ expect(BeTrueMatcher.new.matches?(0)).to eq(false)
end
it "provides a useful failure message" do
matcher = BeTrueMatcher.new
matcher.matches?("some string")
- matcher.failure_message.should == ["Expected \"some string\"", "to be true"]
+ expect(matcher.failure_message).to eq(["Expected \"some string\"", "to be true"])
end
it "provides a useful negative failure message" do
matcher = BeTrueMatcher.new
matcher.matches?(true)
- matcher.negative_failure_message.should == ["Expected true", "not to be true"]
+ expect(matcher.negative_failure_message).to eq(["Expected true", "not to be true"])
end
end
diff --git a/spec/mspec/spec/matchers/block_caller_spec.rb b/spec/mspec/spec/matchers/block_caller_spec.rb
index d6793b9779..5d7085fa63 100644
--- a/spec/mspec/spec/matchers/block_caller_spec.rb
+++ b/spec/mspec/spec/matchers/block_caller_spec.rb
@@ -2,12 +2,12 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe BlockingMatcher do
+RSpec.describe BlockingMatcher do
it 'matches when a Proc blocks the caller' do
- BlockingMatcher.new.matches?(proc { sleep }).should == true
+ expect(BlockingMatcher.new.matches?(proc { sleep })).to eq(true)
end
it 'does not match when a Proc does not block the caller' do
- BlockingMatcher.new.matches?(proc { 1 }).should == false
+ expect(BlockingMatcher.new.matches?(proc { 1 })).to eq(false)
end
end
diff --git a/spec/mspec/spec/matchers/complain_spec.rb b/spec/mspec/spec/matchers/complain_spec.rb
index 90f94c3684..399ef3105b 100644
--- a/spec/mspec/spec/matchers/complain_spec.rb
+++ b/spec/mspec/spec/matchers/complain_spec.rb
@@ -2,52 +2,57 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe ComplainMatcher do
+RSpec.describe ComplainMatcher do
it "matches when executing the proc results in output to $stderr" do
proc = lambda { warn "I'm gonna tell yo mama" }
- ComplainMatcher.new(nil).matches?(proc).should == true
+ expect(ComplainMatcher.new(nil).matches?(proc)).to eq(true)
end
it "matches when executing the proc results in the expected output to $stderr" do
proc = lambda { warn "Que haces?" }
- ComplainMatcher.new("Que haces?\n").matches?(proc).should == true
- ComplainMatcher.new("Que pasa?\n").matches?(proc).should == false
- ComplainMatcher.new(/Que/).matches?(proc).should == true
- ComplainMatcher.new(/Quoi/).matches?(proc).should == false
+ expect(ComplainMatcher.new("Que haces?\n").matches?(proc)).to eq(true)
+ expect(ComplainMatcher.new("Que pasa?\n").matches?(proc)).to eq(false)
+ expect(ComplainMatcher.new(/Que/).matches?(proc)).to eq(true)
+ expect(ComplainMatcher.new(/Quoi/).matches?(proc)).to eq(false)
end
it "does not match when there is no output to $stderr" do
- ComplainMatcher.new(nil).matches?(lambda {}).should == false
+ expect(ComplainMatcher.new(nil).matches?(lambda {})).to eq(false)
end
it "provides a useful failure message" do
matcher = ComplainMatcher.new(nil)
matcher.matches?(lambda { })
- matcher.failure_message.should == ["Expected a warning", "but received none"]
+ expect(matcher.failure_message).to eq(["Expected a warning", "but received none"])
matcher = ComplainMatcher.new("listen here")
matcher.matches?(lambda { warn "look out" })
- matcher.failure_message.should ==
+ expect(matcher.failure_message).to eq(
["Expected warning: \"listen here\"", "but got: \"look out\""]
+ )
matcher = ComplainMatcher.new(/talk/)
matcher.matches?(lambda { warn "listen up" })
- matcher.failure_message.should ==
+ expect(matcher.failure_message).to eq(
["Expected warning to match: /talk/", "but got: \"listen up\""]
+ )
end
it "provides a useful negative failure message" do
proc = lambda { warn "ouch" }
matcher = ComplainMatcher.new(nil)
matcher.matches?(proc)
- matcher.negative_failure_message.should ==
+ expect(matcher.negative_failure_message).to eq(
["Unexpected warning: ", "\"ouch\""]
+ )
matcher = ComplainMatcher.new("ouchy")
matcher.matches?(proc)
- matcher.negative_failure_message.should ==
+ expect(matcher.negative_failure_message).to eq(
["Expected warning: \"ouchy\"", "but got: \"ouch\""]
+ )
matcher = ComplainMatcher.new(/ou/)
matcher.matches?(proc)
- matcher.negative_failure_message.should ==
+ expect(matcher.negative_failure_message).to eq(
["Expected warning not to match: /ou/", "but got: \"ouch\""]
+ )
end
context "`verbose` option specified" do
@@ -64,10 +69,10 @@ describe ComplainMatcher do
proc = lambda { verbose = $VERBOSE }
ComplainMatcher.new(nil, verbose: true).matches?(proc)
- verbose.should == true
+ expect(verbose).to eq(true)
ComplainMatcher.new(nil, verbose: false).matches?(proc)
- verbose.should == false
+ expect(verbose).to eq(false)
end
it "sets $VERBOSE with false by default" do
@@ -75,15 +80,15 @@ describe ComplainMatcher do
proc = lambda { verbose = $VERBOSE }
ComplainMatcher.new(nil).matches?(proc)
- verbose.should == false
+ expect(verbose).to eq(false)
end
it "does not have side effect" do
proc = lambda { safe_value = $VERBOSE }
- lambda do
+ expect do
ComplainMatcher.new(nil, verbose: true).matches?(proc)
- end.should_not change { $VERBOSE }
+ end.not_to change { $VERBOSE }
end
it "accepts a verbose level as single argument" do
@@ -91,7 +96,7 @@ describe ComplainMatcher do
proc = lambda { verbose = $VERBOSE }
ComplainMatcher.new(verbose: true).matches?(proc)
- verbose.should == true
+ expect(verbose).to eq(true)
end
end
end
diff --git a/spec/mspec/spec/matchers/eql_spec.rb b/spec/mspec/spec/matchers/eql_spec.rb
index f29e6976da..66307d2a9d 100644
--- a/spec/mspec/spec/matchers/eql_spec.rb
+++ b/spec/mspec/spec/matchers/eql_spec.rb
@@ -2,32 +2,32 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe EqlMatcher do
+RSpec.describe EqlMatcher do
it "matches when actual is eql? to expected" do
- EqlMatcher.new(1).matches?(1).should == true
- EqlMatcher.new(1.5).matches?(1.5).should == true
- EqlMatcher.new("red").matches?("red").should == true
- EqlMatcher.new(:blue).matches?(:blue).should == true
- EqlMatcher.new(Object).matches?(Object).should == true
+ expect(EqlMatcher.new(1).matches?(1)).to eq(true)
+ expect(EqlMatcher.new(1.5).matches?(1.5)).to eq(true)
+ expect(EqlMatcher.new("red").matches?("red")).to eq(true)
+ expect(EqlMatcher.new(:blue).matches?(:blue)).to eq(true)
+ expect(EqlMatcher.new(Object).matches?(Object)).to eq(true)
o = Object.new
- EqlMatcher.new(o).matches?(o).should == true
+ expect(EqlMatcher.new(o).matches?(o)).to eq(true)
end
it "does not match when actual is not eql? to expected" do
- EqlMatcher.new(1).matches?(1.0).should == false
- EqlMatcher.new(Hash).matches?(Object).should == false
+ expect(EqlMatcher.new(1).matches?(1.0)).to eq(false)
+ expect(EqlMatcher.new(Hash).matches?(Object)).to eq(false)
end
it "provides a useful failure message" do
matcher = EqlMatcher.new("red")
matcher.matches?("red")
- matcher.failure_message.should == ["Expected \"red\"", "to have same value and type as \"red\""]
+ expect(matcher.failure_message).to eq(["Expected \"red\"", "to have same value and type as \"red\""])
end
it "provides a useful negative failure message" do
matcher = EqlMatcher.new(1)
matcher.matches?(1.0)
- matcher.negative_failure_message.should == ["Expected 1.0", "not to have same value or type as 1"]
+ expect(matcher.negative_failure_message).to eq(["Expected 1.0", "not to have same value or type as 1"])
end
end
diff --git a/spec/mspec/spec/matchers/equal_element_spec.rb b/spec/mspec/spec/matchers/equal_element_spec.rb
index 06fae762c4..3a5ae4ede2 100644
--- a/spec/mspec/spec/matchers/equal_element_spec.rb
+++ b/spec/mspec/spec/matchers/equal_element_spec.rb
@@ -2,74 +2,74 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe EqualElementMatcher do
+RSpec.describe EqualElementMatcher do
it "matches if it finds an element with the passed name, no matter what attributes/content" do
- EqualElementMatcher.new("A").matches?('<A></A>').should be_true
- EqualElementMatcher.new("A").matches?('<A HREF="http://example.com"></A>').should be_true
- EqualElementMatcher.new("A").matches?('<A HREF="http://example.com"></A>').should be_true
+ expect(EqualElementMatcher.new("A").matches?('<A></A>')).to be_truthy
+ expect(EqualElementMatcher.new("A").matches?('<A HREF="http://example.com"></A>')).to be_truthy
+ expect(EqualElementMatcher.new("A").matches?('<A HREF="http://example.com"></A>')).to be_truthy
- EqualElementMatcher.new("BASE").matches?('<BASE></A>').should be_false
- EqualElementMatcher.new("BASE").matches?('<A></BASE>').should be_false
- EqualElementMatcher.new("BASE").matches?('<A></A>').should be_false
- EqualElementMatcher.new("BASE").matches?('<A HREF="http://example.com"></A>').should be_false
- EqualElementMatcher.new("BASE").matches?('<A HREF="http://example.com"></A>').should be_false
+ expect(EqualElementMatcher.new("BASE").matches?('<BASE></A>')).to be_falsey
+ expect(EqualElementMatcher.new("BASE").matches?('<A></BASE>')).to be_falsey
+ expect(EqualElementMatcher.new("BASE").matches?('<A></A>')).to be_falsey
+ expect(EqualElementMatcher.new("BASE").matches?('<A HREF="http://example.com"></A>')).to be_falsey
+ expect(EqualElementMatcher.new("BASE").matches?('<A HREF="http://example.com"></A>')).to be_falsey
end
it "matches if it finds an element with the passed name and the passed attributes" do
- EqualElementMatcher.new("A", {}).matches?('<A></A>').should be_true
- EqualElementMatcher.new("A", nil).matches?('<A HREF="http://example.com"></A>').should be_true
- EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://example.com"></A>').should be_true
+ expect(EqualElementMatcher.new("A", {}).matches?('<A></A>')).to be_truthy
+ expect(EqualElementMatcher.new("A", nil).matches?('<A HREF="http://example.com"></A>')).to be_truthy
+ expect(EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://example.com"></A>')).to be_truthy
- EqualElementMatcher.new("A", {}).matches?('<A HREF="http://example.com"></A>').should be_false
- EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A></A>').should be_false
- EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://test.com"></A>').should be_false
- EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://example.com" HREF="http://example.com"></A>').should be_false
+ expect(EqualElementMatcher.new("A", {}).matches?('<A HREF="http://example.com"></A>')).to be_falsey
+ expect(EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A></A>')).to be_falsey
+ expect(EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://test.com"></A>')).to be_falsey
+ expect(EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('<A HREF="http://example.com" HREF="http://example.com"></A>')).to be_falsey
end
it "matches if it finds an element with the passed name, the passed attributes and the passed content" do
- EqualElementMatcher.new("A", {}, "").matches?('<A></A>').should be_true
- EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com">Example</A>').should be_true
+ expect(EqualElementMatcher.new("A", {}, "").matches?('<A></A>')).to be_truthy
+ expect(EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com">Example</A>')).to be_truthy
- EqualElementMatcher.new("A", {}, "Test").matches?('<A></A>').should be_false
- EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com"></A>').should be_false
- EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com">Test</A>').should be_false
+ expect(EqualElementMatcher.new("A", {}, "Test").matches?('<A></A>')).to be_falsey
+ expect(EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com"></A>')).to be_falsey
+ expect(EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('<A HREF="http://example.com">Test</A>')).to be_falsey
end
it "can match unclosed elements" do
- EqualElementMatcher.new("BASE", nil, nil, :not_closed => true).matches?('<BASE>').should be_true
- EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, nil, :not_closed => true).matches?('<BASE HREF="http://example.com">').should be_true
- EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Example", :not_closed => true).matches?('<BASE HREF="http://example.com">Example').should be_true
+ expect(EqualElementMatcher.new("BASE", nil, nil, :not_closed => true).matches?('<BASE>')).to be_truthy
+ expect(EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, nil, :not_closed => true).matches?('<BASE HREF="http://example.com">')).to be_truthy
+ expect(EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Example", :not_closed => true).matches?('<BASE HREF="http://example.com">Example')).to be_truthy
- EqualElementMatcher.new("BASE", {}, nil, :not_closed => true).matches?('<BASE HREF="http://example.com">').should be_false
- EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "", :not_closed => true).matches?('<BASE HREF="http://example.com">Example').should be_false
- EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Test", :not_closed => true).matches?('<BASE HREF="http://example.com">Example').should be_false
+ expect(EqualElementMatcher.new("BASE", {}, nil, :not_closed => true).matches?('<BASE HREF="http://example.com">')).to be_falsey
+ expect(EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "", :not_closed => true).matches?('<BASE HREF="http://example.com">Example')).to be_falsey
+ expect(EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Test", :not_closed => true).matches?('<BASE HREF="http://example.com">Example')).to be_falsey
end
it "provides a useful failure message" do
equal_element = EqualElementMatcher.new("A", {}, "Test")
- equal_element.matches?('<A></A>').should be_false
- equal_element.failure_message.should == [%{Expected "<A></A>"}, %{to be a 'A' element with no attributes and "Test" as content}]
+ expect(equal_element.matches?('<A></A>')).to be_falsey
+ expect(equal_element.failure_message).to eq([%{Expected "<A></A>"}, %{to be a 'A' element with no attributes and "Test" as content}])
equal_element = EqualElementMatcher.new("A", {}, "")
- equal_element.matches?('<A>Test</A>').should be_false
- equal_element.failure_message.should == [%{Expected "<A>Test</A>"}, %{to be a 'A' element with no attributes and no content}]
+ expect(equal_element.matches?('<A>Test</A>')).to be_falsey
+ expect(equal_element.failure_message).to eq([%{Expected "<A>Test</A>"}, %{to be a 'A' element with no attributes and no content}])
equal_element = EqualElementMatcher.new("A", "HREF" => "http://www.example.com")
- equal_element.matches?('<A>Test</A>').should be_false
- equal_element.failure_message.should == [%{Expected "<A>Test</A>"}, %{to be a 'A' element with HREF="http://www.example.com" and any content}]
+ expect(equal_element.matches?('<A>Test</A>')).to be_falsey
+ expect(equal_element.failure_message).to eq([%{Expected "<A>Test</A>"}, %{to be a 'A' element with HREF="http://www.example.com" and any content}])
end
it "provides a useful negative failure message" do
equal_element = EqualElementMatcher.new("A", {}, "Test")
- equal_element.matches?('<A></A>').should be_false
- equal_element.negative_failure_message.should == [%{Expected "<A></A>"}, %{not to be a 'A' element with no attributes and "Test" as content}]
+ expect(equal_element.matches?('<A></A>')).to be_falsey
+ expect(equal_element.negative_failure_message).to eq([%{Expected "<A></A>"}, %{not to be a 'A' element with no attributes and "Test" as content}])
equal_element = EqualElementMatcher.new("A", {}, "")
- equal_element.matches?('<A>Test</A>').should be_false
- equal_element.negative_failure_message.should == [%{Expected "<A>Test</A>"}, %{not to be a 'A' element with no attributes and no content}]
+ expect(equal_element.matches?('<A>Test</A>')).to be_falsey
+ expect(equal_element.negative_failure_message).to eq([%{Expected "<A>Test</A>"}, %{not to be a 'A' element with no attributes and no content}])
equal_element = EqualElementMatcher.new("A", "HREF" => "http://www.example.com")
- equal_element.matches?('<A>Test</A>').should be_false
- equal_element.negative_failure_message.should == [%{Expected "<A>Test</A>"}, %{not to be a 'A' element with HREF="http://www.example.com" and any content}]
+ expect(equal_element.matches?('<A>Test</A>')).to be_falsey
+ expect(equal_element.negative_failure_message).to eq([%{Expected "<A>Test</A>"}, %{not to be a 'A' element with HREF="http://www.example.com" and any content}])
end
end
diff --git a/spec/mspec/spec/matchers/equal_spec.rb b/spec/mspec/spec/matchers/equal_spec.rb
index a61432b750..2df1de54b4 100644
--- a/spec/mspec/spec/matchers/equal_spec.rb
+++ b/spec/mspec/spec/matchers/equal_spec.rb
@@ -2,31 +2,31 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe EqualMatcher do
+RSpec.describe EqualMatcher do
it "matches when actual is equal? to expected" do
- EqualMatcher.new(1).matches?(1).should == true
- EqualMatcher.new(:blue).matches?(:blue).should == true
- EqualMatcher.new(Object).matches?(Object).should == true
+ expect(EqualMatcher.new(1).matches?(1)).to eq(true)
+ expect(EqualMatcher.new(:blue).matches?(:blue)).to eq(true)
+ expect(EqualMatcher.new(Object).matches?(Object)).to eq(true)
o = Object.new
- EqualMatcher.new(o).matches?(o).should == true
+ expect(EqualMatcher.new(o).matches?(o)).to eq(true)
end
it "does not match when actual is not a equal? to expected" do
- EqualMatcher.new(1).matches?(1.0).should == false
- EqualMatcher.new("blue").matches?("blue").should == false
- EqualMatcher.new(Hash).matches?(Object).should == false
+ expect(EqualMatcher.new(1).matches?(1.0)).to eq(false)
+ expect(EqualMatcher.new("blue").matches?("blue")).to eq(false)
+ expect(EqualMatcher.new(Hash).matches?(Object)).to eq(false)
end
it "provides a useful failure message" do
matcher = EqualMatcher.new("red")
matcher.matches?("red")
- matcher.failure_message.should == ["Expected \"red\"", "to be identical to \"red\""]
+ expect(matcher.failure_message).to eq(["Expected \"red\"", "to be identical to \"red\""])
end
it "provides a useful negative failure message" do
matcher = EqualMatcher.new(1)
matcher.matches?(1)
- matcher.negative_failure_message.should == ["Expected 1", "not to be identical to 1"]
+ expect(matcher.negative_failure_message).to eq(["Expected 1", "not to be identical to 1"])
end
end
diff --git a/spec/mspec/spec/matchers/have_class_variable_spec.rb b/spec/mspec/spec/matchers/have_class_variable_spec.rb
index 01ba9d0f57..d6fcf9d4e2 100644
--- a/spec/mspec/spec/matchers/have_class_variable_spec.rb
+++ b/spec/mspec/spec/matchers/have_class_variable_spec.rb
@@ -8,42 +8,42 @@ class IVarModMock
end
end
-describe HaveClassVariableMatcher, "on RUBY_VERSION >= 1.9" do
+RSpec.describe HaveClassVariableMatcher, "on RUBY_VERSION >= 1.9" do
it "matches when mod has the class variable, given as string" do
matcher = HaveClassVariableMatcher.new('@foo')
- matcher.matches?(IVarModMock).should be_true
+ expect(matcher.matches?(IVarModMock)).to be_truthy
end
it "matches when mod has the class variable, given as symbol" do
matcher = HaveClassVariableMatcher.new(:@foo)
- matcher.matches?(IVarModMock).should be_true
+ expect(matcher.matches?(IVarModMock)).to be_truthy
end
it "does not match when mod hasn't got the class variable, given as string" do
matcher = HaveClassVariableMatcher.new('@bar')
- matcher.matches?(IVarModMock).should be_false
+ expect(matcher.matches?(IVarModMock)).to be_falsey
end
it "does not match when mod hasn't got the class variable, given as symbol" do
matcher = HaveClassVariableMatcher.new(:@bar)
- matcher.matches?(IVarModMock).should be_false
+ expect(matcher.matches?(IVarModMock)).to be_falsey
end
it "provides a failure message for #should" do
matcher = HaveClassVariableMatcher.new(:@bar)
matcher.matches?(IVarModMock)
- matcher.failure_message.should == [
+ expect(matcher.failure_message).to eq([
"Expected IVarModMock to have class variable '@bar'",
"but it does not"
- ]
+ ])
end
it "provides a failure messoge for #should_not" do
matcher = HaveClassVariableMatcher.new(:@bar)
matcher.matches?(IVarModMock)
- matcher.negative_failure_message.should == [
+ expect(matcher.negative_failure_message).to eq([
"Expected IVarModMock NOT to have class variable '@bar'",
"but it does"
- ]
+ ])
end
end
diff --git a/spec/mspec/spec/matchers/have_constant_spec.rb b/spec/mspec/spec/matchers/have_constant_spec.rb
index 20c5f161d4..0bf44dbe2b 100644
--- a/spec/mspec/spec/matchers/have_constant_spec.rb
+++ b/spec/mspec/spec/matchers/have_constant_spec.rb
@@ -6,32 +6,32 @@ class HCMSpecs
X = :x
end
-describe HaveConstantMatcher do
+RSpec.describe HaveConstantMatcher do
it "matches when mod has the constant" do
matcher = HaveConstantMatcher.new :X
- matcher.matches?(HCMSpecs).should be_true
+ expect(matcher.matches?(HCMSpecs)).to be_truthy
end
it "does not match when mod does not have the constant" do
matcher = HaveConstantMatcher.new :A
- matcher.matches?(HCMSpecs).should be_false
+ expect(matcher.matches?(HCMSpecs)).to be_falsey
end
it "provides a failure message for #should" do
matcher = HaveConstantMatcher.new :A
matcher.matches?(HCMSpecs)
- matcher.failure_message.should == [
+ expect(matcher.failure_message).to eq([
"Expected HCMSpecs to have constant 'A'",
"but it does not"
- ]
+ ])
end
it "provides a failure messoge for #should_not" do
matcher = HaveConstantMatcher.new :X
matcher.matches?(HCMSpecs)
- matcher.negative_failure_message.should == [
+ expect(matcher.negative_failure_message).to eq([
"Expected HCMSpecs NOT to have constant 'X'",
"but it does"
- ]
+ ])
end
end
diff --git a/spec/mspec/spec/matchers/have_instance_method_spec.rb b/spec/mspec/spec/matchers/have_instance_method_spec.rb
index 738f5f875d..7c2e50dba6 100644
--- a/spec/mspec/spec/matchers/have_instance_method_spec.rb
+++ b/spec/mspec/spec/matchers/have_instance_method_spec.rb
@@ -12,42 +12,42 @@ class HIMMSpecs
end
end
-describe HaveInstanceMethodMatcher do
+RSpec.describe HaveInstanceMethodMatcher do
it "inherits from MethodMatcher" do
- HaveInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ expect(HaveInstanceMethodMatcher.new(:m)).to be_kind_of(MethodMatcher)
end
it "matches when mod has the instance method" do
matcher = HaveInstanceMethodMatcher.new :instance_method
- matcher.matches?(HIMMSpecs).should be_true
- matcher.matches?(HIMMSpecs::Subclass).should be_true
+ expect(matcher.matches?(HIMMSpecs)).to be_truthy
+ expect(matcher.matches?(HIMMSpecs::Subclass)).to be_truthy
end
it "does not match when mod does not have the instance method" do
matcher = HaveInstanceMethodMatcher.new :another_method
- matcher.matches?(HIMMSpecs).should be_false
+ expect(matcher.matches?(HIMMSpecs)).to be_falsey
end
it "does not match if the method is in a superclass and include_super is false" do
matcher = HaveInstanceMethodMatcher.new :instance_method, false
- matcher.matches?(HIMMSpecs::Subclass).should be_false
+ expect(matcher.matches?(HIMMSpecs::Subclass)).to be_falsey
end
it "provides a failure message for #should" do
matcher = HaveInstanceMethodMatcher.new :some_method
matcher.matches?(HIMMSpecs)
- matcher.failure_message.should == [
+ expect(matcher.failure_message).to eq([
"Expected HIMMSpecs to have instance method 'some_method'",
"but it does not"
- ]
+ ])
end
it "provides a failure messoge for #should_not" do
matcher = HaveInstanceMethodMatcher.new :some_method
matcher.matches?(HIMMSpecs)
- matcher.negative_failure_message.should == [
+ expect(matcher.negative_failure_message).to eq([
"Expected HIMMSpecs NOT to have instance method 'some_method'",
"but it does"
- ]
+ ])
end
end
diff --git a/spec/mspec/spec/matchers/have_instance_variable_spec.rb b/spec/mspec/spec/matchers/have_instance_variable_spec.rb
index 4122c6551b..12e2470f14 100644
--- a/spec/mspec/spec/matchers/have_instance_variable_spec.rb
+++ b/spec/mspec/spec/matchers/have_instance_variable_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe HaveInstanceVariableMatcher do
+RSpec.describe HaveInstanceVariableMatcher do
before :each do
@object = Object.new
def @object.instance_variables
@@ -12,39 +12,39 @@ describe HaveInstanceVariableMatcher do
it "matches when object has the instance variable, given as string" do
matcher = HaveInstanceVariableMatcher.new('@foo')
- matcher.matches?(@object).should be_true
+ expect(matcher.matches?(@object)).to be_truthy
end
it "matches when object has the instance variable, given as symbol" do
matcher = HaveInstanceVariableMatcher.new(:@foo)
- matcher.matches?(@object).should be_true
+ expect(matcher.matches?(@object)).to be_truthy
end
it "does not match when object hasn't got the instance variable, given as string" do
matcher = HaveInstanceVariableMatcher.new('@bar')
- matcher.matches?(@object).should be_false
+ expect(matcher.matches?(@object)).to be_falsey
end
it "does not match when object hasn't got the instance variable, given as symbol" do
matcher = HaveInstanceVariableMatcher.new(:@bar)
- matcher.matches?(@object).should be_false
+ expect(matcher.matches?(@object)).to be_falsey
end
it "provides a failure message for #should" do
matcher = HaveInstanceVariableMatcher.new(:@bar)
matcher.matches?(@object)
- matcher.failure_message.should == [
+ expect(matcher.failure_message).to eq([
"Expected #{@object.inspect} to have instance variable '@bar'",
"but it does not"
- ]
+ ])
end
it "provides a failure messoge for #should_not" do
matcher = HaveInstanceVariableMatcher.new(:@bar)
matcher.matches?(@object)
- matcher.negative_failure_message.should == [
+ expect(matcher.negative_failure_message).to eq([
"Expected #{@object.inspect} NOT to have instance variable '@bar'",
"but it does"
- ]
+ ])
end
end
diff --git a/spec/mspec/spec/matchers/have_method_spec.rb b/spec/mspec/spec/matchers/have_method_spec.rb
index 41bd485119..4fc0bf5e45 100644
--- a/spec/mspec/spec/matchers/have_method_spec.rb
+++ b/spec/mspec/spec/matchers/have_method_spec.rb
@@ -12,44 +12,44 @@ class HMMSpecs
end
end
-describe HaveMethodMatcher do
+RSpec.describe HaveMethodMatcher do
it "inherits from MethodMatcher" do
- HaveMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ expect(HaveMethodMatcher.new(:m)).to be_kind_of(MethodMatcher)
end
it "matches when mod has the method" do
matcher = HaveMethodMatcher.new :instance_method
- matcher.matches?(HMMSpecs).should be_true
- matcher.matches?(HMMSpecs.new).should be_true
- matcher.matches?(HMMSpecs::Subclass).should be_true
- matcher.matches?(HMMSpecs::Subclass.new).should be_true
+ expect(matcher.matches?(HMMSpecs)).to be_truthy
+ expect(matcher.matches?(HMMSpecs.new)).to be_truthy
+ expect(matcher.matches?(HMMSpecs::Subclass)).to be_truthy
+ expect(matcher.matches?(HMMSpecs::Subclass.new)).to be_truthy
end
it "does not match when mod does not have the method" do
matcher = HaveMethodMatcher.new :another_method
- matcher.matches?(HMMSpecs).should be_false
+ expect(matcher.matches?(HMMSpecs)).to be_falsey
end
it "does not match if the method is in a superclass and include_super is false" do
matcher = HaveMethodMatcher.new :instance_method, false
- matcher.matches?(HMMSpecs::Subclass).should be_false
+ expect(matcher.matches?(HMMSpecs::Subclass)).to be_falsey
end
it "provides a failure message for #should" do
matcher = HaveMethodMatcher.new :some_method
matcher.matches?(HMMSpecs)
- matcher.failure_message.should == [
+ expect(matcher.failure_message).to eq([
"Expected HMMSpecs to have method 'some_method'",
"but it does not"
- ]
+ ])
end
it "provides a failure messoge for #should_not" do
matcher = HaveMethodMatcher.new :some_method
matcher.matches?(HMMSpecs)
- matcher.negative_failure_message.should == [
+ expect(matcher.negative_failure_message).to eq([
"Expected HMMSpecs NOT to have method 'some_method'",
"but it does"
- ]
+ ])
end
end
diff --git a/spec/mspec/spec/matchers/have_private_instance_method_spec.rb b/spec/mspec/spec/matchers/have_private_instance_method_spec.rb
index 827c6b6034..0e65c264d9 100644
--- a/spec/mspec/spec/matchers/have_private_instance_method_spec.rb
+++ b/spec/mspec/spec/matchers/have_private_instance_method_spec.rb
@@ -16,42 +16,42 @@ class HPIMMSpecs
end
end
-describe HavePrivateInstanceMethodMatcher do
+RSpec.describe HavePrivateInstanceMethodMatcher do
it "inherits from MethodMatcher" do
- HavePrivateInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ expect(HavePrivateInstanceMethodMatcher.new(:m)).to be_kind_of(MethodMatcher)
end
it "matches when mod has the private instance method" do
matcher = HavePrivateInstanceMethodMatcher.new :private_method
- matcher.matches?(HPIMMSpecs).should be_true
- matcher.matches?(HPIMMSpecs::Subclass).should be_true
+ expect(matcher.matches?(HPIMMSpecs)).to be_truthy
+ expect(matcher.matches?(HPIMMSpecs::Subclass)).to be_truthy
end
it "does not match when mod does not have the private instance method" do
matcher = HavePrivateInstanceMethodMatcher.new :another_method
- matcher.matches?(HPIMMSpecs).should be_false
+ expect(matcher.matches?(HPIMMSpecs)).to be_falsey
end
it "does not match if the method is in a superclass and include_super is false" do
matcher = HavePrivateInstanceMethodMatcher.new :private_method, false
- matcher.matches?(HPIMMSpecs::Subclass).should be_false
+ expect(matcher.matches?(HPIMMSpecs::Subclass)).to be_falsey
end
it "provides a failure message for #should" do
matcher = HavePrivateInstanceMethodMatcher.new :some_method
matcher.matches?(HPIMMSpecs)
- matcher.failure_message.should == [
+ expect(matcher.failure_message).to eq([
"Expected HPIMMSpecs to have private instance method 'some_method'",
"but it does not"
- ]
+ ])
end
it "provides a failure message for #should_not" do
matcher = HavePrivateInstanceMethodMatcher.new :some_method
matcher.matches?(HPIMMSpecs)
- matcher.negative_failure_message.should == [
+ expect(matcher.negative_failure_message).to eq([
"Expected HPIMMSpecs NOT to have private instance method 'some_method'",
"but it does"
- ]
+ ])
end
end
diff --git a/spec/mspec/spec/matchers/have_private_method_spec.rb b/spec/mspec/spec/matchers/have_private_method_spec.rb
index e63a9a3c2f..f433288057 100644
--- a/spec/mspec/spec/matchers/have_private_method_spec.rb
+++ b/spec/mspec/spec/matchers/have_private_method_spec.rb
@@ -9,36 +9,36 @@ class HPMMSpecs
private_class_method :private_method
end
-describe HavePrivateMethodMatcher do
+RSpec.describe HavePrivateMethodMatcher do
it "inherits from MethodMatcher" do
- HavePrivateMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ expect(HavePrivateMethodMatcher.new(:m)).to be_kind_of(MethodMatcher)
end
it "matches when mod has the private method" do
matcher = HavePrivateMethodMatcher.new :private_method
- matcher.matches?(HPMMSpecs).should be_true
+ expect(matcher.matches?(HPMMSpecs)).to be_truthy
end
it "does not match when mod does not have the private method" do
matcher = HavePrivateMethodMatcher.new :another_method
- matcher.matches?(HPMMSpecs).should be_false
+ expect(matcher.matches?(HPMMSpecs)).to be_falsey
end
it "provides a failure message for #should" do
matcher = HavePrivateMethodMatcher.new :some_method
matcher.matches?(HPMMSpecs)
- matcher.failure_message.should == [
+ expect(matcher.failure_message).to eq([
"Expected HPMMSpecs to have private method 'some_method'",
"but it does not"
- ]
+ ])
end
it "provides a failure message for #should_not" do
matcher = HavePrivateMethodMatcher.new :private_method
matcher.matches?(HPMMSpecs)
- matcher.negative_failure_message.should == [
+ expect(matcher.negative_failure_message).to eq([
"Expected HPMMSpecs NOT to have private method 'private_method'",
"but it does"
- ]
+ ])
end
end
diff --git a/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb b/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb
index 460d0368fb..45b39004a3 100644
--- a/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb
+++ b/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb
@@ -16,42 +16,42 @@ class HPIMMSpecs
end
end
-describe HaveProtectedInstanceMethodMatcher do
+RSpec.describe HaveProtectedInstanceMethodMatcher do
it "inherits from MethodMatcher" do
- HaveProtectedInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ expect(HaveProtectedInstanceMethodMatcher.new(:m)).to be_kind_of(MethodMatcher)
end
it "matches when mod has the protected instance method" do
matcher = HaveProtectedInstanceMethodMatcher.new :protected_method
- matcher.matches?(HPIMMSpecs).should be_true
- matcher.matches?(HPIMMSpecs::Subclass).should be_true
+ expect(matcher.matches?(HPIMMSpecs)).to be_truthy
+ expect(matcher.matches?(HPIMMSpecs::Subclass)).to be_truthy
end
it "does not match when mod does not have the protected instance method" do
matcher = HaveProtectedInstanceMethodMatcher.new :another_method
- matcher.matches?(HPIMMSpecs).should be_false
+ expect(matcher.matches?(HPIMMSpecs)).to be_falsey
end
it "does not match if the method is in a superclass and include_super is false" do
matcher = HaveProtectedInstanceMethodMatcher.new :protected_method, false
- matcher.matches?(HPIMMSpecs::Subclass).should be_false
+ expect(matcher.matches?(HPIMMSpecs::Subclass)).to be_falsey
end
it "provides a failure message for #should" do
matcher = HaveProtectedInstanceMethodMatcher.new :some_method
matcher.matches?(HPIMMSpecs)
- matcher.failure_message.should == [
+ expect(matcher.failure_message).to eq([
"Expected HPIMMSpecs to have protected instance method 'some_method'",
"but it does not"
- ]
+ ])
end
it "provides a failure messoge for #should_not" do
matcher = HaveProtectedInstanceMethodMatcher.new :some_method
matcher.matches?(HPIMMSpecs)
- matcher.negative_failure_message.should == [
+ expect(matcher.negative_failure_message).to eq([
"Expected HPIMMSpecs NOT to have protected instance method 'some_method'",
"but it does"
- ]
+ ])
end
end
diff --git a/spec/mspec/spec/matchers/have_public_instance_method_spec.rb b/spec/mspec/spec/matchers/have_public_instance_method_spec.rb
index bff1046f04..771d5b7911 100644
--- a/spec/mspec/spec/matchers/have_public_instance_method_spec.rb
+++ b/spec/mspec/spec/matchers/have_public_instance_method_spec.rb
@@ -12,42 +12,42 @@ class HPIMMSpecs
end
end
-describe HavePublicInstanceMethodMatcher do
+RSpec.describe HavePublicInstanceMethodMatcher do
it "inherits from MethodMatcher" do
- HavePublicInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ expect(HavePublicInstanceMethodMatcher.new(:m)).to be_kind_of(MethodMatcher)
end
it "matches when mod has the public instance method" do
matcher = HavePublicInstanceMethodMatcher.new :public_method
- matcher.matches?(HPIMMSpecs).should be_true
- matcher.matches?(HPIMMSpecs::Subclass).should be_true
+ expect(matcher.matches?(HPIMMSpecs)).to be_truthy
+ expect(matcher.matches?(HPIMMSpecs::Subclass)).to be_truthy
end
it "does not match when mod does not have the public instance method" do
matcher = HavePublicInstanceMethodMatcher.new :another_method
- matcher.matches?(HPIMMSpecs).should be_false
+ expect(matcher.matches?(HPIMMSpecs)).to be_falsey
end
it "does not match if the method is in a superclass and include_super is false" do
matcher = HavePublicInstanceMethodMatcher.new :public_method, false
- matcher.matches?(HPIMMSpecs::Subclass).should be_false
+ expect(matcher.matches?(HPIMMSpecs::Subclass)).to be_falsey
end
it "provides a failure message for #should" do
matcher = HavePublicInstanceMethodMatcher.new :some_method
matcher.matches?(HPIMMSpecs)
- matcher.failure_message.should == [
+ expect(matcher.failure_message).to eq([
"Expected HPIMMSpecs to have public instance method 'some_method'",
"but it does not"
- ]
+ ])
end
it "provides a failure messoge for #should_not" do
matcher = HavePublicInstanceMethodMatcher.new :some_method
matcher.matches?(HPIMMSpecs)
- matcher.negative_failure_message.should == [
+ expect(matcher.negative_failure_message).to eq([
"Expected HPIMMSpecs NOT to have public instance method 'some_method'",
"but it does"
- ]
+ ])
end
end
diff --git a/spec/mspec/spec/matchers/have_singleton_method_spec.rb b/spec/mspec/spec/matchers/have_singleton_method_spec.rb
index 57c37e01d9..61ef00d49c 100644
--- a/spec/mspec/spec/matchers/have_singleton_method_spec.rb
+++ b/spec/mspec/spec/matchers/have_singleton_method_spec.rb
@@ -7,14 +7,14 @@ class HSMMSpecs
end
end
-describe HaveSingletonMethodMatcher do
+RSpec.describe HaveSingletonMethodMatcher do
it "inherits from MethodMatcher" do
- HaveSingletonMethodMatcher.new(:m).should be_kind_of(MethodMatcher)
+ expect(HaveSingletonMethodMatcher.new(:m)).to be_kind_of(MethodMatcher)
end
it "matches when the class has a singleton method" do
matcher = HaveSingletonMethodMatcher.new :singleton_method
- matcher.matches?(HSMMSpecs).should be_true
+ expect(matcher.matches?(HSMMSpecs)).to be_truthy
end
it "matches when the object has a singleton method" do
@@ -22,24 +22,24 @@ describe HaveSingletonMethodMatcher do
def obj.singleton_method; end
matcher = HaveSingletonMethodMatcher.new :singleton_method
- matcher.matches?(obj).should be_true
+ expect(matcher.matches?(obj)).to be_truthy
end
it "provides a failure message for #should" do
matcher = HaveSingletonMethodMatcher.new :some_method
matcher.matches?(HSMMSpecs)
- matcher.failure_message.should == [
+ expect(matcher.failure_message).to eq([
"Expected HSMMSpecs to have singleton method 'some_method'",
"but it does not"
- ]
+ ])
end
it "provides a failure message for #should_not" do
matcher = HaveSingletonMethodMatcher.new :singleton_method
matcher.matches?(HSMMSpecs)
- matcher.negative_failure_message.should == [
+ expect(matcher.negative_failure_message).to eq([
"Expected HSMMSpecs NOT to have singleton method 'singleton_method'",
"but it does"
- ]
+ ])
end
end
diff --git a/spec/mspec/spec/matchers/include_any_of_spec.rb b/spec/mspec/spec/matchers/include_any_of_spec.rb
index 697c8d8886..1473bb6d0b 100644
--- a/spec/mspec/spec/matchers/include_any_of_spec.rb
+++ b/spec/mspec/spec/matchers/include_any_of_spec.rb
@@ -2,41 +2,41 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe IncludeAnyOfMatcher do
+RSpec.describe IncludeAnyOfMatcher do
it "matches when actual includes expected" do
- IncludeAnyOfMatcher.new(2).matches?([1,2,3]).should == true
- IncludeAnyOfMatcher.new("b").matches?("abc").should == true
+ expect(IncludeAnyOfMatcher.new(2).matches?([1,2,3])).to eq(true)
+ expect(IncludeAnyOfMatcher.new("b").matches?("abc")).to eq(true)
end
it "does not match when actual does not include expected" do
- IncludeAnyOfMatcher.new(4).matches?([1,2,3]).should == false
- IncludeAnyOfMatcher.new("d").matches?("abc").should == false
+ expect(IncludeAnyOfMatcher.new(4).matches?([1,2,3])).to eq(false)
+ expect(IncludeAnyOfMatcher.new("d").matches?("abc")).to eq(false)
end
it "matches when actual includes all expected" do
- IncludeAnyOfMatcher.new(3, 2, 1).matches?([1,2,3]).should == true
- IncludeAnyOfMatcher.new("a", "b", "c").matches?("abc").should == true
+ expect(IncludeAnyOfMatcher.new(3, 2, 1).matches?([1,2,3])).to eq(true)
+ expect(IncludeAnyOfMatcher.new("a", "b", "c").matches?("abc")).to eq(true)
end
it "matches when actual includes any expected" do
- IncludeAnyOfMatcher.new(3, 4, 5).matches?([1,2,3]).should == true
- IncludeAnyOfMatcher.new("c", "d", "e").matches?("abc").should == true
+ expect(IncludeAnyOfMatcher.new(3, 4, 5).matches?([1,2,3])).to eq(true)
+ expect(IncludeAnyOfMatcher.new("c", "d", "e").matches?("abc")).to eq(true)
end
it "does not match when actual does not include any expected" do
- IncludeAnyOfMatcher.new(4, 5).matches?([1,2,3]).should == false
- IncludeAnyOfMatcher.new("de").matches?("abc").should == false
+ expect(IncludeAnyOfMatcher.new(4, 5).matches?([1,2,3])).to eq(false)
+ expect(IncludeAnyOfMatcher.new("de").matches?("abc")).to eq(false)
end
it "provides a useful failure message" do
matcher = IncludeAnyOfMatcher.new(5, 6)
matcher.matches?([1,2,3])
- matcher.failure_message.should == ["Expected [1, 2, 3]", "to include any of [5, 6]"]
+ expect(matcher.failure_message).to eq(["Expected [1, 2, 3]", "to include any of [5, 6]"])
end
it "provides a useful negative failure message" do
matcher = IncludeAnyOfMatcher.new(1, 2, 3)
matcher.matches?([1,2])
- matcher.negative_failure_message.should == ["Expected [1, 2]", "not to include any of [1, 2, 3]"]
+ expect(matcher.negative_failure_message).to eq(["Expected [1, 2]", "not to include any of [1, 2, 3]"])
end
end
diff --git a/spec/mspec/spec/matchers/include_spec.rb b/spec/mspec/spec/matchers/include_spec.rb
index f045c5e0cb..6bf1bef085 100644
--- a/spec/mspec/spec/matchers/include_spec.rb
+++ b/spec/mspec/spec/matchers/include_spec.rb
@@ -2,36 +2,36 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe IncludeMatcher do
+RSpec.describe IncludeMatcher do
it "matches when actual includes expected" do
- IncludeMatcher.new(2).matches?([1,2,3]).should == true
- IncludeMatcher.new("b").matches?("abc").should == true
+ expect(IncludeMatcher.new(2).matches?([1,2,3])).to eq(true)
+ expect(IncludeMatcher.new("b").matches?("abc")).to eq(true)
end
it "does not match when actual does not include expected" do
- IncludeMatcher.new(4).matches?([1,2,3]).should == false
- IncludeMatcher.new("d").matches?("abc").should == false
+ expect(IncludeMatcher.new(4).matches?([1,2,3])).to eq(false)
+ expect(IncludeMatcher.new("d").matches?("abc")).to eq(false)
end
it "matches when actual includes all expected" do
- IncludeMatcher.new(3, 2, 1).matches?([1,2,3]).should == true
- IncludeMatcher.new("a", "b", "c").matches?("abc").should == true
+ expect(IncludeMatcher.new(3, 2, 1).matches?([1,2,3])).to eq(true)
+ expect(IncludeMatcher.new("a", "b", "c").matches?("abc")).to eq(true)
end
it "does not match when actual does not include all expected" do
- IncludeMatcher.new(3, 2, 4).matches?([1,2,3]).should == false
- IncludeMatcher.new("a", "b", "c", "d").matches?("abc").should == false
+ expect(IncludeMatcher.new(3, 2, 4).matches?([1,2,3])).to eq(false)
+ expect(IncludeMatcher.new("a", "b", "c", "d").matches?("abc")).to eq(false)
end
it "provides a useful failure message" do
matcher = IncludeMatcher.new(5, 2)
matcher.matches?([1,2,3])
- matcher.failure_message.should == ["Expected [1, 2, 3]", "to include 5"]
+ expect(matcher.failure_message).to eq(["Expected [1, 2, 3]", "to include 5"])
end
it "provides a useful negative failure message" do
matcher = IncludeMatcher.new(1, 2, 3)
matcher.matches?([1,2,3])
- matcher.negative_failure_message.should == ["Expected [1, 2, 3]", "not to include 3"]
+ expect(matcher.negative_failure_message).to eq(["Expected [1, 2, 3]", "not to include 3"])
end
end
diff --git a/spec/mspec/spec/matchers/infinity_spec.rb b/spec/mspec/spec/matchers/infinity_spec.rb
index 6eb8ac2940..78c4194526 100644
--- a/spec/mspec/spec/matchers/infinity_spec.rb
+++ b/spec/mspec/spec/matchers/infinity_spec.rb
@@ -4,31 +4,31 @@ require 'mspec/guards'
require 'mspec/helpers'
require 'mspec/matchers'
-describe InfinityMatcher do
+RSpec.describe InfinityMatcher do
it "matches when actual is infinite and has the correct sign" do
- InfinityMatcher.new(1).matches?(infinity_value).should == true
- InfinityMatcher.new(-1).matches?(-infinity_value).should == true
+ expect(InfinityMatcher.new(1).matches?(infinity_value)).to eq(true)
+ expect(InfinityMatcher.new(-1).matches?(-infinity_value)).to eq(true)
end
it "does not match when actual is not infinite" do
- InfinityMatcher.new(1).matches?(1.0).should == false
- InfinityMatcher.new(-1).matches?(-1.0).should == false
+ expect(InfinityMatcher.new(1).matches?(1.0)).to eq(false)
+ expect(InfinityMatcher.new(-1).matches?(-1.0)).to eq(false)
end
it "does not match when actual is infinite but has the incorrect sign" do
- InfinityMatcher.new(1).matches?(-infinity_value).should == false
- InfinityMatcher.new(-1).matches?(infinity_value).should == false
+ expect(InfinityMatcher.new(1).matches?(-infinity_value)).to eq(false)
+ expect(InfinityMatcher.new(-1).matches?(infinity_value)).to eq(false)
end
it "provides a useful failure message" do
matcher = InfinityMatcher.new(-1)
matcher.matches?(0)
- matcher.failure_message.should == ["Expected 0", "to be -Infinity"]
+ expect(matcher.failure_message).to eq(["Expected 0", "to be -Infinity"])
end
it "provides a useful negative failure message" do
matcher = InfinityMatcher.new(1)
matcher.matches?(infinity_value)
- matcher.negative_failure_message.should == ["Expected Infinity", "not to be Infinity"]
+ expect(matcher.negative_failure_message).to eq(["Expected Infinity", "not to be Infinity"])
end
end
diff --git a/spec/mspec/spec/matchers/match_yaml_spec.rb b/spec/mspec/spec/matchers/match_yaml_spec.rb
index 4f16aee0ec..85123bb87d 100644
--- a/spec/mspec/spec/matchers/match_yaml_spec.rb
+++ b/spec/mspec/spec/matchers/match_yaml_spec.rb
@@ -2,38 +2,38 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe MatchYAMLMatcher do
+RSpec.describe MatchYAMLMatcher do
before :each do
@matcher = MatchYAMLMatcher.new("--- \nfoo: bar\n")
end
it "compares YAML documents and matches if they're equivalent" do
- @matcher.matches?("--- \nfoo: bar\n").should == true
+ expect(@matcher.matches?("--- \nfoo: bar\n")).to eq(true)
end
it "compares YAML documents and does not match if they're not equivalent" do
- @matcher.matches?("--- \nbar: foo\n").should == false
- @matcher.matches?("--- \nfoo: \nbar\n").should == false
+ expect(@matcher.matches?("--- \nbar: foo\n")).to eq(false)
+ expect(@matcher.matches?("--- \nfoo: \nbar\n")).to eq(false)
end
it "also receives objects that respond_to to_yaml" do
matcher = MatchYAMLMatcher.new("some string")
- matcher.matches?("some string").should == true
+ expect(matcher.matches?("some string")).to eq(true)
matcher = MatchYAMLMatcher.new(['a', 'b'])
- matcher.matches?("--- \n- a\n- b\n").should == true
+ expect(matcher.matches?("--- \n- a\n- b\n")).to eq(true)
matcher = MatchYAMLMatcher.new("foo" => "bar")
- matcher.matches?("--- \nfoo: bar\n").should == true
+ expect(matcher.matches?("--- \nfoo: bar\n")).to eq(true)
end
it "matches documents with trailing whitespace" do
- @matcher.matches?("--- \nfoo: bar \n").should == true
- @matcher.matches?("--- \nfoo: bar \n").should == true
+ expect(@matcher.matches?("--- \nfoo: bar \n")).to eq(true)
+ expect(@matcher.matches?("--- \nfoo: bar \n")).to eq(true)
end
it "fails with a descriptive error message" do
- @matcher.matches?("foo").should == false
- @matcher.failure_message.should == ["Expected \"foo\"", " to match \"--- \\nfoo: bar\\n\""]
+ expect(@matcher.matches?("foo")).to eq(false)
+ expect(@matcher.failure_message).to eq(["Expected \"foo\"", " to match \"--- \\nfoo: bar\\n\""])
end
end
diff --git a/spec/mspec/spec/matchers/output_spec.rb b/spec/mspec/spec/matchers/output_spec.rb
index 264da3b569..3baad9a4b2 100644
--- a/spec/mspec/spec/matchers/output_spec.rb
+++ b/spec/mspec/spec/matchers/output_spec.rb
@@ -2,73 +2,83 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe OutputMatcher do
+RSpec.describe OutputMatcher do
it "matches when executing the proc results in the expected output to $stdout" do
proc = Proc.new { puts "bang!" }
- OutputMatcher.new("bang!\n", nil).matches?(proc).should == true
- OutputMatcher.new("pop", nil).matches?(proc).should == false
- OutputMatcher.new(/bang/, nil).matches?(proc).should == true
- OutputMatcher.new(/po/, nil).matches?(proc).should == false
+ expect(OutputMatcher.new("bang!\n", nil).matches?(proc)).to eq(true)
+ expect(OutputMatcher.new("pop", nil).matches?(proc)).to eq(false)
+ expect(OutputMatcher.new(/bang/, nil).matches?(proc)).to eq(true)
+ expect(OutputMatcher.new(/po/, nil).matches?(proc)).to eq(false)
end
it "matches when executing the proc results in the expected output to $stderr" do
proc = Proc.new { $stderr.write "boom!" }
- OutputMatcher.new(nil, "boom!").matches?(proc).should == true
- OutputMatcher.new(nil, "fizzle").matches?(proc).should == false
- OutputMatcher.new(nil, /boom/).matches?(proc).should == true
- OutputMatcher.new(nil, /fizzl/).matches?(proc).should == false
+ expect(OutputMatcher.new(nil, "boom!").matches?(proc)).to eq(true)
+ expect(OutputMatcher.new(nil, "fizzle").matches?(proc)).to eq(false)
+ expect(OutputMatcher.new(nil, /boom/).matches?(proc)).to eq(true)
+ expect(OutputMatcher.new(nil, /fizzl/).matches?(proc)).to eq(false)
end
it "provides a useful failure message" do
proc = Proc.new { print "unexpected"; $stderr.print "unerror" }
matcher = OutputMatcher.new("expected", "error")
matcher.matches?(proc)
- matcher.failure_message.should ==
+ expect(matcher.failure_message).to eq(
["Expected:\n $stdout: \"expected\"\n $stderr: \"error\"\n",
" got:\n $stdout: \"unexpected\"\n $stderr: \"unerror\"\n"]
+ )
matcher = OutputMatcher.new("expected", nil)
matcher.matches?(proc)
- matcher.failure_message.should ==
+ expect(matcher.failure_message).to eq(
["Expected:\n $stdout: \"expected\"\n",
" got:\n $stdout: \"unexpected\"\n"]
+ )
matcher = OutputMatcher.new(nil, "error")
matcher.matches?(proc)
- matcher.failure_message.should ==
+ expect(matcher.failure_message).to eq(
["Expected:\n $stderr: \"error\"\n",
" got:\n $stderr: \"unerror\"\n"]
+ )
matcher = OutputMatcher.new(/base/, nil)
matcher.matches?(proc)
- matcher.failure_message.should ==
+ expect(matcher.failure_message).to eq(
["Expected:\n $stdout: /base/\n",
" got:\n $stdout: \"unexpected\"\n"]
+ )
matcher = OutputMatcher.new(nil, /octave/)
matcher.matches?(proc)
- matcher.failure_message.should ==
+ expect(matcher.failure_message).to eq(
["Expected:\n $stderr: /octave/\n",
" got:\n $stderr: \"unerror\"\n"]
+ )
end
it "provides a useful negative failure message" do
proc = Proc.new { puts "expected"; $stderr.puts "error" }
matcher = OutputMatcher.new("expected", "error")
matcher.matches?(proc)
- matcher.negative_failure_message.should ==
+ expect(matcher.negative_failure_message).to eq(
["Expected output not to be:\n", " $stdout: \"expected\"\n $stderr: \"error\"\n"]
+ )
matcher = OutputMatcher.new("expected", nil)
matcher.matches?(proc)
- matcher.negative_failure_message.should ==
+ expect(matcher.negative_failure_message).to eq(
["Expected output not to be:\n", " $stdout: \"expected\"\n"]
+ )
matcher = OutputMatcher.new(nil, "error")
matcher.matches?(proc)
- matcher.negative_failure_message.should ==
+ expect(matcher.negative_failure_message).to eq(
["Expected output not to be:\n", " $stderr: \"error\"\n"]
+ )
matcher = OutputMatcher.new(/expect/, nil)
matcher.matches?(proc)
- matcher.negative_failure_message.should ==
+ expect(matcher.negative_failure_message).to eq(
["Expected output not to be:\n", " $stdout: \"expected\"\n"]
+ )
matcher = OutputMatcher.new(nil, /err/)
matcher.matches?(proc)
- matcher.negative_failure_message.should ==
+ expect(matcher.negative_failure_message).to eq(
["Expected output not to be:\n", " $stderr: \"error\"\n"]
+ )
end
end
diff --git a/spec/mspec/spec/matchers/output_to_fd_spec.rb b/spec/mspec/spec/matchers/output_to_fd_spec.rb
index e584c4e003..a39cab3206 100644
--- a/spec/mspec/spec/matchers/output_to_fd_spec.rb
+++ b/spec/mspec/spec/matchers/output_to_fd_spec.rb
@@ -2,43 +2,43 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe OutputToFDMatcher do
+RSpec.describe OutputToFDMatcher do
# Figure out how in the hell to achieve this
it "matches when running the block produces the expected output to the given FD" do
- OutputToFDMatcher.new("Hi\n", STDERR).matches?(lambda { $stderr.print "Hi\n" }).should == true
+ expect(OutputToFDMatcher.new("Hi\n", STDERR).matches?(lambda { $stderr.print "Hi\n" })).to eq(true)
end
it "does not match if running the block does not produce the expected output to the FD" do
- OutputToFDMatcher.new("Hi\n", STDERR).matches?(lambda { $stderr.puts("Hello\n") }).should == false
+ expect(OutputToFDMatcher.new("Hi\n", STDERR).matches?(lambda { $stderr.puts("Hello\n") })).to eq(false)
end
it "propagate the exception if one is thrown while matching" do
exc = RuntimeError.new("propagates")
- lambda {
- OutputToFDMatcher.new("Hi\n", STDERR).matches?(lambda {
+ expect {
+ expect(OutputToFDMatcher.new("Hi\n", STDERR).matches?(lambda {
raise exc
- }).should == false
- }.should raise_error(exc)
+ })).to eq(false)
+ }.to raise_error(exc)
end
it "defaults to matching against STDOUT" do
object = Object.new
object.extend MSpecMatchers
- object.send(:output_to_fd, "Hi\n").matches?(lambda { $stdout.print "Hi\n" }).should == true
+ expect(object.send(:output_to_fd, "Hi\n").matches?(lambda { $stdout.print "Hi\n" })).to eq(true)
end
it "accepts any IO instance" do
io = IO.new STDOUT.fileno
- OutputToFDMatcher.new("Hi\n", io).matches?(lambda { io.print "Hi\n" }).should == true
+ expect(OutputToFDMatcher.new("Hi\n", io).matches?(lambda { io.print "Hi\n" })).to eq(true)
end
it "allows matching with a Regexp" do
s = "Hi there\n"
- OutputToFDMatcher.new(/Hi/, STDERR).matches?(lambda { $stderr.print s }).should == true
- OutputToFDMatcher.new(/Hi?/, STDERR).matches?(lambda { $stderr.print s }).should == true
- OutputToFDMatcher.new(/[hH]i?/, STDERR).matches?(lambda { $stderr.print s }).should == true
- OutputToFDMatcher.new(/.*/, STDERR).matches?(lambda { $stderr.print s }).should == true
- OutputToFDMatcher.new(/H.*?here/, STDERR).matches?(lambda { $stderr.print s }).should == true
- OutputToFDMatcher.new(/Ahoy/, STDERR).matches?(lambda { $stderr.print s }).should == false
+ expect(OutputToFDMatcher.new(/Hi/, STDERR).matches?(lambda { $stderr.print s })).to eq(true)
+ expect(OutputToFDMatcher.new(/Hi?/, STDERR).matches?(lambda { $stderr.print s })).to eq(true)
+ expect(OutputToFDMatcher.new(/[hH]i?/, STDERR).matches?(lambda { $stderr.print s })).to eq(true)
+ expect(OutputToFDMatcher.new(/.*/, STDERR).matches?(lambda { $stderr.print s })).to eq(true)
+ expect(OutputToFDMatcher.new(/H.*?here/, STDERR).matches?(lambda { $stderr.print s })).to eq(true)
+ expect(OutputToFDMatcher.new(/Ahoy/, STDERR).matches?(lambda { $stderr.print s })).to eq(false)
end
end
diff --git a/spec/mspec/spec/matchers/raise_error_spec.rb b/spec/mspec/spec/matchers/raise_error_spec.rb
index a40acc0ea0..3849c7dd2a 100644
--- a/spec/mspec/spec/matchers/raise_error_spec.rb
+++ b/spec/mspec/spec/matchers/raise_error_spec.rb
@@ -1,79 +1,94 @@
require 'spec_helper'
-require 'mspec/expectations/expectations'
-require 'mspec/matchers'
class ExpectedException < Exception; end
class UnexpectedException < Exception; end
-describe RaiseErrorMatcher do
+RSpec.describe RaiseErrorMatcher do
+ before :each do
+ state = double("run state").as_null_object
+ allow(MSpec).to receive(:current).and_return(state)
+ end
+
it "matches when the proc raises the expected exception" do
proc = Proc.new { raise ExpectedException }
matcher = RaiseErrorMatcher.new(ExpectedException, nil)
- matcher.matches?(proc).should == true
+ expect(matcher.matches?(proc)).to eq(true)
end
- it "executes its optional block if matched" do
+ it "executes its optional {/} block if matched" do
+ ensure_mspec_method(-> {}.method(:should))
+
run = false
- proc = Proc.new { raise ExpectedException }
- matcher = RaiseErrorMatcher.new(ExpectedException, nil) { |error|
+ -> { raise ExpectedException }.should PublicMSpecMatchers.raise_error { |error|
+ expect(error.class).to eq(ExpectedException)
run = true
- error.class.should == ExpectedException
}
+ expect(run).to eq(true)
+ end
+
+ it "executes its optional do/end block if matched" do
+ ensure_mspec_method(-> {}.method(:should))
- matcher.matches?(proc).should == true
- run.should == true
+ run = false
+ -> { raise ExpectedException }.should PublicMSpecMatchers.raise_error do |error|
+ expect(error.class).to eq(ExpectedException)
+ run = true
+ end
+ expect(run).to eq(true)
end
it "matches when the proc raises the expected exception with the expected message" do
proc = Proc.new { raise ExpectedException, "message" }
matcher = RaiseErrorMatcher.new(ExpectedException, "message")
- matcher.matches?(proc).should == true
+ expect(matcher.matches?(proc)).to eq(true)
end
it "matches when the proc raises the expected exception with a matching message" do
proc = Proc.new { raise ExpectedException, "some message" }
matcher = RaiseErrorMatcher.new(ExpectedException, /some/)
- matcher.matches?(proc).should == true
+ expect(matcher.matches?(proc)).to eq(true)
end
it "does not match when the proc does not raise the expected exception" do
exc = UnexpectedException.new
matcher = RaiseErrorMatcher.new(ExpectedException, nil)
- matcher.matching_exception?(exc).should == false
- lambda {
+ expect(matcher.matching_exception?(exc)).to eq(false)
+ expect {
matcher.matches?(Proc.new { raise exc })
- }.should raise_error(UnexpectedException)
+ }.to raise_error(UnexpectedException)
end
it "does not match when the proc raises the expected exception with an unexpected message" do
exc = ExpectedException.new("unexpected")
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
- matcher.matching_exception?(exc).should == false
- lambda {
+ expect(matcher.matching_exception?(exc)).to eq(false)
+ expect {
matcher.matches?(Proc.new { raise exc })
- }.should raise_error(ExpectedException)
+ }.to raise_error(ExpectedException)
end
it "does not match when the proc does not raise an exception" do
proc = Proc.new {}
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
- matcher.matches?(proc).should == false
+ expect(matcher.matches?(proc)).to eq(false)
end
it "provides a useful failure message when the exception class differs" do
exc = UnexpectedException.new("message")
matcher = RaiseErrorMatcher.new(ExpectedException, "message")
- matcher.matching_exception?(exc).should == false
+ expect(matcher.matching_exception?(exc)).to eq(false)
begin
matcher.matches?(Proc.new { raise exc })
rescue UnexpectedException => e
- matcher.failure_message.should ==
- ["Expected ExpectedException (message)", "but got: UnexpectedException (message)"]
- ExceptionState.new(nil, nil, e).message.should ==
- "Expected ExpectedException (message)\nbut got: UnexpectedException (message)"
+ expect(matcher.failure_message).to eq(
+ ['Expected ExpectedException("message")', 'but got: UnexpectedException("message")']
+ )
+ expect(ExceptionState.new(nil, nil, e).message).to eq(
+ "Expected ExpectedException(\"message\")\nbut got: UnexpectedException(\"message\")"
+ )
else
raise "no exception"
end
@@ -83,14 +98,16 @@ describe RaiseErrorMatcher do
exc = ExpectedException.new("unexpected")
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
- matcher.matching_exception?(exc).should == false
+ expect(matcher.matching_exception?(exc)).to eq(false)
begin
matcher.matches?(Proc.new { raise exc })
rescue ExpectedException => e
- matcher.failure_message.should ==
- ["Expected ExpectedException (expected)", "but got: ExpectedException (unexpected)"]
- ExceptionState.new(nil, nil, e).message.should ==
- "Expected ExpectedException (expected)\nbut got: ExpectedException (unexpected)"
+ expect(matcher.failure_message).to eq(
+ ['Expected ExpectedException("expected")', 'but got: ExpectedException("unexpected")']
+ )
+ expect(ExceptionState.new(nil, nil, e).message).to eq(
+ "Expected ExpectedException(\"expected\")\nbut got: ExpectedException(\"unexpected\")"
+ )
else
raise "no exception"
end
@@ -100,14 +117,16 @@ describe RaiseErrorMatcher do
exc = UnexpectedException.new("unexpected")
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
- matcher.matching_exception?(exc).should == false
+ expect(matcher.matching_exception?(exc)).to eq(false)
begin
matcher.matches?(Proc.new { raise exc })
rescue UnexpectedException => e
- matcher.failure_message.should ==
- ["Expected ExpectedException (expected)", "but got: UnexpectedException (unexpected)"]
- ExceptionState.new(nil, nil, e).message.should ==
- "Expected ExpectedException (expected)\nbut got: UnexpectedException (unexpected)"
+ expect(matcher.failure_message).to eq(
+ ['Expected ExpectedException("expected")', 'but got: UnexpectedException("unexpected")']
+ )
+ expect(ExceptionState.new(nil, nil, e).message).to eq(
+ "Expected ExpectedException(\"expected\")\nbut got: UnexpectedException(\"unexpected\")"
+ )
else
raise "no exception"
end
@@ -117,16 +136,18 @@ describe RaiseErrorMatcher do
proc = Proc.new { 120 }
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
matcher.matches?(proc)
- matcher.failure_message.should ==
- ["Expected ExpectedException (expected)", "but no exception was raised (120 was returned)"]
+ expect(matcher.failure_message).to eq(
+ ['Expected ExpectedException("expected")', "but no exception was raised (120 was returned)"]
+ )
end
it "provides a useful failure message when no exception is raised and nil is returned" do
proc = Proc.new { nil }
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
matcher.matches?(proc)
- matcher.failure_message.should ==
- ["Expected ExpectedException (expected)", "but no exception was raised (nil was returned)"]
+ expect(matcher.failure_message).to eq(
+ ['Expected ExpectedException("expected")', "but no exception was raised (nil was returned)"]
+ )
end
it "provides a useful failure message when no exception is raised and the result raises in #pretty_inspect" do
@@ -137,23 +158,77 @@ describe RaiseErrorMatcher do
proc = Proc.new { result }
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
matcher.matches?(proc)
- matcher.failure_message.should ==
- ["Expected ExpectedException (expected)", "but no exception was raised (#<Object>(#pretty_inspect raised #<ArgumentError: bad>) was returned)"]
+ expect(matcher.failure_message).to eq(
+ ['Expected ExpectedException("expected")', 'but no exception was raised (#<Object>(#pretty_inspect raised #<ArgumentError: bad>) was returned)']
+ )
end
it "provides a useful negative failure message" do
proc = Proc.new { raise ExpectedException, "expected" }
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
matcher.matches?(proc)
- matcher.negative_failure_message.should ==
- ["Expected to not get ExpectedException (expected)", ""]
+ expect(matcher.negative_failure_message).to eq(
+ ['Expected to not get ExpectedException("expected")', ""]
+ )
end
it "provides a useful negative failure message for strict subclasses of the matched exception class" do
proc = Proc.new { raise UnexpectedException, "unexpected" }
matcher = RaiseErrorMatcher.new(Exception, nil)
matcher.matches?(proc)
- matcher.negative_failure_message.should ==
- ["Expected to not get Exception", "but got: UnexpectedException (unexpected)"]
+ expect(matcher.negative_failure_message).to eq(
+ ['Expected to not get Exception', 'but got: UnexpectedException']
+ )
+ end
+
+ it "matches cause if given" do
+ cause = RuntimeError.new("foo")
+ proc = -> do
+ raise cause
+ rescue
+ raise "bar"
+ end
+
+ matcher = RaiseErrorMatcher.new(RuntimeError, cause: cause)
+ expect(matcher.matches?(proc)).to eq(true)
+ end
+
+ it "matches message and cause if given" do
+ cause = RuntimeError.new("foo")
+ proc = -> do
+ raise cause
+ rescue
+ raise "bar"
+ end
+
+ matcher = RaiseErrorMatcher.new(RuntimeError, "bar", cause: cause)
+ expect(matcher.matches?(proc)).to eq(true)
+ end
+
+ it "provides useful negative failure message when cause does not match" do
+ cause = RuntimeError.new("bar")
+ proc = -> do
+ raise "foo"
+ end
+
+ matcher = RaiseErrorMatcher.new(RuntimeError, cause: cause)
+
+ begin
+ matcher.matches?(proc)
+ rescue RuntimeError
+ expect(matcher.failure_message).to eq(
+ ['Expected RuntimeError(cause: #<RuntimeError: bar>)', 'but got: RuntimeError(cause: nil)']
+ )
+ end
+
+ matcher = RaiseErrorMatcher.new(RuntimeError, "foo", cause: cause)
+
+ begin
+ matcher.matches?(proc)
+ rescue RuntimeError
+ expect(matcher.failure_message).to eq(
+ ['Expected RuntimeError("foo", cause: #<RuntimeError: bar>)', 'but got: RuntimeError("foo", cause: nil)']
+ )
+ end
end
end
diff --git a/spec/mspec/spec/matchers/respond_to_spec.rb b/spec/mspec/spec/matchers/respond_to_spec.rb
index 988caf4dff..6f1cd8d148 100644
--- a/spec/mspec/spec/matchers/respond_to_spec.rb
+++ b/spec/mspec/spec/matchers/respond_to_spec.rb
@@ -2,32 +2,32 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe RespondToMatcher do
+RSpec.describe RespondToMatcher do
it "matches when actual does respond_to? expected" do
- RespondToMatcher.new(:to_s).matches?(Object.new).should == true
- RespondToMatcher.new(:inject).matches?([]).should == true
- RespondToMatcher.new(:[]).matches?(1).should == true
- RespondToMatcher.new(:[]=).matches?("string").should == true
+ expect(RespondToMatcher.new(:to_s).matches?(Object.new)).to eq(true)
+ expect(RespondToMatcher.new(:inject).matches?([])).to eq(true)
+ expect(RespondToMatcher.new(:[]).matches?(1)).to eq(true)
+ expect(RespondToMatcher.new(:[]=).matches?("string")).to eq(true)
end
it "does not match when actual does not respond_to? expected" do
- RespondToMatcher.new(:to_i).matches?(Object.new).should == false
- RespondToMatcher.new(:inject).matches?(1).should == false
- RespondToMatcher.new(:non_existent_method).matches?([]).should == false
- RespondToMatcher.new(:[]=).matches?(1).should == false
+ expect(RespondToMatcher.new(:to_i).matches?(Object.new)).to eq(false)
+ expect(RespondToMatcher.new(:inject).matches?(1)).to eq(false)
+ expect(RespondToMatcher.new(:non_existent_method).matches?([])).to eq(false)
+ expect(RespondToMatcher.new(:[]=).matches?(1)).to eq(false)
end
it "provides a useful failure message" do
matcher = RespondToMatcher.new(:non_existent_method)
matcher.matches?('string')
- matcher.failure_message.should == [
- "Expected \"string\" (String)", "to respond to non_existent_method"]
+ expect(matcher.failure_message).to eq([
+ "Expected \"string\" (String)", "to respond to non_existent_method"])
end
it "provides a useful negative failure message" do
matcher = RespondToMatcher.new(:to_i)
matcher.matches?(4.0)
- matcher.negative_failure_message.should == [
- "Expected 4.0 (Float)", "not to respond to to_i"]
+ expect(matcher.negative_failure_message).to eq([
+ "Expected 4.0 (Float)", "not to respond to to_i"])
end
end
diff --git a/spec/mspec/spec/matchers/signed_zero_spec.rb b/spec/mspec/spec/matchers/signed_zero_spec.rb
index 9c5c50c602..6d1c1007bc 100644
--- a/spec/mspec/spec/matchers/signed_zero_spec.rb
+++ b/spec/mspec/spec/matchers/signed_zero_spec.rb
@@ -2,31 +2,31 @@ require 'spec_helper'
require 'mspec/expectations/expectations'
require 'mspec/matchers'
-describe SignedZeroMatcher do
+RSpec.describe SignedZeroMatcher do
it "matches when actual is zero and has the correct sign" do
- SignedZeroMatcher.new(1).matches?(0.0).should == true
- SignedZeroMatcher.new(-1).matches?(-0.0).should == true
+ expect(SignedZeroMatcher.new(1).matches?(0.0)).to eq(true)
+ expect(SignedZeroMatcher.new(-1).matches?(-0.0)).to eq(true)
end
it "does not match when actual is non-zero" do
- SignedZeroMatcher.new(1).matches?(1.0).should == false
- SignedZeroMatcher.new(-1).matches?(-1.0).should == false
+ expect(SignedZeroMatcher.new(1).matches?(1.0)).to eq(false)
+ expect(SignedZeroMatcher.new(-1).matches?(-1.0)).to eq(false)
end
it "does not match when actual is zero but has the incorrect sign" do
- SignedZeroMatcher.new(1).matches?(-0.0).should == false
- SignedZeroMatcher.new(-1).matches?(0.0).should == false
+ expect(SignedZeroMatcher.new(1).matches?(-0.0)).to eq(false)
+ expect(SignedZeroMatcher.new(-1).matches?(0.0)).to eq(false)
end
it "provides a useful failure message" do
matcher = SignedZeroMatcher.new(-1)
matcher.matches?(0.0)
- matcher.failure_message.should == ["Expected 0.0", "to be -0.0"]
+ expect(matcher.failure_message).to eq(["Expected 0.0", "to be -0.0"])
end
it "provides a useful negative failure message" do
matcher = SignedZeroMatcher.new(-1)
matcher.matches?(-0.0)
- matcher.negative_failure_message.should == ["Expected -0.0", "not to be -0.0"]
+ expect(matcher.negative_failure_message).to eq(["Expected -0.0", "not to be -0.0"])
end
end
diff --git a/spec/mspec/spec/mocks/mock_spec.rb b/spec/mspec/spec/mocks/mock_spec.rb
index 8cf04cf462..7426e0ff88 100644
--- a/spec/mspec/spec/mocks/mock_spec.rb
+++ b/spec/mspec/spec/mocks/mock_spec.rb
@@ -7,78 +7,78 @@ require 'mspec/runner/mspec'
require 'mspec/mocks/mock'
require 'mspec/mocks/proxy'
-describe Mock, ".mocks" do
+RSpec.describe Mock, ".mocks" do
it "returns a Hash" do
- Mock.mocks.should be_kind_of(Hash)
+ expect(Mock.mocks).to be_kind_of(Hash)
end
end
-describe Mock, ".stubs" do
+RSpec.describe Mock, ".stubs" do
it "returns a Hash" do
- Mock.stubs.should be_kind_of(Hash)
+ expect(Mock.stubs).to be_kind_of(Hash)
end
end
-describe Mock, ".replaced_name" do
+RSpec.describe Mock, ".replaced_name" do
it "returns the name for a method that is being replaced by a mock method" do
m = double('a fake id')
- Mock.replaced_name(m, :method_call).should == :"__mspec_#{m.object_id}_method_call__"
+ expect(Mock.replaced_name(Mock.replaced_key(m, :method_call))).to eq(:"__mspec_method_call__")
end
end
-describe Mock, ".replaced_key" do
+RSpec.describe Mock, ".replaced_key" do
it "returns a key used internally by Mock" do
m = double('a fake id')
- Mock.replaced_key(m, :method_call).should == [:"__mspec_#{m.object_id}_method_call__", :method_call]
+ expect(Mock.replaced_key(m, :method_call)).to eq([m.object_id, :method_call])
end
end
-describe Mock, ".replaced?" do
+RSpec.describe Mock, ".replaced?" do
before :each do
@mock = double('install_method')
- MSpec.stub(:actions)
- MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ allow(MSpec).to receive(:actions)
+ allow(MSpec).to receive(:current).and_return(double("spec state").as_null_object)
end
it "returns true if a method has been stubbed on an object" do
Mock.install_method @mock, :method_call
- Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_true
+ expect(Mock.replaced?(Mock.replaced_key(@mock, :method_call))).to be_truthy
end
it "returns true if a method has been mocked on an object" do
Mock.install_method @mock, :method_call, :stub
- Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_true
+ expect(Mock.replaced?(Mock.replaced_key(@mock, :method_call))).to be_truthy
end
it "returns false if a method has not been stubbed or mocked" do
- Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_false
+ expect(Mock.replaced?(Mock.replaced_key(@mock, :method_call))).to be_falsey
end
end
-describe Mock, ".name_or_inspect" do
+RSpec.describe Mock, ".name_or_inspect" do
before :each do
@mock = double("I have a #name")
end
it "returns the value of @name if set" do
@mock.instance_variable_set(:@name, "Myself")
- Mock.name_or_inspect(@mock).should == "Myself"
+ expect(Mock.name_or_inspect(@mock)).to eq("Myself")
end
end
-describe Mock, ".install_method for mocks" do
+RSpec.describe Mock, ".install_method for mocks" do
before :each do
@mock = double('install_method')
- MSpec.stub(:actions)
- MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ allow(MSpec).to receive(:actions)
+ allow(MSpec).to receive(:current).and_return(double("spec state").as_null_object)
end
after :each do
- Mock.cleanup
+ Mock.reset
end
it "returns a MockProxy instance" do
- Mock.install_method(@mock, :method_call).should be_an_instance_of(MockProxy)
+ expect(Mock.install_method(@mock, :method_call)).to be_an_instance_of(MockProxy)
end
it "does not override a previously mocked method with the same name" do
@@ -86,7 +86,7 @@ describe Mock, ".install_method for mocks" do
Mock.install_method(@mock, :method_call).with(:c).and_return(2)
@mock.method_call(:a, :b)
@mock.method_call(:c)
- lambda { @mock.method_call(:d) }.should raise_error(SpecExpectationNotMetError)
+ expect { @mock.method_call(:d) }.to raise_error(SpecExpectationNotMetError)
end
# This illustrates RSpec's behavior. This spec fails in mock call count verification
@@ -105,44 +105,44 @@ describe Mock, ".install_method for mocks" do
#
it "does not override a previously mocked method having the same arguments" do
Mock.install_method(@mock, :method_call).with(:a).and_return(true)
- @mock.method_call(:a).should == true
+ expect(@mock.method_call(:a)).to eq(true)
Mock.install_method(@mock, :method_call).with(:a).and_return(false)
- @mock.method_call(:a).should == true
- lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ expect(@mock.method_call(:a)).to eq(true)
+ expect { Mock.verify_count }.to raise_error(SpecExpectationNotMetError)
end
it "properly sends #respond_to? calls to the aliased respond_to? method when not matching mock expectations" do
Mock.install_method(@mock, :respond_to?).with(:to_str).and_return('mock to_str')
Mock.install_method(@mock, :respond_to?).with(:to_int).and_return('mock to_int')
- @mock.respond_to?(:to_str).should == 'mock to_str'
- @mock.respond_to?(:to_int).should == 'mock to_int'
- @mock.respond_to?(:to_s).should == true
- @mock.respond_to?(:not_really_a_real_method_seriously).should == false
+ expect(@mock.respond_to?(:to_str)).to eq('mock to_str')
+ expect(@mock.respond_to?(:to_int)).to eq('mock to_int')
+ expect(@mock.respond_to?(:to_s)).to eq(true)
+ expect(@mock.respond_to?(:not_really_a_real_method_seriously)).to eq(false)
end
it "adds to the expectation tally" do
state = double("run state").as_null_object
- state.stub(:state).and_return(double("spec state"))
- MSpec.should_receive(:current).and_return(state)
- MSpec.should_receive(:actions).with(:expectation, state.state)
+ allow(state).to receive(:state).and_return(double("spec state"))
+ expect(MSpec).to receive(:current).and_return(state)
+ expect(MSpec).to receive(:actions).with(:expectation, state.state)
Mock.install_method(@mock, :method_call).and_return(1)
- @mock.method_call.should == 1
+ expect(@mock.method_call).to eq(1)
end
it "registers that an expectation has been encountered" do
state = double("run state").as_null_object
- state.stub(:state).and_return(double("spec state"))
- MSpec.should_receive(:expectation)
+ allow(state).to receive(:state).and_return(double("spec state"))
+ expect(MSpec).to receive(:expectation)
Mock.install_method(@mock, :method_call).and_return(1)
- @mock.method_call.should == 1
+ expect(@mock.method_call).to eq(1)
end
end
-describe Mock, ".install_method for stubs" do
+RSpec.describe Mock, ".install_method for stubs" do
before :each do
@mock = double('install_method')
- MSpec.stub(:actions)
- MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ allow(MSpec).to receive(:actions)
+ allow(MSpec).to receive(:current).and_return(double("spec state").as_null_object)
end
after :each do
@@ -150,7 +150,7 @@ describe Mock, ".install_method for stubs" do
end
it "returns a MockProxy instance" do
- Mock.install_method(@mock, :method_call, :stub).should be_an_instance_of(MockProxy)
+ expect(Mock.install_method(@mock, :method_call, :stub)).to be_an_instance_of(MockProxy)
end
# This illustrates RSpec's behavior. This spec passes on RSpec and we mimic it
@@ -166,26 +166,26 @@ describe Mock, ".install_method for stubs" do
# end
it "inserts new stubs before old stubs" do
Mock.install_method(@mock, :method_call, :stub).with(:a).and_return(true)
- @mock.method_call(:a).should == true
+ expect(@mock.method_call(:a)).to eq(true)
Mock.install_method(@mock, :method_call, :stub).with(:a).and_return(false)
- @mock.method_call(:a).should == false
+ expect(@mock.method_call(:a)).to eq(false)
Mock.verify_count
end
it "does not add to the expectation tally" do
state = double("run state").as_null_object
- state.stub(:state).and_return(double("spec state"))
- MSpec.should_not_receive(:actions)
+ allow(state).to receive(:state).and_return(double("spec state"))
+ expect(MSpec).not_to receive(:actions)
Mock.install_method(@mock, :method_call, :stub).and_return(1)
- @mock.method_call.should == 1
+ expect(@mock.method_call).to eq(1)
end
end
-describe Mock, ".install_method" do
+RSpec.describe Mock, ".install_method" do
before :each do
@mock = double('install_method')
- MSpec.stub(:actions)
- MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ allow(MSpec).to receive(:actions)
+ allow(MSpec).to receive(:current).and_return(double("spec state").as_null_object)
end
after :each do
@@ -193,24 +193,24 @@ describe Mock, ".install_method" do
end
it "does not alias a mocked or stubbed method when installing a new mock or stub" do
- @mock.should_not respond_to(:method_call)
+ expect(@mock).not_to respond_to(:method_call)
Mock.install_method @mock, :method_call
- @mock.should respond_to(:method_call)
- @mock.should_not respond_to(Mock.replaced_name(@mock, :method_call))
+ expect(@mock).to respond_to(:method_call)
+ expect(@mock).not_to respond_to(Mock.replaced_name(Mock.replaced_key(@mock, :method_call)))
Mock.install_method @mock, :method_call, :stub
- @mock.should respond_to(:method_call)
- @mock.should_not respond_to(Mock.replaced_name(@mock, :method_call))
+ expect(@mock).to respond_to(:method_call)
+ expect(@mock).not_to respond_to(Mock.replaced_name(Mock.replaced_key(@mock, :method_call)))
end
end
class MockAndRaiseError < Exception; end
-describe Mock, ".verify_call" do
+RSpec.describe Mock, ".verify_call" do
before :each do
- MSpec.stub(:actions)
- MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ allow(MSpec).to receive(:actions)
+ allow(MSpec).to receive(:current).and_return(double("spec state").as_null_object)
@mock = double('verify_call')
@proxy = Mock.install_method @mock, :method_call
@@ -228,23 +228,23 @@ describe Mock, ".verify_call" do
it "raises an SpecExpectationNotMetError when the mock method does not receive the expected arguments" do
@proxy.with(4, 2)
- lambda {
+ expect {
Mock.verify_call @mock, :method_call, 42
- }.should raise_error(SpecExpectationNotMetError)
+ }.to raise_error(SpecExpectationNotMetError)
end
it "raises an SpecExpectationNotMetError when the mock method is called with arguments but expects none" do
- lambda {
+ expect {
@proxy.with(:no_args)
Mock.verify_call @mock, :method_call, "hello"
- }.should raise_error(SpecExpectationNotMetError)
+ }.to raise_error(SpecExpectationNotMetError)
end
it "raises an SpecExpectationNotMetError when the mock method is called with no arguments but expects some" do
@proxy.with("hello", "beautiful", "world")
- lambda {
+ expect {
Mock.verify_call @mock, :method_call
- }.should raise_error(SpecExpectationNotMetError)
+ }.to raise_error(SpecExpectationNotMetError)
end
it "does not raise an exception when the mock method is called with arguments and is expecting :any_args" do
@@ -257,14 +257,14 @@ describe Mock, ".verify_call" do
Mock.verify_call @mock, :method_call do
ScratchPad.record true
end
- ScratchPad.recorded.should == true
+ expect(ScratchPad.recorded).to eq(true)
end
it "does not yield a passed block when it is not expected to" do
Mock.verify_call @mock, :method_call do
ScratchPad.record true
end
- ScratchPad.recorded.should == nil
+ expect(ScratchPad.recorded).to eq(nil)
end
it "can yield subsequently" do
@@ -274,28 +274,28 @@ describe Mock, ".verify_call" do
Mock.verify_call @mock, :method_call do |arg|
ScratchPad << arg
end
- ScratchPad.recorded.should == [1, 2, 3]
+ expect(ScratchPad.recorded).to eq([1, 2, 3])
end
it "can yield and return an expected value" do
@proxy.and_yield(1).and_return(3)
- Mock.verify_call(@mock, :method_call) { |arg| ScratchPad.record arg }.should == 3
- ScratchPad.recorded.should == 1
+ expect(Mock.verify_call(@mock, :method_call) { |arg| ScratchPad.record arg }).to eq(3)
+ expect(ScratchPad.recorded).to eq(1)
end
it "raises an exception when it is expected to yield but no block is given" do
@proxy.and_yield(1, 2, 3)
- lambda {
+ expect {
Mock.verify_call(@mock, :method_call)
- }.should raise_error(SpecExpectationNotMetError)
+ }.to raise_error(SpecExpectationNotMetError)
end
it "raises an exception when it is expected to yield more arguments than the block can take" do
@proxy.and_yield(1, 2, 3)
- lambda {
+ expect {
Mock.verify_call(@mock, :method_call) {|a, b|}
- }.should raise_error(SpecExpectationNotMetError)
+ }.to raise_error(SpecExpectationNotMetError)
end
it "does not raise an exception when it is expected to yield to a block that can take any number of arguments" do
@@ -307,16 +307,16 @@ describe Mock, ".verify_call" do
it "raises an exception when expected to" do
@proxy.and_raise(MockAndRaiseError)
- lambda {
+ expect {
Mock.verify_call @mock, :method_call
- }.should raise_error(MockAndRaiseError)
+ }.to raise_error(MockAndRaiseError)
end
end
-describe Mock, ".verify_call mixing mocks and stubs" do
+RSpec.describe Mock, ".verify_call mixing mocks and stubs" do
before :each do
- MSpec.stub(:actions)
- MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ allow(MSpec).to receive(:actions)
+ allow(MSpec).to receive(:current).and_return(double("spec state").as_null_object)
@mock = double('verify_call')
end
@@ -330,17 +330,17 @@ describe Mock, ".verify_call mixing mocks and stubs" do
Mock.install_method @mock, :method_call, :stub
Mock.install_method(@mock, :method_call, :mock).with("arg")
- -> {
+ expect {
@mock.method_call
- }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \(\)/)
+ }.to raise_error(SpecExpectationNotMetError, /called with unexpected arguments \(\)/)
- -> {
+ expect {
@mock.method_call("a", "b")
- }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("a", "b"\)/)
+ }.to raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("a", "b"\)/)
- -> {
+ expect {
@mock.method_call("foo")
- }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("foo"\)/)
+ }.to raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("foo"\)/)
@mock.method_call("arg")
end
@@ -349,26 +349,26 @@ describe Mock, ".verify_call mixing mocks and stubs" do
Mock.install_method(@mock, :method_call, :mock).with("arg")
Mock.install_method @mock, :method_call, :stub
- -> {
+ expect {
@mock.method_call
- }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \(\)/)
+ }.to raise_error(SpecExpectationNotMetError, /called with unexpected arguments \(\)/)
- -> {
+ expect {
@mock.method_call("a", "b")
- }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("a", "b"\)/)
+ }.to raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("a", "b"\)/)
- -> {
+ expect {
@mock.method_call("foo")
- }.should raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("foo"\)/)
+ }.to raise_error(SpecExpectationNotMetError, /called with unexpected arguments \("foo"\)/)
@mock.method_call("arg")
end
end
-describe Mock, ".verify_count" do
+RSpec.describe Mock, ".verify_count" do
before :each do
- MSpec.stub(:actions)
- MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ allow(MSpec).to receive(:actions)
+ allow(MSpec).to receive(:current).and_return(double("spec state").as_null_object)
@mock = double('verify_count')
@proxy = Mock.install_method @mock, :method_call
@@ -388,7 +388,7 @@ describe Mock, ".verify_count" do
it "raises an SpecExpectationNotMetError when the mock receives less than at least the expected number of calls" do
@proxy.at_least(2)
@mock.method_call
- lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ expect { Mock.verify_count }.to raise_error(SpecExpectationNotMetError)
end
it "does not raise an exception when the mock receives at most the expected number of calls" do
@@ -403,7 +403,7 @@ describe Mock, ".verify_count" do
@mock.method_call
@mock.method_call
@mock.method_call
- lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ expect { Mock.verify_count }.to raise_error(SpecExpectationNotMetError)
end
it "does not raise an exception when the mock receives exactly the expected number of calls" do
@@ -416,7 +416,7 @@ describe Mock, ".verify_count" do
it "raises an SpecExpectationNotMetError when the mock receives less than exactly the expected number of calls" do
@proxy.exactly(2)
@mock.method_call
- lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ expect { Mock.verify_count }.to raise_error(SpecExpectationNotMetError)
end
it "raises an SpecExpectationNotMetError when the mock receives more than exactly the expected number of calls" do
@@ -424,14 +424,14 @@ describe Mock, ".verify_count" do
@mock.method_call
@mock.method_call
@mock.method_call
- lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError)
+ expect { Mock.verify_count }.to raise_error(SpecExpectationNotMetError)
end
end
-describe Mock, ".verify_count mixing mocks and stubs" do
+RSpec.describe Mock, ".verify_count mixing mocks and stubs" do
before :each do
- MSpec.stub(:actions)
- MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ allow(MSpec).to receive(:actions)
+ allow(MSpec).to receive(:current).and_return(double("spec state").as_null_object)
@mock = double('verify_count')
end
@@ -449,9 +449,9 @@ describe Mock, ".verify_count mixing mocks and stubs" do
Mock.install_method @mock, :method_call, :stub
Mock.install_method @mock, :method_call, :mock
- -> {
+ expect {
Mock.verify_count
- }.should raise_error(SpecExpectationNotMetError, /received it 0 times/)
+ }.to raise_error(SpecExpectationNotMetError, /received it 0 times/)
@mock.method_call
Mock.verify_count
@@ -461,19 +461,19 @@ describe Mock, ".verify_count mixing mocks and stubs" do
Mock.install_method @mock, :method_call, :mock
Mock.install_method @mock, :method_call, :stub
- -> {
+ expect {
Mock.verify_count
- }.should raise_error(SpecExpectationNotMetError, /received it 0 times/)
+ }.to raise_error(SpecExpectationNotMetError, /received it 0 times/)
@mock.method_call
Mock.verify_count
end
end
-describe Mock, ".cleanup" do
+RSpec.describe Mock, ".cleanup" do
before :each do
- MSpec.stub(:actions)
- MSpec.stub(:current).and_return(double("spec state").as_null_object)
+ allow(MSpec).to receive(:actions)
+ allow(MSpec).to receive(:current).and_return(double("spec state").as_null_object)
@mock = double('cleanup')
@proxy = Mock.install_method @mock, :method_call
@@ -484,47 +484,46 @@ describe Mock, ".cleanup" do
end
it "removes the mock method call if it did not override an existing method" do
- @mock.should respond_to(:method_call)
+ expect(@mock).to respond_to(:method_call)
Mock.cleanup
- @mock.should_not respond_to(:method_call)
+ expect(@mock).not_to respond_to(:method_call)
end
it "removes the replaced method if the mock method overrides an existing method" do
def @mock.already_here() :hey end
- @mock.should respond_to(:already_here)
- replaced_name = Mock.replaced_name(@mock, :already_here)
+ expect(@mock).to respond_to(:already_here)
+ replaced_name = Mock.replaced_name(Mock.replaced_key(@mock, :already_here))
Mock.install_method @mock, :already_here
- @mock.should respond_to(replaced_name)
+ expect(@mock).to respond_to(replaced_name)
Mock.cleanup
- @mock.should_not respond_to(replaced_name)
- @mock.should respond_to(:already_here)
- @mock.already_here.should == :hey
+ expect(@mock).not_to respond_to(replaced_name)
+ expect(@mock).to respond_to(:already_here)
+ expect(@mock.already_here).to eq(:hey)
end
it "removes all mock expectations" do
- Mock.mocks.should == { Mock.replaced_key(@mock, :method_call) => [@proxy] }
+ expect(Mock.mocks).to eq({ Mock.replaced_key(@mock, :method_call) => [@proxy] })
Mock.cleanup
- Mock.mocks.should == {}
+ expect(Mock.mocks).to eq({})
end
it "removes all stubs" do
Mock.cleanup # remove @proxy
@stub = Mock.install_method @mock, :method_call, :stub
- Mock.stubs.should == { Mock.replaced_key(@mock, :method_call) => [@stub] }
+ expect(Mock.stubs).to eq({ Mock.replaced_key(@mock, :method_call) => [@stub] })
Mock.cleanup
- Mock.stubs.should == {}
+ expect(Mock.stubs).to eq({})
end
it "removes the replaced name for mocks" do
replaced_key = Mock.replaced_key(@mock, :method_call)
- Mock.should_receive(:clear_replaced).with(replaced_key)
+ expect(Mock).to receive(:clear_replaced).with(replaced_key)
- replaced_name = Mock.replaced_name(@mock, :method_call)
- Mock.replaced?(replaced_name).should be_true
+ expect(Mock.replaced?(replaced_key)).to be_truthy
Mock.cleanup
- Mock.replaced?(replaced_name).should be_false
+ expect(Mock.replaced?(replaced_key)).to be_falsey
end
end
diff --git a/spec/mspec/spec/mocks/proxy_spec.rb b/spec/mspec/spec/mocks/proxy_spec.rb
index d9e754b972..b994634694 100644
--- a/spec/mspec/spec/mocks/proxy_spec.rb
+++ b/spec/mspec/spec/mocks/proxy_spec.rb
@@ -1,276 +1,276 @@
require 'spec_helper'
require 'mspec/mocks/proxy'
-describe MockObject, ".new" do
+RSpec.describe MockObject, ".new" do
it "creates a new mock object" do
m = MockObject.new('not a null object')
- lambda { m.not_a_method }.should raise_error(NoMethodError)
+ expect { m.not_a_method }.to raise_error(NoMethodError)
end
it "creates a new mock object that follows the NullObject pattern" do
m = MockObject.new('null object', :null_object => true)
- m.not_really_a_method.should equal(m)
+ expect(m.not_really_a_method).to equal(m)
end
end
-describe MockProxy, ".new" do
+RSpec.describe MockProxy, ".new" do
it "creates a mock proxy by default" do
- MockProxy.new.mock?.should be_true
+ expect(MockProxy.new.mock?).to be_truthy
end
it "creates a stub proxy by request" do
- MockProxy.new(:stub).stub?.should be_true
+ expect(MockProxy.new(:stub).stub?).to be_truthy
end
it "sets the call expectation to 1 call for a mock" do
- MockProxy.new.count.should == [:exactly, 1]
+ expect(MockProxy.new.count).to eq([:exactly, 1])
end
it "sets the call expectation to any number of times for a stub" do
- MockProxy.new(:stub).count.should == [:any_number_of_times, 0]
+ expect(MockProxy.new(:stub).count).to eq([:any_number_of_times, 0])
end
end
-describe MockProxy, "#count" do
+RSpec.describe MockProxy, "#count" do
before :each do
@proxy = MockProxy.new
end
it "returns the expected number of calls the mock should receive" do
- @proxy.count.should == [:exactly, 1]
- @proxy.at_least(3).count.should == [:at_least, 3]
+ expect(@proxy.count).to eq([:exactly, 1])
+ expect(@proxy.at_least(3).count).to eq([:at_least, 3])
end
end
-describe MockProxy, "#arguments" do
+RSpec.describe MockProxy, "#arguments" do
before :each do
@proxy = MockProxy.new
end
it "returns the expected arguments" do
- @proxy.arguments.should == :any_args
+ expect(@proxy.arguments).to eq(:any_args)
end
end
-describe MockProxy, "#with" do
+RSpec.describe MockProxy, "#with" do
before :each do
@proxy = MockProxy.new
end
it "returns self" do
- @proxy.with(:a).should be_equal(@proxy)
+ expect(@proxy.with(:a)).to be_equal(@proxy)
end
it "raises an ArgumentError if no arguments are given" do
- lambda { @proxy.with }.should raise_error(ArgumentError)
+ expect { @proxy.with }.to raise_error(ArgumentError)
end
it "accepts any number of arguments" do
- @proxy.with(1, 2, 3).should be_an_instance_of(MockProxy)
- @proxy.arguments.should == [1,2,3]
+ expect(@proxy.with(1, 2, 3)).to be_an_instance_of(MockProxy)
+ expect(@proxy.arguments).to eq([1,2,3])
end
end
-describe MockProxy, "#once" do
+RSpec.describe MockProxy, "#once" do
before :each do
@proxy = MockProxy.new
end
it "returns self" do
- @proxy.once.should be_equal(@proxy)
+ expect(@proxy.once).to be_equal(@proxy)
end
it "sets the expected calls to 1" do
@proxy.once
- @proxy.count.should == [:exactly, 1]
+ expect(@proxy.count).to eq([:exactly, 1])
end
it "accepts no arguments" do
- lambda { @proxy.once(:a) }.should raise_error
+ expect { @proxy.once(:a) }.to raise_error
end
end
-describe MockProxy, "#twice" do
+RSpec.describe MockProxy, "#twice" do
before :each do
@proxy = MockProxy.new
end
it "returns self" do
- @proxy.twice.should be_equal(@proxy)
+ expect(@proxy.twice).to be_equal(@proxy)
end
it "sets the expected calls to 2" do
@proxy.twice
- @proxy.count.should == [:exactly, 2]
+ expect(@proxy.count).to eq([:exactly, 2])
end
it "accepts no arguments" do
- lambda { @proxy.twice(:b) }.should raise_error
+ expect { @proxy.twice(:b) }.to raise_error
end
end
-describe MockProxy, "#exactly" do
+RSpec.describe MockProxy, "#exactly" do
before :each do
@proxy = MockProxy.new
end
it "returns self" do
- @proxy.exactly(2).should be_equal(@proxy)
+ expect(@proxy.exactly(2)).to be_equal(@proxy)
end
it "sets the expected calls to exactly n" do
@proxy.exactly(5)
- @proxy.count.should == [:exactly, 5]
+ expect(@proxy.count).to eq([:exactly, 5])
end
it "does not accept an argument that Integer() cannot convert" do
- lambda { @proxy.exactly('x') }.should raise_error
+ expect { @proxy.exactly('x') }.to raise_error
end
end
-describe MockProxy, "#at_least" do
+RSpec.describe MockProxy, "#at_least" do
before :each do
@proxy = MockProxy.new
end
it "returns self" do
- @proxy.at_least(3).should be_equal(@proxy)
+ expect(@proxy.at_least(3)).to be_equal(@proxy)
end
it "sets the expected calls to at least n" do
@proxy.at_least(3)
- @proxy.count.should == [:at_least, 3]
+ expect(@proxy.count).to eq([:at_least, 3])
end
it "accepts :once :twice" do
@proxy.at_least(:once)
- @proxy.count.should == [:at_least, 1]
+ expect(@proxy.count).to eq([:at_least, 1])
@proxy.at_least(:twice)
- @proxy.count.should == [:at_least, 2]
+ expect(@proxy.count).to eq([:at_least, 2])
end
it "does not accept an argument that Integer() cannot convert" do
- lambda { @proxy.at_least('x') }.should raise_error
+ expect { @proxy.at_least('x') }.to raise_error
end
end
-describe MockProxy, "#at_most" do
+RSpec.describe MockProxy, "#at_most" do
before :each do
@proxy = MockProxy.new
end
it "returns self" do
- @proxy.at_most(2).should be_equal(@proxy)
+ expect(@proxy.at_most(2)).to be_equal(@proxy)
end
it "sets the expected calls to at most n" do
@proxy.at_most(2)
- @proxy.count.should == [:at_most, 2]
+ expect(@proxy.count).to eq([:at_most, 2])
end
it "accepts :once, :twice" do
@proxy.at_most(:once)
- @proxy.count.should == [:at_most, 1]
+ expect(@proxy.count).to eq([:at_most, 1])
@proxy.at_most(:twice)
- @proxy.count.should == [:at_most, 2]
+ expect(@proxy.count).to eq([:at_most, 2])
end
it "does not accept an argument that Integer() cannot convert" do
- lambda { @proxy.at_most('x') }.should raise_error
+ expect { @proxy.at_most('x') }.to raise_error
end
end
-describe MockProxy, "#any_number_of_times" do
+RSpec.describe MockProxy, "#any_number_of_times" do
before :each do
@proxy = MockProxy.new
end
it "returns self" do
- @proxy.any_number_of_times.should be_equal(@proxy)
+ expect(@proxy.any_number_of_times).to be_equal(@proxy)
end
it "sets the expected calls to any number of times" do
@proxy.any_number_of_times
- @proxy.count.should == [:any_number_of_times, 0]
+ expect(@proxy.count).to eq([:any_number_of_times, 0])
end
it "does not accept an argument" do
- lambda { @proxy.any_number_of_times(2) }.should raise_error
+ expect { @proxy.any_number_of_times(2) }.to raise_error
end
end
-describe MockProxy, "#and_return" do
+RSpec.describe MockProxy, "#and_return" do
before :each do
@proxy = MockProxy.new
end
it "returns self" do
- @proxy.and_return(false).should equal(@proxy)
+ expect(@proxy.and_return(false)).to equal(@proxy)
end
it "sets the expected return value" do
@proxy.and_return(false)
- @proxy.returning.should == false
+ expect(@proxy.returning).to eq(false)
end
it "accepts any number of return values" do
@proxy.and_return(1, 2, 3)
- @proxy.returning.should == 1
- @proxy.returning.should == 2
- @proxy.returning.should == 3
+ expect(@proxy.returning).to eq(1)
+ expect(@proxy.returning).to eq(2)
+ expect(@proxy.returning).to eq(3)
end
it "implicitly sets the expected number of calls" do
@proxy.and_return(1, 2, 3)
- @proxy.count.should == [:exactly, 3]
+ expect(@proxy.count).to eq([:exactly, 3])
end
it "only sets the expected number of calls if it is higher than what is already set" do
@proxy.at_least(5).times.and_return(1, 2, 3)
- @proxy.count.should == [:at_least, 5]
+ expect(@proxy.count).to eq([:at_least, 5])
@proxy.at_least(2).times.and_return(1, 2, 3)
- @proxy.count.should == [:at_least, 3]
+ expect(@proxy.count).to eq([:at_least, 3])
end
end
-describe MockProxy, "#returning" do
+RSpec.describe MockProxy, "#returning" do
before :each do
@proxy = MockProxy.new
end
it "returns nil by default" do
- @proxy.returning.should be_nil
+ expect(@proxy.returning).to be_nil
end
it "returns the value set by #and_return" do
@proxy.and_return(2)
- @proxy.returning.should == 2
- @proxy.returning.should == 2
+ expect(@proxy.returning).to eq(2)
+ expect(@proxy.returning).to eq(2)
end
it "returns a sequence of values set by #and_return" do
@proxy.and_return(1,2,3,4)
- @proxy.returning.should == 1
- @proxy.returning.should == 2
- @proxy.returning.should == 3
- @proxy.returning.should == 4
- @proxy.returning.should == 4
- @proxy.returning.should == 4
+ expect(@proxy.returning).to eq(1)
+ expect(@proxy.returning).to eq(2)
+ expect(@proxy.returning).to eq(3)
+ expect(@proxy.returning).to eq(4)
+ expect(@proxy.returning).to eq(4)
+ expect(@proxy.returning).to eq(4)
end
end
-describe MockProxy, "#calls" do
+RSpec.describe MockProxy, "#calls" do
before :each do
@proxy = MockProxy.new
end
it "returns the number of times the proxy is called" do
- @proxy.calls.should == 0
+ expect(@proxy.calls).to eq(0)
end
end
-describe MockProxy, "#called" do
+RSpec.describe MockProxy, "#called" do
before :each do
@proxy = MockProxy.new
end
@@ -278,128 +278,128 @@ describe MockProxy, "#called" do
it "increments the number of times the proxy is called" do
@proxy.called
@proxy.called
- @proxy.calls.should == 2
+ expect(@proxy.calls).to eq(2)
end
end
-describe MockProxy, "#times" do
+RSpec.describe MockProxy, "#times" do
before :each do
@proxy = MockProxy.new
end
it "is a no-op" do
- @proxy.times.should == @proxy
+ expect(@proxy.times).to eq(@proxy)
end
end
-describe MockProxy, "#stub?" do
+RSpec.describe MockProxy, "#stub?" do
it "returns true if the proxy is created as a stub" do
- MockProxy.new(:stub).stub?.should be_true
+ expect(MockProxy.new(:stub).stub?).to be_truthy
end
it "returns false if the proxy is created as a mock" do
- MockProxy.new(:mock).stub?.should be_false
+ expect(MockProxy.new(:mock).stub?).to be_falsey
end
end
-describe MockProxy, "#mock?" do
+RSpec.describe MockProxy, "#mock?" do
it "returns true if the proxy is created as a mock" do
- MockProxy.new(:mock).mock?.should be_true
+ expect(MockProxy.new(:mock).mock?).to be_truthy
end
it "returns false if the proxy is created as a stub" do
- MockProxy.new(:stub).mock?.should be_false
+ expect(MockProxy.new(:stub).mock?).to be_falsey
end
end
-describe MockProxy, "#and_yield" do
+RSpec.describe MockProxy, "#and_yield" do
before :each do
@proxy = MockProxy.new
end
it "returns self" do
- @proxy.and_yield(false).should equal(@proxy)
+ expect(@proxy.and_yield(false)).to equal(@proxy)
end
it "sets the expected values to yield" do
- @proxy.and_yield(1).yielding.should == [[1]]
+ expect(@proxy.and_yield(1).yielding).to eq([[1]])
end
it "accepts multiple values to yield" do
- @proxy.and_yield(1, 2, 3).yielding.should == [[1, 2, 3]]
+ expect(@proxy.and_yield(1, 2, 3).yielding).to eq([[1, 2, 3]])
end
end
-describe MockProxy, "#raising" do
+RSpec.describe MockProxy, "#raising" do
before :each do
@proxy = MockProxy.new
end
it "returns nil by default" do
- @proxy.raising.should be_nil
+ expect(@proxy.raising).to be_nil
end
it "returns the exception object passed to #and_raise" do
exc = double("exception")
@proxy.and_raise(exc)
- @proxy.raising.should equal(exc)
+ expect(@proxy.raising).to equal(exc)
end
it "returns an instance of RuntimeError when a String is passed to #and_raise" do
@proxy.and_raise("an error")
exc = @proxy.raising
- exc.should be_an_instance_of(RuntimeError)
- exc.message.should == "an error"
+ expect(exc).to be_an_instance_of(RuntimeError)
+ expect(exc.message).to eq("an error")
end
end
-describe MockProxy, "#yielding" do
+RSpec.describe MockProxy, "#yielding" do
before :each do
@proxy = MockProxy.new
end
it "returns an empty array by default" do
- @proxy.yielding.should == []
+ expect(@proxy.yielding).to eq([])
end
it "returns an array of arrays of values the proxy should yield" do
@proxy.and_yield(3)
- @proxy.yielding.should == [[3]]
+ expect(@proxy.yielding).to eq([[3]])
end
it "returns an accumulation of arrays of values the proxy should yield" do
@proxy.and_yield(1).and_yield(2, 3)
- @proxy.yielding.should == [[1], [2, 3]]
+ expect(@proxy.yielding).to eq([[1], [2, 3]])
end
end
-describe MockProxy, "#yielding?" do
+RSpec.describe MockProxy, "#yielding?" do
before :each do
@proxy = MockProxy.new
end
it "returns false if the proxy is not yielding" do
- @proxy.yielding?.should be_false
+ expect(@proxy.yielding?).to be_falsey
end
it "returns true if the proxy is yielding" do
@proxy.and_yield(1)
- @proxy.yielding?.should be_true
+ expect(@proxy.yielding?).to be_truthy
end
end
-describe MockIntObject, "#to_int" do
+RSpec.describe MockIntObject, "#to_int" do
before :each do
@int = MockIntObject.new(10)
end
it "returns the number if to_int is called" do
- @int.to_int.should == 10
- @int.count.should == [:at_least, 1]
+ expect(@int.to_int).to eq(10)
+ expect(@int.count).to eq([:at_least, 1])
end
it "tries to convert the target to int if to_int is called" do
- MockIntObject.new(@int).to_int.should == 10
- @int.count.should == [:at_least, 1]
+ expect(MockIntObject.new(@int).to_int).to eq(10)
+ expect(@int.count).to eq([:at_least, 1])
end
end
diff --git a/spec/mspec/spec/runner/actions/filter_spec.rb b/spec/mspec/spec/runner/actions/filter_spec.rb
index d185781757..7582b31c1d 100644
--- a/spec/mspec/spec/runner/actions/filter_spec.rb
+++ b/spec/mspec/spec/runner/actions/filter_spec.rb
@@ -3,82 +3,82 @@ require 'mspec/runner/actions/filter'
require 'mspec/runner/mspec'
require 'mspec/runner/tag'
-describe ActionFilter do
+RSpec.describe ActionFilter do
it "creates a filter when not passed a description" do
- MatchFilter.should_not_receive(:new)
+ expect(MatchFilter).not_to receive(:new)
ActionFilter.new(nil, nil)
end
it "creates a filter from a single description" do
- MatchFilter.should_receive(:new).with(nil, "match me")
+ expect(MatchFilter).to receive(:new).with(nil, "match me")
ActionFilter.new(nil, "match me")
end
it "creates a filter from an array of descriptions" do
- MatchFilter.should_receive(:new).with(nil, "match me", "again")
+ expect(MatchFilter).to receive(:new).with(nil, "match me", "again")
ActionFilter.new(nil, ["match me", "again"])
end
end
-describe ActionFilter, "#===" do
+RSpec.describe ActionFilter, "#===" do
before :each do
- MSpec.stub(:read_tags).and_return(["match"])
+ allow(MSpec).to receive(:read_tags).and_return(["match"])
@action = ActionFilter.new(nil, ["catch", "if you"])
end
it "returns false if there are no filters" do
action = ActionFilter.new
- action.===("anything").should == false
+ expect(action.===("anything")).to eq(false)
end
it "returns true if the argument matches any of the descriptions" do
- @action.===("catch").should == true
- @action.===("if you can").should == true
+ expect(@action.===("catch")).to eq(true)
+ expect(@action.===("if you can")).to eq(true)
end
it "returns false if the argument does not match any of the descriptions" do
- @action.===("patch me").should == false
- @action.===("if I can").should == false
+ expect(@action.===("patch me")).to eq(false)
+ expect(@action.===("if I can")).to eq(false)
end
end
-describe ActionFilter, "#load" do
+RSpec.describe ActionFilter, "#load" do
before :each do
@tag = SpecTag.new "tag(comment):description"
end
it "creates a filter from a single tag" do
- MSpec.should_receive(:read_tags).with(["tag"]).and_return([@tag])
- MatchFilter.should_receive(:new).with(nil, "description")
+ expect(MSpec).to receive(:read_tags).with(["tag"]).and_return([@tag])
+ expect(MatchFilter).to receive(:new).with(nil, "description")
ActionFilter.new("tag", nil).load
end
it "creates a filter from an array of tags" do
- MSpec.should_receive(:read_tags).with(["tag", "key"]).and_return([@tag])
- MatchFilter.should_receive(:new).with(nil, "description")
+ expect(MSpec).to receive(:read_tags).with(["tag", "key"]).and_return([@tag])
+ expect(MatchFilter).to receive(:new).with(nil, "description")
ActionFilter.new(["tag", "key"], nil).load
end
it "creates a filter from both tags and descriptions" do
- MSpec.should_receive(:read_tags).and_return([@tag])
+ expect(MSpec).to receive(:read_tags).and_return([@tag])
filter = ActionFilter.new("tag", ["match me", "again"])
- MatchFilter.should_receive(:new).with(nil, "description")
+ expect(MatchFilter).to receive(:new).with(nil, "description")
filter.load
end
end
-describe ActionFilter, "#register" do
+RSpec.describe ActionFilter, "#register" do
it "registers itself with MSpec for the :load actions" do
filter = ActionFilter.new
- MSpec.should_receive(:register).with(:load, filter)
+ expect(MSpec).to receive(:register).with(:load, filter)
filter.register
end
end
-describe ActionFilter, "#unregister" do
+RSpec.describe ActionFilter, "#unregister" do
it "unregisters itself with MSpec for the :load actions" do
filter = ActionFilter.new
- MSpec.should_receive(:unregister).with(:load, filter)
+ expect(MSpec).to receive(:unregister).with(:load, filter)
filter.unregister
end
end
diff --git a/spec/mspec/spec/runner/actions/tag_spec.rb b/spec/mspec/spec/runner/actions/tag_spec.rb
index 92df362d02..738e9a18c9 100644
--- a/spec/mspec/spec/runner/actions/tag_spec.rb
+++ b/spec/mspec/spec/runner/actions/tag_spec.rb
@@ -4,211 +4,211 @@ require 'mspec/runner/mspec'
require 'mspec/runner/example'
require 'mspec/runner/tag'
-describe TagAction, ".new" do
+RSpec.describe TagAction, ".new" do
it "creates an MatchFilter with its tag and desc arguments" do
filter = double('action filter').as_null_object
- MatchFilter.should_receive(:new).with(nil, "some", "thing").and_return(filter)
+ expect(MatchFilter).to receive(:new).with(nil, "some", "thing").and_return(filter)
TagAction.new :add, :all, nil, nil, ["tag", "key"], ["some", "thing"]
end
end
-describe TagAction, "#===" do
+RSpec.describe TagAction, "#===" do
before :each do
- MSpec.stub(:read_tags).and_return(["match"])
+ allow(MSpec).to receive(:read_tags).and_return(["match"])
@action = TagAction.new :add, :fail, nil, nil, nil, ["catch", "if you"]
end
it "returns true if there are no filters" do
action = TagAction.new :add, :all, nil, nil
- action.===("anything").should == true
+ expect(action.===("anything")).to eq(true)
end
it "returns true if the argument matches any of the descriptions" do
- @action.===("catch").should == true
- @action.===("if you can").should == true
+ expect(@action.===("catch")).to eq(true)
+ expect(@action.===("if you can")).to eq(true)
end
it "returns false if the argument does not match any of the descriptions" do
- @action.===("patch me").should == false
- @action.===("if I can").should == false
+ expect(@action.===("patch me")).to eq(false)
+ expect(@action.===("if I can")).to eq(false)
end
end
-describe TagAction, "#exception?" do
+RSpec.describe TagAction, "#exception?" do
before :each do
@action = TagAction.new :add, :fail, nil, nil, nil, nil
end
it "returns false if no exception has been raised while evaluating an example" do
- @action.exception?.should be_false
+ expect(@action.exception?).to be_falsey
end
it "returns true if an exception was raised while evaluating an example" do
@action.exception ExceptionState.new nil, nil, Exception.new("failed")
- @action.exception?.should be_true
+ expect(@action.exception?).to be_truthy
end
end
-describe TagAction, "#outcome?" do
+RSpec.describe TagAction, "#outcome?" do
before :each do
- MSpec.stub(:read_tags).and_return([])
+ allow(MSpec).to receive(:read_tags).and_return([])
@exception = ExceptionState.new nil, nil, Exception.new("failed")
end
it "returns true if outcome is :fail and the spec fails" do
action = TagAction.new :add, :fail, nil, nil, nil, nil
action.exception @exception
- action.outcome?.should == true
+ expect(action.outcome?).to eq(true)
end
it "returns false if the outcome is :fail and the spec passes" do
action = TagAction.new :add, :fail, nil, nil, nil, nil
- action.outcome?.should == false
+ expect(action.outcome?).to eq(false)
end
it "returns true if the outcome is :pass and the spec passes" do
action = TagAction.new :del, :pass, nil, nil, nil, nil
- action.outcome?.should == true
+ expect(action.outcome?).to eq(true)
end
it "returns false if the outcome is :pass and the spec fails" do
action = TagAction.new :del, :pass, nil, nil, nil, nil
action.exception @exception
- action.outcome?.should == false
+ expect(action.outcome?).to eq(false)
end
it "returns true if the outcome is :all" do
action = TagAction.new :add, :all, nil, nil, nil, nil
action.exception @exception
- action.outcome?.should == true
+ expect(action.outcome?).to eq(true)
end
end
-describe TagAction, "#before" do
+RSpec.describe TagAction, "#before" do
it "resets the #exception? flag to false" do
action = TagAction.new :add, :fail, nil, nil, nil, nil
- action.exception?.should be_false
+ expect(action.exception?).to be_falsey
action.exception ExceptionState.new(nil, nil, Exception.new("Fail!"))
- action.exception?.should be_true
+ expect(action.exception?).to be_truthy
action.before(ExampleState.new(ContextState.new("describe"), "it"))
- action.exception?.should be_false
+ expect(action.exception?).to be_falsey
end
end
-describe TagAction, "#exception" do
+RSpec.describe TagAction, "#exception" do
it "sets the #exception? flag" do
action = TagAction.new :add, :fail, nil, nil, nil, nil
- action.exception?.should be_false
+ expect(action.exception?).to be_falsey
action.exception ExceptionState.new(nil, nil, Exception.new("Fail!"))
- action.exception?.should be_true
+ expect(action.exception?).to be_truthy
end
end
-describe TagAction, "#after when action is :add" do
+RSpec.describe TagAction, "#after when action is :add" do
before :each do
- MSpec.stub(:read_tags).and_return([])
+ allow(MSpec).to receive(:read_tags).and_return([])
context = ContextState.new "Catch#me"
@state = ExampleState.new context, "if you can"
@tag = SpecTag.new "tag(comment):Catch#me if you can"
- SpecTag.stub(:new).and_return(@tag)
+ allow(SpecTag).to receive(:new).and_return(@tag)
@exception = ExceptionState.new nil, nil, Exception.new("failed")
end
it "does not write a tag if the description does not match" do
- MSpec.should_not_receive(:write_tag)
+ expect(MSpec).not_to receive(:write_tag)
action = TagAction.new :add, :all, "tag", "comment", nil, "match"
action.after @state
end
it "does not write a tag if outcome is :fail and the spec passed" do
- MSpec.should_not_receive(:write_tag)
+ expect(MSpec).not_to receive(:write_tag)
action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
action.after @state
end
it "writes a tag if the outcome is :fail and the spec failed" do
- MSpec.should_receive(:write_tag).with(@tag)
+ expect(MSpec).to receive(:write_tag).with(@tag)
action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
action.exception @exception
action.after @state
end
it "does not write a tag if outcome is :pass and the spec failed" do
- MSpec.should_not_receive(:write_tag)
+ expect(MSpec).not_to receive(:write_tag)
action = TagAction.new :add, :pass, "tag", "comment", nil, "can"
action.exception @exception
action.after @state
end
it "writes a tag if the outcome is :pass and the spec passed" do
- MSpec.should_receive(:write_tag).with(@tag)
+ expect(MSpec).to receive(:write_tag).with(@tag)
action = TagAction.new :add, :pass, "tag", "comment", nil, "can"
action.after @state
end
it "writes a tag if the outcome is :all" do
- MSpec.should_receive(:write_tag).with(@tag)
+ expect(MSpec).to receive(:write_tag).with(@tag)
action = TagAction.new :add, :all, "tag", "comment", nil, "can"
action.after @state
end
end
-describe TagAction, "#after when action is :del" do
+RSpec.describe TagAction, "#after when action is :del" do
before :each do
- MSpec.stub(:read_tags).and_return([])
+ allow(MSpec).to receive(:read_tags).and_return([])
context = ContextState.new "Catch#me"
@state = ExampleState.new context, "if you can"
@tag = SpecTag.new "tag(comment):Catch#me if you can"
- SpecTag.stub(:new).and_return(@tag)
+ allow(SpecTag).to receive(:new).and_return(@tag)
@exception = ExceptionState.new nil, nil, Exception.new("failed")
end
it "does not delete a tag if the description does not match" do
- MSpec.should_not_receive(:delete_tag)
+ expect(MSpec).not_to receive(:delete_tag)
action = TagAction.new :del, :all, "tag", "comment", nil, "match"
action.after @state
end
it "does not delete a tag if outcome is :fail and the spec passed" do
- MSpec.should_not_receive(:delete_tag)
+ expect(MSpec).not_to receive(:delete_tag)
action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
action.after @state
end
it "deletes a tag if the outcome is :fail and the spec failed" do
- MSpec.should_receive(:delete_tag).with(@tag)
+ expect(MSpec).to receive(:delete_tag).with(@tag)
action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
action.exception @exception
action.after @state
end
it "does not delete a tag if outcome is :pass and the spec failed" do
- MSpec.should_not_receive(:delete_tag)
+ expect(MSpec).not_to receive(:delete_tag)
action = TagAction.new :del, :pass, "tag", "comment", nil, "can"
action.exception @exception
action.after @state
end
it "deletes a tag if the outcome is :pass and the spec passed" do
- MSpec.should_receive(:delete_tag).with(@tag)
+ expect(MSpec).to receive(:delete_tag).with(@tag)
action = TagAction.new :del, :pass, "tag", "comment", nil, "can"
action.after @state
end
it "deletes a tag if the outcome is :all" do
- MSpec.should_receive(:delete_tag).with(@tag)
+ expect(MSpec).to receive(:delete_tag).with(@tag)
action = TagAction.new :del, :all, "tag", "comment", nil, "can"
action.after @state
end
end
-describe TagAction, "#finish" do
+RSpec.describe TagAction, "#finish" do
before :each do
$stdout = @out = IOStub.new
context = ContextState.new "Catch#me"
@state = ExampleState.new context, "if you can"
- MSpec.stub(:write_tag).and_return(true)
- MSpec.stub(:delete_tag).and_return(true)
+ allow(MSpec).to receive(:write_tag).and_return(true)
+ allow(MSpec).to receive(:delete_tag).and_return(true)
end
after :each do
@@ -217,99 +217,97 @@ describe TagAction, "#finish" do
it "reports no specs tagged if none where tagged" do
action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
- action.stub(:outcome?).and_return(false)
+ allow(action).to receive(:outcome?).and_return(false)
action.after @state
action.finish
- @out.should == "\nTagAction: no specs were tagged with 'tag'\n"
+ expect(@out).to eq("\nTagAction: no specs were tagged with 'tag'\n")
end
it "reports no specs tagged if none where tagged" do
action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
- action.stub(:outcome?).and_return(false)
+ allow(action).to receive(:outcome?).and_return(false)
action.after @state
action.finish
- @out.should == "\nTagAction: no tags 'tag' were deleted\n"
+ expect(@out).to eq("\nTagAction: no tags 'tag' were deleted\n")
end
it "reports the spec descriptions that were tagged" do
action = TagAction.new :add, :fail, "tag", "comment", nil, "can"
- action.stub(:outcome?).and_return(true)
+ allow(action).to receive(:outcome?).and_return(true)
action.after @state
action.finish
- @out.should ==
-%[
+ expect(@out).to eq(%[
TagAction: specs tagged with 'tag':
Catch#me if you can
-]
+])
end
it "reports the spec descriptions for the tags that were deleted" do
action = TagAction.new :del, :fail, "tag", "comment", nil, "can"
- action.stub(:outcome?).and_return(true)
+ allow(action).to receive(:outcome?).and_return(true)
action.after @state
action.finish
- @out.should ==
-%[
+ expect(@out).to eq(%[
TagAction: tag 'tag' deleted for specs:
Catch#me if you can
-]
+])
end
end
-describe TagAction, "#register" do
+RSpec.describe TagAction, "#register" do
before :each do
- MSpec.stub(:register)
- MSpec.stub(:read_tags).and_return([])
+ allow(MSpec).to receive(:register)
+ allow(MSpec).to receive(:read_tags).and_return([])
@action = TagAction.new :add, :all, nil, nil, nil, nil
end
it "registers itself with MSpec for the :before event" do
- MSpec.should_receive(:register).with(:before, @action)
+ expect(MSpec).to receive(:register).with(:before, @action)
@action.register
end
it "registers itself with MSpec for the :after event" do
- MSpec.should_receive(:register).with(:after, @action)
+ expect(MSpec).to receive(:register).with(:after, @action)
@action.register
end
it "registers itself with MSpec for the :exception event" do
- MSpec.should_receive(:register).with(:exception, @action)
+ expect(MSpec).to receive(:register).with(:exception, @action)
@action.register
end
it "registers itself with MSpec for the :finish event" do
- MSpec.should_receive(:register).with(:finish, @action)
+ expect(MSpec).to receive(:register).with(:finish, @action)
@action.register
end
end
-describe TagAction, "#unregister" do
+RSpec.describe TagAction, "#unregister" do
before :each do
- MSpec.stub(:unregister)
- MSpec.stub(:read_tags).and_return([])
+ allow(MSpec).to receive(:unregister)
+ allow(MSpec).to receive(:read_tags).and_return([])
@action = TagAction.new :add, :all, nil, nil, nil, nil
end
it "unregisters itself with MSpec for the :before event" do
- MSpec.should_receive(:unregister).with(:before, @action)
+ expect(MSpec).to receive(:unregister).with(:before, @action)
@action.unregister
end
it "unregisters itself with MSpec for the :after event" do
- MSpec.should_receive(:unregister).with(:after, @action)
+ expect(MSpec).to receive(:unregister).with(:after, @action)
@action.unregister
end
it "unregisters itself with MSpec for the :exception event" do
- MSpec.should_receive(:unregister).with(:exception, @action)
+ expect(MSpec).to receive(:unregister).with(:exception, @action)
@action.unregister
end
it "unregisters itself with MSpec for the :finish event" do
- MSpec.should_receive(:unregister).with(:finish, @action)
+ expect(MSpec).to receive(:unregister).with(:finish, @action)
@action.unregister
end
end
diff --git a/spec/mspec/spec/runner/actions/taglist_spec.rb b/spec/mspec/spec/runner/actions/taglist_spec.rb
index 418c761c2d..b6a5400f7d 100644
--- a/spec/mspec/spec/runner/actions/taglist_spec.rb
+++ b/spec/mspec/spec/runner/actions/taglist_spec.rb
@@ -4,34 +4,34 @@ require 'mspec/runner/mspec'
require 'mspec/runner/example'
require 'mspec/runner/tag'
-describe TagListAction, "#include?" do
+RSpec.describe TagListAction, "#include?" do
it "returns true" do
- TagListAction.new.include?(:anything).should be_true
+ expect(TagListAction.new.include?(:anything)).to be_truthy
end
end
-describe TagListAction, "#===" do
+RSpec.describe TagListAction, "#===" do
before :each do
tag = SpecTag.new "fails:description"
- MSpec.stub(:read_tags).and_return([tag])
+ allow(MSpec).to receive(:read_tags).and_return([tag])
@filter = double("MatchFilter").as_null_object
- MatchFilter.stub(:new).and_return(@filter)
+ allow(MatchFilter).to receive(:new).and_return(@filter)
@action = TagListAction.new
@action.load
end
it "returns true if filter === string returns true" do
- @filter.should_receive(:===).with("str").and_return(true)
- @action.===("str").should be_true
+ expect(@filter).to receive(:===).with("str").and_return(true)
+ expect(@action.===("str")).to be_truthy
end
it "returns false if filter === string returns false" do
- @filter.should_receive(:===).with("str").and_return(false)
- @action.===("str").should be_false
+ expect(@filter).to receive(:===).with("str").and_return(false)
+ expect(@action.===("str")).to be_falsey
end
end
-describe TagListAction, "#start" do
+RSpec.describe TagListAction, "#start" do
before :each do
@stdout = $stdout
$stdout = IOStub.new
@@ -44,48 +44,48 @@ describe TagListAction, "#start" do
it "prints a banner for specific tags" do
action = TagListAction.new ["fails", "unstable"]
action.start
- $stdout.should == "\nListing specs tagged with 'fails', 'unstable'\n\n"
+ expect($stdout).to eq("\nListing specs tagged with 'fails', 'unstable'\n\n")
end
it "prints a banner for all tags" do
action = TagListAction.new
action.start
- $stdout.should == "\nListing all tagged specs\n\n"
+ expect($stdout).to eq("\nListing all tagged specs\n\n")
end
end
-describe TagListAction, "#load" do
+RSpec.describe TagListAction, "#load" do
before :each do
@t1 = SpecTag.new "fails:I fail"
@t2 = SpecTag.new "unstable:I'm unstable"
end
it "creates a MatchFilter for matching tags" do
- MSpec.should_receive(:read_tags).with(["fails"]).and_return([@t1])
- MatchFilter.should_receive(:new).with(nil, "I fail")
+ expect(MSpec).to receive(:read_tags).with(["fails"]).and_return([@t1])
+ expect(MatchFilter).to receive(:new).with(nil, "I fail")
TagListAction.new(["fails"]).load
end
it "creates a MatchFilter for all tags" do
- MSpec.should_receive(:read_tags).and_return([@t1, @t2])
- MatchFilter.should_receive(:new).with(nil, "I fail", "I'm unstable")
+ expect(MSpec).to receive(:read_tags).and_return([@t1, @t2])
+ expect(MatchFilter).to receive(:new).with(nil, "I fail", "I'm unstable")
TagListAction.new.load
end
it "does not create a MatchFilter if there are no matching tags" do
- MSpec.stub(:read_tags).and_return([])
- MatchFilter.should_not_receive(:new)
+ allow(MSpec).to receive(:read_tags).and_return([])
+ expect(MatchFilter).not_to receive(:new)
TagListAction.new(["fails"]).load
end
end
-describe TagListAction, "#after" do
+RSpec.describe TagListAction, "#after" do
before :each do
@stdout = $stdout
$stdout = IOStub.new
@state = double("ExampleState")
- @state.stub(:description).and_return("str")
+ allow(@state).to receive(:description).and_return("str")
@action = TagListAction.new
end
@@ -95,58 +95,58 @@ describe TagListAction, "#after" do
end
it "prints nothing if the filter does not match" do
- @action.should_receive(:===).with("str").and_return(false)
+ expect(@action).to receive(:===).with("str").and_return(false)
@action.after(@state)
- $stdout.should == ""
+ expect($stdout).to eq("")
end
it "prints the example description if the filter matches" do
- @action.should_receive(:===).with("str").and_return(true)
+ expect(@action).to receive(:===).with("str").and_return(true)
@action.after(@state)
- $stdout.should == "str\n"
+ expect($stdout).to eq("str\n")
end
end
-describe TagListAction, "#register" do
+RSpec.describe TagListAction, "#register" do
before :each do
- MSpec.stub(:register)
+ allow(MSpec).to receive(:register)
@action = TagListAction.new
end
it "registers itself with MSpec for the :start event" do
- MSpec.should_receive(:register).with(:start, @action)
+ expect(MSpec).to receive(:register).with(:start, @action)
@action.register
end
it "registers itself with MSpec for the :load event" do
- MSpec.should_receive(:register).with(:load, @action)
+ expect(MSpec).to receive(:register).with(:load, @action)
@action.register
end
it "registers itself with MSpec for the :after event" do
- MSpec.should_receive(:register).with(:after, @action)
+ expect(MSpec).to receive(:register).with(:after, @action)
@action.register
end
end
-describe TagListAction, "#unregister" do
+RSpec.describe TagListAction, "#unregister" do
before :each do
- MSpec.stub(:unregister)
+ allow(MSpec).to receive(:unregister)
@action = TagListAction.new
end
it "unregisters itself with MSpec for the :start event" do
- MSpec.should_receive(:unregister).with(:start, @action)
+ expect(MSpec).to receive(:unregister).with(:start, @action)
@action.unregister
end
it "unregisters itself with MSpec for the :load event" do
- MSpec.should_receive(:unregister).with(:load, @action)
+ expect(MSpec).to receive(:unregister).with(:load, @action)
@action.unregister
end
it "unregisters itself with MSpec for the :after event" do
- MSpec.should_receive(:unregister).with(:after, @action)
+ expect(MSpec).to receive(:unregister).with(:after, @action)
@action.unregister
end
end
diff --git a/spec/mspec/spec/runner/actions/tagpurge_spec.rb b/spec/mspec/spec/runner/actions/tagpurge_spec.rb
index 27ad2a1470..37df0afd5a 100644
--- a/spec/mspec/spec/runner/actions/tagpurge_spec.rb
+++ b/spec/mspec/spec/runner/actions/tagpurge_spec.rb
@@ -4,7 +4,7 @@ require 'mspec/runner/mspec'
require 'mspec/runner/example'
require 'mspec/runner/tag'
-describe TagPurgeAction, "#start" do
+RSpec.describe TagPurgeAction, "#start" do
before :each do
@stdout = $stdout
$stdout = IOStub.new
@@ -17,45 +17,45 @@ describe TagPurgeAction, "#start" do
it "prints a banner" do
action = TagPurgeAction.new
action.start
- $stdout.should == "\nRemoving tags not matching any specs\n\n"
+ expect($stdout).to eq("\nRemoving tags not matching any specs\n\n")
end
end
-describe TagPurgeAction, "#load" do
+RSpec.describe TagPurgeAction, "#load" do
before :each do
@t1 = SpecTag.new "fails:I fail"
@t2 = SpecTag.new "unstable:I'm unstable"
end
it "creates a MatchFilter for all tags" do
- MSpec.should_receive(:read_tags).and_return([@t1, @t2])
- MatchFilter.should_receive(:new).with(nil, "I fail", "I'm unstable")
+ expect(MSpec).to receive(:read_tags).and_return([@t1, @t2])
+ expect(MatchFilter).to receive(:new).with(nil, "I fail", "I'm unstable")
TagPurgeAction.new.load
end
end
-describe TagPurgeAction, "#after" do
+RSpec.describe TagPurgeAction, "#after" do
before :each do
@state = double("ExampleState")
- @state.stub(:description).and_return("str")
+ allow(@state).to receive(:description).and_return("str")
@action = TagPurgeAction.new
end
it "does not save the description if the filter does not match" do
- @action.should_receive(:===).with("str").and_return(false)
+ expect(@action).to receive(:===).with("str").and_return(false)
@action.after @state
- @action.matching.should == []
+ expect(@action.matching).to eq([])
end
it "saves the description if the filter matches" do
- @action.should_receive(:===).with("str").and_return(true)
+ expect(@action).to receive(:===).with("str").and_return(true)
@action.after @state
- @action.matching.should == ["str"]
+ expect(@action.matching).to eq(["str"])
end
end
-describe TagPurgeAction, "#unload" do
+RSpec.describe TagPurgeAction, "#unload" do
before :each do
@stdout = $stdout
$stdout = IOStub.new
@@ -64,11 +64,11 @@ describe TagPurgeAction, "#unload" do
@t2 = SpecTag.new "unstable:I'm unstable"
@t3 = SpecTag.new "fails:I'm unstable"
- MSpec.stub(:read_tags).and_return([@t1, @t2, @t3])
- MSpec.stub(:write_tags)
+ allow(MSpec).to receive(:read_tags).and_return([@t1, @t2, @t3])
+ allow(MSpec).to receive(:write_tags)
@state = double("ExampleState")
- @state.stub(:description).and_return("I'm unstable")
+ allow(@state).to receive(:description).and_return("I'm unstable")
@action = TagPurgeAction.new
@action.load
@@ -80,37 +80,37 @@ describe TagPurgeAction, "#unload" do
end
it "does not rewrite any tags if there were no tags for the specs" do
- MSpec.should_receive(:read_tags).and_return([])
- MSpec.should_receive(:delete_tags)
- MSpec.should_not_receive(:write_tags)
+ expect(MSpec).to receive(:read_tags).and_return([])
+ expect(MSpec).to receive(:delete_tags)
+ expect(MSpec).not_to receive(:write_tags)
@action.load
@action.after @state
@action.unload
- $stdout.should == ""
+ expect($stdout).to eq("")
end
it "rewrites tags that were matched" do
- MSpec.should_receive(:write_tags).with([@t2, @t3])
+ expect(MSpec).to receive(:write_tags).with([@t2, @t3])
@action.unload
end
it "prints tags that were not matched" do
@action.unload
- $stdout.should == "I fail\n"
+ expect($stdout).to eq("I fail\n")
end
end
-describe TagPurgeAction, "#unload" do
+RSpec.describe TagPurgeAction, "#unload" do
before :each do
@stdout = $stdout
$stdout = IOStub.new
- MSpec.stub(:read_tags).and_return([])
+ allow(MSpec).to receive(:read_tags).and_return([])
@state = double("ExampleState")
- @state.stub(:description).and_return("I'm unstable")
+ allow(@state).to receive(:description).and_return("I'm unstable")
@action = TagPurgeAction.new
@action.load
@@ -122,33 +122,33 @@ describe TagPurgeAction, "#unload" do
end
it "deletes the tag file if no tags were found" do
- MSpec.should_not_receive(:write_tags)
- MSpec.should_receive(:delete_tags)
+ expect(MSpec).not_to receive(:write_tags)
+ expect(MSpec).to receive(:delete_tags)
@action.unload
- $stdout.should == ""
+ expect($stdout).to eq("")
end
end
-describe TagPurgeAction, "#register" do
+RSpec.describe TagPurgeAction, "#register" do
before :each do
- MSpec.stub(:register)
+ allow(MSpec).to receive(:register)
@action = TagPurgeAction.new
end
it "registers itself with MSpec for the :unload event" do
- MSpec.should_receive(:register).with(:unload, @action)
+ expect(MSpec).to receive(:register).with(:unload, @action)
@action.register
end
end
-describe TagPurgeAction, "#unregister" do
+RSpec.describe TagPurgeAction, "#unregister" do
before :each do
- MSpec.stub(:unregister)
+ allow(MSpec).to receive(:unregister)
@action = TagPurgeAction.new
end
it "unregisters itself with MSpec for the :unload event" do
- MSpec.should_receive(:unregister).with(:unload, @action)
+ expect(MSpec).to receive(:unregister).with(:unload, @action)
@action.unregister
end
end
diff --git a/spec/mspec/spec/runner/actions/tally_spec.rb b/spec/mspec/spec/runner/actions/tally_spec.rb
index be4635ffeb..d80ab1164a 100644
--- a/spec/mspec/spec/runner/actions/tally_spec.rb
+++ b/spec/mspec/spec/runner/actions/tally_spec.rb
@@ -4,169 +4,169 @@ require 'mspec/runner/actions/tally'
require 'mspec/runner/mspec'
require 'mspec/runner/example'
-describe Tally, "#files!" do
+RSpec.describe Tally, "#files!" do
before :each do
@tally = Tally.new
end
it "increments the count returned by #files" do
@tally.files! 3
- @tally.files.should == 3
+ expect(@tally.files).to eq(3)
@tally.files!
- @tally.files.should == 4
+ expect(@tally.files).to eq(4)
end
end
-describe Tally, "#examples!" do
+RSpec.describe Tally, "#examples!" do
before :each do
@tally = Tally.new
end
it "increments the count returned by #examples" do
@tally.examples! 2
- @tally.examples.should == 2
+ expect(@tally.examples).to eq(2)
@tally.examples! 2
- @tally.examples.should == 4
+ expect(@tally.examples).to eq(4)
end
end
-describe Tally, "#expectations!" do
+RSpec.describe Tally, "#expectations!" do
before :each do
@tally = Tally.new
end
it "increments the count returned by #expectations" do
@tally.expectations!
- @tally.expectations.should == 1
+ expect(@tally.expectations).to eq(1)
@tally.expectations! 3
- @tally.expectations.should == 4
+ expect(@tally.expectations).to eq(4)
end
end
-describe Tally, "#failures!" do
+RSpec.describe Tally, "#failures!" do
before :each do
@tally = Tally.new
end
it "increments the count returned by #failures" do
@tally.failures! 1
- @tally.failures.should == 1
+ expect(@tally.failures).to eq(1)
@tally.failures!
- @tally.failures.should == 2
+ expect(@tally.failures).to eq(2)
end
end
-describe Tally, "#errors!" do
+RSpec.describe Tally, "#errors!" do
before :each do
@tally = Tally.new
end
it "increments the count returned by #errors" do
@tally.errors!
- @tally.errors.should == 1
+ expect(@tally.errors).to eq(1)
@tally.errors! 2
- @tally.errors.should == 3
+ expect(@tally.errors).to eq(3)
end
end
-describe Tally, "#guards!" do
+RSpec.describe Tally, "#guards!" do
before :each do
@tally = Tally.new
end
it "increments the count returned by #guards" do
@tally.guards!
- @tally.guards.should == 1
+ expect(@tally.guards).to eq(1)
@tally.guards! 2
- @tally.guards.should == 3
+ expect(@tally.guards).to eq(3)
end
end
-describe Tally, "#file" do
+RSpec.describe Tally, "#file" do
before :each do
@tally = Tally.new
end
it "returns a formatted string of the number of #files" do
- @tally.file.should == "0 files"
+ expect(@tally.file).to eq("0 files")
@tally.files!
- @tally.file.should == "1 file"
+ expect(@tally.file).to eq("1 file")
@tally.files!
- @tally.file.should == "2 files"
+ expect(@tally.file).to eq("2 files")
end
end
-describe Tally, "#example" do
+RSpec.describe Tally, "#example" do
before :each do
@tally = Tally.new
end
it "returns a formatted string of the number of #examples" do
- @tally.example.should == "0 examples"
+ expect(@tally.example).to eq("0 examples")
@tally.examples!
- @tally.example.should == "1 example"
+ expect(@tally.example).to eq("1 example")
@tally.examples!
- @tally.example.should == "2 examples"
+ expect(@tally.example).to eq("2 examples")
end
end
-describe Tally, "#expectation" do
+RSpec.describe Tally, "#expectation" do
before :each do
@tally = Tally.new
end
it "returns a formatted string of the number of #expectations" do
- @tally.expectation.should == "0 expectations"
+ expect(@tally.expectation).to eq("0 expectations")
@tally.expectations!
- @tally.expectation.should == "1 expectation"
+ expect(@tally.expectation).to eq("1 expectation")
@tally.expectations!
- @tally.expectation.should == "2 expectations"
+ expect(@tally.expectation).to eq("2 expectations")
end
end
-describe Tally, "#failure" do
+RSpec.describe Tally, "#failure" do
before :each do
@tally = Tally.new
end
it "returns a formatted string of the number of #failures" do
- @tally.failure.should == "0 failures"
+ expect(@tally.failure).to eq("0 failures")
@tally.failures!
- @tally.failure.should == "1 failure"
+ expect(@tally.failure).to eq("1 failure")
@tally.failures!
- @tally.failure.should == "2 failures"
+ expect(@tally.failure).to eq("2 failures")
end
end
-describe Tally, "#error" do
+RSpec.describe Tally, "#error" do
before :each do
@tally = Tally.new
end
it "returns a formatted string of the number of #errors" do
- @tally.error.should == "0 errors"
+ expect(@tally.error).to eq("0 errors")
@tally.errors!
- @tally.error.should == "1 error"
+ expect(@tally.error).to eq("1 error")
@tally.errors!
- @tally.error.should == "2 errors"
+ expect(@tally.error).to eq("2 errors")
end
end
-describe Tally, "#guard" do
+RSpec.describe Tally, "#guard" do
before :each do
@tally = Tally.new
end
it "returns a formatted string of the number of #guards" do
- @tally.guard.should == "0 guards"
+ expect(@tally.guard).to eq("0 guards")
@tally.guards!
- @tally.guard.should == "1 guard"
+ expect(@tally.guard).to eq("1 guard")
@tally.guards!
- @tally.guard.should == "2 guards"
+ expect(@tally.guard).to eq("2 guards")
end
end
-describe Tally, "#format" do
+RSpec.describe Tally, "#format" do
before :each do
@tally = Tally.new
end
@@ -181,7 +181,7 @@ describe Tally, "#format" do
@tally.expectations! 4
@tally.errors!
@tally.tagged!
- @tally.format.should == "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged"
+ expect(@tally.format).to eq("1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged")
end
it "includes guards if MSpec is in verify mode" do
@@ -192,8 +192,9 @@ describe Tally, "#format" do
@tally.errors!
@tally.tagged!
@tally.guards!
- @tally.format.should ==
+ expect(@tally.format).to eq(
"1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged, 1 guard"
+ )
end
it "includes guards if MSpec is in report mode" do
@@ -204,8 +205,9 @@ describe Tally, "#format" do
@tally.errors!
@tally.tagged!
@tally.guards! 2
- @tally.format.should ==
+ expect(@tally.format).to eq(
"1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged, 2 guards"
+ )
end
it "includes guards if MSpec is in report_on mode" do
@@ -215,23 +217,24 @@ describe Tally, "#format" do
@tally.expectations! 4
@tally.errors!
@tally.guards! 2
- @tally.format.should ==
+ expect(@tally.format).to eq(
"1 file, 2 examples, 4 expectations, 0 failures, 1 error, 0 tagged, 2 guards"
+ )
end
end
-describe TallyAction, "#counter" do
+RSpec.describe TallyAction, "#counter" do
before :each do
@tally = TallyAction.new
@state = ExampleState.new("describe", "it")
end
it "returns the Tally object" do
- @tally.counter.should be_kind_of(Tally)
+ expect(@tally.counter).to be_kind_of(Tally)
end
end
-describe TallyAction, "#load" do
+RSpec.describe TallyAction, "#load" do
before :each do
@tally = TallyAction.new
@state = ExampleState.new("describe", "it")
@@ -239,11 +242,11 @@ describe TallyAction, "#load" do
it "increments the count returned by Tally#files" do
@tally.load
- @tally.counter.files.should == 1
+ expect(@tally.counter.files).to eq(1)
end
end
-describe TallyAction, "#expectation" do
+RSpec.describe TallyAction, "#expectation" do
before :each do
@tally = TallyAction.new
@state = ExampleState.new("describe", "it")
@@ -251,11 +254,11 @@ describe TallyAction, "#expectation" do
it "increments the count returned by Tally#expectations" do
@tally.expectation @state
- @tally.counter.expectations.should == 1
+ expect(@tally.counter.expectations).to eq(1)
end
end
-describe TallyAction, "#example" do
+RSpec.describe TallyAction, "#example" do
before :each do
@tally = TallyAction.new
@state = ExampleState.new("describe", "it")
@@ -263,14 +266,14 @@ describe TallyAction, "#example" do
it "increments counts returned by Tally#examples" do
@tally.example @state, nil
- @tally.counter.examples.should == 1
- @tally.counter.expectations.should == 0
- @tally.counter.failures.should == 0
- @tally.counter.errors.should == 0
+ expect(@tally.counter.examples).to eq(1)
+ expect(@tally.counter.expectations).to eq(0)
+ expect(@tally.counter.failures).to eq(0)
+ expect(@tally.counter.errors).to eq(0)
end
end
-describe TallyAction, "#exception" do
+RSpec.describe TallyAction, "#exception" do
before :each do
@tally = TallyAction.new
@state = ExampleState.new("describe", "it")
@@ -279,14 +282,14 @@ describe TallyAction, "#exception" do
it "increments counts returned by Tally#failures" do
exc = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("Failed!")
@tally.exception exc
- @tally.counter.examples.should == 0
- @tally.counter.expectations.should == 0
- @tally.counter.failures.should == 1
- @tally.counter.errors.should == 0
+ expect(@tally.counter.examples).to eq(0)
+ expect(@tally.counter.expectations).to eq(0)
+ expect(@tally.counter.failures).to eq(1)
+ expect(@tally.counter.errors).to eq(0)
end
end
-describe TallyAction, "#exception" do
+RSpec.describe TallyAction, "#exception" do
before :each do
@tally = TallyAction.new
@state = ExampleState.new("describe", "it")
@@ -295,14 +298,14 @@ describe TallyAction, "#exception" do
it "increments counts returned by Tally#errors" do
exc = ExceptionState.new nil, nil, Exception.new("Error!")
@tally.exception exc
- @tally.counter.examples.should == 0
- @tally.counter.expectations.should == 0
- @tally.counter.failures.should == 0
- @tally.counter.errors.should == 1
+ expect(@tally.counter.examples).to eq(0)
+ expect(@tally.counter.expectations).to eq(0)
+ expect(@tally.counter.failures).to eq(0)
+ expect(@tally.counter.errors).to eq(1)
end
end
-describe TallyAction, "#format" do
+RSpec.describe TallyAction, "#format" do
before :each do
@tally = TallyAction.new
@state = ExampleState.new("describe", "it")
@@ -315,38 +318,38 @@ describe TallyAction, "#format" do
@tally.expectation @state
exc = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("Failed!")
@tally.exception exc
- @tally.format.should == "1 file, 1 example, 2 expectations, 1 failure, 0 errors, 0 tagged"
+ expect(@tally.format).to eq("1 file, 1 example, 2 expectations, 1 failure, 0 errors, 0 tagged")
end
end
-describe TallyAction, "#register" do
+RSpec.describe TallyAction, "#register" do
before :each do
@tally = TallyAction.new
@state = ExampleState.new("describe", "it")
end
it "registers itself with MSpec for appropriate actions" do
- MSpec.should_receive(:register).with(:load, @tally)
- MSpec.should_receive(:register).with(:exception, @tally)
- MSpec.should_receive(:register).with(:example, @tally)
- MSpec.should_receive(:register).with(:tagged, @tally)
- MSpec.should_receive(:register).with(:expectation, @tally)
+ expect(MSpec).to receive(:register).with(:load, @tally)
+ expect(MSpec).to receive(:register).with(:exception, @tally)
+ expect(MSpec).to receive(:register).with(:example, @tally)
+ expect(MSpec).to receive(:register).with(:tagged, @tally)
+ expect(MSpec).to receive(:register).with(:expectation, @tally)
@tally.register
end
end
-describe TallyAction, "#unregister" do
+RSpec.describe TallyAction, "#unregister" do
before :each do
@tally = TallyAction.new
@state = ExampleState.new("describe", "it")
end
it "unregisters itself with MSpec for appropriate actions" do
- MSpec.should_receive(:unregister).with(:load, @tally)
- MSpec.should_receive(:unregister).with(:exception, @tally)
- MSpec.should_receive(:unregister).with(:example, @tally)
- MSpec.should_receive(:unregister).with(:tagged, @tally)
- MSpec.should_receive(:unregister).with(:expectation, @tally)
+ expect(MSpec).to receive(:unregister).with(:load, @tally)
+ expect(MSpec).to receive(:unregister).with(:exception, @tally)
+ expect(MSpec).to receive(:unregister).with(:example, @tally)
+ expect(MSpec).to receive(:unregister).with(:tagged, @tally)
+ expect(MSpec).to receive(:unregister).with(:expectation, @tally)
@tally.unregister
end
end
diff --git a/spec/mspec/spec/runner/actions/timer_spec.rb b/spec/mspec/spec/runner/actions/timer_spec.rb
index 417367d5a2..28a317177b 100644
--- a/spec/mspec/spec/runner/actions/timer_spec.rb
+++ b/spec/mspec/spec/runner/actions/timer_spec.rb
@@ -3,7 +3,7 @@ require 'mspec/runner/actions/timer'
require 'mspec/runner/mspec'
require 'time'
-describe TimerAction do
+RSpec.describe TimerAction do
before :each do
@timer = TimerAction.new
@start_time = Time.utc(2009, 3, 30, 14, 5, 19)
@@ -11,34 +11,34 @@ describe TimerAction do
end
it "responds to #start by recording the current time" do
- Time.should_receive(:now)
+ expect(Time).to receive(:now)
@timer.start
end
it "responds to #finish by recording the current time" do
- Time.should_receive(:now)
+ expect(Time).to receive(:now)
@timer.finish
end
it "responds to #elapsed by returning the difference between stop and start" do
- Time.stub(:now).and_return(@start_time)
+ allow(Time).to receive(:now).and_return(@start_time)
@timer.start
- Time.stub(:now).and_return(@stop_time)
+ allow(Time).to receive(:now).and_return(@stop_time)
@timer.finish
- @timer.elapsed.should == 33
+ expect(@timer.elapsed).to eq(33)
end
it "responds to #format by returning a readable string of elapsed time" do
- Time.stub(:now).and_return(@start_time)
+ allow(Time).to receive(:now).and_return(@start_time)
@timer.start
- Time.stub(:now).and_return(@stop_time)
+ allow(Time).to receive(:now).and_return(@stop_time)
@timer.finish
- @timer.format.should == "Finished in 33.000000 seconds"
+ expect(@timer.format).to eq("Finished in 33.000000 seconds")
end
it "responds to #register by registering itself with MSpec for appropriate actions" do
- MSpec.should_receive(:register).with(:start, @timer)
- MSpec.should_receive(:register).with(:finish, @timer)
+ expect(MSpec).to receive(:register).with(:start, @timer)
+ expect(MSpec).to receive(:register).with(:finish, @timer)
@timer.register
end
end
diff --git a/spec/mspec/spec/runner/context_spec.rb b/spec/mspec/spec/runner/context_spec.rb
index c5aa691323..9ebc708c0c 100644
--- a/spec/mspec/spec/runner/context_spec.rb
+++ b/spec/mspec/spec/runner/context_spec.rb
@@ -6,7 +6,7 @@ require 'mspec/mocks/mock'
require 'mspec/runner/context'
require 'mspec/runner/example'
-describe ContextState, "#describe" do
+RSpec.describe ContextState, "#describe" do
before :each do
@state = ContextState.new "C#m"
@proc = proc { ScratchPad.record :a }
@@ -15,63 +15,63 @@ describe ContextState, "#describe" do
it "evaluates the passed block" do
@state.describe(&@proc)
- ScratchPad.recorded.should == :a
+ expect(ScratchPad.recorded).to eq(:a)
end
it "evaluates the passed block via #protect" do
- @state.should_receive(:protect).with("C#m", @proc, false)
+ expect(@state).to receive(:protect).with("C#m", @proc, false)
@state.describe(&@proc)
end
it "registers #parent as the current MSpec ContextState" do
parent = ContextState.new ""
@state.parent = parent
- MSpec.should_receive(:register_current).with(parent)
+ expect(MSpec).to receive(:register_current).with(parent)
@state.describe { }
end
it "registers self with MSpec when #shared? is true" do
state = ContextState.new "something shared", :shared => true
- MSpec.should_receive(:register_shared).with(state)
+ expect(MSpec).to receive(:register_shared).with(state)
state.describe { }
end
end
-describe ContextState, "#shared?" do
+RSpec.describe ContextState, "#shared?" do
it "returns false when the ContextState is not shared" do
- ContextState.new("").shared?.should be_false
+ expect(ContextState.new("").shared?).to be_falsey
end
it "returns true when the ContextState is shared" do
- ContextState.new("", {:shared => true}).shared?.should be_true
+ expect(ContextState.new("", {:shared => true}).shared?).to be_truthy
end
end
-describe ContextState, "#to_s" do
+RSpec.describe ContextState, "#to_s" do
it "returns a description string for self when passed a Module" do
- ContextState.new(Object).to_s.should == "Object"
+ expect(ContextState.new(Object).to_s).to eq("Object")
end
it "returns a description string for self when passed a String" do
- ContextState.new("SomeClass").to_s.should == "SomeClass"
+ expect(ContextState.new("SomeClass").to_s).to eq("SomeClass")
end
end
-describe ContextState, "#description" do
+RSpec.describe ContextState, "#description" do
before :each do
@state = ContextState.new "when empty"
@parent = ContextState.new "Toplevel"
end
it "returns a composite description string from self and all parents" do
- @parent.description.should == "Toplevel"
- @state.description.should == "when empty"
+ expect(@parent.description).to eq("Toplevel")
+ expect(@state.description).to eq("when empty")
@state.parent = @parent
- @state.description.should == "Toplevel when empty"
+ expect(@state.description).to eq("Toplevel when empty")
end
end
-describe ContextState, "#it" do
+RSpec.describe ContextState, "#it" do
before :each do
@state = ContextState.new ""
@proc = lambda {|*| }
@@ -80,25 +80,25 @@ describe ContextState, "#it" do
end
it "creates an ExampleState instance for the block" do
- ExampleState.should_receive(:new).with(@state, "it", @proc).and_return(@ex)
+ expect(ExampleState).to receive(:new).with(@state, "it", @proc).and_return(@ex)
@state.describe(&@proc)
@state.it("it", &@proc)
end
it "calls registered :add actions" do
- ExampleState.should_receive(:new).with(@state, "it", @proc).and_return(@ex)
+ expect(ExampleState).to receive(:new).with(@state, "it", @proc).and_return(@ex)
add_action = double("add")
- add_action.should_receive(:add).with(@ex).and_return { ScratchPad.record :add }
+ expect(add_action).to receive(:add).with(@ex) { ScratchPad.record :add }
MSpec.register :add, add_action
@state.it("it", &@proc)
- ScratchPad.recorded.should == :add
+ expect(ScratchPad.recorded).to eq(:add)
MSpec.unregister :add, add_action
end
end
-describe ContextState, "#examples" do
+RSpec.describe ContextState, "#examples" do
before :each do
@state = ContextState.new ""
end
@@ -106,11 +106,11 @@ describe ContextState, "#examples" do
it "returns a list of all examples in this ContextState" do
@state.it("first") { }
@state.it("second") { }
- @state.examples.size.should == 2
+ expect(@state.examples.size).to eq(2)
end
end
-describe ContextState, "#before" do
+RSpec.describe ContextState, "#before" do
before :each do
@state = ContextState.new ""
@proc = lambda {|*| }
@@ -118,16 +118,16 @@ describe ContextState, "#before" do
it "records the block for :each" do
@state.before(:each, &@proc)
- @state.before(:each).should == [@proc]
+ expect(@state.before(:each)).to eq([@proc])
end
it "records the block for :all" do
@state.before(:all, &@proc)
- @state.before(:all).should == [@proc]
+ expect(@state.before(:all)).to eq([@proc])
end
end
-describe ContextState, "#after" do
+RSpec.describe ContextState, "#after" do
before :each do
@state = ContextState.new ""
@proc = lambda {|*| }
@@ -135,16 +135,16 @@ describe ContextState, "#after" do
it "records the block for :each" do
@state.after(:each, &@proc)
- @state.after(:each).should == [@proc]
+ expect(@state.after(:each)).to eq([@proc])
end
it "records the block for :all" do
@state.after(:all, &@proc)
- @state.after(:all).should == [@proc]
+ expect(@state.after(:all)).to eq([@proc])
end
end
-describe ContextState, "#pre" do
+RSpec.describe ContextState, "#pre" do
before :each do
@a = lambda {|*| }
@b = lambda {|*| }
@@ -161,17 +161,17 @@ describe ContextState, "#pre" do
it "returns before(:each) actions in the order they were defined" do
@state.before(:each, &@a)
@state.before(:each, &@b)
- @state.pre(:each).should == [@c, @a, @b]
+ expect(@state.pre(:each)).to eq([@c, @a, @b])
end
it "returns before(:all) actions in the order they were defined" do
@state.before(:all, &@a)
@state.before(:all, &@b)
- @state.pre(:all).should == [@c, @a, @b]
+ expect(@state.pre(:all)).to eq([@c, @a, @b])
end
end
-describe ContextState, "#post" do
+RSpec.describe ContextState, "#post" do
before :each do
@a = lambda {|*| }
@b = lambda {|*| }
@@ -188,17 +188,17 @@ describe ContextState, "#post" do
it "returns after(:each) actions in the reverse order they were defined" do
@state.after(:each, &@a)
@state.after(:each, &@b)
- @state.post(:each).should == [@b, @a, @c]
+ expect(@state.post(:each)).to eq([@b, @a, @c])
end
it "returns after(:all) actions in the reverse order they were defined" do
@state.after(:all, &@a)
@state.after(:all, &@b)
- @state.post(:all).should == [@b, @a, @c]
+ expect(@state.post(:all)).to eq([@b, @a, @c])
end
end
-describe ContextState, "#protect" do
+RSpec.describe ContextState, "#protect" do
before :each do
ScratchPad.record []
@a = lambda {|*| ScratchPad << :a }
@@ -207,41 +207,41 @@ describe ContextState, "#protect" do
end
it "returns true and does execute any blocks if check and MSpec.mode?(:pretend) are true" do
- MSpec.should_receive(:mode?).with(:pretend).and_return(true)
- ContextState.new("").protect("message", [@a, @b]).should be_true
- ScratchPad.recorded.should == []
+ expect(MSpec).to receive(:mode?).with(:pretend).and_return(true)
+ expect(ContextState.new("").protect("message", [@a, @b])).to be_truthy
+ expect(ScratchPad.recorded).to eq([])
end
it "executes the blocks if MSpec.mode?(:pretend) is false" do
- MSpec.should_receive(:mode?).with(:pretend).and_return(false)
+ expect(MSpec).to receive(:mode?).with(:pretend).and_return(false)
ContextState.new("").protect("message", [@a, @b])
- ScratchPad.recorded.should == [:a, :b]
+ expect(ScratchPad.recorded).to eq([:a, :b])
end
it "executes the blocks if check is false" do
ContextState.new("").protect("message", [@a, @b], false)
- ScratchPad.recorded.should == [:a, :b]
+ expect(ScratchPad.recorded).to eq([:a, :b])
end
it "returns true if none of the blocks raise an exception" do
- ContextState.new("").protect("message", [@a, @b]).should be_true
+ expect(ContextState.new("").protect("message", [@a, @b])).to be_truthy
end
it "returns false if any of the blocks raise an exception" do
- ContextState.new("").protect("message", [@a, @c, @b]).should be_false
+ expect(ContextState.new("").protect("message", [@a, @c, @b])).to be_falsey
end
end
-describe ContextState, "#parent=" do
+RSpec.describe ContextState, "#parent=" do
before :each do
@state = ContextState.new ""
@parent = double("describe")
- @parent.stub(:parent).and_return(nil)
- @parent.stub(:child)
+ allow(@parent).to receive(:parent).and_return(nil)
+ allow(@parent).to receive(:child)
end
it "does not set self as a child of parent if shared" do
- @parent.should_not_receive(:child)
+ expect(@parent).not_to receive(:child)
state = ContextState.new "", :shared => true
state.parent = @parent
end
@@ -249,70 +249,70 @@ describe ContextState, "#parent=" do
it "does not set parents if shared" do
state = ContextState.new "", :shared => true
state.parent = @parent
- state.parents.should == [state]
+ expect(state.parents).to eq([state])
end
it "sets self as a child of parent" do
- @parent.should_receive(:child).with(@state)
+ expect(@parent).to receive(:child).with(@state)
@state.parent = @parent
end
it "creates the list of parents" do
@state.parent = @parent
- @state.parents.should == [@parent, @state]
+ expect(@state.parents).to eq([@parent, @state])
end
end
-describe ContextState, "#parent" do
+RSpec.describe ContextState, "#parent" do
before :each do
@state = ContextState.new ""
@parent = double("describe")
- @parent.stub(:parent).and_return(nil)
- @parent.stub(:child)
+ allow(@parent).to receive(:parent).and_return(nil)
+ allow(@parent).to receive(:child)
end
it "returns nil if parent has not been set" do
- @state.parent.should be_nil
+ expect(@state.parent).to be_nil
end
it "returns the parent" do
@state.parent = @parent
- @state.parent.should == @parent
+ expect(@state.parent).to eq(@parent)
end
end
-describe ContextState, "#parents" do
+RSpec.describe ContextState, "#parents" do
before :each do
@first = ContextState.new ""
@second = ContextState.new ""
@parent = double("describe")
- @parent.stub(:parent).and_return(nil)
- @parent.stub(:child)
+ allow(@parent).to receive(:parent).and_return(nil)
+ allow(@parent).to receive(:child)
end
it "returns a list of all enclosing ContextState instances" do
@first.parent = @parent
@second.parent = @first
- @second.parents.should == [@parent, @first, @second]
+ expect(@second.parents).to eq([@parent, @first, @second])
end
end
-describe ContextState, "#child" do
+RSpec.describe ContextState, "#child" do
before :each do
@first = ContextState.new ""
@second = ContextState.new ""
@parent = double("describe")
- @parent.stub(:parent).and_return(nil)
- @parent.stub(:child)
+ allow(@parent).to receive(:parent).and_return(nil)
+ allow(@parent).to receive(:child)
end
it "adds the ContextState to the list of contained ContextStates" do
@first.child @second
- @first.children.should == [@second]
+ expect(@first.children).to eq([@second])
end
end
-describe ContextState, "#children" do
+RSpec.describe ContextState, "#children" do
before :each do
@parent = ContextState.new ""
@first = ContextState.new ""
@@ -322,12 +322,12 @@ describe ContextState, "#children" do
it "returns the list of directly contained ContextStates" do
@first.parent = @parent
@second.parent = @first
- @parent.children.should == [@first]
- @first.children.should == [@second]
+ expect(@parent.children).to eq([@first])
+ expect(@first.children).to eq([@second])
end
end
-describe ContextState, "#state" do
+RSpec.describe ContextState, "#state" do
before :each do
MSpec.store :before, []
MSpec.store :after, []
@@ -336,7 +336,7 @@ describe ContextState, "#state" do
end
it "returns nil if no spec is being executed" do
- @state.state.should == nil
+ expect(@state.state).to eq(nil)
end
it "returns a ExampleState instance if an example is being executed" do
@@ -344,16 +344,16 @@ describe ContextState, "#state" do
@state.describe { }
@state.it("") { ScratchPad.record ScratchPad.recorded.state }
@state.process
- @state.state.should == nil
- ScratchPad.recorded.should be_kind_of(ExampleState)
+ expect(@state.state).to eq(nil)
+ expect(ScratchPad.recorded).to be_kind_of(ExampleState)
end
end
-describe ContextState, "#process" do
+RSpec.describe ContextState, "#process" do
before :each do
MSpec.store :before, []
MSpec.store :after, []
- MSpec.stub(:register_current)
+ allow(MSpec).to receive(:register_current)
@state = ContextState.new ""
@state.describe { }
@@ -368,7 +368,7 @@ describe ContextState, "#process" do
@state.before(:all, &@b)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == [:a, :b]
+ expect(ScratchPad.recorded).to eq([:a, :b])
end
it "calls each after(:all) block" do
@@ -376,22 +376,22 @@ describe ContextState, "#process" do
@state.after(:all, &@b)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == [:b, :a]
+ expect(ScratchPad.recorded).to eq([:b, :a])
end
it "calls each it block" do
@state.it("one", &@a)
@state.it("two", &@b)
@state.process
- ScratchPad.recorded.should == [:a, :b]
+ expect(ScratchPad.recorded).to eq([:a, :b])
end
it "does not call the #it block if #filtered? returns true" do
@state.it("one", &@a)
@state.it("two", &@b)
- @state.examples.first.stub(:filtered?).and_return(true)
+ allow(@state.examples.first).to receive(:filtered?).and_return(true)
@state.process
- ScratchPad.recorded.should == [:b]
+ expect(ScratchPad.recorded).to eq([:b])
end
it "calls each before(:each) block" do
@@ -399,7 +399,7 @@ describe ContextState, "#process" do
@state.before(:each, &@b)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == [:a, :b]
+ expect(ScratchPad.recorded).to eq([:a, :b])
end
it "calls each after(:each) block" do
@@ -407,20 +407,20 @@ describe ContextState, "#process" do
@state.after(:each, &@b)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == [:b, :a]
+ expect(ScratchPad.recorded).to eq([:b, :a])
end
it "calls Mock.cleanup for each it block" do
@state.it("") { }
@state.it("") { }
- Mock.should_receive(:cleanup).twice
+ expect(Mock).to receive(:cleanup).twice
@state.process
end
it "calls Mock.verify_count for each it block" do
@state.it("") { }
@state.it("") { }
- Mock.should_receive(:verify_count).twice
+ expect(Mock).to receive(:verify_count).twice
@state.process
end
@@ -428,7 +428,7 @@ describe ContextState, "#process" do
ScratchPad.record []
@state.describe { ScratchPad << :a }
@state.process
- ScratchPad.recorded.should == [:a]
+ expect(ScratchPad.recorded).to eq([:a])
end
it "creates a new ExampleState instance for each example" do
@@ -436,21 +436,21 @@ describe ContextState, "#process" do
@state.describe { }
@state.it("it") { ScratchPad.record ScratchPad.recorded.state }
@state.process
- ScratchPad.recorded.should be_kind_of(ExampleState)
+ expect(ScratchPad.recorded).to be_kind_of(ExampleState)
end
it "clears the expectations flag before evaluating the #it block" do
MSpec.clear_expectations
- MSpec.should_receive(:clear_expectations)
+ expect(MSpec).to receive(:clear_expectations)
@state.it("it") { ScratchPad.record MSpec.expectation? }
@state.process
- ScratchPad.recorded.should be_false
+ expect(ScratchPad.recorded).to be_falsey
end
it "shuffles the spec list if MSpec.randomize? is true" do
MSpec.randomize = true
begin
- MSpec.should_receive(:shuffle)
+ expect(MSpec).to receive(:shuffle)
@state.it("") { }
@state.process
ensure
@@ -459,24 +459,24 @@ describe ContextState, "#process" do
end
it "sets the current MSpec ContextState" do
- MSpec.should_receive(:register_current).with(@state)
+ expect(MSpec).to receive(:register_current).with(@state)
@state.process
end
it "resets the current MSpec ContextState to nil when there are examples" do
- MSpec.should_receive(:register_current).with(nil)
+ expect(MSpec).to receive(:register_current).with(nil)
@state.it("") { }
@state.process
end
it "resets the current MSpec ContextState to nil when there are no examples" do
- MSpec.should_receive(:register_current).with(nil)
+ expect(MSpec).to receive(:register_current).with(nil)
@state.process
end
it "call #process on children when there are examples" do
child = ContextState.new ""
- child.should_receive(:process)
+ expect(child).to receive(:process)
@state.child child
@state.it("") { }
@state.process
@@ -484,13 +484,13 @@ describe ContextState, "#process" do
it "call #process on children when there are no examples" do
child = ContextState.new ""
- child.should_receive(:process)
+ expect(child).to receive(:process)
@state.child child
@state.process
end
end
-describe ContextState, "#process" do
+RSpec.describe ContextState, "#process" do
before :each do
MSpec.store :exception, []
@@ -514,23 +514,23 @@ describe ContextState, "#process" do
it "raises an SpecExpectationNotFoundError if an #it block does not contain an expectation" do
@state.it("it") { }
@state.process
- ScratchPad.recorded.should == :exception
+ expect(ScratchPad.recorded).to eq(:exception)
end
it "does not raise an SpecExpectationNotFoundError if an #it block does contain an expectation" do
@state.it("it") { MSpec.expectation }
@state.process
- ScratchPad.recorded.should be_nil
+ expect(ScratchPad.recorded).to be_nil
end
it "does not raise an SpecExpectationNotFoundError if the #it block causes a failure" do
@state.it("it") { raise Exception, "Failed!" }
@state.process
- ScratchPad.recorded.should be_nil
+ expect(ScratchPad.recorded).to be_nil
end
end
-describe ContextState, "#process" do
+RSpec.describe ContextState, "#process" do
before :each do
MSpec.store :example, []
@@ -554,18 +554,18 @@ describe ContextState, "#process" do
@state.it("") { MSpec.expectation }
@state.process
- ScratchPad.recorded.first.should be_kind_of(ExampleState)
- ScratchPad.recorded.last.should be_kind_of(Proc)
+ expect(ScratchPad.recorded.first).to be_kind_of(ExampleState)
+ expect(ScratchPad.recorded.last).to be_kind_of(Proc)
end
it "does not call registered example actions if the example has no block" do
@state.it("empty example")
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
end
-describe ContextState, "#process" do
+RSpec.describe ContextState, "#process" do
before :each do
MSpec.store :before, []
MSpec.store :after, []
@@ -582,30 +582,30 @@ describe ContextState, "#process" do
it "calls registered :before actions with the current ExampleState instance" do
before = double("before")
- before.should_receive(:before).and_return {
+ expect(before).to receive(:before) {
ScratchPad.record :before
@spec_state = @state.state
}
MSpec.register :before, before
@state.process
- ScratchPad.recorded.should == :before
- @spec_state.should be_kind_of(ExampleState)
+ expect(ScratchPad.recorded).to eq(:before)
+ expect(@spec_state).to be_kind_of(ExampleState)
end
it "calls registered :after actions with the current ExampleState instance" do
after = double("after")
- after.should_receive(:after).and_return {
+ expect(after).to receive(:after) {
ScratchPad.record :after
@spec_state = @state.state
}
MSpec.register :after, after
@state.process
- ScratchPad.recorded.should == :after
- @spec_state.should be_kind_of(ExampleState)
+ expect(ScratchPad.recorded).to eq(:after)
+ expect(@spec_state).to be_kind_of(ExampleState)
end
end
-describe ContextState, "#process" do
+RSpec.describe ContextState, "#process" do
before :each do
MSpec.store :enter, []
MSpec.store :leave, []
@@ -622,22 +622,22 @@ describe ContextState, "#process" do
it "calls registered :enter actions with the current #describe string" do
enter = double("enter")
- enter.should_receive(:enter).with("C#m").and_return { ScratchPad.record :enter }
+ expect(enter).to receive(:enter).with("C#m") { ScratchPad.record :enter }
MSpec.register :enter, enter
@state.process
- ScratchPad.recorded.should == :enter
+ expect(ScratchPad.recorded).to eq(:enter)
end
it "calls registered :leave actions" do
leave = double("leave")
- leave.should_receive(:leave).and_return { ScratchPad.record :leave }
+ expect(leave).to receive(:leave) { ScratchPad.record :leave }
MSpec.register :leave, leave
@state.process
- ScratchPad.recorded.should == :leave
+ expect(ScratchPad.recorded).to eq(:leave)
end
end
-describe ContextState, "#process when an exception is raised in before(:all)" do
+RSpec.describe ContextState, "#process when an exception is raised in before(:all)" do
before :each do
MSpec.store :before, []
MSpec.store :after, []
@@ -661,43 +661,43 @@ describe ContextState, "#process when an exception is raised in before(:all)" do
@state.before(:each, &@a)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
it "does not call the it block" do
@state.it("one", &@a)
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
it "does not call after(:each)" do
@state.after(:each, &@a)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
it "does not call after(:each)" do
@state.after(:all, &@a)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
it "does not call Mock.verify_count" do
@state.it("") { }
- Mock.should_not_receive(:verify_count)
+ expect(Mock).not_to receive(:verify_count)
@state.process
end
it "calls Mock.cleanup" do
@state.it("") { }
- Mock.should_receive(:cleanup)
+ expect(Mock).to receive(:cleanup)
@state.process
end
end
-describe ContextState, "#process when an exception is raised in before(:each)" do
+RSpec.describe ContextState, "#process when an exception is raised in before(:each)" do
before :each do
MSpec.store :before, []
MSpec.store :after, []
@@ -720,24 +720,24 @@ describe ContextState, "#process when an exception is raised in before(:each)" d
it "does not call the it block" do
@state.it("one", &@a)
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
- it "does call after(:each)" do
+ it "calls after(:each)" do
@state.after(:each, &@a)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == [:a]
+ expect(ScratchPad.recorded).to eq([:a])
end
- it "does not call Mock.verify_count" do
+ it "calls Mock.verify_count" do
@state.it("") { }
- Mock.should_not_receive(:verify_count)
+ expect(Mock).to receive(:verify_count)
@state.process
end
end
-describe ContextState, "#process in pretend mode" do
+RSpec.describe ContextState, "#process in pretend mode" do
before :all do
MSpec.register_mode :pretend
end
@@ -763,30 +763,30 @@ describe ContextState, "#process in pretend mode" do
it "calls registered :before actions with the current ExampleState instance" do
before = double("before")
- before.should_receive(:before).and_return {
+ expect(before).to receive(:before) {
ScratchPad.record :before
@spec_state = @state.state
}
MSpec.register :before, before
@state.process
- ScratchPad.recorded.should == :before
- @spec_state.should be_kind_of(ExampleState)
+ expect(ScratchPad.recorded).to eq(:before)
+ expect(@spec_state).to be_kind_of(ExampleState)
end
it "calls registered :after actions with the current ExampleState instance" do
after = double("after")
- after.should_receive(:after).and_return {
+ expect(after).to receive(:after) {
ScratchPad.record :after
@spec_state = @state.state
}
MSpec.register :after, after
@state.process
- ScratchPad.recorded.should == :after
- @spec_state.should be_kind_of(ExampleState)
+ expect(ScratchPad.recorded).to eq(:after)
+ expect(@spec_state).to be_kind_of(ExampleState)
end
end
-describe ContextState, "#process in pretend mode" do
+RSpec.describe ContextState, "#process in pretend mode" do
before :all do
MSpec.register_mode :pretend
end
@@ -811,7 +811,7 @@ describe ContextState, "#process in pretend mode" do
ScratchPad.record []
@state.describe { ScratchPad << :a }
@state.process
- ScratchPad.recorded.should == [:a]
+ expect(ScratchPad.recorded).to eq([:a])
end
it "does not call any before(:all) block" do
@@ -819,7 +819,7 @@ describe ContextState, "#process in pretend mode" do
@state.before(:all, &@b)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
it "does not call any after(:all) block" do
@@ -827,14 +827,14 @@ describe ContextState, "#process in pretend mode" do
@state.after(:all, &@b)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
it "does not call any it block" do
@state.it("one", &@a)
@state.it("two", &@b)
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
it "does not call any before(:each) block" do
@@ -842,7 +842,7 @@ describe ContextState, "#process in pretend mode" do
@state.before(:each, &@b)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
it "does not call any after(:each) block" do
@@ -850,18 +850,18 @@ describe ContextState, "#process in pretend mode" do
@state.after(:each, &@b)
@state.it("") { }
@state.process
- ScratchPad.recorded.should == []
+ expect(ScratchPad.recorded).to eq([])
end
it "does not call Mock.cleanup" do
@state.it("") { }
@state.it("") { }
- Mock.should_not_receive(:cleanup)
+ expect(Mock).not_to receive(:cleanup)
@state.process
end
end
-describe ContextState, "#process in pretend mode" do
+RSpec.describe ContextState, "#process in pretend mode" do
before :all do
MSpec.register_mode :pretend
end
@@ -886,26 +886,26 @@ describe ContextState, "#process in pretend mode" do
it "calls registered :enter actions with the current #describe string" do
enter = double("enter")
- enter.should_receive(:enter).and_return { ScratchPad.record :enter }
+ expect(enter).to receive(:enter) { ScratchPad.record :enter }
MSpec.register :enter, enter
@state.process
- ScratchPad.recorded.should == :enter
+ expect(ScratchPad.recorded).to eq(:enter)
end
it "calls registered :leave actions" do
leave = double("leave")
- leave.should_receive(:leave).and_return { ScratchPad.record :leave }
+ expect(leave).to receive(:leave) { ScratchPad.record :leave }
MSpec.register :leave, leave
@state.process
- ScratchPad.recorded.should == :leave
+ expect(ScratchPad.recorded).to eq(:leave)
end
end
-describe ContextState, "#it_should_behave_like" do
+RSpec.describe ContextState, "#it_should_behave_like" do
before :each do
@shared_desc = :shared_context
@shared = ContextState.new(@shared_desc, :shared => true)
- MSpec.stub(:retrieve_shared).and_return(@shared)
+ allow(MSpec).to receive(:retrieve_shared).and_return(@shared)
@state = ContextState.new "Top level"
@a = lambda {|*| }
@@ -913,8 +913,8 @@ describe ContextState, "#it_should_behave_like" do
end
it "raises an Exception if unable to find the shared ContextState" do
- MSpec.should_receive(:retrieve_shared).and_return(nil)
- lambda { @state.it_should_behave_like "this" }.should raise_error(Exception)
+ expect(MSpec).to receive(:retrieve_shared).and_return(nil)
+ expect { @state.it_should_behave_like :this }.to raise_error(Exception)
end
describe "for nested ContextState instances" do
@@ -925,24 +925,24 @@ describe ContextState, "#it_should_behave_like" do
@shared.children << @nested
@nested_dup = @nested.dup
- @nested.stub(:dup).and_return(@nested_dup)
+ allow(@nested).to receive(:dup).and_return(@nested_dup)
end
it "duplicates the nested ContextState" do
@state.it_should_behave_like @shared_desc
- @state.children.first.should equal(@nested_dup)
+ expect(@state.children.first).to equal(@nested_dup)
end
it "sets the parent of the nested ContextState to the containing ContextState" do
@state.it_should_behave_like @shared_desc
- @nested_dup.parent.should equal(@state)
+ expect(@nested_dup.parent).to equal(@state)
end
it "sets the context for nested examples to the nested ContextState's dup" do
@shared.it "an example", &@a
@shared.it "another example", &@b
@state.it_should_behave_like @shared_desc
- @nested_dup.examples.each { |x| x.context.should equal(@nested_dup) }
+ @nested_dup.examples.each { |x| expect(x.context).to equal(@nested_dup) }
end
it "omits the shored ContextState's description" do
@@ -950,58 +950,58 @@ describe ContextState, "#it_should_behave_like" do
@nested.it "another example", &@b
@state.it_should_behave_like @shared_desc
- @nested_dup.description.should == "Top level nested context"
- @nested_dup.examples.first.description.should == "Top level nested context an example"
- @nested_dup.examples.last.description.should == "Top level nested context another example"
+ expect(@nested_dup.description).to eq("Top level nested context")
+ expect(@nested_dup.examples.first.description).to eq("Top level nested context an example")
+ expect(@nested_dup.examples.last.description).to eq("Top level nested context another example")
end
end
it "adds duped examples from the shared ContextState" do
@shared.it "some method", &@a
ex_dup = @shared.examples.first.dup
- @shared.examples.first.stub(:dup).and_return(ex_dup)
+ allow(@shared.examples.first).to receive(:dup).and_return(ex_dup)
@state.it_should_behave_like @shared_desc
- @state.examples.should == [ex_dup]
+ expect(@state.examples).to eq([ex_dup])
end
it "sets the context for examples to the containing ContextState" do
@shared.it "an example", &@a
@shared.it "another example", &@b
@state.it_should_behave_like @shared_desc
- @state.examples.each { |x| x.context.should equal(@state) }
+ @state.examples.each { |x| expect(x.context).to equal(@state) }
end
it "adds before(:all) blocks from the shared ContextState" do
@shared.before :all, &@a
@shared.before :all, &@b
@state.it_should_behave_like @shared_desc
- @state.before(:all).should include(*@shared.before(:all))
+ expect(@state.before(:all)).to include(*@shared.before(:all))
end
it "adds before(:each) blocks from the shared ContextState" do
@shared.before :each, &@a
@shared.before :each, &@b
@state.it_should_behave_like @shared_desc
- @state.before(:each).should include(*@shared.before(:each))
+ expect(@state.before(:each)).to include(*@shared.before(:each))
end
it "adds after(:each) blocks from the shared ContextState" do
@shared.after :each, &@a
@shared.after :each, &@b
@state.it_should_behave_like @shared_desc
- @state.after(:each).should include(*@shared.after(:each))
+ expect(@state.after(:each)).to include(*@shared.after(:each))
end
it "adds after(:all) blocks from the shared ContextState" do
@shared.after :all, &@a
@shared.after :all, &@b
@state.it_should_behave_like @shared_desc
- @state.after(:all).should include(*@shared.after(:all))
+ expect(@state.after(:all)).to include(*@shared.after(:all))
end
end
-describe ContextState, "#filter_examples" do
+RSpec.describe ContextState, "#filter_examples" do
before :each do
@state = ContextState.new ""
@state.it("one") { }
@@ -1009,20 +1009,20 @@ describe ContextState, "#filter_examples" do
end
it "removes examples that are filtered" do
- @state.examples.first.stub(:filtered?).and_return(true)
- @state.examples.size.should == 2
+ allow(@state.examples.first).to receive(:filtered?).and_return(true)
+ expect(@state.examples.size).to eq(2)
@state.filter_examples
- @state.examples.size.should == 1
+ expect(@state.examples.size).to eq(1)
end
it "returns true if there are remaining examples to evaluate" do
- @state.examples.first.stub(:filtered?).and_return(true)
- @state.filter_examples.should be_true
+ allow(@state.examples.first).to receive(:filtered?).and_return(true)
+ expect(@state.filter_examples).to be_truthy
end
it "returns false if there are no remaining examples to evaluate" do
- @state.examples.first.stub(:filtered?).and_return(true)
- @state.examples.last.stub(:filtered?).and_return(true)
- @state.filter_examples.should be_false
+ allow(@state.examples.first).to receive(:filtered?).and_return(true)
+ allow(@state.examples.last).to receive(:filtered?).and_return(true)
+ expect(@state.filter_examples).to be_falsey
end
end
diff --git a/spec/mspec/spec/runner/example_spec.rb b/spec/mspec/spec/runner/example_spec.rb
index 7d3ad6be30..8bac166da8 100644
--- a/spec/mspec/spec/runner/example_spec.rb
+++ b/spec/mspec/spec/runner/example_spec.rb
@@ -4,36 +4,36 @@ require 'mspec/runner/mspec'
require 'mspec/mocks/mock'
require 'mspec/runner/example'
-describe ExampleState do
+RSpec.describe ExampleState do
it "is initialized with the ContextState, #it string, and #it block" do
prc = lambda { }
context = ContextState.new ""
- ExampleState.new(context, "does", prc).should be_kind_of(ExampleState)
+ expect(ExampleState.new(context, "does", prc)).to be_kind_of(ExampleState)
end
end
-describe ExampleState, "#describe" do
+RSpec.describe ExampleState, "#describe" do
before :each do
@context = ContextState.new "Object#to_s"
@state = ExampleState.new @context, "it"
end
it "returns the ContextState#description" do
- @state.describe.should == @context.description
+ expect(@state.describe).to eq(@context.description)
end
end
-describe ExampleState, "#it" do
+RSpec.describe ExampleState, "#it" do
before :each do
@state = ExampleState.new ContextState.new("describe"), "it"
end
it "returns the argument to the #it block" do
- @state.it.should == "it"
+ expect(@state.it).to eq("it")
end
end
-describe ExampleState, "#context=" do
+RSpec.describe ExampleState, "#context=" do
before :each do
@state = ExampleState.new ContextState.new("describe"), "it"
@context = ContextState.new "New#context"
@@ -41,28 +41,28 @@ describe ExampleState, "#context=" do
it "sets the containing ContextState" do
@state.context = @context
- @state.context.should == @context
+ expect(@state.context).to eq(@context)
end
it "resets the description" do
- @state.description.should == "describe it"
+ expect(@state.description).to eq("describe it")
@state.context = @context
- @state.description.should == "New#context it"
+ expect(@state.description).to eq("New#context it")
end
end
-describe ExampleState, "#example" do
+RSpec.describe ExampleState, "#example" do
before :each do
@proc = lambda { }
@state = ExampleState.new ContextState.new("describe"), "it", @proc
end
it "returns the #it block" do
- @state.example.should == @proc
+ expect(@state.example).to eq(@proc)
end
end
-describe ExampleState, "#filtered?" do
+RSpec.describe ExampleState, "#filtered?" do
before :each do
MSpec.store :include, []
MSpec.store :exclude, []
@@ -77,41 +77,41 @@ describe ExampleState, "#filtered?" do
end
it "returns false if MSpec include filters list is empty" do
- @state.filtered?.should == false
+ expect(@state.filtered?).to eq(false)
end
it "returns false if MSpec include filters match this spec" do
- @filter.should_receive(:===).and_return(true)
+ expect(@filter).to receive(:===).and_return(true)
MSpec.register :include, @filter
- @state.filtered?.should == false
+ expect(@state.filtered?).to eq(false)
end
it "returns true if MSpec include filters do not match this spec" do
- @filter.should_receive(:===).and_return(false)
+ expect(@filter).to receive(:===).and_return(false)
MSpec.register :include, @filter
- @state.filtered?.should == true
+ expect(@state.filtered?).to eq(true)
end
it "returns false if MSpec exclude filters list is empty" do
- @state.filtered?.should == false
+ expect(@state.filtered?).to eq(false)
end
it "returns false if MSpec exclude filters do not match this spec" do
- @filter.should_receive(:===).and_return(false)
+ expect(@filter).to receive(:===).and_return(false)
MSpec.register :exclude, @filter
- @state.filtered?.should == false
+ expect(@state.filtered?).to eq(false)
end
it "returns true if MSpec exclude filters match this spec" do
- @filter.should_receive(:===).and_return(true)
+ expect(@filter).to receive(:===).and_return(true)
MSpec.register :exclude, @filter
- @state.filtered?.should == true
+ expect(@state.filtered?).to eq(true)
end
it "returns true if MSpec include and exclude filters match this spec" do
- @filter.should_receive(:===).twice.and_return(true)
+ expect(@filter).to receive(:===).twice.and_return(true)
MSpec.register :include, @filter
MSpec.register :exclude, @filter
- @state.filtered?.should == true
+ expect(@state.filtered?).to eq(true)
end
end
diff --git a/spec/mspec/spec/runner/exception_spec.rb b/spec/mspec/spec/runner/exception_spec.rb
index 0e0a819992..a77a2c9cf4 100644
--- a/spec/mspec/spec/runner/exception_spec.rb
+++ b/spec/mspec/spec/runner/exception_spec.rb
@@ -4,16 +4,16 @@ require 'mspec/runner/example'
require 'mspec/runner/exception'
require 'mspec/utils/script'
-describe ExceptionState, "#initialize" do
+RSpec.describe ExceptionState, "#initialize" do
it "takes a state, location (e.g. before :each), and exception" do
context = ContextState.new "Class#method"
state = ExampleState.new context, "does something"
exc = Exception.new "Fail!"
- ExceptionState.new(state, "location", exc).should be_kind_of(ExceptionState)
+ expect(ExceptionState.new(state, "location", exc)).to be_kind_of(ExceptionState)
end
end
-describe ExceptionState, "#description" do
+RSpec.describe ExceptionState, "#description" do
before :each do
context = ContextState.new "Class#method"
@state = ExampleState.new context, "does something"
@@ -21,99 +21,99 @@ describe ExceptionState, "#description" do
it "returns the state description if state was not nil" do
exc = ExceptionState.new(@state, nil, nil)
- exc.description.should == "Class#method does something"
+ expect(exc.description).to eq("Class#method does something")
end
it "returns the location if it is not nil and description is nil" do
exc = ExceptionState.new(nil, "location", nil)
- exc.description.should == "An exception occurred during: location"
+ expect(exc.description).to eq("An exception occurred during: location")
end
it "returns both description and location if neither are nil" do
exc = ExceptionState.new(@state, "location", nil)
- exc.description.should == "An exception occurred during: location\nClass#method does something"
+ expect(exc.description).to eq("An exception occurred during: location\nClass#method does something")
end
end
-describe ExceptionState, "#describe" do
+RSpec.describe ExceptionState, "#describe" do
before :each do
context = ContextState.new "Class#method"
@state = ExampleState.new context, "does something"
end
it "returns the ExampleState#describe string if created with a non-nil state" do
- ExceptionState.new(@state, nil, nil).describe.should == @state.describe
+ expect(ExceptionState.new(@state, nil, nil).describe).to eq(@state.describe)
end
it "returns an empty string if created with a nil state" do
- ExceptionState.new(nil, nil, nil).describe.should == ""
+ expect(ExceptionState.new(nil, nil, nil).describe).to eq("")
end
end
-describe ExceptionState, "#it" do
+RSpec.describe ExceptionState, "#it" do
before :each do
context = ContextState.new "Class#method"
@state = ExampleState.new context, "does something"
end
it "returns the ExampleState#it string if created with a non-nil state" do
- ExceptionState.new(@state, nil, nil).it.should == @state.it
+ expect(ExceptionState.new(@state, nil, nil).it).to eq(@state.it)
end
it "returns an empty string if created with a nil state" do
- ExceptionState.new(nil, nil, nil).it.should == ""
+ expect(ExceptionState.new(nil, nil, nil).it).to eq("")
end
end
-describe ExceptionState, "#failure?" do
+RSpec.describe ExceptionState, "#failure?" do
before :each do
@state = ExampleState.new ContextState.new("C#m"), "works"
end
it "returns true if the exception is an SpecExpectationNotMetError" do
exc = ExceptionState.new @state, "", SpecExpectationNotMetError.new("Fail!")
- exc.failure?.should be_true
+ expect(exc.failure?).to be_truthy
end
it "returns true if the exception is an SpecExpectationNotFoundError" do
exc = ExceptionState.new @state, "", SpecExpectationNotFoundError.new("Fail!")
- exc.failure?.should be_true
+ expect(exc.failure?).to be_truthy
end
it "returns false if the exception is not an SpecExpectationNotMetError or an SpecExpectationNotFoundError" do
exc = ExceptionState.new @state, "", Exception.new("Fail!")
- exc.failure?.should be_false
+ expect(exc.failure?).to be_falsey
end
end
-describe ExceptionState, "#message" do
+RSpec.describe ExceptionState, "#message" do
before :each do
@state = ExampleState.new ContextState.new("C#m"), "works"
end
it "returns <No message> if the exception message is empty" do
exc = ExceptionState.new @state, "", Exception.new("")
- exc.message.should == "Exception: <No message>"
+ expect(exc.message).to eq("Exception: <No message>")
end
it "returns the message without exception class when the exception is an SpecExpectationNotMetError" do
exc = ExceptionState.new @state, "", SpecExpectationNotMetError.new("Fail!")
- exc.message.should == "Fail!"
+ expect(exc.message).to eq("Fail!")
end
it "returns SpecExpectationNotFoundError#message when the exception is an SpecExpectationNotFoundError" do
e = SpecExpectationNotFoundError.new
exc = ExceptionState.new @state, "", e
- exc.message.should == e.message
+ expect(exc.message).to eq(e.message)
end
it "returns the message with exception class when the exception is not an SpecExpectationNotMetError or an SpecExpectationNotFoundError" do
exc = ExceptionState.new @state, "", Exception.new("Fail!")
- exc.message.should == "Exception: Fail!"
+ expect(exc.message).to eq("Exception: Fail!")
end
end
-describe ExceptionState, "#backtrace" do
+RSpec.describe ExceptionState, "#backtrace" do
before :each do
@state = ExampleState.new ContextState.new("C#m"), "works"
begin
@@ -128,19 +128,19 @@ describe ExceptionState, "#backtrace" do
end
it "returns a string representation of the exception backtrace" do
- @exc.backtrace.should be_kind_of(String)
+ expect(@exc.backtrace).to be_kind_of(String)
end
it "does not filter files from the backtrace if $MSPEC_DEBUG is true" do
$MSPEC_DEBUG = true
- @exc.backtrace.should == @exception.backtrace.join("\n")
+ expect(@exc.backtrace).to eq(@exception.backtrace.join("\n"))
end
it "filters files matching config[:backtrace_filter]" do
MSpecScript.set :backtrace_filter, %r[mspec/lib]
$MSPEC_DEBUG = nil
@exc.backtrace.split("\n").each do |line|
- line.should_not =~ %r[mspec/lib]
+ expect(line).not_to match(%r[mspec/lib])
end
end
end
diff --git a/spec/mspec/spec/runner/filters/match_spec.rb b/spec/mspec/spec/runner/filters/match_spec.rb
index f2c665c495..970da00446 100644
--- a/spec/mspec/spec/runner/filters/match_spec.rb
+++ b/spec/mspec/spec/runner/filters/match_spec.rb
@@ -2,33 +2,33 @@ require File.dirname(__FILE__) + '/../../spec_helper'
require 'mspec/runner/mspec'
require 'mspec/runner/filters/match'
-describe MatchFilter, "#===" do
+RSpec.describe MatchFilter, "#===" do
before :each do
@filter = MatchFilter.new nil, 'a', 'b', 'c'
end
it "returns true if the argument matches any of the #initialize strings" do
- @filter.===('aaa').should == true
- @filter.===('bccb').should == true
+ expect(@filter.===('aaa')).to eq(true)
+ expect(@filter.===('bccb')).to eq(true)
end
it "returns false if the argument matches none of the #initialize strings" do
- @filter.===('d').should == false
+ expect(@filter.===('d')).to eq(false)
end
end
-describe MatchFilter, "#register" do
+RSpec.describe MatchFilter, "#register" do
it "registers itself with MSpec for the designated action list" do
filter = MatchFilter.new :include
- MSpec.should_receive(:register).with(:include, filter)
+ expect(MSpec).to receive(:register).with(:include, filter)
filter.register
end
end
-describe MatchFilter, "#unregister" do
+RSpec.describe MatchFilter, "#unregister" do
it "unregisters itself with MSpec for the designated action list" do
filter = MatchFilter.new :exclude
- MSpec.should_receive(:unregister).with(:exclude, filter)
+ expect(MSpec).to receive(:unregister).with(:exclude, filter)
filter.unregister
end
end
diff --git a/spec/mspec/spec/runner/filters/profile_spec.rb b/spec/mspec/spec/runner/filters/profile_spec.rb
index 89d0ad1911..25f5e07aef 100644
--- a/spec/mspec/spec/runner/filters/profile_spec.rb
+++ b/spec/mspec/spec/runner/filters/profile_spec.rb
@@ -2,45 +2,45 @@ require File.dirname(__FILE__) + '/../../spec_helper'
require 'mspec/runner/mspec'
require 'mspec/runner/filters/profile'
-describe ProfileFilter, "#find" do
+RSpec.describe ProfileFilter, "#find" do
before :each do
@filter = ProfileFilter.new nil
- File.stub(:exist?).and_return(false)
+ allow(File).to receive(:exist?).and_return(false)
@file = "rails.yaml"
end
it "attempts to locate the file through the expanded path name" do
- File.should_receive(:expand_path).with(@file).and_return(@file)
- File.should_receive(:exist?).with(@file).and_return(true)
- @filter.find(@file).should == @file
+ expect(File).to receive(:expand_path).with(@file).and_return(@file)
+ expect(File).to receive(:exist?).with(@file).and_return(true)
+ expect(@filter.find(@file)).to eq(@file)
end
it "attempts to locate the file in 'spec/profiles'" do
path = File.join "spec/profiles", @file
- File.should_receive(:exist?).with(path).and_return(true)
- @filter.find(@file).should == path
+ expect(File).to receive(:exist?).with(path).and_return(true)
+ expect(@filter.find(@file)).to eq(path)
end
it "attempts to locate the file in 'spec'" do
path = File.join "spec", @file
- File.should_receive(:exist?).with(path).and_return(true)
- @filter.find(@file).should == path
+ expect(File).to receive(:exist?).with(path).and_return(true)
+ expect(@filter.find(@file)).to eq(path)
end
it "attempts to locate the file in 'profiles'" do
path = File.join "profiles", @file
- File.should_receive(:exist?).with(path).and_return(true)
- @filter.find(@file).should == path
+ expect(File).to receive(:exist?).with(path).and_return(true)
+ expect(@filter.find(@file)).to eq(path)
end
it "attempts to locate the file in '.'" do
path = File.join ".", @file
- File.should_receive(:exist?).with(path).and_return(true)
- @filter.find(@file).should == path
+ expect(File).to receive(:exist?).with(path).and_return(true)
+ expect(@filter.find(@file)).to eq(path)
end
end
-describe ProfileFilter, "#parse" do
+RSpec.describe ProfileFilter, "#parse" do
before :each do
@filter = ProfileFilter.new nil
@file = File.open(File.dirname(__FILE__) + "/b.yaml", "r")
@@ -51,14 +51,14 @@ describe ProfileFilter, "#parse" do
end
it "creates a Hash of the contents of the YAML file" do
- @filter.parse(@file).should == {
+ expect(@filter.parse(@file)).to eq({
"B." => ["b", "bb"],
"B::C#" => ["b!", "b=", "b?", "-", "[]", "[]="]
- }
+ })
end
end
-describe ProfileFilter, "#load" do
+RSpec.describe ProfileFilter, "#load" do
before :each do
@filter = ProfileFilter.new nil
@files = [
@@ -68,50 +68,50 @@ describe ProfileFilter, "#load" do
end
it "generates a composite hash from multiple YAML files" do
- @filter.load(*@files).should == {
+ expect(@filter.load(*@files)).to eq({
"A#" => ["a", "aa"],
"B." => ["b", "bb"],
"B::C#" => ["b!", "b=", "b?", "-", "[]", "[]="]
- }
+ })
end
end
-describe ProfileFilter, "#===" do
+RSpec.describe ProfileFilter, "#===" do
before :each do
@filter = ProfileFilter.new nil
- @filter.stub(:load).and_return({ "A#" => ["[]=", "a", "a!", "a?", "aa="]})
+ allow(@filter).to receive(:load).and_return({ "A#" => ["[]=", "a", "a!", "a?", "aa="]})
@filter.send :initialize, nil
end
it "returns true if the spec description is for a method in the profile" do
- @filter.===("The A#[]= method").should == true
- @filter.===("A#a returns").should == true
- @filter.===("A#a! replaces").should == true
- @filter.===("A#a? returns").should == true
- @filter.===("A#aa= raises").should == true
+ expect(@filter.===("The A#[]= method")).to eq(true)
+ expect(@filter.===("A#a returns")).to eq(true)
+ expect(@filter.===("A#a! replaces")).to eq(true)
+ expect(@filter.===("A#a? returns")).to eq(true)
+ expect(@filter.===("A#aa= raises")).to eq(true)
end
it "returns false if the spec description is for a method not in the profile" do
- @filter.===("The A#[] method").should == false
- @filter.===("B#a returns").should == false
- @filter.===("A.a! replaces").should == false
- @filter.===("AA#a? returns").should == false
- @filter.===("A#aa raises").should == false
+ expect(@filter.===("The A#[] method")).to eq(false)
+ expect(@filter.===("B#a returns")).to eq(false)
+ expect(@filter.===("A.a! replaces")).to eq(false)
+ expect(@filter.===("AA#a? returns")).to eq(false)
+ expect(@filter.===("A#aa raises")).to eq(false)
end
end
-describe ProfileFilter, "#register" do
+RSpec.describe ProfileFilter, "#register" do
it "registers itself with MSpec for the designated action list" do
filter = ProfileFilter.new :include
- MSpec.should_receive(:register).with(:include, filter)
+ expect(MSpec).to receive(:register).with(:include, filter)
filter.register
end
end
-describe ProfileFilter, "#unregister" do
+RSpec.describe ProfileFilter, "#unregister" do
it "unregisters itself with MSpec for the designated action list" do
filter = ProfileFilter.new :exclude
- MSpec.should_receive(:unregister).with(:exclude, filter)
+ expect(MSpec).to receive(:unregister).with(:exclude, filter)
filter.unregister
end
end
diff --git a/spec/mspec/spec/runner/filters/regexp_spec.rb b/spec/mspec/spec/runner/filters/regexp_spec.rb
index 8e9b0ec7e8..1d1d3554f6 100644
--- a/spec/mspec/spec/runner/filters/regexp_spec.rb
+++ b/spec/mspec/spec/runner/filters/regexp_spec.rb
@@ -2,30 +2,30 @@ require File.dirname(__FILE__) + '/../../spec_helper'
require 'mspec/runner/mspec'
require 'mspec/runner/filters/regexp'
-describe MatchFilter, "#===" do
+RSpec.describe MatchFilter, "#===" do
before :each do
@filter = RegexpFilter.new nil, 'a(b|c)', 'b[^ab]', 'cc?'
end
it "returns true if the argument matches any of the #initialize strings" do
- @filter.===('ab').should == true
- @filter.===('bc suffix').should == true
- @filter.===('prefix cc').should == true
+ expect(@filter.===('ab')).to eq(true)
+ expect(@filter.===('bc suffix')).to eq(true)
+ expect(@filter.===('prefix cc')).to eq(true)
end
it "returns false if the argument matches none of the #initialize strings" do
- @filter.===('aa').should == false
- @filter.===('ba').should == false
- @filter.===('prefix d suffix').should == false
+ expect(@filter.===('aa')).to eq(false)
+ expect(@filter.===('ba')).to eq(false)
+ expect(@filter.===('prefix d suffix')).to eq(false)
end
end
-describe RegexpFilter, "#to_regexp" do
+RSpec.describe RegexpFilter, "#to_regexp" do
before :each do
@filter = RegexpFilter.new nil
end
it "converts its arguments to Regexp instances" do
- @filter.send(:to_regexp, 'a(b|c)', 'b[^ab]', 'cc?').should == [/a(b|c)/, /b[^ab]/, /cc?/]
+ expect(@filter.send(:to_regexp, 'a(b|c)', 'b[^ab]', 'cc?')).to eq([/a(b|c)/, /b[^ab]/, /cc?/])
end
end
diff --git a/spec/mspec/spec/runner/filters/tag_spec.rb b/spec/mspec/spec/runner/filters/tag_spec.rb
index fe1f3df039..356175a754 100644
--- a/spec/mspec/spec/runner/filters/tag_spec.rb
+++ b/spec/mspec/spec/runner/filters/tag_spec.rb
@@ -3,90 +3,90 @@ require 'mspec/runner/mspec'
require 'mspec/runner/filters/match'
require 'mspec/runner/filters/tag'
-describe TagFilter, "#load" do
+RSpec.describe TagFilter, "#load" do
before :each do
@match = double("match filter").as_null_object
@filter = TagFilter.new :include, "tag", "key"
@tag = SpecTag.new "tag(comment):description"
- MSpec.stub(:read_tags).and_return([@tag])
- MSpec.stub(:register)
+ allow(MSpec).to receive(:read_tags).and_return([@tag])
+ allow(MSpec).to receive(:register)
end
it "loads tags from the tag file" do
- MSpec.should_receive(:read_tags).with(["tag", "key"]).and_return([])
+ expect(MSpec).to receive(:read_tags).with(["tag", "key"]).and_return([])
@filter.load
end
it "registers itself with MSpec for the :include action" do
filter = TagFilter.new(:include)
- MSpec.should_receive(:register).with(:include, filter)
+ expect(MSpec).to receive(:register).with(:include, filter)
filter.load
end
it "registers itself with MSpec for the :exclude action" do
filter = TagFilter.new(:exclude)
- MSpec.should_receive(:register).with(:exclude, filter)
+ expect(MSpec).to receive(:register).with(:exclude, filter)
filter.load
end
end
-describe TagFilter, "#unload" do
+RSpec.describe TagFilter, "#unload" do
before :each do
@filter = TagFilter.new :include, "tag", "key"
@tag = SpecTag.new "tag(comment):description"
- MSpec.stub(:read_tags).and_return([@tag])
- MSpec.stub(:register)
+ allow(MSpec).to receive(:read_tags).and_return([@tag])
+ allow(MSpec).to receive(:register)
end
it "unregisters itself" do
@filter.load
- MSpec.should_receive(:unregister).with(:include, @filter)
+ expect(MSpec).to receive(:unregister).with(:include, @filter)
@filter.unload
end
end
-describe TagFilter, "#register" do
+RSpec.describe TagFilter, "#register" do
before :each do
- MSpec.stub(:register)
+ allow(MSpec).to receive(:register)
end
it "registers itself with MSpec for the :load, :unload actions" do
filter = TagFilter.new(nil)
- MSpec.should_receive(:register).with(:load, filter)
- MSpec.should_receive(:register).with(:unload, filter)
+ expect(MSpec).to receive(:register).with(:load, filter)
+ expect(MSpec).to receive(:register).with(:unload, filter)
filter.register
end
end
-describe TagFilter, "#unregister" do
+RSpec.describe TagFilter, "#unregister" do
before :each do
- MSpec.stub(:unregister)
+ allow(MSpec).to receive(:unregister)
end
it "unregisters itself with MSpec for the :load, :unload actions" do
filter = TagFilter.new(nil)
- MSpec.should_receive(:unregister).with(:load, filter)
- MSpec.should_receive(:unregister).with(:unload, filter)
+ expect(MSpec).to receive(:unregister).with(:load, filter)
+ expect(MSpec).to receive(:unregister).with(:unload, filter)
filter.unregister
end
end
-describe TagFilter, "#===" do
+RSpec.describe TagFilter, "#===" do
before :each do
@filter = TagFilter.new nil, "tag", "key"
@tag = SpecTag.new "tag(comment):description"
- MSpec.stub(:read_tags).and_return([@tag])
- MSpec.stub(:register)
+ allow(MSpec).to receive(:read_tags).and_return([@tag])
+ allow(MSpec).to receive(:register)
@filter.load
end
it "returns true if the argument matches any of the descriptions" do
- @filter.===('description').should == true
+ expect(@filter.===('description')).to eq(true)
end
it "returns false if the argument matches none of the descriptions" do
- @filter.===('descriptionA').should == false
- @filter.===('adescription').should == false
+ expect(@filter.===('descriptionA')).to eq(false)
+ expect(@filter.===('adescription')).to eq(false)
end
end
diff --git a/spec/mspec/spec/runner/formatters/describe_spec.rb b/spec/mspec/spec/runner/formatters/describe_spec.rb
index 415ced71fb..55f497aca6 100644
--- a/spec/mspec/spec/runner/formatters/describe_spec.rb
+++ b/spec/mspec/spec/runner/formatters/describe_spec.rb
@@ -2,14 +2,14 @@ require File.dirname(__FILE__) + '/../../spec_helper'
require 'mspec/runner/formatters/describe'
require 'mspec/runner/example'
-describe DescribeFormatter, "#finish" do
+RSpec.describe DescribeFormatter, "#finish" do
before :each do
- MSpec.stub(:register)
- MSpec.stub(:unregister)
+ allow(MSpec).to receive(:register)
+ allow(MSpec).to receive(:unregister)
@timer = double("timer").as_null_object
- TimerAction.stub(:new).and_return(@timer)
- @timer.stub(:format).and_return("Finished in 2.0 seconds")
+ allow(TimerAction).to receive(:new).and_return(@timer)
+ allow(@timer).to receive(:format).and_return("Finished in 2.0 seconds")
$stdout = @out = IOStub.new
context = ContextState.new "Class#method"
@@ -32,36 +32,36 @@ describe DescribeFormatter, "#finish" do
it "prints a summary of elapsed time" do
@formatter.finish
- @out.should =~ /^Finished in 2.0 seconds$/
+ expect(@out).to match(/^Finished in 2.0 seconds$/)
end
it "prints a tally of counts" do
@formatter.finish
- @out.should =~ /^1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged$/
+ expect(@out).to match(/^1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged$/)
end
it "does not print exceptions" do
@formatter.finish
- @out.should == %[
+ expect(@out).to eq(%[
Finished in 2.0 seconds
1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged
-]
+])
end
it "prints a summary of failures and errors for each describe block" do
exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
- exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ allow(exc).to receive(:backtrace).and_return("path/to/some/file.rb:35:in method")
@formatter.exception exc
@formatter.finish
- @out.should == %[
+ expect(@out).to eq(%[
Class#method 0 failures, 1 error
Finished in 2.0 seconds
1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged
-]
+])
end
end
diff --git a/spec/mspec/spec/runner/formatters/dotted_spec.rb b/spec/mspec/spec/runner/formatters/dotted_spec.rb
index 5af2ff55f8..336b1227ed 100644
--- a/spec/mspec/spec/runner/formatters/dotted_spec.rb
+++ b/spec/mspec/spec/runner/formatters/dotted_spec.rb
@@ -4,7 +4,7 @@ require 'mspec/runner/mspec'
require 'mspec/runner/example'
require 'mspec/utils/script'
-describe DottedFormatter, "#initialize" do
+RSpec.describe DottedFormatter, "#initialize" do
it "permits zero arguments" do
DottedFormatter.new
end
@@ -14,33 +14,33 @@ describe DottedFormatter, "#initialize" do
end
end
-describe DottedFormatter, "#register" do
+RSpec.describe DottedFormatter, "#register" do
before :each do
@formatter = DottedFormatter.new
- MSpec.stub(:register)
+ allow(MSpec).to receive(:register)
end
it "registers self with MSpec for appropriate actions" do
- MSpec.should_receive(:register).with(:exception, @formatter)
- MSpec.should_receive(:register).with(:before, @formatter)
- MSpec.should_receive(:register).with(:after, @formatter)
- MSpec.should_receive(:register).with(:finish, @formatter)
+ expect(MSpec).to receive(:register).with(:exception, @formatter)
+ expect(MSpec).to receive(:register).with(:before, @formatter)
+ expect(MSpec).to receive(:register).with(:after, @formatter)
+ expect(MSpec).to receive(:register).with(:finish, @formatter)
@formatter.register
end
it "creates TimerAction and TallyAction" do
timer = double("timer")
tally = double("tally")
- timer.should_receive(:register)
- tally.should_receive(:register)
- tally.should_receive(:counter)
- TimerAction.should_receive(:new).and_return(timer)
- TallyAction.should_receive(:new).and_return(tally)
+ expect(timer).to receive(:register)
+ expect(tally).to receive(:register)
+ expect(tally).to receive(:counter)
+ expect(TimerAction).to receive(:new).and_return(timer)
+ expect(TallyAction).to receive(:new).and_return(tally)
@formatter.register
end
end
-describe DottedFormatter, "#print" do
+RSpec.describe DottedFormatter, "#print" do
before :each do
$stdout = IOStub.new
end
@@ -52,25 +52,25 @@ describe DottedFormatter, "#print" do
it "writes to $stdout by default" do
formatter = DottedFormatter.new
formatter.print "begonias"
- $stdout.should == "begonias"
+ expect($stdout).to eq("begonias")
end
it "writes to the file specified when the formatter was created" do
out = IOStub.new
- File.should_receive(:open).with("some/file", "w").and_return(out)
+ expect(File).to receive(:open).with("some/file", "w").and_return(out)
formatter = DottedFormatter.new "some/file"
formatter.print "begonias"
- out.should == "begonias"
+ expect(out).to eq("begonias")
end
it "flushes the IO output" do
- $stdout.should_receive(:flush)
+ expect($stdout).to receive(:flush)
formatter = DottedFormatter.new
formatter.print "begonias"
end
end
-describe DottedFormatter, "#exception" do
+RSpec.describe DottedFormatter, "#exception" do
before :each do
@formatter = DottedFormatter.new
@failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
@@ -79,27 +79,27 @@ describe DottedFormatter, "#exception" do
it "sets the #failure? flag" do
@formatter.exception @failure
- @formatter.failure?.should be_true
+ expect(@formatter.failure?).to be_truthy
@formatter.exception @error
- @formatter.failure?.should be_false
+ expect(@formatter.failure?).to be_falsey
end
it "sets the #exception? flag" do
@formatter.exception @error
- @formatter.exception?.should be_true
+ expect(@formatter.exception?).to be_truthy
@formatter.exception @failure
- @formatter.exception?.should be_true
+ expect(@formatter.exception?).to be_truthy
end
it "adds the exception to the list of exceptions" do
- @formatter.exceptions.should == []
+ expect(@formatter.exceptions).to eq([])
@formatter.exception @error
@formatter.exception @failure
- @formatter.exceptions.should == [@error, @failure]
+ expect(@formatter.exceptions).to eq([@error, @failure])
end
end
-describe DottedFormatter, "#exception?" do
+RSpec.describe DottedFormatter, "#exception?" do
before :each do
@formatter = DottedFormatter.new
@failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
@@ -107,29 +107,29 @@ describe DottedFormatter, "#exception?" do
end
it "returns false if there have been no exceptions" do
- @formatter.exception?.should be_false
+ expect(@formatter.exception?).to be_falsey
end
it "returns true if any exceptions are errors" do
@formatter.exception @failure
@formatter.exception @error
- @formatter.exception?.should be_true
+ expect(@formatter.exception?).to be_truthy
end
it "returns true if all exceptions are failures" do
@formatter.exception @failure
@formatter.exception @failure
- @formatter.exception?.should be_true
+ expect(@formatter.exception?).to be_truthy
end
it "returns true if all exceptions are errors" do
@formatter.exception @error
@formatter.exception @error
- @formatter.exception?.should be_true
+ expect(@formatter.exception?).to be_truthy
end
end
-describe DottedFormatter, "#failure?" do
+RSpec.describe DottedFormatter, "#failure?" do
before :each do
@formatter = DottedFormatter.new
@failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
@@ -137,23 +137,23 @@ describe DottedFormatter, "#failure?" do
end
it "returns false if there have been no exceptions" do
- @formatter.failure?.should be_false
+ expect(@formatter.failure?).to be_falsey
end
it "returns false if any exceptions are errors" do
@formatter.exception @failure
@formatter.exception @error
- @formatter.failure?.should be_false
+ expect(@formatter.failure?).to be_falsey
end
it "returns true if all exceptions are failures" do
@formatter.exception @failure
@formatter.exception @failure
- @formatter.failure?.should be_true
+ expect(@formatter.failure?).to be_truthy
end
end
-describe DottedFormatter, "#before" do
+RSpec.describe DottedFormatter, "#before" do
before :each do
@state = ExampleState.new ContextState.new("describe"), "it"
@formatter = DottedFormatter.new
@@ -161,19 +161,19 @@ describe DottedFormatter, "#before" do
end
it "resets the #failure? flag to false" do
- @formatter.failure?.should be_true
+ expect(@formatter.failure?).to be_truthy
@formatter.before @state
- @formatter.failure?.should be_false
+ expect(@formatter.failure?).to be_falsey
end
it "resets the #exception? flag to false" do
- @formatter.exception?.should be_true
+ expect(@formatter.exception?).to be_truthy
@formatter.before @state
- @formatter.exception?.should be_false
+ expect(@formatter.exception?).to be_falsey
end
end
-describe DottedFormatter, "#after" do
+RSpec.describe DottedFormatter, "#after" do
before :each do
$stdout = @out = IOStub.new
@formatter = DottedFormatter.new
@@ -186,21 +186,21 @@ describe DottedFormatter, "#after" do
it "prints a '.' if there was no exception raised" do
@formatter.after(@state)
- @out.should == "."
+ expect(@out).to eq(".")
end
it "prints an 'F' if there was an expectation failure" do
exc = SpecExpectationNotMetError.new "failed"
@formatter.exception ExceptionState.new(@state, nil, exc)
@formatter.after(@state)
- @out.should == "F"
+ expect(@out).to eq("F")
end
it "prints an 'E' if there was an exception other than expectation failure" do
exc = MSpecExampleError.new("boom!")
@formatter.exception ExceptionState.new(@state, nil, exc)
@formatter.after(@state)
- @out.should == "E"
+ expect(@out).to eq("E")
end
it "prints an 'E' if there are mixed exceptions and exepctation failures" do
@@ -209,21 +209,21 @@ describe DottedFormatter, "#after" do
exc = MSpecExampleError.new("boom!")
@formatter.exception ExceptionState.new(@state, nil, exc)
@formatter.after(@state)
- @out.should == "E"
+ expect(@out).to eq("E")
end
end
-describe DottedFormatter, "#finish" do
+RSpec.describe DottedFormatter, "#finish" do
before :each do
@tally = double("tally").as_null_object
- TallyAction.stub(:new).and_return(@tally)
+ allow(TallyAction).to receive(:new).and_return(@tally)
@timer = double("timer").as_null_object
- TimerAction.stub(:new).and_return(@timer)
+ allow(TimerAction).to receive(:new).and_return(@timer)
$stdout = @out = IOStub.new
context = ContextState.new "Class#method"
@state = ExampleState.new(context, "runs")
- MSpec.stub(:register)
+ allow(MSpec).to receive(:register)
@formatter = DottedFormatter.new
@formatter.register
end
@@ -237,40 +237,39 @@ describe DottedFormatter, "#finish" do
@formatter.exception exc
@formatter.after @state
@formatter.finish
- @out.should =~ /^1\)\nClass#method runs ERROR$/
+ expect(@out).to match(/^1\)\nClass#method runs ERROR$/)
end
it "prints a backtrace for an exception" do
exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
- exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ allow(exc).to receive(:backtrace).and_return("path/to/some/file.rb:35:in method")
@formatter.exception exc
@formatter.after @state
@formatter.finish
- @out.should =~ %r[path/to/some/file.rb:35:in method$]
+ expect(@out).to match(%r[path/to/some/file.rb:35:in method$])
end
it "prints a summary of elapsed time" do
- @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ expect(@timer).to receive(:format).and_return("Finished in 2.0 seconds")
@formatter.finish
- @out.should =~ /^Finished in 2.0 seconds$/
+ expect(@out).to match(/^Finished in 2.0 seconds$/)
end
it "prints a tally of counts" do
- @tally.should_receive(:format).and_return("1 example, 0 failures")
+ expect(@tally).to receive(:format).and_return("1 example, 0 failures")
@formatter.finish
- @out.should =~ /^1 example, 0 failures$/
+ expect(@out).to match(/^1 example, 0 failures$/)
end
it "prints errors, backtraces, elapsed time, and tallies" do
exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
- exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ allow(exc).to receive(:backtrace).and_return("path/to/some/file.rb:35:in method")
@formatter.exception exc
- @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
- @tally.should_receive(:format).and_return("1 example, 1 failure")
+ expect(@timer).to receive(:format).and_return("Finished in 2.0 seconds")
+ expect(@tally).to receive(:format).and_return("1 example, 1 failure")
@formatter.after @state
@formatter.finish
- @out.should ==
-%[E
+ expect(@out).to eq(%[E
1)
Class#method runs ERROR
@@ -280,6 +279,6 @@ path/to/some/file.rb:35:in method
Finished in 2.0 seconds
1 example, 1 failure
-]
+])
end
end
diff --git a/spec/mspec/spec/runner/formatters/file_spec.rb b/spec/mspec/spec/runner/formatters/file_spec.rb
index 946683ad58..ae11d60845 100644
--- a/spec/mspec/spec/runner/formatters/file_spec.rb
+++ b/spec/mspec/spec/runner/formatters/file_spec.rb
@@ -3,27 +3,27 @@ require 'mspec/runner/formatters/file'
require 'mspec/runner/mspec'
require 'mspec/runner/example'
-describe FileFormatter, "#register" do
+RSpec.describe FileFormatter, "#register" do
before :each do
@formatter = FileFormatter.new
- MSpec.stub(:register)
- MSpec.stub(:unregister)
+ allow(MSpec).to receive(:register)
+ allow(MSpec).to receive(:unregister)
end
it "registers self with MSpec for :load, :unload actions" do
- MSpec.should_receive(:register).with(:load, @formatter)
- MSpec.should_receive(:register).with(:unload, @formatter)
+ expect(MSpec).to receive(:register).with(:load, @formatter)
+ expect(MSpec).to receive(:register).with(:unload, @formatter)
@formatter.register
end
it "unregisters self with MSpec for :before, :after actions" do
- MSpec.should_receive(:unregister).with(:before, @formatter)
- MSpec.should_receive(:unregister).with(:after, @formatter)
+ expect(MSpec).to receive(:unregister).with(:before, @formatter)
+ expect(MSpec).to receive(:unregister).with(:after, @formatter)
@formatter.register
end
end
-describe FileFormatter, "#load" do
+RSpec.describe FileFormatter, "#load" do
before :each do
@state = ExampleState.new ContextState.new("describe"), "it"
@formatter = FileFormatter.new
@@ -31,19 +31,19 @@ describe FileFormatter, "#load" do
end
it "resets the #failure? flag to false" do
- @formatter.failure?.should be_true
+ expect(@formatter.failure?).to be_truthy
@formatter.load @state
- @formatter.failure?.should be_false
+ expect(@formatter.failure?).to be_falsey
end
it "resets the #exception? flag to false" do
- @formatter.exception?.should be_true
+ expect(@formatter.exception?).to be_truthy
@formatter.load @state
- @formatter.exception?.should be_false
+ expect(@formatter.exception?).to be_falsey
end
end
-describe FileFormatter, "#unload" do
+RSpec.describe FileFormatter, "#unload" do
before :each do
$stdout = @out = IOStub.new
@formatter = FileFormatter.new
@@ -56,21 +56,21 @@ describe FileFormatter, "#unload" do
it "prints a '.' if there was no exception raised" do
@formatter.unload(@state)
- @out.should == "."
+ expect(@out).to eq(".")
end
it "prints an 'F' if there was an expectation failure" do
exc = SpecExpectationNotMetError.new "failed"
@formatter.exception ExceptionState.new(@state, nil, exc)
@formatter.unload(@state)
- @out.should == "F"
+ expect(@out).to eq("F")
end
it "prints an 'E' if there was an exception other than expectation failure" do
exc = MSpecExampleError.new("boom!")
@formatter.exception ExceptionState.new(@state, nil, exc)
@formatter.unload(@state)
- @out.should == "E"
+ expect(@out).to eq("E")
end
it "prints an 'E' if there are mixed exceptions and exepctation failures" do
@@ -79,6 +79,6 @@ describe FileFormatter, "#unload" do
exc = MSpecExampleError.new("boom!")
@formatter.exception ExceptionState.new(@state, nil, exc)
@formatter.unload(@state)
- @out.should == "E"
+ expect(@out).to eq("E")
end
end
diff --git a/spec/mspec/spec/runner/formatters/html_spec.rb b/spec/mspec/spec/runner/formatters/html_spec.rb
index 3783ab6a89..ed973ad93f 100644
--- a/spec/mspec/spec/runner/formatters/html_spec.rb
+++ b/spec/mspec/spec/runner/formatters/html_spec.rb
@@ -4,22 +4,23 @@ require 'mspec/runner/formatters/html'
require 'mspec/runner/mspec'
require 'mspec/runner/example'
require 'mspec/utils/script'
+require 'mspec/helpers'
-describe HtmlFormatter do
+RSpec.describe HtmlFormatter do
before :each do
@formatter = HtmlFormatter.new
end
it "responds to #register by registering itself with MSpec for appropriate actions" do
- MSpec.stub(:register)
- MSpec.should_receive(:register).with(:start, @formatter)
- MSpec.should_receive(:register).with(:enter, @formatter)
- MSpec.should_receive(:register).with(:leave, @formatter)
+ allow(MSpec).to receive(:register)
+ expect(MSpec).to receive(:register).with(:start, @formatter)
+ expect(MSpec).to receive(:register).with(:enter, @formatter)
+ expect(MSpec).to receive(:register).with(:leave, @formatter)
@formatter.register
end
end
-describe HtmlFormatter, "#start" do
+RSpec.describe HtmlFormatter, "#start" do
before :each do
$stdout = @out = IOStub.new
@formatter = HtmlFormatter.new
@@ -32,9 +33,8 @@ describe HtmlFormatter, "#start" do
it "prints the HTML head" do
@formatter.start
ruby_engine = RUBY_ENGINE
- ruby_engine.should =~ /^#{ruby_engine}/
- @out.should ==
-%[<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ expect(ruby_engine).to match(/^#{ruby_engine}/)
+ expect(@out).to eq(%[<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
@@ -55,11 +55,11 @@ ul {
</style>
</head>
<body>
-]
+])
end
end
-describe HtmlFormatter, "#enter" do
+RSpec.describe HtmlFormatter, "#enter" do
before :each do
$stdout = @out = IOStub.new
@formatter = HtmlFormatter.new
@@ -71,11 +71,11 @@ describe HtmlFormatter, "#enter" do
it "prints the #describe string" do
@formatter.enter "describe"
- @out.should == "<div><p>describe</p>\n<ul>\n"
+ expect(@out).to eq("<div><p>describe</p>\n<ul>\n")
end
end
-describe HtmlFormatter, "#leave" do
+RSpec.describe HtmlFormatter, "#leave" do
before :each do
$stdout = @out = IOStub.new
@formatter = HtmlFormatter.new
@@ -87,11 +87,11 @@ describe HtmlFormatter, "#leave" do
it "prints the closing tags for the #describe string" do
@formatter.leave
- @out.should == "</ul>\n</div>\n"
+ expect(@out).to eq("</ul>\n</div>\n")
end
end
-describe HtmlFormatter, "#exception" do
+RSpec.describe HtmlFormatter, "#exception" do
before :each do
$stdout = @out = IOStub.new
@formatter = HtmlFormatter.new
@@ -108,14 +108,13 @@ describe HtmlFormatter, "#exception" do
@formatter.exception exc
exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
@formatter.exception exc
- @out.should ==
-%[<li class="fail">- it (<a href="#details-1">FAILED - 1</a>)</li>
+ expect(@out).to eq(%[<li class="fail">- it (<a href="#details-1">FAILED - 1</a>)</li>
<li class="fail">- it (<a href="#details-2">ERROR - 2</a>)</li>
-]
+])
end
end
-describe HtmlFormatter, "#after" do
+RSpec.describe HtmlFormatter, "#after" do
before :each do
$stdout = @out = IOStub.new
@formatter = HtmlFormatter.new
@@ -129,7 +128,7 @@ describe HtmlFormatter, "#after" do
it "prints the #it once when there are no exceptions raised" do
@formatter.after @state
- @out.should == %[<li class="pass">- it</li>\n]
+ expect(@out).to eq(%[<li class="pass">- it</li>\n])
end
it "does not print any output if an exception is raised" do
@@ -137,68 +136,73 @@ describe HtmlFormatter, "#after" do
@formatter.exception exc
out = @out.dup
@formatter.after @state
- @out.should == out
+ expect(@out).to eq(out)
end
end
-describe HtmlFormatter, "#finish" do
+RSpec.describe HtmlFormatter, "#finish" do
before :each do
@tally = double("tally").as_null_object
- TallyAction.stub(:new).and_return(@tally)
+ allow(TallyAction).to receive(:new).and_return(@tally)
@timer = double("timer").as_null_object
- TimerAction.stub(:new).and_return(@timer)
+ allow(TimerAction).to receive(:new).and_return(@timer)
+
+ @out = tmp("HtmlFormatter")
- $stdout = @out = IOStub.new
context = ContextState.new "describe"
@state = ExampleState.new(context, "it")
- MSpec.stub(:register)
- @formatter = HtmlFormatter.new
+ allow(MSpec).to receive(:register)
+ @formatter = HtmlFormatter.new(@out)
@formatter.register
@exception = MSpecExampleError.new("broken")
- @exception.stub(:backtrace).and_return(["file.rb:1", "file.rb:2"])
+ allow(@exception).to receive(:backtrace).and_return(["file.rb:1", "file.rb:2"])
end
after :each do
- $stdout = STDOUT
+ rm_r @out
end
it "prints a failure message for an exception" do
exc = ExceptionState.new @state, nil, @exception
@formatter.exception exc
@formatter.finish
- @out.should include "<p>describe it ERROR</p>"
+ output = File.read(@out)
+ expect(output).to include "<p>describe it ERROR</p>"
end
it "prints a backtrace for an exception" do
exc = ExceptionState.new @state, nil, @exception
- exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ allow(exc).to receive(:backtrace).and_return("path/to/some/file.rb:35:in method")
@formatter.exception exc
@formatter.finish
- @out.should =~ %r[<pre>.*path/to/some/file.rb:35:in method.*</pre>]m
+ output = File.read(@out)
+ expect(output).to match(%r[<pre>.*path/to/some/file.rb:35:in method.*</pre>]m)
end
it "prints a summary of elapsed time" do
- @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ expect(@timer).to receive(:format).and_return("Finished in 2.0 seconds")
@formatter.finish
- @out.should include "<p>Finished in 2.0 seconds</p>\n"
+ output = File.read(@out)
+ expect(output).to include "<p>Finished in 2.0 seconds</p>\n"
end
it "prints a tally of counts" do
- @tally.should_receive(:format).and_return("1 example, 0 failures")
+ expect(@tally).to receive(:format).and_return("1 example, 0 failures")
@formatter.finish
- @out.should include '<p class="pass">1 example, 0 failures</p>'
+ output = File.read(@out)
+ expect(output).to include '<p class="pass">1 example, 0 failures</p>'
end
it "prints errors, backtraces, elapsed time, and tallies" do
exc = ExceptionState.new @state, nil, @exception
- exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ allow(exc).to receive(:backtrace).and_return("path/to/some/file.rb:35:in method")
@formatter.exception exc
- @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
- @tally.should_receive(:format).and_return("1 example, 1 failures")
+ expect(@timer).to receive(:format).and_return("Finished in 2.0 seconds")
+ expect(@tally).to receive(:format).and_return("1 example, 1 failures")
@formatter.finish
- @out.should ==
-%[<li class=\"fail\">- it (<a href=\"#details-1\">ERROR - 1</a>)</li>
+ output = File.read(@out)
+ expect(output).to eq(%[<li class=\"fail\">- it (<a href=\"#details-1\">ERROR - 1</a>)</li>
<hr>
<ol id="details">
<li id="details-1"><p>describe it ERROR</p>
@@ -211,6 +215,6 @@ path/to/some/file.rb:35:in method</pre>
<p class="fail">1 example, 1 failures</p>
</body>
</html>
-]
+])
end
end
diff --git a/spec/mspec/spec/runner/formatters/junit_spec.rb b/spec/mspec/spec/runner/formatters/junit_spec.rb
index 66e7d70e92..3b3da73849 100644
--- a/spec/mspec/spec/runner/formatters/junit_spec.rb
+++ b/spec/mspec/spec/runner/formatters/junit_spec.rb
@@ -2,22 +2,23 @@
require File.dirname(__FILE__) + '/../../spec_helper'
require 'mspec/runner/formatters/junit'
require 'mspec/runner/example'
+require 'mspec/helpers'
-describe JUnitFormatter, "#initialize" do
+RSpec.describe JUnitFormatter, "#initialize" do
it "permits zero arguments" do
- lambda { JUnitFormatter.new }.should_not raise_error
+ expect { JUnitFormatter.new }.not_to raise_error
end
it "accepts one argument" do
- lambda { JUnitFormatter.new nil }.should_not raise_error
+ expect { JUnitFormatter.new nil }.not_to raise_error
end
end
-describe JUnitFormatter, "#print" do
+RSpec.describe JUnitFormatter, "#print" do
before :each do
$stdout = IOStub.new
@out = IOStub.new
- File.stub(:open).and_return(@out)
+ allow(File).to receive(:open).and_return(@out)
@formatter = JUnitFormatter.new "some/file"
end
@@ -27,121 +28,132 @@ describe JUnitFormatter, "#print" do
it "writes to $stdout if #switch has not been called" do
@formatter.print "begonias"
- $stdout.should == "begonias"
- @out.should == ""
+ expect($stdout).to eq("begonias")
+ expect(@out).to eq("")
end
it "writes to the file passed to #initialize once #switch has been called" do
@formatter.switch
@formatter.print "begonias"
- $stdout.should == ""
- @out.should == "begonias"
+ expect($stdout).to eq("")
+ expect(@out).to eq("begonias")
end
it "writes to $stdout once #switch is called if no file was passed to #initialize" do
formatter = JUnitFormatter.new
formatter.switch
formatter.print "begonias"
- $stdout.should == "begonias"
- @out.should == ""
+ expect($stdout).to eq("begonias")
+ expect(@out).to eq("")
end
end
-describe JUnitFormatter, "#finish" do
+RSpec.describe JUnitFormatter, "#finish" do
before :each do
@tally = double("tally").as_null_object
@counter = double("counter").as_null_object
- @tally.stub(:counter).and_return(@counter)
- TallyAction.stub(:new).and_return(@tally)
+ allow(@tally).to receive(:counter).and_return(@counter)
+ allow(TallyAction).to receive(:new).and_return(@tally)
@timer = double("timer").as_null_object
- TimerAction.stub(:new).and_return(@timer)
+ allow(TimerAction).to receive(:new).and_return(@timer)
+
+ @out = tmp("JUnitFormatter")
- $stdout = IOStub.new
context = ContextState.new "describe"
@state = ExampleState.new(context, "it")
- @formatter = JUnitFormatter.new
- @formatter.stub(:backtrace).and_return("")
- MSpec.stub(:register)
+ @formatter = JUnitFormatter.new(@out)
+ allow(@formatter).to receive(:backtrace).and_return("")
+ allow(MSpec).to receive(:register)
@formatter.register
exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
- exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ allow(exc).to receive(:backtrace).and_return("path/to/some/file.rb:35:in method")
@formatter.exception exc
@formatter.after @state
end
after :each do
- $stdout = STDOUT
+ rm_r @out
end
it "calls #switch" do
- @formatter.should_receive(:switch)
+ expect(@formatter).to receive(:switch).and_call_original
@formatter.finish
end
it "outputs a failure message and backtrace" do
@formatter.finish
- $stdout.should include 'message="error in describe it" type="error"'
- $stdout.should include "MSpecExampleError: broken\n"
- $stdout.should include "path/to/some/file.rb:35:in method"
+ output = File.read(@out)
+ expect(output).to include 'message="error in describe it" type="error"'
+ expect(output).to include "MSpecExampleError: broken\n"
+ expect(output).to include "path/to/some/file.rb:35:in method"
end
it "encodes message and backtrace in latin1 for jenkins" do
exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken…")
- exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in methød")
+ allow(exc).to receive(:backtrace).and_return("path/to/some/file.rb:35:in methød")
@formatter.exception exc
@formatter.finish
- $stdout.should =~ /MSpecExampleError: broken((\.\.\.)|\?)\n/
- $stdout.should =~ /path\/to\/some\/file\.rb:35:in meth(\?|o)d/
+ output = File.binread(@out)
+ expect(output).to match(/MSpecExampleError: broken((\.\.\.)|\?)\n/)
+ expect(output).to match(/path\/to\/some\/file\.rb:35:in meth(\?|o)d/)
end
it "outputs an elapsed time" do
- @timer.should_receive(:elapsed).and_return(4.2)
+ expect(@timer).to receive(:elapsed).and_return(4.2)
@formatter.finish
- $stdout.should include 'time="4.2"'
+ output = File.read(@out)
+ expect(output).to include 'time="4.2"'
end
it "outputs overall elapsed time" do
- @timer.should_receive(:elapsed).and_return(4.2)
+ expect(@timer).to receive(:elapsed).and_return(4.2)
@formatter.finish
- $stdout.should include 'timeCount="4.2"'
+ output = File.read(@out)
+ expect(output).to include 'timeCount="4.2"'
end
it "outputs the number of examples as test count" do
- @counter.should_receive(:examples).and_return(9)
+ expect(@counter).to receive(:examples).and_return(9)
@formatter.finish
- $stdout.should include 'tests="9"'
+ output = File.read(@out)
+ expect(output).to include 'tests="9"'
end
it "outputs overall number of examples as test count" do
- @counter.should_receive(:examples).and_return(9)
+ expect(@counter).to receive(:examples).and_return(9)
@formatter.finish
- $stdout.should include 'testCount="9"'
+ output = File.read(@out)
+ expect(output).to include 'testCount="9"'
end
it "outputs a failure count" do
- @counter.should_receive(:failures).and_return(2)
+ expect(@counter).to receive(:failures).and_return(2)
@formatter.finish
- $stdout.should include 'failureCount="2"'
+ output = File.read(@out)
+ expect(output).to include 'failureCount="2"'
end
it "outputs overall failure count" do
- @counter.should_receive(:failures).and_return(2)
+ expect(@counter).to receive(:failures).and_return(2)
@formatter.finish
- $stdout.should include 'failures="2"'
+ output = File.read(@out)
+ expect(output).to include 'failures="2"'
end
it "outputs an error count" do
- @counter.should_receive(:errors).and_return(1)
+ expect(@counter).to receive(:errors).and_return(1)
@formatter.finish
- $stdout.should include 'errors="1"'
+ output = File.read(@out)
+ expect(output).to include 'errors="1"'
end
it "outputs overall error count" do
- @counter.should_receive(:errors).and_return(1)
+ expect(@counter).to receive(:errors).and_return(1)
@formatter.finish
- $stdout.should include 'errorCount="1"'
+ output = File.read(@out)
+ expect(output).to include 'errorCount="1"'
end
end
diff --git a/spec/mspec/spec/runner/formatters/method_spec.rb b/spec/mspec/spec/runner/formatters/method_spec.rb
index 77204f74c5..02bf47d538 100644
--- a/spec/mspec/spec/runner/formatters/method_spec.rb
+++ b/spec/mspec/spec/runner/formatters/method_spec.rb
@@ -4,29 +4,29 @@ require 'mspec/runner/mspec'
require 'mspec/runner/example'
require 'mspec/utils/script'
-describe MethodFormatter, "#method_type" do
+RSpec.describe MethodFormatter, "#method_type" do
before :each do
@formatter = MethodFormatter.new
end
it "returns 'class' if the separator is '.' or '::'" do
- @formatter.method_type('.').should == "class"
- @formatter.method_type('::').should == "class"
+ expect(@formatter.method_type('.')).to eq("class")
+ expect(@formatter.method_type('::')).to eq("class")
end
it "returns 'instance' if the separator is '#'" do
- @formatter.method_type('#').should == "instance"
+ expect(@formatter.method_type('#')).to eq("instance")
end
it "returns 'unknown' for all other cases" do
- @formatter.method_type(nil).should == "unknown"
+ expect(@formatter.method_type(nil)).to eq("unknown")
end
end
-describe MethodFormatter, "#before" do
+RSpec.describe MethodFormatter, "#before" do
before :each do
@formatter = MethodFormatter.new
- MSpec.stub(:register)
+ allow(MSpec).to receive(:register)
@formatter.register
end
@@ -38,32 +38,32 @@ describe MethodFormatter, "#before" do
state = ExampleState.new ContextState.new("describe"), "it"
@formatter.before state
- @formatter.tally.counter.examples.should == 0
- @formatter.tally.counter.expectations.should == 0
- @formatter.tally.counter.failures.should == 0
- @formatter.tally.counter.errors.should == 0
+ expect(@formatter.tally.counter.examples).to eq(0)
+ expect(@formatter.tally.counter.expectations).to eq(0)
+ expect(@formatter.tally.counter.failures).to eq(0)
+ expect(@formatter.tally.counter.errors).to eq(0)
end
it "records the class, method if available" do
state = ExampleState.new ContextState.new("Some#method"), "it"
@formatter.before state
key = "Some#method"
- @formatter.methods.keys.should include(key)
+ expect(@formatter.methods.keys).to include(key)
h = @formatter.methods[key]
- h[:class].should == "Some"
- h[:method].should == "method"
- h[:description].should == "Some#method it"
+ expect(h[:class]).to eq("Some")
+ expect(h[:method]).to eq("method")
+ expect(h[:description]).to eq("Some#method it")
end
it "does not record class, method unless both are available" do
state = ExampleState.new ContextState.new("Some method"), "it"
@formatter.before state
key = "Some method"
- @formatter.methods.keys.should include(key)
+ expect(@formatter.methods.keys).to include(key)
h = @formatter.methods[key]
- h[:class].should == ""
- h[:method].should == ""
- h[:description].should == "Some method it"
+ expect(h[:class]).to eq("")
+ expect(h[:method]).to eq("")
+ expect(h[:description]).to eq("Some method it")
end
it "sets the method type to unknown if class and method are not available" do
@@ -71,7 +71,7 @@ describe MethodFormatter, "#before" do
@formatter.before state
key = "Some method"
h = @formatter.methods[key]
- h[:type].should == "unknown"
+ expect(h[:type]).to eq("unknown")
end
it "sets the method type based on the class, method separator" do
@@ -79,7 +79,7 @@ describe MethodFormatter, "#before" do
state = ExampleState.new ContextState.new(k), "it"
@formatter.before state
h = @formatter.methods[k]
- h[:type].should == t
+ expect(h[:type]).to eq(t)
end
end
@@ -87,14 +87,14 @@ describe MethodFormatter, "#before" do
state = ExampleState.new ContextState.new("describe"), "it"
@formatter.exceptions << "stuff"
@formatter.before state
- @formatter.exceptions.should be_empty
+ expect(@formatter.exceptions).to be_empty
end
end
-describe MethodFormatter, "#after" do
+RSpec.describe MethodFormatter, "#after" do
before :each do
@formatter = MethodFormatter.new
- MSpec.stub(:register)
+ allow(MSpec).to receive(:register)
@formatter.register
end
@@ -109,10 +109,10 @@ describe MethodFormatter, "#after" do
@formatter.after state
h = @formatter.methods["Some#method"]
- h[:examples].should == 3
- h[:expectations].should == 4
- h[:failures].should == 2
- h[:errors].should == 1
+ expect(h[:examples]).to eq(3)
+ expect(h[:expectations]).to eq(4)
+ expect(h[:failures]).to eq(2)
+ expect(h[:errors]).to eq(1)
end
it "renders the list of exceptions" do
@@ -125,20 +125,20 @@ describe MethodFormatter, "#after" do
@formatter.after state
h = @formatter.methods["Some#method"]
- h[:exceptions].should == [
+ expect(h[:exceptions]).to eq([
%[failed\n\n],
%[failed\n\n]
- ]
+ ])
end
end
-describe MethodFormatter, "#after" do
+RSpec.describe MethodFormatter, "#after" do
before :each do
$stdout = IOStub.new
context = ContextState.new "Class#method"
@state = ExampleState.new(context, "runs")
@formatter = MethodFormatter.new
- MSpec.stub(:register)
+ allow(MSpec).to receive(:register)
@formatter.register
end
@@ -159,8 +159,7 @@ describe MethodFormatter, "#after" do
@formatter.after @state
@formatter.finish
- $stdout.should ==
-%[---
+ expect($stdout).to eq(%[---
"Class#method":
class: "Class"
method: "method"
@@ -173,6 +172,6 @@ describe MethodFormatter, "#after" do
exceptions:
- "failed\\n\\n"
- "failed\\n\\n"
-]
+])
end
end
diff --git a/spec/mspec/spec/runner/formatters/multi_spec.rb b/spec/mspec/spec/runner/formatters/multi_spec.rb
index d0ed8edc93..2d13c05836 100644
--- a/spec/mspec/spec/runner/formatters/multi_spec.rb
+++ b/spec/mspec/spec/runner/formatters/multi_spec.rb
@@ -4,20 +4,20 @@ require 'mspec/runner/formatters/multi'
require 'mspec/runner/example'
require 'yaml'
-describe MultiFormatter, "#aggregate_results" do
+RSpec.describe MultiFormatter, "#aggregate_results" do
before :each do
@stdout, $stdout = $stdout, IOStub.new
@file = double("file").as_null_object
- File.stub(:delete)
- File.stub(:read)
+ allow(File).to receive(:delete)
+ allow(File).to receive(:read)
@hash = { "files"=>1, "examples"=>1, "expectations"=>2, "failures"=>0, "errors"=>0 }
- YAML.stub(:load).and_return(@hash)
+ allow(YAML).to receive(:load).and_return(@hash)
@formatter = DottedFormatter.new.extend(MultiFormatter)
- @formatter.timer.stub(:format).and_return("Finished in 42 seconds")
+ allow(@formatter.timer).to receive(:format).and_return("Finished in 42 seconds")
end
after :each do
@@ -27,13 +27,12 @@ describe MultiFormatter, "#aggregate_results" do
it "outputs a summary without errors" do
@formatter.aggregate_results(["a", "b"])
@formatter.finish
- $stdout.should ==
-%[
+ expect($stdout).to eq(%[
Finished in 42 seconds
2 files, 2 examples, 4 expectations, 0 failures, 0 errors, 0 tagged
-]
+])
end
it "outputs a summary with errors" do
@@ -43,8 +42,7 @@ Finished in 42 seconds
]
@formatter.aggregate_results(["a"])
@formatter.finish
- $stdout.should ==
-%[
+ expect($stdout).to eq(%[
1)
Some#method works real good FAILED
@@ -65,6 +63,6 @@ foo.rb:2
Finished in 42 seconds
1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged
-]
+])
end
end
diff --git a/spec/mspec/spec/runner/formatters/specdoc_spec.rb b/spec/mspec/spec/runner/formatters/specdoc_spec.rb
index edb439fc11..54b5e2cf0d 100644
--- a/spec/mspec/spec/runner/formatters/specdoc_spec.rb
+++ b/spec/mspec/spec/runner/formatters/specdoc_spec.rb
@@ -2,19 +2,19 @@ require File.dirname(__FILE__) + '/../../spec_helper'
require 'mspec/runner/formatters/specdoc'
require 'mspec/runner/example'
-describe SpecdocFormatter do
+RSpec.describe SpecdocFormatter do
before :each do
@formatter = SpecdocFormatter.new
end
it "responds to #register by registering itself with MSpec for appropriate actions" do
- MSpec.stub(:register)
- MSpec.should_receive(:register).with(:enter, @formatter)
+ allow(MSpec).to receive(:register)
+ expect(MSpec).to receive(:register).with(:enter, @formatter)
@formatter.register
end
end
-describe SpecdocFormatter, "#enter" do
+RSpec.describe SpecdocFormatter, "#enter" do
before :each do
$stdout = @out = IOStub.new
@formatter = SpecdocFormatter.new
@@ -26,11 +26,11 @@ describe SpecdocFormatter, "#enter" do
it "prints the #describe string" do
@formatter.enter("describe")
- @out.should == "\ndescribe\n"
+ expect(@out).to eq("\ndescribe\n")
end
end
-describe SpecdocFormatter, "#before" do
+RSpec.describe SpecdocFormatter, "#before" do
before :each do
$stdout = @out = IOStub.new
@formatter = SpecdocFormatter.new
@@ -43,19 +43,19 @@ describe SpecdocFormatter, "#before" do
it "prints the #it string" do
@formatter.before @state
- @out.should == "- it"
+ expect(@out).to eq("- it")
end
it "resets the #exception? flag" do
exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
@formatter.exception exc
- @formatter.exception?.should be_true
+ expect(@formatter.exception?).to be_truthy
@formatter.before @state
- @formatter.exception?.should be_false
+ expect(@formatter.exception?).to be_falsey
end
end
-describe SpecdocFormatter, "#exception" do
+RSpec.describe SpecdocFormatter, "#exception" do
before :each do
$stdout = @out = IOStub.new
@formatter = SpecdocFormatter.new
@@ -70,13 +70,13 @@ describe SpecdocFormatter, "#exception" do
it "prints 'ERROR' if an exception is not an SpecExpectationNotMetError" do
exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
@formatter.exception exc
- @out.should == " (ERROR - 1)"
+ expect(@out).to eq(" (ERROR - 1)")
end
it "prints 'FAILED' if an exception is an SpecExpectationNotMetError" do
exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing")
@formatter.exception exc
- @out.should == " (FAILED - 1)"
+ expect(@out).to eq(" (FAILED - 1)")
end
it "prints the #it string if an exception has already been raised" do
@@ -84,11 +84,11 @@ describe SpecdocFormatter, "#exception" do
@formatter.exception exc
exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
@formatter.exception exc
- @out.should == " (FAILED - 1)\n- it (ERROR - 2)"
+ expect(@out).to eq(" (FAILED - 1)\n- it (ERROR - 2)")
end
end
-describe SpecdocFormatter, "#after" do
+RSpec.describe SpecdocFormatter, "#after" do
before :each do
$stdout = @out = IOStub.new
@formatter = SpecdocFormatter.new
@@ -101,6 +101,6 @@ describe SpecdocFormatter, "#after" do
it "prints a newline character" do
@formatter.after @state
- @out.should == "\n"
+ expect(@out).to eq("\n")
end
end
diff --git a/spec/mspec/spec/runner/formatters/spinner_spec.rb b/spec/mspec/spec/runner/formatters/spinner_spec.rb
index a122620e39..5c93d38822 100644
--- a/spec/mspec/spec/runner/formatters/spinner_spec.rb
+++ b/spec/mspec/spec/runner/formatters/spinner_spec.rb
@@ -3,7 +3,7 @@ require 'mspec/runner/formatters/spinner'
require 'mspec/runner/mspec'
require 'mspec/runner/example'
-describe SpinnerFormatter, "#initialize" do
+RSpec.describe SpinnerFormatter, "#initialize" do
it "permits zero arguments" do
SpinnerFormatter.new
end
@@ -13,33 +13,33 @@ describe SpinnerFormatter, "#initialize" do
end
end
-describe SpinnerFormatter, "#register" do
+RSpec.describe SpinnerFormatter, "#register" do
before :each do
@formatter = SpinnerFormatter.new
- MSpec.stub(:register)
+ allow(MSpec).to receive(:register)
end
it "registers self with MSpec for appropriate actions" do
- MSpec.should_receive(:register).with(:start, @formatter)
- MSpec.should_receive(:register).with(:unload, @formatter)
- MSpec.should_receive(:register).with(:after, @formatter)
- MSpec.should_receive(:register).with(:finish, @formatter)
+ expect(MSpec).to receive(:register).with(:start, @formatter)
+ expect(MSpec).to receive(:register).with(:unload, @formatter)
+ expect(MSpec).to receive(:register).with(:after, @formatter)
+ expect(MSpec).to receive(:register).with(:finish, @formatter)
@formatter.register
end
it "creates TimerAction and TallyAction" do
timer = double("timer")
tally = double("tally")
- timer.should_receive(:register)
- tally.should_receive(:register)
- tally.should_receive(:counter)
- TimerAction.should_receive(:new).and_return(timer)
- TallyAction.should_receive(:new).and_return(tally)
+ expect(timer).to receive(:register)
+ expect(tally).to receive(:register)
+ expect(tally).to receive(:counter)
+ expect(TimerAction).to receive(:new).and_return(timer)
+ expect(TallyAction).to receive(:new).and_return(tally)
@formatter.register
end
end
-describe SpinnerFormatter, "#print" do
+RSpec.describe SpinnerFormatter, "#print" do
after :each do
$stdout = STDOUT
end
@@ -48,11 +48,11 @@ describe SpinnerFormatter, "#print" do
$stdout = IOStub.new
formatter = SpinnerFormatter.new "some/file"
formatter.print "begonias"
- $stdout.should == "begonias"
+ expect($stdout).to eq("begonias")
end
end
-describe SpinnerFormatter, "#after" do
+RSpec.describe SpinnerFormatter, "#after" do
before :each do
$stdout = IOStub.new
MSpec.store(:files, ["a", "b", "c", "d"])
@@ -78,6 +78,6 @@ describe SpinnerFormatter, "#after" do
output = "\r[/ | 0% | 00:00:00] #{green} 0F #{green} 0E#{reset} " \
"\r[- | 0% | 00:00:00] #{green} 0F #{green} 0E#{reset} " \
"\r[\\ | ========== 25% | 00:00:00] #{green} 0F #{green} 0E#{reset} "
- $stdout.should == output
+ expect($stdout).to eq(output)
end
end
diff --git a/spec/mspec/spec/runner/formatters/summary_spec.rb b/spec/mspec/spec/runner/formatters/summary_spec.rb
index 16a156b695..c87d940042 100644
--- a/spec/mspec/spec/runner/formatters/summary_spec.rb
+++ b/spec/mspec/spec/runner/formatters/summary_spec.rb
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/../../spec_helper'
require 'mspec/runner/formatters/summary'
require 'mspec/runner/example'
-describe SummaryFormatter, "#after" do
+RSpec.describe SummaryFormatter, "#after" do
before :each do
$stdout = @out = IOStub.new
@formatter = SummaryFormatter.new
@@ -21,6 +21,6 @@ describe SummaryFormatter, "#after" do
exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful")
@formatter.exception exc
@formatter.after(@state)
- @out.should == ""
+ expect(@out).to eq("")
end
end
diff --git a/spec/mspec/spec/runner/formatters/unit_spec.rb b/spec/mspec/spec/runner/formatters/unit_spec.rb
index 18167a32b8..d349e6871d 100644
--- a/spec/mspec/spec/runner/formatters/unit_spec.rb
+++ b/spec/mspec/spec/runner/formatters/unit_spec.rb
@@ -3,17 +3,17 @@ require 'mspec/runner/formatters/unit'
require 'mspec/runner/example'
require 'mspec/utils/script'
-describe UnitdiffFormatter, "#finish" do
+RSpec.describe UnitdiffFormatter, "#finish" do
before :each do
@tally = double("tally").as_null_object
- TallyAction.stub(:new).and_return(@tally)
+ allow(TallyAction).to receive(:new).and_return(@tally)
@timer = double("timer").as_null_object
- TimerAction.stub(:new).and_return(@timer)
+ allow(TimerAction).to receive(:new).and_return(@timer)
$stdout = @out = IOStub.new
context = ContextState.new "describe"
@state = ExampleState.new(context, "it")
- MSpec.stub(:register)
+ allow(MSpec).to receive(:register)
@formatter = UnitdiffFormatter.new
@formatter.register
end
@@ -27,39 +27,38 @@ describe UnitdiffFormatter, "#finish" do
@formatter.exception exc
@formatter.after @state
@formatter.finish
- @out.should =~ /^1\)\ndescribe it ERROR$/
+ expect(@out).to match(/^1\)\ndescribe it ERROR$/)
end
it "prints a backtrace for an exception" do
exc = ExceptionState.new @state, nil, Exception.new("broken")
- exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ allow(exc).to receive(:backtrace).and_return("path/to/some/file.rb:35:in method")
@formatter.exception exc
@formatter.finish
- @out.should =~ %r[path/to/some/file.rb:35:in method$]
+ expect(@out).to match(%r[path/to/some/file.rb:35:in method$])
end
it "prints a summary of elapsed time" do
- @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
+ expect(@timer).to receive(:format).and_return("Finished in 2.0 seconds")
@formatter.finish
- @out.should =~ /^Finished in 2.0 seconds$/
+ expect(@out).to match(/^Finished in 2.0 seconds$/)
end
it "prints a tally of counts" do
- @tally.should_receive(:format).and_return("1 example, 0 failures")
+ expect(@tally).to receive(:format).and_return("1 example, 0 failures")
@formatter.finish
- @out.should =~ /^1 example, 0 failures$/
+ expect(@out).to match(/^1 example, 0 failures$/)
end
it "prints errors, backtraces, elapsed time, and tallies" do
exc = ExceptionState.new @state, nil, Exception.new("broken")
- exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ allow(exc).to receive(:backtrace).and_return("path/to/some/file.rb:35:in method")
@formatter.exception exc
@formatter.after @state
- @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
- @tally.should_receive(:format).and_return("1 example, 0 failures")
+ expect(@timer).to receive(:format).and_return("Finished in 2.0 seconds")
+ expect(@tally).to receive(:format).and_return("1 example, 0 failures")
@formatter.finish
- @out.should ==
-%[E
+ expect(@out).to eq(%[E
Finished in 2.0 seconds
@@ -69,6 +68,6 @@ Exception: broken:
path/to/some/file.rb:35:in method
1 example, 0 failures
-]
+])
end
end
diff --git a/spec/mspec/spec/runner/formatters/yaml_spec.rb b/spec/mspec/spec/runner/formatters/yaml_spec.rb
index eb4d99f74c..2e334fdbb9 100644
--- a/spec/mspec/spec/runner/formatters/yaml_spec.rb
+++ b/spec/mspec/spec/runner/formatters/yaml_spec.rb
@@ -1,8 +1,9 @@
require File.dirname(__FILE__) + '/../../spec_helper'
require 'mspec/runner/formatters/yaml'
require 'mspec/runner/example'
+require 'mspec/helpers'
-describe YamlFormatter, "#initialize" do
+RSpec.describe YamlFormatter, "#initialize" do
it "permits zero arguments" do
YamlFormatter.new
end
@@ -12,11 +13,11 @@ describe YamlFormatter, "#initialize" do
end
end
-describe YamlFormatter, "#print" do
+RSpec.describe YamlFormatter, "#print" do
before :each do
$stdout = IOStub.new
@out = IOStub.new
- File.stub(:open).and_return(@out)
+ allow(File).to receive(:open).and_return(@out)
@formatter = YamlFormatter.new "some/file"
end
@@ -26,100 +27,108 @@ describe YamlFormatter, "#print" do
it "writes to $stdout if #switch has not been called" do
@formatter.print "begonias"
- $stdout.should == "begonias"
- @out.should == ""
+ expect($stdout).to eq("begonias")
+ expect(@out).to eq("")
end
it "writes to the file passed to #initialize once #switch has been called" do
@formatter.switch
@formatter.print "begonias"
- $stdout.should == ""
- @out.should == "begonias"
+ expect($stdout).to eq("")
+ expect(@out).to eq("begonias")
end
it "writes to $stdout once #switch is called if no file was passed to #initialize" do
formatter = YamlFormatter.new
formatter.switch
formatter.print "begonias"
- $stdout.should == "begonias"
- @out.should == ""
+ expect($stdout).to eq("begonias")
+ expect(@out).to eq("")
end
end
-describe YamlFormatter, "#finish" do
+RSpec.describe YamlFormatter, "#finish" do
before :each do
@tally = double("tally").as_null_object
@counter = double("counter").as_null_object
- @tally.stub(:counter).and_return(@counter)
- TallyAction.stub(:new).and_return(@tally)
+ allow(@tally).to receive(:counter).and_return(@counter)
+ allow(TallyAction).to receive(:new).and_return(@tally)
@timer = double("timer").as_null_object
- TimerAction.stub(:new).and_return(@timer)
+ allow(TimerAction).to receive(:new).and_return(@timer)
+
+ @out = tmp("YamlFormatter")
- $stdout = IOStub.new
context = ContextState.new "describe"
@state = ExampleState.new(context, "it")
- @formatter = YamlFormatter.new
- @formatter.stub(:backtrace).and_return("")
- MSpec.stub(:register)
+ @formatter = YamlFormatter.new(@out)
+ allow(@formatter).to receive(:backtrace).and_return("")
+ allow(MSpec).to receive(:register)
@formatter.register
exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
- exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
+ allow(exc).to receive(:backtrace).and_return("path/to/some/file.rb:35:in method")
@formatter.exception exc
@formatter.after @state
end
after :each do
- $stdout = STDOUT
+ rm_r @out
end
it "calls #switch" do
- @formatter.should_receive(:switch)
+ expect(@formatter).to receive(:switch).and_call_original
@formatter.finish
end
it "outputs a failure message and backtrace" do
@formatter.finish
- $stdout.should include "describe it ERROR"
- $stdout.should include "MSpecExampleError: broken\\n"
- $stdout.should include "path/to/some/file.rb:35:in method"
+ output = File.read(@out)
+ expect(output).to include "describe it ERROR"
+ expect(output).to include "MSpecExampleError: broken\\n"
+ expect(output).to include "path/to/some/file.rb:35:in method"
end
it "outputs an elapsed time" do
- @timer.should_receive(:elapsed).and_return(4.2)
+ expect(@timer).to receive(:elapsed).and_return(4.2)
@formatter.finish
- $stdout.should include "time: 4.2"
+ output = File.read(@out)
+ expect(output).to include "time: 4.2"
end
it "outputs a file count" do
- @counter.should_receive(:files).and_return(3)
+ expect(@counter).to receive(:files).and_return(3)
@formatter.finish
- $stdout.should include "files: 3"
+ output = File.read(@out)
+ expect(output).to include "files: 3"
end
it "outputs an example count" do
- @counter.should_receive(:examples).and_return(3)
+ expect(@counter).to receive(:examples).and_return(3)
@formatter.finish
- $stdout.should include "examples: 3"
+ output = File.read(@out)
+ expect(output).to include "examples: 3"
end
it "outputs an expectation count" do
- @counter.should_receive(:expectations).and_return(9)
+ expect(@counter).to receive(:expectations).and_return(9)
@formatter.finish
- $stdout.should include "expectations: 9"
+ output = File.read(@out)
+ expect(output).to include "expectations: 9"
end
it "outputs a failure count" do
- @counter.should_receive(:failures).and_return(2)
+ expect(@counter).to receive(:failures).and_return(2)
@formatter.finish
- $stdout.should include "failures: 2"
+ output = File.read(@out)
+ expect(output).to include "failures: 2"
end
it "outputs an error count" do
- @counter.should_receive(:errors).and_return(1)
+ expect(@counter).to receive(:errors).and_return(1)
@formatter.finish
- $stdout.should include "errors: 1"
+ output = File.read(@out)
+ expect(output).to include "errors: 1"
end
end
diff --git a/spec/mspec/spec/runner/mspec_spec.rb b/spec/mspec/spec/runner/mspec_spec.rb
index 7dad4458d3..4af01806c0 100644
--- a/spec/mspec/spec/runner/mspec_spec.rb
+++ b/spec/mspec/spec/runner/mspec_spec.rb
@@ -6,93 +6,93 @@ require 'mspec/matchers/base'
require 'mspec/runner/mspec'
require 'mspec/runner/example'
-describe MSpec, ".register_files" do
+RSpec.describe MSpec, ".register_files" do
it "records which spec files to run" do
MSpec.register_files [:one, :two, :three]
- MSpec.files_array.should == [:one, :two, :three]
+ expect(MSpec.files_array).to eq([:one, :two, :three])
end
end
-describe MSpec, ".register_mode" do
+RSpec.describe MSpec, ".register_mode" do
before :each do
MSpec.clear_modes
end
it "sets execution mode flags" do
MSpec.register_mode :verify
- MSpec.retrieve(:modes).should == [:verify]
+ expect(MSpec.retrieve(:modes)).to eq([:verify])
end
end
-describe MSpec, ".register_tags_patterns" do
+RSpec.describe MSpec, ".register_tags_patterns" do
it "records the patterns for generating a tag file from a spec file" do
MSpec.register_tags_patterns [[/spec\/ruby/, "spec/tags"], [/frozen/, "ruby"]]
- MSpec.retrieve(:tags_patterns).should == [[/spec\/ruby/, "spec/tags"], [/frozen/, "ruby"]]
+ expect(MSpec.retrieve(:tags_patterns)).to eq([[/spec\/ruby/, "spec/tags"], [/frozen/, "ruby"]])
end
end
-describe MSpec, ".register_exit" do
+RSpec.describe MSpec, ".register_exit" do
before :each do
MSpec.store :exit, 0
end
it "records the exit code" do
- MSpec.exit_code.should == 0
+ expect(MSpec.exit_code).to eq(0)
MSpec.register_exit 1
- MSpec.exit_code.should == 1
+ expect(MSpec.exit_code).to eq(1)
end
end
-describe MSpec, ".exit_code" do
+RSpec.describe MSpec, ".exit_code" do
it "retrieves the code set with .register_exit" do
MSpec.register_exit 99
- MSpec.exit_code.should == 99
+ expect(MSpec.exit_code).to eq(99)
end
end
-describe MSpec, ".store" do
+RSpec.describe MSpec, ".store" do
it "records data for MSpec settings" do
MSpec.store :anything, :value
- MSpec.retrieve(:anything).should == :value
+ expect(MSpec.retrieve(:anything)).to eq(:value)
end
end
-describe MSpec, ".retrieve" do
+RSpec.describe MSpec, ".retrieve" do
it "accesses .store'd data" do
MSpec.register :retrieve, :first
- MSpec.retrieve(:retrieve).should == [:first]
+ expect(MSpec.retrieve(:retrieve)).to eq([:first])
end
end
-describe MSpec, ".randomize" do
+RSpec.describe MSpec, ".randomize" do
it "sets the flag to randomize spec execution order" do
- MSpec.randomize?.should == false
+ expect(MSpec.randomize?).to eq(false)
MSpec.randomize = true
- MSpec.randomize?.should == true
+ expect(MSpec.randomize?).to eq(true)
MSpec.randomize = false
- MSpec.randomize?.should == false
+ expect(MSpec.randomize?).to eq(false)
end
end
-describe MSpec, ".register" do
+RSpec.describe MSpec, ".register" do
it "is the gateway behind the register(symbol, action) facility" do
MSpec.register :bonus, :first
MSpec.register :bonus, :second
MSpec.register :bonus, :second
- MSpec.retrieve(:bonus).should == [:first, :second]
+ expect(MSpec.retrieve(:bonus)).to eq([:first, :second])
end
end
-describe MSpec, ".unregister" do
+RSpec.describe MSpec, ".unregister" do
it "is the gateway behind the unregister(symbol, actions) facility" do
MSpec.register :unregister, :first
MSpec.register :unregister, :second
MSpec.unregister :unregister, :second
- MSpec.retrieve(:unregister).should == [:first]
+ expect(MSpec.retrieve(:unregister)).to eq([:first])
end
end
-describe MSpec, ".protect" do
+RSpec.describe MSpec, ".protect" do
before :each do
MSpec.clear_current
@cs = ContextState.new "C#m"
@@ -103,11 +103,11 @@ describe MSpec, ".protect" do
end
it "returns true if no exception is raised" do
- MSpec.protect("passed") { 1 }.should be_true
+ expect(MSpec.protect("passed") { 1 }).to be_truthy
end
it "returns false if an exception is raised" do
- MSpec.protect("testing") { raise ScratchPad.recorded }.should be_false
+ expect(MSpec.protect("testing") { raise ScratchPad.recorded }).to be_falsey
end
it "rescues any exceptions raised when evaluating the block argument" do
@@ -120,231 +120,230 @@ describe MSpec, ".protect" do
rescue SystemExit
ScratchPad.record :system_exit
end
- ScratchPad.recorded.should == :system_exit
+ expect(ScratchPad.recorded).to eq(:system_exit)
end
it "calls all the exception actions" do
exc = ExceptionState.new @es, "testing", ScratchPad.recorded
- ExceptionState.stub(:new).and_return(exc)
+ allow(ExceptionState).to receive(:new).and_return(exc)
action = double("exception")
- action.should_receive(:exception).with(exc)
+ expect(action).to receive(:exception).with(exc)
MSpec.register :exception, action
MSpec.protect("testing") { raise ScratchPad.recorded }
MSpec.unregister :exception, action
end
it "registers a non-zero exit code when an exception is raised" do
- MSpec.should_receive(:register_exit).with(1)
+ expect(MSpec).to receive(:register_exit).with(1)
MSpec.protect("testing") { raise ScratchPad.recorded }
end
end
-describe MSpec, ".register_current" do
+RSpec.describe MSpec, ".register_current" do
before :each do
MSpec.clear_current
end
it "sets the value returned by MSpec.current" do
- MSpec.current.should be_nil
+ expect(MSpec.current).to be_nil
MSpec.register_current :a
- MSpec.current.should == :a
+ expect(MSpec.current).to eq(:a)
end
end
-describe MSpec, ".clear_current" do
+RSpec.describe MSpec, ".clear_current" do
it "sets the value returned by MSpec.current to nil" do
MSpec.register_current :a
- MSpec.current.should_not be_nil
+ expect(MSpec.current).not_to be_nil
MSpec.clear_current
- MSpec.current.should be_nil
+ expect(MSpec.current).to be_nil
end
end
-describe MSpec, ".current" do
+RSpec.describe MSpec, ".current" do
before :each do
MSpec.clear_current
end
it "returns nil if no ContextState has been registered" do
- MSpec.current.should be_nil
+ expect(MSpec.current).to be_nil
end
it "returns the most recently registered ContextState" do
first = ContextState.new ""
second = ContextState.new ""
MSpec.register_current first
- MSpec.current.should == first
+ expect(MSpec.current).to eq(first)
MSpec.register_current second
- MSpec.current.should == second
+ expect(MSpec.current).to eq(second)
end
end
-describe MSpec, ".actions" do
+RSpec.describe MSpec, ".actions" do
before :each do
MSpec.store :start, []
ScratchPad.record []
start_one = double("one")
- start_one.stub(:start).and_return { ScratchPad << :one }
+ allow(start_one).to receive(:start) { ScratchPad << :one }
start_two = double("two")
- start_two.stub(:start).and_return { ScratchPad << :two }
+ allow(start_two).to receive(:start) { ScratchPad << :two }
MSpec.register :start, start_one
MSpec.register :start, start_two
end
it "does not attempt to run any actions if none have been registered" do
MSpec.store :finish, nil
- lambda { MSpec.actions :finish }.should_not raise_error
+ expect { MSpec.actions :finish }.not_to raise_error
end
it "runs each action registered as a start action" do
MSpec.actions :start
- ScratchPad.recorded.should == [:one, :two]
+ expect(ScratchPad.recorded).to eq([:one, :two])
end
end
-describe MSpec, ".mode?" do
+RSpec.describe MSpec, ".mode?" do
before :each do
MSpec.clear_modes
end
it "returns true if the mode has been set" do
- MSpec.mode?(:verify).should == false
+ expect(MSpec.mode?(:verify)).to eq(false)
MSpec.register_mode :verify
- MSpec.mode?(:verify).should == true
+ expect(MSpec.mode?(:verify)).to eq(true)
end
end
-describe MSpec, ".clear_modes" do
+RSpec.describe MSpec, ".clear_modes" do
it "clears all registered modes" do
MSpec.register_mode(:pretend)
MSpec.register_mode(:verify)
- MSpec.mode?(:pretend).should == true
- MSpec.mode?(:verify).should == true
+ expect(MSpec.mode?(:pretend)).to eq(true)
+ expect(MSpec.mode?(:verify)).to eq(true)
MSpec.clear_modes
- MSpec.mode?(:pretend).should == false
- MSpec.mode?(:verify).should == false
+ expect(MSpec.mode?(:pretend)).to eq(false)
+ expect(MSpec.mode?(:verify)).to eq(false)
end
end
-describe MSpec, ".guarded?" do
+RSpec.describe MSpec, ".guarded?" do
before :each do
MSpec.instance_variable_set :@guarded, []
end
it "returns false if no guard has run" do
- MSpec.guarded?.should == false
+ expect(MSpec.guarded?).to eq(false)
end
it "returns true if a single guard has run" do
MSpec.guard
- MSpec.guarded?.should == true
+ expect(MSpec.guarded?).to eq(true)
end
it "returns true if more than one guard has run" do
MSpec.guard
MSpec.guard
- MSpec.guarded?.should == true
+ expect(MSpec.guarded?).to eq(true)
end
it "returns true until all guards have finished" do
MSpec.guard
MSpec.guard
- MSpec.guarded?.should == true
+ expect(MSpec.guarded?).to eq(true)
MSpec.unguard
- MSpec.guarded?.should == true
+ expect(MSpec.guarded?).to eq(true)
MSpec.unguard
- MSpec.guarded?.should == false
+ expect(MSpec.guarded?).to eq(false)
end
end
-describe MSpec, ".describe" do
+RSpec.describe MSpec, ".describe" do
before :each do
MSpec.clear_current
@cs = ContextState.new ""
- ContextState.stub(:new).and_return(@cs)
- MSpec.stub(:current).and_return(nil)
- MSpec.stub(:register_current)
+ allow(ContextState).to receive(:new).and_return(@cs)
+ allow(MSpec).to receive(:current).and_return(nil)
+ allow(MSpec).to receive(:register_current)
end
it "creates a new ContextState for the block" do
- ContextState.should_receive(:new).and_return(@cs)
+ expect(ContextState).to receive(:new).and_return(@cs)
MSpec.describe(Object) { }
end
it "accepts an optional second argument" do
- ContextState.should_receive(:new).and_return(@cs)
+ expect(ContextState).to receive(:new).and_return(@cs)
MSpec.describe(Object, "msg") { }
end
it "registers the newly created ContextState" do
- MSpec.should_receive(:register_current).with(@cs).twice
+ expect(MSpec).to receive(:register_current).with(@cs).twice
MSpec.describe(Object) { }
end
it "invokes the ContextState#describe method" do
- prc = lambda { }
- @cs.should_receive(:describe).with(&prc)
- MSpec.describe(Object, "msg", &prc)
+ expect(@cs).to receive(:describe)
+ MSpec.describe(Object, "msg") {}
end
end
-describe MSpec, ".process" do
+RSpec.describe MSpec, ".process" do
before :each do
- MSpec.stub(:files)
+ allow(MSpec).to receive(:files)
MSpec.store :start, []
MSpec.store :finish, []
- STDOUT.stub(:puts)
+ allow(STDOUT).to receive(:puts)
end
it "prints the RUBY_DESCRIPTION" do
- STDOUT.should_receive(:puts).with(RUBY_DESCRIPTION)
+ expect(STDOUT).to receive(:puts).with(RUBY_DESCRIPTION)
MSpec.process
end
it "calls all start actions" do
start = double("start")
- start.stub(:start).and_return { ScratchPad.record :start }
+ allow(start).to receive(:start) { ScratchPad.record :start }
MSpec.register :start, start
MSpec.process
- ScratchPad.recorded.should == :start
+ expect(ScratchPad.recorded).to eq(:start)
end
it "calls all finish actions" do
finish = double("finish")
- finish.stub(:finish).and_return { ScratchPad.record :finish }
+ allow(finish).to receive(:finish) { ScratchPad.record :finish }
MSpec.register :finish, finish
MSpec.process
- ScratchPad.recorded.should == :finish
+ expect(ScratchPad.recorded).to eq(:finish)
end
it "calls the files method" do
- MSpec.should_receive(:files)
+ expect(MSpec).to receive(:files)
MSpec.process
end
end
-describe MSpec, ".files" do
+RSpec.describe MSpec, ".files" do
before :each do
MSpec.store :load, []
MSpec.store :unload, []
MSpec.register_files [:one, :two, :three]
- Kernel.stub(:load)
+ allow(Kernel).to receive(:load)
end
it "calls load actions before each file" do
load = double("load")
- load.stub(:load).and_return { ScratchPad.record :load }
+ allow(load).to receive(:load) { ScratchPad.record :load }
MSpec.register :load, load
MSpec.files
- ScratchPad.recorded.should == :load
+ expect(ScratchPad.recorded).to eq(:load)
end
it "shuffles the file list if .randomize? is true" do
MSpec.randomize = true
- MSpec.should_receive(:shuffle)
+ expect(MSpec).to receive(:shuffle)
MSpec.files
MSpec.randomize = false
end
@@ -352,14 +351,14 @@ describe MSpec, ".files" do
it "registers the current file" do
load = double("load")
files = []
- load.stub(:load).and_return { files << MSpec.file }
+ allow(load).to receive(:load) { files << MSpec.file }
MSpec.register :load, load
MSpec.files
- files.should == [:one, :two, :three]
+ expect(files).to eq([:one, :two, :three])
end
end
-describe MSpec, ".shuffle" do
+RSpec.describe MSpec, ".shuffle" do
before :each do
@base = (0..100).to_a
@list = @base.clone
@@ -368,30 +367,30 @@ describe MSpec, ".shuffle" do
it "does not alter the elements in the list" do
@base.each do |elt|
- @list.should include(elt)
+ expect(@list).to include(elt)
end
end
it "changes the order of the list" do
# obviously, this spec has a certain probability
# of failing. If it fails, run it again.
- @list.should_not == @base
+ expect(@list).not_to eq(@base)
end
end
-describe MSpec, ".tags_file" do
+RSpec.describe MSpec, ".tags_file" do
before :each do
MSpec.store :file, "path/to/spec/something/some_spec.rb"
MSpec.store :tags_patterns, nil
end
it "returns the default tags file for the current spec file" do
- MSpec.tags_file.should == "path/to/spec/tags/something/some_tags.txt"
+ expect(MSpec.tags_file).to eq("path/to/spec/tags/something/some_tags.txt")
end
it "returns the tags file for the current spec file with custom tags_patterns" do
MSpec.register_tags_patterns [[/^(.*)\/spec/, '\1/tags'], [/_spec.rb/, "_tags.txt"]]
- MSpec.tags_file.should == "path/to/tags/something/some_tags.txt"
+ expect(MSpec.tags_file).to eq("path/to/tags/something/some_tags.txt")
end
it "performs multiple substitutions" do
@@ -400,31 +399,31 @@ describe MSpec, ".tags_file" do
[%r(/spec/), "/spec/tags/"],
[/_spec.rb/, "_tags.txt"]
]
- MSpec.tags_file.should == "path/to/spec/tags/other/some_tags.txt"
+ expect(MSpec.tags_file).to eq("path/to/spec/tags/other/some_tags.txt")
end
it "handles cases where no substitution is performed" do
MSpec.register_tags_patterns [[/nothing/, "something"]]
- MSpec.tags_file.should == "path/to/spec/something/some_spec.rb"
+ expect(MSpec.tags_file).to eq("path/to/spec/something/some_spec.rb")
end
end
-describe MSpec, ".read_tags" do
+RSpec.describe MSpec, ".read_tags" do
before :each do
- MSpec.stub(:tags_file).and_return(File.dirname(__FILE__) + '/tags.txt')
+ allow(MSpec).to receive(:tags_file).and_return(File.dirname(__FILE__) + '/tags.txt')
end
it "returns a list of tag instances for matching tag names found" do
one = SpecTag.new "fail(broken):Some#method? works"
- MSpec.read_tags(["fail", "pass"]).should == [one]
+ expect(MSpec.read_tags(["fail", "pass"])).to eq([one])
end
it "returns [] if no tags names match" do
- MSpec.read_tags("super").should == []
+ expect(MSpec.read_tags("super")).to eq([])
end
end
-describe MSpec, ".read_tags" do
+RSpec.describe MSpec, ".read_tags" do
before :each do
@tag = SpecTag.new "fails:Some#method"
File.open(tmp("tags.txt", false), "w") do |f|
@@ -432,18 +431,18 @@ describe MSpec, ".read_tags" do
f.puts @tag
f.puts ""
end
- MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ allow(MSpec).to receive(:tags_file).and_return(tmp("tags.txt", false))
end
it "does not return a tag object for empty lines" do
- MSpec.read_tags(["fails"]).should == [@tag]
+ expect(MSpec.read_tags(["fails"])).to eq([@tag])
end
end
-describe MSpec, ".write_tags" do
+RSpec.describe MSpec, ".write_tags" do
before :each do
FileUtils.cp File.dirname(__FILE__) + "/tags.txt", tmp("tags.txt", false)
- MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ allow(MSpec).to receive(:tags_file).and_return(tmp("tags.txt", false))
@tag1 = SpecTag.new "check(broken):Tag#rewrite works"
@tag2 = SpecTag.new "broken:Tag#write_tags fails"
end
@@ -453,22 +452,22 @@ describe MSpec, ".write_tags" do
end
it "overwrites the tags in the tag file" do
- IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works
+ expect(IO.read(tmp("tags.txt", false))).to eq(%[fail(broken):Some#method? works
incomplete(20%):The#best method ever
benchmark(0.01825):The#fastest method today
extended():\"Multi-line\\ntext\\ntag\"
-]
+])
MSpec.write_tags [@tag1, @tag2]
- IO.read(tmp("tags.txt", false)).should == %[check(broken):Tag#rewrite works
+ expect(IO.read(tmp("tags.txt", false))).to eq(%[check(broken):Tag#rewrite works
broken:Tag#write_tags fails
-]
+])
end
end
-describe MSpec, ".write_tag" do
+RSpec.describe MSpec, ".write_tag" do
before :each do
- FileUtils.stub(:mkdir_p)
- MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ allow(FileUtils).to receive(:mkdir_p)
+ allow(MSpec).to receive(:tags_file).and_return(tmp("tags.txt", false))
@tag = SpecTag.new "fail(broken):Some#method works"
end
@@ -478,20 +477,20 @@ describe MSpec, ".write_tag" do
it "writes a tag to the tags file for the current spec file" do
MSpec.write_tag @tag
- IO.read(tmp("tags.txt", false)).should == "fail(broken):Some#method works\n"
+ expect(IO.read(tmp("tags.txt", false))).to eq("fail(broken):Some#method works\n")
end
it "does not write a duplicate tag" do
File.open(tmp("tags.txt", false), "w") { |f| f.puts @tag }
MSpec.write_tag @tag
- IO.read(tmp("tags.txt", false)).should == "fail(broken):Some#method works\n"
+ expect(IO.read(tmp("tags.txt", false))).to eq("fail(broken):Some#method works\n")
end
end
-describe MSpec, ".delete_tag" do
+RSpec.describe MSpec, ".delete_tag" do
before :each do
FileUtils.cp File.dirname(__FILE__) + "/tags.txt", tmp("tags.txt", false)
- MSpec.stub(:tags_file).and_return(tmp("tags.txt", false))
+ allow(MSpec).to receive(:tags_file).and_return(tmp("tags.txt", false))
@tag = SpecTag.new "fail(Comments don't matter):Some#method? works"
end
@@ -500,84 +499,84 @@ describe MSpec, ".delete_tag" do
end
it "deletes the tag if it exists" do
- MSpec.delete_tag(@tag).should == true
- IO.read(tmp("tags.txt", false)).should == %[incomplete(20%):The#best method ever
+ expect(MSpec.delete_tag(@tag)).to eq(true)
+ expect(IO.read(tmp("tags.txt", false))).to eq(%[incomplete(20%):The#best method ever
benchmark(0.01825):The#fastest method today
extended():\"Multi-line\\ntext\\ntag\"
-]
+])
end
it "deletes a tag with escaped newlines" do
- MSpec.delete_tag(SpecTag.new('extended:"Multi-line\ntext\ntag"')).should == true
- IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works
+ expect(MSpec.delete_tag(SpecTag.new('extended:"Multi-line\ntext\ntag"'))).to eq(true)
+ expect(IO.read(tmp("tags.txt", false))).to eq(%[fail(broken):Some#method? works
incomplete(20%):The#best method ever
benchmark(0.01825):The#fastest method today
-]
+])
end
it "does not change the tags file contents if the tag doesn't exist" do
@tag.tag = "failed"
- MSpec.delete_tag(@tag).should == false
- IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works
+ expect(MSpec.delete_tag(@tag)).to eq(false)
+ expect(IO.read(tmp("tags.txt", false))).to eq(%[fail(broken):Some#method? works
incomplete(20%):The#best method ever
benchmark(0.01825):The#fastest method today
extended():\"Multi-line\\ntext\\ntag\"
-]
+])
end
it "deletes the tag file if it is empty" do
- MSpec.delete_tag(@tag).should == true
- MSpec.delete_tag(SpecTag.new("incomplete:The#best method ever")).should == true
- MSpec.delete_tag(SpecTag.new("benchmark:The#fastest method today")).should == true
- MSpec.delete_tag(SpecTag.new('extended:"Multi-line\ntext\ntag"')).should == true
- File.exist?(tmp("tags.txt", false)).should == false
+ expect(MSpec.delete_tag(@tag)).to eq(true)
+ expect(MSpec.delete_tag(SpecTag.new("incomplete:The#best method ever"))).to eq(true)
+ expect(MSpec.delete_tag(SpecTag.new("benchmark:The#fastest method today"))).to eq(true)
+ expect(MSpec.delete_tag(SpecTag.new('extended:"Multi-line\ntext\ntag"'))).to eq(true)
+ expect(File.exist?(tmp("tags.txt", false))).to eq(false)
end
end
-describe MSpec, ".delete_tags" do
+RSpec.describe MSpec, ".delete_tags" do
before :each do
@tags = tmp("tags.txt", false)
FileUtils.cp File.dirname(__FILE__) + "/tags.txt", @tags
- MSpec.stub(:tags_file).and_return(@tags)
+ allow(MSpec).to receive(:tags_file).and_return(@tags)
end
it "deletes the tag file" do
MSpec.delete_tags
- File.exist?(@tags).should be_false
+ expect(File.exist?(@tags)).to be_falsey
end
end
-describe MSpec, ".expectation" do
+RSpec.describe MSpec, ".expectation" do
it "sets the flag that an expectation has been reported" do
MSpec.clear_expectations
- MSpec.expectation?.should be_false
+ expect(MSpec.expectation?).to be_falsey
MSpec.expectation
- MSpec.expectation?.should be_true
+ expect(MSpec.expectation?).to be_truthy
end
end
-describe MSpec, ".expectation?" do
+RSpec.describe MSpec, ".expectation?" do
it "returns true if an expectation has been reported" do
MSpec.expectation
- MSpec.expectation?.should be_true
+ expect(MSpec.expectation?).to be_truthy
end
it "returns false if an expectation has not been reported" do
MSpec.clear_expectations
- MSpec.expectation?.should be_false
+ expect(MSpec.expectation?).to be_falsey
end
end
-describe MSpec, ".clear_expectations" do
+RSpec.describe MSpec, ".clear_expectations" do
it "clears the flag that an expectation has been reported" do
MSpec.expectation
- MSpec.expectation?.should be_true
+ expect(MSpec.expectation?).to be_truthy
MSpec.clear_expectations
- MSpec.expectation?.should be_false
+ expect(MSpec.expectation?).to be_falsey
end
end
-describe MSpec, ".register_shared" do
+RSpec.describe MSpec, ".register_shared" do
it "stores a shared ContextState by description" do
parent = ContextState.new "container"
state = ContextState.new "shared"
@@ -585,14 +584,14 @@ describe MSpec, ".register_shared" do
prc = lambda { }
state.describe(&prc)
MSpec.register_shared(state)
- MSpec.retrieve(:shared)["shared"].should == state
+ expect(MSpec.retrieve(:shared)["shared"]).to eq(state)
end
end
-describe MSpec, ".retrieve_shared" do
+RSpec.describe MSpec, ".retrieve_shared" do
it "retrieves the shared ContextState matching description" do
state = ContextState.new ""
MSpec.retrieve(:shared)["shared"] = state
- MSpec.retrieve_shared(:shared).should == state
+ expect(MSpec.retrieve_shared(:shared)).to eq(state)
end
end
diff --git a/spec/mspec/spec/runner/shared_spec.rb b/spec/mspec/spec/runner/shared_spec.rb
index b91800b7db..153b8f0698 100644
--- a/spec/mspec/spec/runner/shared_spec.rb
+++ b/spec/mspec/spec/runner/shared_spec.rb
@@ -3,7 +3,7 @@ require 'mspec/runner/shared'
require 'mspec/runner/context'
require 'mspec/runner/example'
-describe Object, "#it_behaves_like" do
+RSpec.describe Object, "#it_behaves_like" do
before :each do
ScratchPad.clear
@@ -14,14 +14,14 @@ describe Object, "#it_behaves_like" do
@state.singleton_class.send(:public, :it_behaves_like)
@shared = ContextState.new :shared_spec, :shared => true
- MSpec.stub(:retrieve_shared).and_return(@shared)
+ allow(MSpec).to receive(:retrieve_shared).and_return(@shared)
end
it "creates @method set to the name of the aliased method" do
@shared.it("an example") { ScratchPad.record @method }
@state.it_behaves_like :shared_spec, :some_method
@state.process
- ScratchPad.recorded.should == :some_method
+ expect(ScratchPad.recorded).to eq(:some_method)
end
it "creates @object if the passed object" do
@@ -29,7 +29,7 @@ describe Object, "#it_behaves_like" do
@shared.it("an example") { ScratchPad.record @object }
@state.it_behaves_like :shared_spec, :some_method, object
@state.process
- ScratchPad.recorded.should == object
+ expect(ScratchPad.recorded).to eq(object)
end
it "creates @object if the passed false" do
@@ -37,11 +37,11 @@ describe Object, "#it_behaves_like" do
@shared.it("an example") { ScratchPad.record @object }
@state.it_behaves_like :shared_spec, :some_method, object
@state.process
- ScratchPad.recorded.should == object
+ expect(ScratchPad.recorded).to eq(object)
end
it "sends :it_should_behave_like" do
- @state.should_receive(:it_should_behave_like)
+ expect(@state).to receive(:it_should_behave_like)
@state.it_behaves_like :shared_spec, :some_method
end
@@ -61,12 +61,12 @@ describe Object, "#it_behaves_like" do
@state.it_behaves_like :shared_spec, :some_method, @obj
@state.process
- ScratchPad.recorded.should == [:some_method, @obj]
+ expect(ScratchPad.recorded).to eq([:some_method, @obj])
@state2.it_behaves_like :shared_spec, :another_method, @obj2
@state2.process
- ScratchPad.recorded.should == [:another_method, @obj2]
+ expect(ScratchPad.recorded).to eq([:another_method, @obj2])
end
it "ensures the shared spec state is distinct for nested shared specs" do
@@ -79,12 +79,12 @@ describe Object, "#it_behaves_like" do
@state.it_behaves_like :shared_spec, :some_method, @obj
@state.process
- ScratchPad.recorded.should == [:shared, :some_method, @obj]
+ expect(ScratchPad.recorded).to eq([:shared, :some_method, @obj])
@state2.it_behaves_like :shared_spec, :another_method, @obj2
@state2.process
- ScratchPad.recorded.should == [:shared, :another_method, @obj2]
+ expect(ScratchPad.recorded).to eq([:shared, :another_method, @obj2])
end
end
end
diff --git a/spec/mspec/spec/runner/tag_spec.rb b/spec/mspec/spec/runner/tag_spec.rb
index db55a1b186..bda9ac4280 100644
--- a/spec/mspec/spec/runner/tag_spec.rb
+++ b/spec/mspec/spec/runner/tag_spec.rb
@@ -1,123 +1,123 @@
require 'spec_helper'
require 'mspec/runner/tag'
-describe SpecTag do
+RSpec.describe SpecTag do
it "accepts an optional string to parse into fields" do
tag = SpecTag.new "tag(comment):description"
- tag.tag.should == "tag"
- tag.comment.should == "comment"
- tag.description.should == "description"
+ expect(tag.tag).to eq("tag")
+ expect(tag.comment).to eq("comment")
+ expect(tag.description).to eq("description")
end
end
-describe SpecTag, "#parse" do
+RSpec.describe SpecTag, "#parse" do
before :each do
@tag = SpecTag.new
end
it "accepts 'tag(comment):description'" do
@tag.parse "tag(I'm real):Some#method returns a value"
- @tag.tag.should == "tag"
- @tag.comment.should == "I'm real"
- @tag.description.should == "Some#method returns a value"
+ expect(@tag.tag).to eq("tag")
+ expect(@tag.comment).to eq("I'm real")
+ expect(@tag.description).to eq("Some#method returns a value")
end
it "accepts 'tag:description'" do
@tag.parse "tag:Another#method"
- @tag.tag.should == "tag"
- @tag.comment.should == nil
- @tag.description.should == "Another#method"
+ expect(@tag.tag).to eq("tag")
+ expect(@tag.comment).to eq(nil)
+ expect(@tag.description).to eq("Another#method")
end
it "accepts 'tag():description'" do
@tag.parse "tag():Another#method"
- @tag.tag.should == "tag"
- @tag.comment.should == nil
- @tag.description.should == "Another#method"
+ expect(@tag.tag).to eq("tag")
+ expect(@tag.comment).to eq(nil)
+ expect(@tag.description).to eq("Another#method")
end
it "accepts 'tag:'" do
@tag.parse "tag:"
- @tag.tag.should == "tag"
- @tag.comment.should == nil
- @tag.description.should == ""
+ expect(@tag.tag).to eq("tag")
+ expect(@tag.comment).to eq(nil)
+ expect(@tag.description).to eq("")
end
it "accepts 'tag(bug:555):Another#method'" do
@tag.parse "tag(bug:555):Another#method"
- @tag.tag.should == "tag"
- @tag.comment.should == "bug:555"
- @tag.description.should == "Another#method"
+ expect(@tag.tag).to eq("tag")
+ expect(@tag.comment).to eq("bug:555")
+ expect(@tag.description).to eq("Another#method")
end
it "accepts 'tag(http://someplace.com/neato):Another#method'" do
@tag.parse "tag(http://someplace.com/neato):Another#method"
- @tag.tag.should == "tag"
- @tag.comment.should == "http://someplace.com/neato"
- @tag.description.should == "Another#method"
+ expect(@tag.tag).to eq("tag")
+ expect(@tag.comment).to eq("http://someplace.com/neato")
+ expect(@tag.description).to eq("Another#method")
end
it "accepts 'tag(comment):\"Multi-line\\ntext\"'" do
@tag.parse 'tag(comment):"Multi-line\ntext"'
- @tag.tag.should == "tag"
- @tag.comment.should == "comment"
- @tag.description.should == "Multi-line\ntext"
+ expect(@tag.tag).to eq("tag")
+ expect(@tag.comment).to eq("comment")
+ expect(@tag.description).to eq("Multi-line\ntext")
end
it "ignores '#anything'" do
@tag.parse "# this could be a comment"
- @tag.tag.should == nil
- @tag.comment.should == nil
- @tag.description.should == nil
+ expect(@tag.tag).to eq(nil)
+ expect(@tag.comment).to eq(nil)
+ expect(@tag.description).to eq(nil)
end
end
-describe SpecTag, "#to_s" do
+RSpec.describe SpecTag, "#to_s" do
it "formats itself as 'tag(comment):description'" do
txt = "tag(comment):description"
tag = SpecTag.new txt
- tag.tag.should == "tag"
- tag.comment.should == "comment"
- tag.description.should == "description"
- tag.to_s.should == txt
+ expect(tag.tag).to eq("tag")
+ expect(tag.comment).to eq("comment")
+ expect(tag.description).to eq("description")
+ expect(tag.to_s).to eq(txt)
end
it "formats itself as 'tag:description" do
txt = "tag:description"
tag = SpecTag.new txt
- tag.tag.should == "tag"
- tag.comment.should == nil
- tag.description.should == "description"
- tag.to_s.should == txt
+ expect(tag.tag).to eq("tag")
+ expect(tag.comment).to eq(nil)
+ expect(tag.description).to eq("description")
+ expect(tag.to_s).to eq(txt)
end
it "formats itself as 'tag(comment):\"multi-line\\ntext\\ntag\"'" do
txt = 'tag(comment):"multi-line\ntext\ntag"'
tag = SpecTag.new txt
- tag.tag.should == "tag"
- tag.comment.should == "comment"
- tag.description.should == "multi-line\ntext\ntag"
- tag.to_s.should == txt
+ expect(tag.tag).to eq("tag")
+ expect(tag.comment).to eq("comment")
+ expect(tag.description).to eq("multi-line\ntext\ntag")
+ expect(tag.to_s).to eq(txt)
end
end
-describe SpecTag, "#==" do
+RSpec.describe SpecTag, "#==" do
it "returns true if the tags have the same fields" do
one = SpecTag.new "tag(this):unicorn"
two = SpecTag.new "tag(this):unicorn"
- one.==(two).should == true
- [one].==([two]).should == true
+ expect(one.==(two)).to eq(true)
+ expect([one].==([two])).to eq(true)
end
end
-describe SpecTag, "#unescape" do
+RSpec.describe SpecTag, "#unescape" do
it "replaces \\n by LF when the description is quoted" do
tag = SpecTag.new 'tag:"desc with\nnew line"'
- tag.description.should == "desc with\nnew line"
+ expect(tag.description).to eq("desc with\nnew line")
end
it "does not replaces \\n by LF when the description is not quoted " do
tag = SpecTag.new 'tag:desc with\nnew line'
- tag.description.should == "desc with\\nnew line"
+ expect(tag.description).to eq("desc with\\nnew line")
end
end
diff --git a/spec/mspec/spec/spec_helper.rb b/spec/mspec/spec/spec_helper.rb
index a307eaf460..5cabfe5626 100644
--- a/spec/mspec/spec/spec_helper.rb
+++ b/spec/mspec/spec/spec_helper.rb
@@ -1,6 +1,9 @@
-require 'mspec/utils/format'
-require 'mspec/helpers/io'
-require 'mspec/helpers/scratch'
+RSpec.configure do |config|
+ config.disable_monkey_patching!
+ config.raise_errors_for_deprecations!
+end
+
+require 'mspec'
# Remove this when MRI has intelligent warnings
$VERBOSE = nil unless $VERBOSE
@@ -37,7 +40,7 @@ class MSpecExampleError < Exception
end
def hide_deprecation_warnings
- MSpec.stub(:deprecate)
+ allow(MSpec).to receive(:deprecate)
end
def run_mspec(command, args)
@@ -53,3 +56,15 @@ def run_mspec(command, args)
out = out.gsub(cwd, "CWD")
return out, ret
end
+
+def ensure_mspec_method(method)
+ file, _line = method.source_location
+ expect(file).to start_with(File.expand_path('../../lib/mspec', __FILE__ ))
+end
+
+PublicMSpecMatchers = Class.new {
+ include MSpecMatchers
+ public :raise_error
+}.new
+
+BACKTRACE_QUOTE = RUBY_VERSION >= "3.4" ? "'" : "`"
diff --git a/spec/mspec/spec/utils/deprecate_spec.rb b/spec/mspec/spec/utils/deprecate_spec.rb
index 7fa60df26a..73eaf7d04e 100644
--- a/spec/mspec/spec/utils/deprecate_spec.rb
+++ b/spec/mspec/spec/utils/deprecate_spec.rb
@@ -1,17 +1,17 @@
require 'spec_helper'
require 'mspec/utils/deprecate'
-describe MSpec, "#deprecate" do
+RSpec.describe MSpec, "#deprecate" do
it "warns when using a deprecated method" do
warning = nil
- $stderr.stub(:puts) { |str| warning = str }
+ allow($stderr).to receive(:puts) { |str| warning = str }
MSpec.deprecate(:some_method, :other_method)
- warning.should start_with(<<-EOS.chomp)
+ expect(warning).to start_with(<<-EOS.chomp)
some_method is deprecated, use other_method instead.
from
EOS
- warning.should include(__FILE__)
- warning.should include('8')
+ expect(warning).to include(__FILE__)
+ expect(warning).to include('8')
end
end
diff --git a/spec/mspec/spec/utils/fixtures/this_file_raises.rb b/spec/mspec/spec/utils/fixtures/this_file_raises.rb
new file mode 100644
index 0000000000..8e37a587bf
--- /dev/null
+++ b/spec/mspec/spec/utils/fixtures/this_file_raises.rb
@@ -0,0 +1 @@
+raise "This is a BAD file"
diff --git a/spec/mspec/spec/utils/fixtures/this_file_raises2.rb b/spec/mspec/spec/utils/fixtures/this_file_raises2.rb
new file mode 100644
index 0000000000..8efc10199a
--- /dev/null
+++ b/spec/mspec/spec/utils/fixtures/this_file_raises2.rb
@@ -0,0 +1 @@
+raise "This is a BAD file 2"
diff --git a/spec/mspec/spec/utils/name_map_spec.rb b/spec/mspec/spec/utils/name_map_spec.rb
index d5d2cca84a..a42dc9ffec 100644
--- a/spec/mspec/spec/utils/name_map_spec.rb
+++ b/spec/mspec/spec/utils/name_map_spec.rb
@@ -21,50 +21,53 @@ module NameMapSpecs
def f; end
end
+ autoload :BadFile, "#{__dir__}/fixtures/this_file_raises.rb"
+ autoload :BadFile2, "#{__dir__}/fixtures/this_file_raises2.rb"
+
def self.n; end
def n; end
end
-describe NameMap, "#exception?" do
+RSpec.describe NameMap, "#exception?" do
before :each do
@map = NameMap.new
end
it "returns true if the constant is Errno" do
- @map.exception?("Errno").should == true
+ expect(@map.exception?("Errno")).to eq(true)
end
it "returns true if the constant is a kind of Exception" do
- @map.exception?("Errno::EBADF").should == true
- @map.exception?("LoadError").should == true
- @map.exception?("SystemExit").should == true
+ expect(@map.exception?("Errno::EBADF")).to eq(true)
+ expect(@map.exception?("LoadError")).to eq(true)
+ expect(@map.exception?("SystemExit")).to eq(true)
end
it "returns false if the constant is not a kind of Exception" do
- @map.exception?("NameMapSpecs::Error").should == false
- @map.exception?("NameMapSpecs").should == false
+ expect(@map.exception?("NameMapSpecs::Error")).to eq(false)
+ expect(@map.exception?("NameMapSpecs")).to eq(false)
end
it "returns false if the constant does not exist" do
- @map.exception?("Nonexistent").should == false
+ expect(@map.exception?("Nonexistent")).to eq(false)
end
end
-describe NameMap, "#class_or_module" do
+RSpec.describe NameMap, "#class_or_module" do
before :each do
@map = NameMap.new true
end
it "returns the constant specified by the string" do
- @map.class_or_module("NameMapSpecs").should == NameMapSpecs
+ expect(@map.class_or_module("NameMapSpecs")).to eq(NameMapSpecs)
end
it "returns the constant specified by the 'A::B' string" do
- @map.class_or_module("NameMapSpecs::A").should == NameMapSpecs::A
+ expect(@map.class_or_module("NameMapSpecs::A")).to eq(NameMapSpecs::A)
end
it "returns nil if the constant is not a class or module" do
- @map.class_or_module("Float::MAX").should == nil
+ expect(@map.class_or_module("Float::MAX")).to eq(nil)
end
it "returns nil if the constant is in the set of excluded constants" do
@@ -75,101 +78,110 @@ describe NameMap, "#class_or_module" do
]
excluded.each do |const|
- @map.class_or_module(const).should == nil
+ expect(@map.class_or_module(const)).to eq(nil)
end
end
it "returns nil if the constant does not exist" do
- @map.class_or_module("Heaven").should == nil
- @map.class_or_module("Hell").should == nil
- @map.class_or_module("Bush::Brain").should == nil
+ expect(@map.class_or_module("Heaven")).to eq(nil)
+ expect(@map.class_or_module("Hell")).to eq(nil)
+ expect(@map.class_or_module("Bush::Brain")).to eq(nil)
+ end
+
+ it "returns nil if accessing the constant raises RuntimeError" do
+ expect { NameMapSpecs::BadFile }.to raise_error(RuntimeError)
+ expect(@map.class_or_module("NameMapSpecs::BadFile")).to eq(nil)
+ end
+
+ it "returns nil if accessing the constant raises RuntimeError when not triggering the autoload before" do
+ expect(@map.class_or_module("NameMapSpecs::BadFile2")).to eq(nil)
end
end
-describe NameMap, "#dir_name" do
+RSpec.describe NameMap, "#dir_name" do
before :each do
@map = NameMap.new
end
it "returns a directory name from the base name and constant" do
- @map.dir_name("NameMapSpecs", 'spec/core').should == 'spec/core/namemapspecs'
+ expect(@map.dir_name("NameMapSpecs", 'spec/core')).to eq('spec/core/namemapspecs')
end
it "returns a directory name from the components in the constants name" do
- @map.dir_name("NameMapSpecs::A", 'spec').should == 'spec/namemapspecs/a'
- @map.dir_name("NameMapSpecs::A::B", 'spec').should == 'spec/namemapspecs/a/b'
+ expect(@map.dir_name("NameMapSpecs::A", 'spec')).to eq('spec/namemapspecs/a')
+ expect(@map.dir_name("NameMapSpecs::A::B", 'spec')).to eq('spec/namemapspecs/a/b')
end
it "returns a directory name without 'class' for constants like TrueClass" do
- @map.dir_name("TrueClass", 'spec').should == 'spec/true'
- @map.dir_name("FalseClass", 'spec').should == 'spec/false'
+ expect(@map.dir_name("TrueClass", 'spec')).to eq('spec/true')
+ expect(@map.dir_name("FalseClass", 'spec')).to eq('spec/false')
end
it "returns 'exception' for the directory name of any Exception subclass" do
- @map.dir_name("SystemExit", 'spec').should == 'spec/exception'
- @map.dir_name("Errno::EBADF", 'spec').should == 'spec/exception'
+ expect(@map.dir_name("SystemExit", 'spec')).to eq('spec/exception')
+ expect(@map.dir_name("Errno::EBADF", 'spec')).to eq('spec/exception')
end
it "returns 'class' for Class" do
- @map.dir_name("Class", 'spec').should == 'spec/class'
+ expect(@map.dir_name("Class", 'spec')).to eq('spec/class')
end
end
# These specs do not cover all the mappings, but only describe how the
# name is derived when the hash item maps to a single value, a hash with
# a specific item, or a hash with a :default item.
-describe NameMap, "#file_name" do
+RSpec.describe NameMap, "#file_name" do
before :each do
@map = NameMap.new
end
it "returns the name of the spec file based on the constant and method" do
- @map.file_name("[]=", "Array").should == "element_set_spec.rb"
+ expect(@map.file_name("[]=", "Array")).to eq("element_set_spec.rb")
end
it "returns the name of the spec file based on the special entry for the method" do
- @map.file_name("~", "Regexp").should == "match_spec.rb"
- @map.file_name("~", "Integer").should == "complement_spec.rb"
+ expect(@map.file_name("~", "Regexp")).to eq("match_spec.rb")
+ expect(@map.file_name("~", "Integer")).to eq("complement_spec.rb")
end
it "returns the name of the spec file based on the default entry for the method" do
- @map.file_name("<<", "NameMapSpecs").should == "append_spec.rb"
+ expect(@map.file_name("<<", "NameMapSpecs")).to eq("append_spec.rb")
end
it "uses the last component of the constant to look up the method name" do
- @map.file_name("^", "NameMapSpecs::Integer").should == "bit_xor_spec.rb"
+ expect(@map.file_name("^", "NameMapSpecs::Integer")).to eq("bit_xor_spec.rb")
end
end
-describe NameMap, "#namespace" do
+RSpec.describe NameMap, "#namespace" do
before :each do
@map = NameMap.new
end
it "prepends the module to the constant name" do
- @map.namespace("SubModule", Integer).should == "SubModule::Integer"
+ expect(@map.namespace("SubModule", Integer)).to eq("SubModule::Integer")
end
it "does not prepend Object, Class, or Module to the constant name" do
- @map.namespace("Object", String).should == "String"
- @map.namespace("Module", Integer).should == "Integer"
- @map.namespace("Class", Float).should == "Float"
+ expect(@map.namespace("Object", String)).to eq("String")
+ expect(@map.namespace("Module", Integer)).to eq("Integer")
+ expect(@map.namespace("Class", Float)).to eq("Float")
end
end
-describe NameMap, "#map" do
+RSpec.describe NameMap, "#map" do
before :each do
@map = NameMap.new
end
it "flattens an object hierarchy into a single Hash" do
- @map.map({}, [NameMapSpecs]).should == {
+ expect(@map.map({}, [NameMapSpecs])).to eq({
"NameMapSpecs." => ["n"],
"NameMapSpecs#" => ["n"],
"NameMapSpecs::A." => ["a"],
"NameMapSpecs::A#" => ["a", "c"],
"NameMapSpecs::A::B#" => ["b"],
"NameMapSpecs::Fixnum#" => ["f"]
- }
+ })
end
end
diff --git a/spec/mspec/spec/utils/options_spec.rb b/spec/mspec/spec/utils/options_spec.rb
index f3a7046526..2e3925f579 100644
--- a/spec/mspec/spec/utils/options_spec.rb
+++ b/spec/mspec/spec/utils/options_spec.rb
@@ -5,155 +5,155 @@ require 'mspec/guards/guard'
require 'mspec/runner/mspec'
require 'mspec/runner/formatters'
-describe MSpecOption, ".new" do
+RSpec.describe MSpecOption, ".new" do
before :each do
@opt = MSpecOption.new("-a", "--bdc", "ARG", "desc", :block)
end
it "sets the short attribute" do
- @opt.short.should == "-a"
+ expect(@opt.short).to eq("-a")
end
it "sets the long attribute" do
- @opt.long.should == "--bdc"
+ expect(@opt.long).to eq("--bdc")
end
it "sets the arg attribute" do
- @opt.arg.should == "ARG"
+ expect(@opt.arg).to eq("ARG")
end
it "sets the description attribute" do
- @opt.description.should == "desc"
+ expect(@opt.description).to eq("desc")
end
it "sets the block attribute" do
- @opt.block.should == :block
+ expect(@opt.block).to eq(:block)
end
end
-describe MSpecOption, "#arg?" do
+RSpec.describe MSpecOption, "#arg?" do
it "returns true if arg attribute is not nil" do
- MSpecOption.new(nil, nil, "ARG", nil, nil).arg?.should be_true
+ expect(MSpecOption.new(nil, nil, "ARG", nil, nil).arg?).to be_truthy
end
it "returns false if arg attribute is nil" do
- MSpecOption.new(nil, nil, nil, nil, nil).arg?.should be_false
+ expect(MSpecOption.new(nil, nil, nil, nil, nil).arg?).to be_falsey
end
end
-describe MSpecOption, "#match?" do
+RSpec.describe MSpecOption, "#match?" do
before :each do
@opt = MSpecOption.new("-a", "--bdc", "ARG", "desc", :block)
end
it "returns true if the argument matches the short option" do
- @opt.match?("-a").should be_true
+ expect(@opt.match?("-a")).to be_truthy
end
it "returns true if the argument matches the long option" do
- @opt.match?("--bdc").should be_true
+ expect(@opt.match?("--bdc")).to be_truthy
end
it "returns false if the argument matches neither the short nor long option" do
- @opt.match?("-b").should be_false
- @opt.match?("-abdc").should be_false
+ expect(@opt.match?("-b")).to be_falsey
+ expect(@opt.match?("-abdc")).to be_falsey
end
end
-describe MSpecOptions, ".new" do
+RSpec.describe MSpecOptions, ".new" do
before :each do
@opt = MSpecOptions.new("cmd", 20, :config)
end
it "sets the banner attribute" do
- @opt.banner.should == "cmd"
+ expect(@opt.banner).to eq("cmd")
end
it "sets the config attribute" do
- @opt.config.should == :config
+ expect(@opt.config).to eq(:config)
end
it "sets the width attribute" do
- @opt.width.should == 20
+ expect(@opt.width).to eq(20)
end
it "sets the default width attribute" do
- MSpecOptions.new.width.should == 30
+ expect(MSpecOptions.new.width).to eq(30)
end
end
-describe MSpecOptions, "#on" do
+RSpec.describe MSpecOptions, "#on" do
before :each do
@opt = MSpecOptions.new
end
it "adds a short option" do
- @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ expect(@opt).to receive(:add).with("-a", nil, nil, "desc", nil)
@opt.on("-a", "desc")
end
it "adds a short option taking an argument" do
- @opt.should_receive(:add).with("-a", nil, "ARG", "desc", nil)
+ expect(@opt).to receive(:add).with("-a", nil, "ARG", "desc", nil)
@opt.on("-a", "ARG", "desc")
end
it "adds a long option" do
- @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ expect(@opt).to receive(:add).with("-a", nil, nil, "desc", nil)
@opt.on("-a", "desc")
end
it "adds a long option taking an argument" do
- @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ expect(@opt).to receive(:add).with("-a", nil, nil, "desc", nil)
@opt.on("-a", "desc")
end
it "adds a short and long option" do
- @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ expect(@opt).to receive(:add).with("-a", nil, nil, "desc", nil)
@opt.on("-a", "desc")
end
it "adds a short and long option taking an argument" do
- @opt.should_receive(:add).with("-a", nil, nil, "desc", nil)
+ expect(@opt).to receive(:add).with("-a", nil, nil, "desc", nil)
@opt.on("-a", "desc")
end
it "raises MSpecOptions::OptionError if pass less than 2 arguments" do
- lambda { @opt.on }.should raise_error(MSpecOptions::OptionError)
- lambda { @opt.on "" }.should raise_error(MSpecOptions::OptionError)
+ expect { @opt.on }.to raise_error(MSpecOptions::OptionError)
+ expect { @opt.on "" }.to raise_error(MSpecOptions::OptionError)
end
end
-describe MSpecOptions, "#add" do
+RSpec.describe MSpecOptions, "#add" do
before :each do
@opt = MSpecOptions.new "cmd", 20
@prc = lambda { }
end
it "adds documentation for an option" do
- @opt.should_receive(:doc).with(" -t, --typo ARG Correct typo ARG")
+ expect(@opt).to receive(:doc).with(" -t, --typo ARG Correct typo ARG")
@opt.add("-t", "--typo", "ARG", "Correct typo ARG", @prc)
end
it "leaves spaces in the documentation for a missing short option" do
- @opt.should_receive(:doc).with(" --typo ARG Correct typo ARG")
+ expect(@opt).to receive(:doc).with(" --typo ARG Correct typo ARG")
@opt.add(nil, "--typo", "ARG", "Correct typo ARG", @prc)
end
it "handles a short option with argument but no long argument" do
- @opt.should_receive(:doc).with(" -t ARG Correct typo ARG")
+ expect(@opt).to receive(:doc).with(" -t ARG Correct typo ARG")
@opt.add("-t", nil, "ARG", "Correct typo ARG", @prc)
end
it "registers an option" do
option = MSpecOption.new "-t", "--typo", "ARG", "Correct typo ARG", @prc
- MSpecOption.should_receive(:new).with(
+ expect(MSpecOption).to receive(:new).with(
"-t", "--typo", "ARG", "Correct typo ARG", @prc).and_return(option)
@opt.add("-t", "--typo", "ARG", "Correct typo ARG", @prc)
- @opt.options.should == [option]
+ expect(@opt.options).to eq([option])
end
end
-describe MSpecOptions, "#match?" do
+RSpec.describe MSpecOptions, "#match?" do
before :each do
@opt = MSpecOptions.new
end
@@ -161,15 +161,15 @@ describe MSpecOptions, "#match?" do
it "returns the MSpecOption instance matching the argument" do
@opt.on "-a", "--abdc", "desc"
option = @opt.match? "-a"
- @opt.match?("--abdc").should be(option)
- option.should be_kind_of(MSpecOption)
- option.short.should == "-a"
- option.long.should == "--abdc"
- option.description.should == "desc"
+ expect(@opt.match?("--abdc")).to be(option)
+ expect(option).to be_kind_of(MSpecOption)
+ expect(option.short).to eq("-a")
+ expect(option.long).to eq("--abdc")
+ expect(option.description).to eq("desc")
end
end
-describe MSpecOptions, "#process" do
+RSpec.describe MSpecOptions, "#process" do
before :each do
@opt = MSpecOptions.new
ScratchPad.clear
@@ -178,62 +178,62 @@ describe MSpecOptions, "#process" do
it "calls the on_extra block if the argument does not match any option" do
@opt.on_extra { ScratchPad.record :extra }
@opt.process ["-a"], "-a", "-a", nil
- ScratchPad.recorded.should == :extra
+ expect(ScratchPad.recorded).to eq(:extra)
end
it "returns the matching option" do
@opt.on "-a", "ARG", "desc"
option = @opt.process [], "-a", "-a", "ARG"
- option.should be_kind_of(MSpecOption)
- option.short.should == "-a"
- option.arg.should == "ARG"
- option.description.should == "desc"
+ expect(option).to be_kind_of(MSpecOption)
+ expect(option.short).to eq("-a")
+ expect(option.arg).to eq("ARG")
+ expect(option.description).to eq("desc")
end
it "raises an MSpecOptions::ParseError if arg is nil and there are no more entries in argv" do
@opt.on "-a", "ARG", "desc"
- lambda { @opt.process [], "-a", "-a", nil }.should raise_error(MSpecOptions::ParseError)
+ expect { @opt.process [], "-a", "-a", nil }.to raise_error(MSpecOptions::ParseError)
end
it "fetches the argument for the option from argv if arg is nil" do
@opt.on("-a", "ARG", "desc") { |o| ScratchPad.record o }
@opt.process ["ARG"], "-a", "-a", nil
- ScratchPad.recorded.should == "ARG"
+ expect(ScratchPad.recorded).to eq("ARG")
end
it "calls the option's block" do
@opt.on("-a", "ARG", "desc") { ScratchPad.record :option }
@opt.process [], "-a", "-a", "ARG"
- ScratchPad.recorded.should == :option
+ expect(ScratchPad.recorded).to eq(:option)
end
it "does not call the option's block if it is nil" do
@opt.on "-a", "ARG", "desc"
- lambda { @opt.process [], "-a", "-a", "ARG" }.should_not raise_error
+ expect { @opt.process [], "-a", "-a", "ARG" }.not_to raise_error
end
end
-describe MSpecOptions, "#split" do
+RSpec.describe MSpecOptions, "#split" do
before :each do
@opt = MSpecOptions.new
end
it "breaks a string at the nth character" do
opt, arg, rest = @opt.split "-bdc", 2
- opt.should == "-b"
- arg.should == "dc"
- rest.should == "dc"
+ expect(opt).to eq("-b")
+ expect(arg).to eq("dc")
+ expect(rest).to eq("dc")
end
it "returns nil for arg if there are no characters left" do
opt, arg, rest = @opt.split "-b", 2
- opt.should == "-b"
- arg.should == nil
- rest.should == ""
+ expect(opt).to eq("-b")
+ expect(arg).to eq(nil)
+ expect(rest).to eq("")
end
end
-describe MSpecOptions, "#parse" do
+RSpec.describe MSpecOptions, "#parse" do
before :each do
@opt = MSpecOptions.new
@prc = lambda { ScratchPad.record :parsed }
@@ -244,43 +244,43 @@ describe MSpecOptions, "#parse" do
it "parses a short option" do
@opt.on "-a", "desc", &@prc
@opt.parse ["-a"]
- ScratchPad.recorded.should == :parsed
+ expect(ScratchPad.recorded).to eq(:parsed)
end
it "parse a long option" do
@opt.on "--abdc", "desc", &@prc
@opt.parse ["--abdc"]
- ScratchPad.recorded.should == :parsed
+ expect(ScratchPad.recorded).to eq(:parsed)
end
it "parses a short option group" do
@opt.on "-a", "ARG", "desc", &@arg_prc
@opt.parse ["-a", "ARG"]
- ScratchPad.recorded.should == [:parsed, "ARG"]
+ expect(ScratchPad.recorded).to eq([:parsed, "ARG"])
end
it "parses a short option with an argument" do
@opt.on "-a", "ARG", "desc", &@arg_prc
@opt.parse ["-a", "ARG"]
- ScratchPad.recorded.should == [:parsed, "ARG"]
+ expect(ScratchPad.recorded).to eq([:parsed, "ARG"])
end
it "parses a short option with connected argument" do
@opt.on "-a", "ARG", "desc", &@arg_prc
@opt.parse ["-aARG"]
- ScratchPad.recorded.should == [:parsed, "ARG"]
+ expect(ScratchPad.recorded).to eq([:parsed, "ARG"])
end
it "parses a long option with an argument" do
@opt.on "--abdc", "ARG", "desc", &@arg_prc
@opt.parse ["--abdc", "ARG"]
- ScratchPad.recorded.should == [:parsed, "ARG"]
+ expect(ScratchPad.recorded).to eq([:parsed, "ARG"])
end
it "parses a long option with an '=' argument" do
@opt.on "--abdc", "ARG", "desc", &@arg_prc
@opt.parse ["--abdc=ARG"]
- ScratchPad.recorded.should == [:parsed, "ARG"]
+ expect(ScratchPad.recorded).to eq([:parsed, "ARG"])
end
it "parses a short option group with the final option taking an argument" do
@@ -288,7 +288,7 @@ describe MSpecOptions, "#parse" do
@opt.on("-a", "desc") { |o| ScratchPad << :a }
@opt.on("-b", "ARG", "desc") { |o| ScratchPad << [:b, o] }
@opt.parse ["-ab", "ARG"]
- ScratchPad.recorded.should == [:a, [:b, "ARG"]]
+ expect(ScratchPad.recorded).to eq([:a, [:b, "ARG"]])
end
it "parses a short option group with a connected argument" do
@@ -297,12 +297,12 @@ describe MSpecOptions, "#parse" do
@opt.on("-b", "ARG", "desc") { |o| ScratchPad << [:b, o] }
@opt.on("-c", "desc") { |o| ScratchPad << :c }
@opt.parse ["-acbARG"]
- ScratchPad.recorded.should == [:a, :c, [:b, "ARG"]]
+ expect(ScratchPad.recorded).to eq([:a, :c, [:b, "ARG"]])
end
it "returns the unprocessed entries" do
@opt.on "-a", "ARG", "desc", &@arg_prc
- @opt.parse(["abdc", "-a", "ilny"]).should == ["abdc"]
+ expect(@opt.parse(["abdc", "-a", "ilny"])).to eq(["abdc"])
end
it "calls the on_extra handler with unrecognized options" do
@@ -310,59 +310,59 @@ describe MSpecOptions, "#parse" do
@opt.on_extra { |o| ScratchPad << o }
@opt.on "-a", "desc"
@opt.parse ["-a", "-b"]
- ScratchPad.recorded.should == ["-b"]
+ expect(ScratchPad.recorded).to eq(["-b"])
end
it "does not attempt to call the block if it is nil" do
@opt.on "-a", "ARG", "desc"
- @opt.parse(["-a", "ARG"]).should == []
+ expect(@opt.parse(["-a", "ARG"])).to eq([])
end
it "raises MSpecOptions::ParseError if passed an unrecognized option" do
- @opt.should_receive(:raise).with(MSpecOptions::ParseError, an_instance_of(String))
- @opt.stub(:puts)
- @opt.stub(:exit)
+ expect(@opt).to receive(:raise).with(MSpecOptions::ParseError, an_instance_of(String))
+ allow(@opt).to receive(:puts)
+ allow(@opt).to receive(:exit)
@opt.parse "-u"
end
end
-describe MSpecOptions, "#banner=" do
+RSpec.describe MSpecOptions, "#banner=" do
before :each do
@opt = MSpecOptions.new
end
it "sets the banner attribute" do
- @opt.banner.should == ""
+ expect(@opt.banner).to eq("")
@opt.banner = "banner"
- @opt.banner.should == "banner"
+ expect(@opt.banner).to eq("banner")
end
end
-describe MSpecOptions, "#width=" do
+RSpec.describe MSpecOptions, "#width=" do
before :each do
@opt = MSpecOptions.new
end
it "sets the width attribute" do
- @opt.width.should == 30
+ expect(@opt.width).to eq(30)
@opt.width = 20
- @opt.width.should == 20
+ expect(@opt.width).to eq(20)
end
end
-describe MSpecOptions, "#config=" do
+RSpec.describe MSpecOptions, "#config=" do
before :each do
@opt = MSpecOptions.new
end
it "sets the config attribute" do
- @opt.config.should be_nil
+ expect(@opt.config).to be_nil
@opt.config = :config
- @opt.config.should == :config
+ expect(@opt.config).to eq(:config)
end
end
-describe MSpecOptions, "#doc" do
+RSpec.describe MSpecOptions, "#doc" do
before :each do
@opt = MSpecOptions.new "command"
end
@@ -370,7 +370,7 @@ describe MSpecOptions, "#doc" do
it "adds text to be displayed with #to_s" do
@opt.doc "Some message"
@opt.doc "Another message"
- @opt.to_s.should == <<-EOD
+ expect(@opt.to_s).to eq <<-EOD
command
Some message
@@ -379,15 +379,15 @@ EOD
end
end
-describe MSpecOptions, "#version" do
+RSpec.describe MSpecOptions, "#version" do
before :each do
@opt = MSpecOptions.new
ScratchPad.clear
end
it "installs a basic -v, --version option" do
- @opt.should_receive(:puts)
- @opt.should_receive(:exit)
+ expect(@opt).to receive(:puts)
+ expect(@opt).to receive(:exit)
@opt.version "1.0.0"
@opt.parse "-v"
end
@@ -395,19 +395,19 @@ describe MSpecOptions, "#version" do
it "accepts a block instead of using the default block" do
@opt.version("1.0.0") { |o| ScratchPad.record :version }
@opt.parse "-v"
- ScratchPad.recorded.should == :version
+ expect(ScratchPad.recorded).to eq(:version)
end
end
-describe MSpecOptions, "#help" do
+RSpec.describe MSpecOptions, "#help" do
before :each do
@opt = MSpecOptions.new
ScratchPad.clear
end
it "installs a basic -h, --help option" do
- @opt.should_receive(:puts)
- @opt.should_receive(:exit).with(1)
+ expect(@opt).to receive(:puts)
+ expect(@opt).to receive(:exit).with(1)
@opt.help
@opt.parse "-h"
end
@@ -415,11 +415,11 @@ describe MSpecOptions, "#help" do
it "accepts a block instead of using the default block" do
@opt.help { |o| ScratchPad.record :help }
@opt.parse "-h"
- ScratchPad.recorded.should == :help
+ expect(ScratchPad.recorded).to eq(:help)
end
end
-describe MSpecOptions, "#on_extra" do
+RSpec.describe MSpecOptions, "#on_extra" do
before :each do
@opt = MSpecOptions.new
ScratchPad.clear
@@ -428,18 +428,18 @@ describe MSpecOptions, "#on_extra" do
it "registers a block to be called when an option is not recognized" do
@opt.on_extra { ScratchPad.record :extra }
@opt.parse "-g"
- ScratchPad.recorded.should == :extra
+ expect(ScratchPad.recorded).to eq(:extra)
end
end
-describe MSpecOptions, "#to_s" do
+RSpec.describe MSpecOptions, "#to_s" do
before :each do
@opt = MSpecOptions.new "command"
end
it "returns the banner and descriptive strings for all registered options" do
@opt.on "-t", "--this ARG", "Adds this ARG to the list"
- @opt.to_s.should == <<-EOD
+ expect(@opt.to_s).to eq <<-EOD
command
-t, --this ARG Adds this ARG to the list
@@ -447,13 +447,13 @@ EOD
end
end
-describe "The -B, --config FILE option" do
+RSpec.describe "The -B, --config FILE option" do
before :each do
@options, @config = new_option
end
it "is enabled with #configure { }" do
- @options.should_receive(:on).with("-B", "--config", "FILE",
+ expect(@options).to receive(:on).with("-B", "--config", "FILE",
an_instance_of(String))
@options.configure {}
end
@@ -464,38 +464,38 @@ describe "The -B, --config FILE option" do
@options.configure { |x| ScratchPad.record x }
@options.parse [opt, "file"]
- ScratchPad.recorded.should == "file"
+ expect(ScratchPad.recorded).to eq("file")
end
end
end
-describe "The -C, --chdir DIR option" do
+RSpec.describe "The -C, --chdir DIR option" do
before :each do
@options, @config = new_option
@options.chdir
end
it "is enabled with #chdir" do
- @options.should_receive(:on).with("-C", "--chdir", "DIR",
+ expect(@options).to receive(:on).with("-C", "--chdir", "DIR",
an_instance_of(String))
@options.chdir
end
it "changes the working directory to DIR" do
- Dir.should_receive(:chdir).with("dir").twice
+ expect(Dir).to receive(:chdir).with("dir").twice
["-C", "--chdir"].each do |opt|
@options.parse [opt, "dir"]
end
end
end
-describe "The --prefix STR option" do
+RSpec.describe "The --prefix STR option" do
before :each do
@options, @config = new_option
end
it "is enabled with #prefix" do
- @options.should_receive(:on).with("--prefix", "STR",
+ expect(@options).to receive(:on).with("--prefix", "STR",
an_instance_of(String))
@options.prefix
end
@@ -503,19 +503,19 @@ describe "The --prefix STR option" do
it "sets the prefix config value" do
@options.prefix
@options.parse ["--prefix", "some/dir"]
- @config[:prefix].should == "some/dir"
+ expect(@config[:prefix]).to eq("some/dir")
end
end
-describe "The -t, --target TARGET option" do
+RSpec.describe "The -t, --target TARGET option" do
before :each do
@options, @config = new_option
@options.targets
end
it "is enabled with #targets" do
- @options.stub(:on)
- @options.should_receive(:on).with("-t", "--target", "TARGET",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-t", "--target", "TARGET",
an_instance_of(String))
@options.targets
end
@@ -525,7 +525,7 @@ describe "The -t, --target TARGET option" do
["r", "ruby"].each do |t|
@config[:target] = nil
@options.parse [opt, t]
- @config[:target].should == "ruby"
+ expect(@config[:target]).to eq("ruby")
end
end
end
@@ -535,7 +535,7 @@ describe "The -t, --target TARGET option" do
["j", "jruby"].each do |t|
@config[:target] = nil
@options.parse [opt, t]
- @config[:target].should == "jruby"
+ expect(@config[:target]).to eq("jruby")
end
end
end
@@ -545,7 +545,7 @@ describe "The -t, --target TARGET option" do
["x", "rubinius"].each do |t|
@config[:target] = nil
@options.parse [opt, t]
- @config[:target].should == "./bin/rbx"
+ expect(@config[:target]).to eq("./bin/rbx")
end
end
end
@@ -555,7 +555,7 @@ describe "The -t, --target TARGET option" do
["X", "rbx"].each do |t|
@config[:target] = nil
@options.parse [opt, t]
- @config[:target].should == "rbx"
+ expect(@config[:target]).to eq("rbx")
end
end
end
@@ -565,7 +565,7 @@ describe "The -t, --target TARGET option" do
["m", "maglev"].each do |t|
@config[:target] = nil
@options.parse [opt, t]
- @config[:target].should == "maglev-ruby"
+ expect(@config[:target]).to eq("maglev-ruby")
end
end
end
@@ -575,7 +575,7 @@ describe "The -t, --target TARGET option" do
["t", "topaz"].each do |t|
@config[:target] = nil
@options.parse [opt, t]
- @config[:target].should == "topaz"
+ expect(@config[:target]).to eq("topaz")
end
end
end
@@ -584,20 +584,20 @@ describe "The -t, --target TARGET option" do
["-t", "--target"].each do |opt|
@config[:target] = nil
@options.parse [opt, "whateva"]
- @config[:target].should == "whateva"
+ expect(@config[:target]).to eq("whateva")
end
end
end
-describe "The -T, --target-opt OPT option" do
+RSpec.describe "The -T, --target-opt OPT option" do
before :each do
@options, @config = new_option
@options.targets
end
it "is enabled with #targets" do
- @options.stub(:on)
- @options.should_receive(:on).with("-T", "--target-opt", "OPT",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-T", "--target-opt", "OPT",
an_instance_of(String))
@options.targets
end
@@ -606,20 +606,20 @@ describe "The -T, --target-opt OPT option" do
["-T", "--target-opt"].each do |opt|
@config[:flags].delete "--whateva"
@options.parse [opt, "--whateva"]
- @config[:flags].should include("--whateva")
+ expect(@config[:flags]).to include("--whateva")
end
end
end
-describe "The -I, --include DIR option" do
+RSpec.describe "The -I, --include DIR option" do
before :each do
@options, @config = new_option
@options.targets
end
it "is enabled with #targets" do
- @options.stub(:on)
- @options.should_receive(:on).with("-I", "--include", "DIR",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-I", "--include", "DIR",
an_instance_of(String))
@options.targets
end
@@ -628,20 +628,20 @@ describe "The -I, --include DIR option" do
["-I", "--include"].each do |opt|
@config[:loadpath].delete "-Ipackage"
@options.parse [opt, "package"]
- @config[:loadpath].should include("-Ipackage")
+ expect(@config[:loadpath]).to include("-Ipackage")
end
end
end
-describe "The -r, --require LIBRARY option" do
+RSpec.describe "The -r, --require LIBRARY option" do
before :each do
@options, @config = new_option
@options.targets
end
it "is enabled with #targets" do
- @options.stub(:on)
- @options.should_receive(:on).with("-r", "--require", "LIBRARY",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-r", "--require", "LIBRARY",
an_instance_of(String))
@options.targets
end
@@ -650,20 +650,20 @@ describe "The -r, --require LIBRARY option" do
["-r", "--require"].each do |opt|
@config[:requires].delete "-rlibrick"
@options.parse [opt, "librick"]
- @config[:requires].should include("-rlibrick")
+ expect(@config[:requires]).to include("-rlibrick")
end
end
end
-describe "The -f, --format FORMAT option" do
+RSpec.describe "The -f, --format FORMAT option" do
before :each do
@options, @config = new_option
@options.formatters
end
it "is enabled with #formatters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-f", "--format", "FORMAT",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-f", "--format", "FORMAT",
an_instance_of(String))
@options.formatters
end
@@ -673,7 +673,7 @@ describe "The -f, --format FORMAT option" do
["s", "specdoc"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == SpecdocFormatter
+ expect(@config[:formatter]).to eq(SpecdocFormatter)
end
end
end
@@ -683,7 +683,7 @@ describe "The -f, --format FORMAT option" do
["h", "html"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == HtmlFormatter
+ expect(@config[:formatter]).to eq(HtmlFormatter)
end
end
end
@@ -693,7 +693,7 @@ describe "The -f, --format FORMAT option" do
["d", "dot", "dotted"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == DottedFormatter
+ expect(@config[:formatter]).to eq(DottedFormatter)
end
end
end
@@ -703,7 +703,7 @@ describe "The -f, --format FORMAT option" do
["b", "describe"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == DescribeFormatter
+ expect(@config[:formatter]).to eq(DescribeFormatter)
end
end
end
@@ -713,7 +713,7 @@ describe "The -f, --format FORMAT option" do
["f", "file"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == FileFormatter
+ expect(@config[:formatter]).to eq(FileFormatter)
end
end
end
@@ -723,7 +723,7 @@ describe "The -f, --format FORMAT option" do
["u", "unit", "unitdiff"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == UnitdiffFormatter
+ expect(@config[:formatter]).to eq(UnitdiffFormatter)
end
end
end
@@ -733,7 +733,7 @@ describe "The -f, --format FORMAT option" do
["m", "summary"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == SummaryFormatter
+ expect(@config[:formatter]).to eq(SummaryFormatter)
end
end
end
@@ -743,7 +743,7 @@ describe "The -f, --format FORMAT option" do
["a", "*", "spin"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == SpinnerFormatter
+ expect(@config[:formatter]).to eq(SpinnerFormatter)
end
end
end
@@ -753,7 +753,7 @@ describe "The -f, --format FORMAT option" do
["t", "method"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == MethodFormatter
+ expect(@config[:formatter]).to eq(MethodFormatter)
end
end
end
@@ -763,7 +763,7 @@ describe "The -f, --format FORMAT option" do
["y", "yaml"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == YamlFormatter
+ expect(@config[:formatter]).to eq(YamlFormatter)
end
end
end
@@ -773,21 +773,21 @@ describe "The -f, --format FORMAT option" do
["j", "junit"].each do |f|
@config[:formatter] = nil
@options.parse [opt, f]
- @config[:formatter].should == JUnitFormatter
+ expect(@config[:formatter]).to eq(JUnitFormatter)
end
end
end
end
-describe "The -o, --output FILE option" do
+RSpec.describe "The -o, --output FILE option" do
before :each do
@options, @config = new_option
@options.formatters
end
it "is enabled with #formatters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-o", "--output", "FILE",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-o", "--output", "FILE",
an_instance_of(String))
@options.formatters
end
@@ -796,20 +796,20 @@ describe "The -o, --output FILE option" do
["-o", "--output"].each do |opt|
@config[:output] = nil
@options.parse [opt, "some/file"]
- @config[:output].should == "some/file"
+ expect(@config[:output]).to eq("some/file")
end
end
end
-describe "The -e, --example STR" do
+RSpec.describe "The -e, --example STR" do
before :each do
@options, @config = new_option
@options.filters
end
it "is enabled with #filters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-e", "--example", "STR",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-e", "--example", "STR",
an_instance_of(String))
@options.filters
end
@@ -818,20 +818,20 @@ describe "The -e, --example STR" do
["-e", "--example"].each do |opt|
@config[:includes] = []
@options.parse [opt, "this spec"]
- @config[:includes].should include("this spec")
+ expect(@config[:includes]).to include("this spec")
end
end
end
-describe "The -E, --exclude STR" do
+RSpec.describe "The -E, --exclude STR" do
before :each do
@options, @config = new_option
@options.filters
end
it "is enabled with #filters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-E", "--exclude", "STR",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-E", "--exclude", "STR",
an_instance_of(String))
@options.filters
end
@@ -840,20 +840,20 @@ describe "The -E, --exclude STR" do
["-E", "--exclude"].each do |opt|
@config[:excludes] = []
@options.parse [opt, "this spec"]
- @config[:excludes].should include("this spec")
+ expect(@config[:excludes]).to include("this spec")
end
end
end
-describe "The -p, --pattern PATTERN" do
+RSpec.describe "The -p, --pattern PATTERN" do
before :each do
@options, @config = new_option
@options.filters
end
it "is enabled with #filters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-p", "--pattern", "PATTERN",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-p", "--pattern", "PATTERN",
an_instance_of(String))
@options.filters
end
@@ -862,20 +862,20 @@ describe "The -p, --pattern PATTERN" do
["-p", "--pattern"].each do |opt|
@config[:patterns] = []
@options.parse [opt, "this spec"]
- @config[:patterns].should include(/this spec/)
+ expect(@config[:patterns]).to include(/this spec/)
end
end
end
-describe "The -P, --excl-pattern PATTERN" do
+RSpec.describe "The -P, --excl-pattern PATTERN" do
before :each do
@options, @config = new_option
@options.filters
end
it "is enabled with #filters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-P", "--excl-pattern", "PATTERN",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-P", "--excl-pattern", "PATTERN",
an_instance_of(String))
@options.filters
end
@@ -884,20 +884,20 @@ describe "The -P, --excl-pattern PATTERN" do
["-P", "--excl-pattern"].each do |opt|
@config[:xpatterns] = []
@options.parse [opt, "this spec"]
- @config[:xpatterns].should include(/this spec/)
+ expect(@config[:xpatterns]).to include(/this spec/)
end
end
end
-describe "The -g, --tag TAG" do
+RSpec.describe "The -g, --tag TAG" do
before :each do
@options, @config = new_option
@options.filters
end
it "is enabled with #filters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-g", "--tag", "TAG",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-g", "--tag", "TAG",
an_instance_of(String))
@options.filters
end
@@ -906,20 +906,20 @@ describe "The -g, --tag TAG" do
["-g", "--tag"].each do |opt|
@config[:tags] = []
@options.parse [opt, "this spec"]
- @config[:tags].should include("this spec")
+ expect(@config[:tags]).to include("this spec")
end
end
end
-describe "The -G, --excl-tag TAG" do
+RSpec.describe "The -G, --excl-tag TAG" do
before :each do
@options, @config = new_option
@options.filters
end
it "is enabled with #filters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-G", "--excl-tag", "TAG",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-G", "--excl-tag", "TAG",
an_instance_of(String))
@options.filters
end
@@ -928,20 +928,20 @@ describe "The -G, --excl-tag TAG" do
["-G", "--excl-tag"].each do |opt|
@config[:xtags] = []
@options.parse [opt, "this spec"]
- @config[:xtags].should include("this spec")
+ expect(@config[:xtags]).to include("this spec")
end
end
end
-describe "The -w, --profile FILE option" do
+RSpec.describe "The -w, --profile FILE option" do
before :each do
@options, @config = new_option
@options.filters
end
it "is enabled with #filters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-w", "--profile", "FILE",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-w", "--profile", "FILE",
an_instance_of(String))
@options.filters
end
@@ -950,20 +950,20 @@ describe "The -w, --profile FILE option" do
["-w", "--profile"].each do |opt|
@config[:profiles] = []
@options.parse [opt, "spec/profiles/rails.yaml"]
- @config[:profiles].should include("spec/profiles/rails.yaml")
+ expect(@config[:profiles]).to include("spec/profiles/rails.yaml")
end
end
end
-describe "The -W, --excl-profile FILE option" do
+RSpec.describe "The -W, --excl-profile FILE option" do
before :each do
@options, @config = new_option
@options.filters
end
it "is enabled with #filters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-W", "--excl-profile", "FILE",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-W", "--excl-profile", "FILE",
an_instance_of(String))
@options.filters
end
@@ -972,93 +972,93 @@ describe "The -W, --excl-profile FILE option" do
["-W", "--excl-profile"].each do |opt|
@config[:xprofiles] = []
@options.parse [opt, "spec/profiles/rails.yaml"]
- @config[:xprofiles].should include("spec/profiles/rails.yaml")
+ expect(@config[:xprofiles]).to include("spec/profiles/rails.yaml")
end
end
end
-describe "The -Z, --dry-run option" do
+RSpec.describe "The -Z, --dry-run option" do
before :each do
@options, @config = new_option
@options.pretend
end
it "is enabled with #pretend" do
- @options.should_receive(:on).with("-Z", "--dry-run", an_instance_of(String))
+ expect(@options).to receive(:on).with("-Z", "--dry-run", an_instance_of(String))
@options.pretend
end
it "registers the MSpec pretend mode" do
- MSpec.should_receive(:register_mode).with(:pretend).twice
+ expect(MSpec).to receive(:register_mode).with(:pretend).twice
["-Z", "--dry-run"].each do |opt|
@options.parse opt
end
end
end
-describe "The --unguarded option" do
+RSpec.describe "The --unguarded option" do
before :each do
@options, @config = new_option
@options.unguarded
end
it "is enabled with #unguarded" do
- @options.stub(:on)
- @options.should_receive(:on).with("--unguarded", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("--unguarded", an_instance_of(String))
@options.unguarded
end
it "registers the MSpec unguarded mode" do
- MSpec.should_receive(:register_mode).with(:unguarded)
+ expect(MSpec).to receive(:register_mode).with(:unguarded)
@options.parse "--unguarded"
end
end
-describe "The --no-ruby_guard option" do
+RSpec.describe "The --no-ruby_guard option" do
before :each do
@options, @config = new_option
@options.unguarded
end
it "is enabled with #unguarded" do
- @options.stub(:on)
- @options.should_receive(:on).with("--no-ruby_bug", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("--no-ruby_bug", an_instance_of(String))
@options.unguarded
end
it "registers the MSpec no_ruby_bug mode" do
- MSpec.should_receive(:register_mode).with(:no_ruby_bug)
+ expect(MSpec).to receive(:register_mode).with(:no_ruby_bug)
@options.parse "--no-ruby_bug"
end
end
-describe "The -H, --random option" do
+RSpec.describe "The -H, --random option" do
before :each do
@options, @config = new_option
@options.randomize
end
it "is enabled with #randomize" do
- @options.should_receive(:on).with("-H", "--random", an_instance_of(String))
+ expect(@options).to receive(:on).with("-H", "--random", an_instance_of(String))
@options.randomize
end
it "registers the MSpec randomize mode" do
- MSpec.should_receive(:randomize=).twice
+ expect(MSpec).to receive(:randomize=).twice
["-H", "--random"].each do |opt|
@options.parse opt
end
end
end
-describe "The -R, --repeat option" do
+RSpec.describe "The -R, --repeat option" do
before :each do
@options, @config = new_option
@options.repeat
end
it "is enabled with #repeat" do
- @options.should_receive(:on).with("-R", "--repeat", "NUMBER", an_instance_of(String))
+ expect(@options).to receive(:on).with("-R", "--repeat", "NUMBER", an_instance_of(String))
@options.repeat
end
@@ -1070,117 +1070,115 @@ describe "The -R, --repeat option" do
MSpec.repeat do
repeat_count += 1
end
- repeat_count.should == 10
+ expect(repeat_count).to eq(10)
end
end
end
-describe "The -V, --verbose option" do
+RSpec.describe "The -V, --verbose option" do
before :each do
@options, @config = new_option
@options.verbose
end
it "is enabled with #verbose" do
- @options.stub(:on)
- @options.should_receive(:on).with("-V", "--verbose", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-V", "--verbose", an_instance_of(String))
@options.verbose
end
it "registers a verbose output object with MSpec" do
- MSpec.should_receive(:register).with(:start, anything()).twice
- MSpec.should_receive(:register).with(:load, anything()).twice
+ expect(MSpec).to receive(:register).with(:start, anything()).twice
+ expect(MSpec).to receive(:register).with(:load, anything()).twice
["-V", "--verbose"].each do |opt|
@options.parse opt
end
end
end
-describe "The -m, --marker MARKER option" do
+RSpec.describe "The -m, --marker MARKER option" do
before :each do
@options, @config = new_option
@options.verbose
end
it "is enabled with #verbose" do
- @options.stub(:on)
- @options.should_receive(:on).with("-m", "--marker", "MARKER",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-m", "--marker", "MARKER",
an_instance_of(String))
@options.verbose
end
it "registers a marker output object with MSpec" do
- MSpec.should_receive(:register).with(:load, anything()).twice
+ expect(MSpec).to receive(:register).with(:load, anything()).twice
["-m", "--marker"].each do |opt|
@options.parse [opt, ","]
end
end
end
-describe "The --int-spec option" do
+RSpec.describe "The --int-spec option" do
before :each do
@options, @config = new_option
@options.interrupt
end
it "is enabled with #interrupt" do
- @options.should_receive(:on).with("--int-spec", an_instance_of(String))
+ expect(@options).to receive(:on).with("--int-spec", an_instance_of(String))
@options.interrupt
end
it "sets the abort config option to false to only abort the running spec with ^C" do
@config[:abort] = true
@options.parse "--int-spec"
- @config[:abort].should == false
+ expect(@config[:abort]).to eq(false)
end
end
-describe "The -Y, --verify option" do
+RSpec.describe "The -Y, --verify option" do
before :each do
@options, @config = new_option
@options.verify
end
it "is enabled with #interrupt" do
- @options.stub(:on)
- @options.should_receive(:on).with("-Y", "--verify", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-Y", "--verify", an_instance_of(String))
@options.verify
end
it "sets the MSpec mode to :verify" do
- MSpec.should_receive(:register_mode).with(:verify).twice
+ expect(MSpec).to receive(:register_mode).with(:verify).twice
["-Y", "--verify"].each do |m|
@options.parse m
end
end
end
-describe "The -O, --report option" do
+RSpec.describe "The -O, --report option" do
before :each do
@options, @config = new_option
@options.verify
end
it "is enabled with #interrupt" do
- @options.stub(:on)
- @options.should_receive(:on).with("-O", "--report", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-O", "--report", an_instance_of(String))
@options.verify
end
it "sets the MSpec mode to :report" do
- MSpec.should_receive(:register_mode).with(:report).twice
+ expect(MSpec).to receive(:register_mode).with(:report).twice
["-O", "--report"].each do |m|
@options.parse m
end
end
end
-describe "The --report-on GUARD option" do
- before :all do
- MSpec.stub(:register_mode)
- end
-
+RSpec.describe "The --report-on GUARD option" do
before :each do
+ allow(MSpec).to receive(:register_mode)
+
@options, @config = new_option
@options.verify
@@ -1192,37 +1190,37 @@ describe "The --report-on GUARD option" do
end
it "is enabled with #interrupt" do
- @options.stub(:on)
- @options.should_receive(:on).with("--report-on", "GUARD", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("--report-on", "GUARD", an_instance_of(String))
@options.verify
end
it "sets the MSpec mode to :report_on" do
- MSpec.should_receive(:register_mode).with(:report_on)
+ expect(MSpec).to receive(:register_mode).with(:report_on)
@options.parse ["--report-on", "ruby_bug"]
end
it "converts the guard name to a symbol" do
name = double("ruby_bug")
- name.should_receive(:to_sym)
+ expect(name).to receive(:to_sym)
@options.parse ["--report-on", name]
end
it "saves the name of the guard" do
@options.parse ["--report-on", "ruby_bug"]
- SpecGuard.guards.should == [:ruby_bug]
+ expect(SpecGuard.guards).to eq([:ruby_bug])
end
end
-describe "The -K, --action-tag TAG option" do
+RSpec.describe "The -K, --action-tag TAG option" do
before :each do
@options, @config = new_option
@options.action_filters
end
it "is enabled with #action_filters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-K", "--action-tag", "TAG",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-K", "--action-tag", "TAG",
an_instance_of(String))
@options.action_filters
end
@@ -1231,20 +1229,20 @@ describe "The -K, --action-tag TAG option" do
["-K", "--action-tag"].each do |opt|
@config[:atags] = []
@options.parse [opt, "action-tag"]
- @config[:atags].should include("action-tag")
+ expect(@config[:atags]).to include("action-tag")
end
end
end
-describe "The -S, --action-string STR option" do
+RSpec.describe "The -S, --action-string STR option" do
before :each do
@options, @config = new_option
@options.action_filters
end
it "is enabled with #action_filters" do
- @options.stub(:on)
- @options.should_receive(:on).with("-S", "--action-string", "STR",
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-S", "--action-string", "STR",
an_instance_of(String))
@options.action_filters
end
@@ -1253,12 +1251,12 @@ describe "The -S, --action-string STR option" do
["-S", "--action-string"].each do |opt|
@config[:astrings] = []
@options.parse [opt, "action-str"]
- @config[:astrings].should include("action-str")
+ expect(@config[:astrings]).to include("action-str")
end
end
end
-describe "The -d, --debug option" do
+RSpec.describe "The -d, --debug option" do
before :each do
@options, @config = new_option
@options.debug
@@ -1269,22 +1267,22 @@ describe "The -d, --debug option" do
end
it "is enabled with #debug" do
- @options.stub(:on)
- @options.should_receive(:on).with("-d", "--debug", an_instance_of(String))
+ allow(@options).to receive(:on)
+ expect(@options).to receive(:on).with("-d", "--debug", an_instance_of(String))
@options.debug
end
it "sets $MSPEC_DEBUG to true" do
["-d", "--debug"].each do |opt|
- $MSPEC_DEBUG.should_not be_true
+ expect($MSPEC_DEBUG).not_to be_truthy
@options.parse opt
- $MSPEC_DEBUG.should be_true
+ expect($MSPEC_DEBUG).to be_truthy
$MSPEC_DEBUG = nil
end
end
end
-describe "MSpecOptions#all" do
+RSpec.describe "MSpecOptions#all" do
it "includes all options" do
meth = MSpecOptions.instance_method(:all)
file, line = meth.source_location
@@ -1299,6 +1297,6 @@ describe "MSpecOptions#all" do
option_methods = contents.scan(/def (\w+).*\n\s*on\(/).map(&:first)
option_methods[0].sub!("configure", "configure {}")
- calls.should == option_methods
+ expect(calls).to eq(option_methods)
end
end
diff --git a/spec/mspec/spec/utils/script_spec.rb b/spec/mspec/spec/utils/script_spec.rb
index c7fa3eb354..c35bda8b47 100644
--- a/spec/mspec/spec/utils/script_spec.rb
+++ b/spec/mspec/spec/utils/script_spec.rb
@@ -4,40 +4,40 @@ require 'mspec/runner/mspec'
require 'mspec/runner/filters'
require 'mspec/runner/actions/filter'
-describe MSpecScript, ".config" do
+RSpec.describe MSpecScript, ".config" do
it "returns a Hash" do
- MSpecScript.config.should be_kind_of(Hash)
+ expect(MSpecScript.config).to be_kind_of(Hash)
end
end
-describe MSpecScript, ".set" do
+RSpec.describe MSpecScript, ".set" do
it "sets the config hash key, value" do
MSpecScript.set :a, 10
- MSpecScript.config[:a].should == 10
+ expect(MSpecScript.config[:a]).to eq(10)
end
end
-describe MSpecScript, ".get" do
+RSpec.describe MSpecScript, ".get" do
it "gets the config hash value for a key" do
MSpecScript.set :a, 10
- MSpecScript.get(:a).should == 10
+ expect(MSpecScript.get(:a)).to eq(10)
end
end
-describe MSpecScript, "#config" do
+RSpec.describe MSpecScript, "#config" do
it "returns the MSpecScript config hash" do
MSpecScript.set :b, 5
- MSpecScript.new.config[:b].should == 5
+ expect(MSpecScript.new.config[:b]).to eq(5)
end
it "returns the MSpecScript config hash from subclasses" do
class MSSClass < MSpecScript; end
MSpecScript.set :b, 5
- MSSClass.new.config[:b].should == 5
+ expect(MSSClass.new.config[:b]).to eq(5)
end
end
-describe MSpecScript, "#load_default" do
+RSpec.describe MSpecScript, "#load_default" do
before :all do
@verbose = $VERBOSE
$VERBOSE = nil
@@ -53,7 +53,7 @@ describe MSpecScript, "#load_default" do
@engine = Object.const_get :RUBY_ENGINE
end
@script = MSpecScript.new
- MSpecScript.stub(:new).and_return(@script)
+ allow(MSpecScript).to receive(:new).and_return(@script)
end
after :each do
@@ -62,8 +62,8 @@ describe MSpecScript, "#load_default" do
end
it "attempts to load 'default.mspec'" do
- @script.stub(:try_load)
- @script.should_receive(:try_load).with('default.mspec').and_return(true)
+ allow(@script).to receive(:try_load)
+ expect(@script).to receive(:try_load).with('default.mspec').and_return(true)
@script.load_default
end
@@ -71,145 +71,140 @@ describe MSpecScript, "#load_default" do
Object.const_set :RUBY_ENGINE, "ybur"
Object.const_set :RUBY_VERSION, "1.8.9"
default = "ybur.1.8.mspec"
- @script.should_receive(:try_load).with('default.mspec').and_return(false)
- @script.should_receive(:try_load).with(default)
- @script.should_receive(:try_load).with('ybur.mspec')
+ expect(@script).to receive(:try_load).with('default.mspec').and_return(false)
+ expect(@script).to receive(:try_load).with(default)
+ expect(@script).to receive(:try_load).with('ybur.mspec')
@script.load_default
end
end
-describe MSpecScript, ".main" do
+RSpec.describe MSpecScript, ".main" do
before :each do
@script = double("MSpecScript").as_null_object
- MSpecScript.stub(:new).and_return(@script)
+ allow(MSpecScript).to receive(:new).and_return(@script)
# Do not require full mspec as it would conflict with RSpec
- MSpecScript.should_receive(:require).with('mspec')
+ expect(MSpecScript).to receive(:require).with('mspec')
end
it "creates an instance of MSpecScript" do
- MSpecScript.should_receive(:new).and_return(@script)
+ expect(MSpecScript).to receive(:new).and_return(@script)
MSpecScript.main
end
it "attempts to load the default config" do
- @script.should_receive(:load_default)
- MSpecScript.main
- end
-
- it "attempts to load the '~/.mspecrc' script" do
- @script.should_receive(:try_load).with('~/.mspecrc')
+ expect(@script).to receive(:load_default)
MSpecScript.main
end
it "calls the #options method on the script" do
- @script.should_receive(:options)
+ expect(@script).to receive(:options)
MSpecScript.main
end
it "calls the #signals method on the script" do
- @script.should_receive(:signals)
+ expect(@script).to receive(:signals)
MSpecScript.main
end
it "calls the #register method on the script" do
- @script.should_receive(:register)
+ expect(@script).to receive(:register)
MSpecScript.main
end
it "calls the #setup_env method on the script" do
- @script.should_receive(:setup_env)
+ expect(@script).to receive(:setup_env)
MSpecScript.main
end
it "calls the #run method on the script" do
- @script.should_receive(:run)
+ expect(@script).to receive(:run)
MSpecScript.main
end
end
-describe MSpecScript, "#initialize" do
+RSpec.describe MSpecScript, "#initialize" do
before :each do
@config = MSpecScript.new.config
end
it "sets the default config values" do
- @config[:formatter].should == nil
- @config[:includes].should == []
- @config[:excludes].should == []
- @config[:patterns].should == []
- @config[:xpatterns].should == []
- @config[:tags].should == []
- @config[:xtags].should == []
- @config[:atags].should == []
- @config[:astrings].should == []
- @config[:abort].should == true
- @config[:config_ext].should == '.mspec'
+ expect(@config[:formatter]).to eq(nil)
+ expect(@config[:includes]).to eq([])
+ expect(@config[:excludes]).to eq([])
+ expect(@config[:patterns]).to eq([])
+ expect(@config[:xpatterns]).to eq([])
+ expect(@config[:tags]).to eq([])
+ expect(@config[:xtags]).to eq([])
+ expect(@config[:atags]).to eq([])
+ expect(@config[:astrings]).to eq([])
+ expect(@config[:abort]).to eq(true)
+ expect(@config[:config_ext]).to eq('.mspec')
end
end
-describe MSpecScript, "#load" do
+RSpec.describe MSpecScript, "#load" do
before :each do
- File.stub(:exist?).and_return(false)
+ allow(File).to receive(:exist?).and_return(false)
@script = MSpecScript.new
@file = "default.mspec"
@base = "default"
end
it "attempts to locate the file through the expanded path name" do
- File.should_receive(:expand_path).with(@file, ".").and_return(@file)
- File.should_receive(:exist?).with(@file).and_return(true)
- Kernel.should_receive(:load).with(@file).and_return(:loaded)
- @script.load(@file).should == :loaded
+ expect(File).to receive(:expand_path).with(@file, ".").and_return(@file)
+ expect(File).to receive(:exist?).with(@file).and_return(true)
+ expect(Kernel).to receive(:load).with(@file).and_return(:loaded)
+ expect(@script.load(@file)).to eq(:loaded)
end
it "appends config[:config_ext] to the name and attempts to locate the file through the expanded path name" do
- File.should_receive(:expand_path).with(@base, ".").and_return(@base)
- File.should_receive(:expand_path).with(@base, "spec").and_return(@base)
- File.should_receive(:expand_path).with(@file, ".").and_return(@file)
- File.should_receive(:exist?).with(@base).and_return(false)
- File.should_receive(:exist?).with(@file).and_return(true)
- Kernel.should_receive(:load).with(@file).and_return(:loaded)
- @script.load(@base).should == :loaded
+ expect(File).to receive(:expand_path).with(@base, ".").and_return(@base)
+ expect(File).to receive(:expand_path).with(@base, "spec").and_return(@base)
+ expect(File).to receive(:expand_path).with(@file, ".").and_return(@file)
+ expect(File).to receive(:exist?).with(@base).and_return(false)
+ expect(File).to receive(:exist?).with(@file).and_return(true)
+ expect(Kernel).to receive(:load).with(@file).and_return(:loaded)
+ expect(@script.load(@base)).to eq(:loaded)
end
it "attempts to locate the file in '.'" do
path = File.expand_path @file, "."
- File.should_receive(:exist?).with(path).and_return(true)
- Kernel.should_receive(:load).with(path).and_return(:loaded)
- @script.load(@file).should == :loaded
+ expect(File).to receive(:exist?).with(path).and_return(true)
+ expect(Kernel).to receive(:load).with(path).and_return(:loaded)
+ expect(@script.load(@file)).to eq(:loaded)
end
it "appends config[:config_ext] to the name and attempts to locate the file in '.'" do
path = File.expand_path @file, "."
- File.should_receive(:exist?).with(path).and_return(true)
- Kernel.should_receive(:load).with(path).and_return(:loaded)
- @script.load(@base).should == :loaded
+ expect(File).to receive(:exist?).with(path).and_return(true)
+ expect(Kernel).to receive(:load).with(path).and_return(:loaded)
+ expect(@script.load(@base)).to eq(:loaded)
end
it "attempts to locate the file in 'spec'" do
path = File.expand_path @file, "spec"
- File.should_receive(:exist?).with(path).and_return(true)
- Kernel.should_receive(:load).with(path).and_return(:loaded)
- @script.load(@file).should == :loaded
+ expect(File).to receive(:exist?).with(path).and_return(true)
+ expect(Kernel).to receive(:load).with(path).and_return(:loaded)
+ expect(@script.load(@file)).to eq(:loaded)
end
it "appends config[:config_ext] to the name and attempts to locate the file in 'spec'" do
path = File.expand_path @file, "spec"
- File.should_receive(:exist?).with(path).and_return(true)
- Kernel.should_receive(:load).with(path).and_return(:loaded)
- @script.load(@base).should == :loaded
+ expect(File).to receive(:exist?).with(path).and_return(true)
+ expect(Kernel).to receive(:load).with(path).and_return(:loaded)
+ expect(@script.load(@base)).to eq(:loaded)
end
it "loads a given file only once" do
path = File.expand_path @file, "spec"
- File.should_receive(:exist?).with(path).and_return(true)
- Kernel.should_receive(:load).once.with(path).and_return(:loaded)
- @script.load(@base).should == :loaded
- @script.load(@base).should == true
+ expect(File).to receive(:exist?).with(path).and_return(true)
+ expect(Kernel).to receive(:load).once.with(path).and_return(:loaded)
+ expect(@script.load(@base)).to eq(:loaded)
+ expect(@script.load(@base)).to eq(true)
end
end
-describe MSpecScript, "#custom_options" do
+RSpec.describe MSpecScript, "#custom_options" do
before :each do
@script = MSpecScript.new
end
@@ -219,12 +214,12 @@ describe MSpecScript, "#custom_options" do
it "prints 'None'" do
options = double("options")
- options.should_receive(:doc).with(" No custom options registered")
+ expect(options).to receive(:doc).with(" No custom options registered")
@script.custom_options options
end
end
-describe MSpecScript, "#register" do
+RSpec.describe MSpecScript, "#register" do
before :each do
@script = MSpecScript.new
@@ -233,8 +228,8 @@ describe MSpecScript, "#register" do
end
it "creates and registers the formatter" do
- @formatter.should_receive(:new).and_return(@formatter)
- @formatter.should_receive(:register)
+ expect(@formatter).to receive(:new).and_return(@formatter)
+ expect(@formatter).to receive(:register)
@script.register
end
@@ -244,24 +239,24 @@ describe MSpecScript, "#register" do
end
it "calls #custom_register" do
- @script.should_receive(:custom_register)
+ expect(@script).to receive(:custom_register)
@script.register
end
it "registers :formatter with the formatter instance" do
- @formatter.stub(:new).and_return(@formatter)
+ allow(@formatter).to receive(:new).and_return(@formatter)
@script.register
- MSpec.formatter.should be(@formatter)
+ expect(MSpec.formatter).to be(@formatter)
end
it "does not register :formatter if config[:formatter] is false" do
@script.config[:formatter] = false
- MSpec.should_not_receive(:store)
+ expect(MSpec).not_to receive(:store)
@script.register
end
end
-describe MSpecScript, "#register" do
+RSpec.describe MSpecScript, "#register" do
before :each do
@script = MSpecScript.new
@@ -269,61 +264,61 @@ describe MSpecScript, "#register" do
@script.config[:formatter] = @formatter
@filter = double("filter")
- @filter.should_receive(:register)
+ expect(@filter).to receive(:register)
@ary = ["some", "spec"]
end
it "creates and registers a MatchFilter for include specs" do
- MatchFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ expect(MatchFilter).to receive(:new).with(:include, *@ary).and_return(@filter)
@script.config[:includes] = @ary
@script.register
end
it "creates and registers a MatchFilter for excluded specs" do
- MatchFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ expect(MatchFilter).to receive(:new).with(:exclude, *@ary).and_return(@filter)
@script.config[:excludes] = @ary
@script.register
end
it "creates and registers a RegexpFilter for include specs" do
- RegexpFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ expect(RegexpFilter).to receive(:new).with(:include, *@ary).and_return(@filter)
@script.config[:patterns] = @ary
@script.register
end
it "creates and registers a RegexpFilter for excluded specs" do
- RegexpFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ expect(RegexpFilter).to receive(:new).with(:exclude, *@ary).and_return(@filter)
@script.config[:xpatterns] = @ary
@script.register
end
it "creates and registers a TagFilter for include specs" do
- TagFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ expect(TagFilter).to receive(:new).with(:include, *@ary).and_return(@filter)
@script.config[:tags] = @ary
@script.register
end
it "creates and registers a TagFilter for excluded specs" do
- TagFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ expect(TagFilter).to receive(:new).with(:exclude, *@ary).and_return(@filter)
@script.config[:xtags] = @ary
@script.register
end
it "creates and registers a ProfileFilter for include specs" do
- ProfileFilter.should_receive(:new).with(:include, *@ary).and_return(@filter)
+ expect(ProfileFilter).to receive(:new).with(:include, *@ary).and_return(@filter)
@script.config[:profiles] = @ary
@script.register
end
it "creates and registers a ProfileFilter for excluded specs" do
- ProfileFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter)
+ expect(ProfileFilter).to receive(:new).with(:exclude, *@ary).and_return(@filter)
@script.config[:xprofiles] = @ary
@script.register
end
end
-describe MSpecScript, "#signals" do
+RSpec.describe MSpecScript, "#signals" do
before :each do
@script = MSpecScript.new
@abort = @script.config[:abort]
@@ -334,42 +329,42 @@ describe MSpecScript, "#signals" do
end
it "traps the INT signal if config[:abort] is true" do
- Signal.should_receive(:trap).with("INT")
+ expect(Signal).to receive(:trap).with("INT")
@script.config[:abort] = true
@script.signals
end
it "does not trap the INT signal if config[:abort] is not true" do
- Signal.should_not_receive(:trap).with("INT")
+ expect(Signal).not_to receive(:trap).with("INT")
@script.config[:abort] = false
@script.signals
end
end
-describe MSpecScript, "#entries" do
+RSpec.describe MSpecScript, "#entries" do
before :each do
@script = MSpecScript.new
- File.stub(:realpath).and_return("name")
- File.stub(:file?).and_return(false)
- File.stub(:directory?).and_return(false)
+ allow(File).to receive(:realpath).and_return("name")
+ allow(File).to receive(:file?).and_return(false)
+ allow(File).to receive(:directory?).and_return(false)
end
it "returns the pattern in an array if it is a file" do
- File.should_receive(:realpath).with("file").and_return("file/expanded.rb")
- File.should_receive(:file?).with("file/expanded.rb").and_return(true)
- @script.entries("file").should == ["file/expanded.rb"]
+ expect(File).to receive(:realpath).with("file").and_return("file/expanded.rb")
+ expect(File).to receive(:file?).with("file/expanded.rb").and_return(true)
+ expect(@script.entries("file")).to eq(["file/expanded.rb"])
end
it "returns Dir['pattern/**/*_spec.rb'] if pattern is a directory" do
- File.should_receive(:directory?).with("name").and_return(true)
- File.stub(:realpath).and_return("name", "name/**/*_spec.rb")
- Dir.should_receive(:[]).with("name/**/*_spec.rb").and_return(["dir1", "dir2"])
- @script.entries("name").should == ["dir1", "dir2"]
+ expect(File).to receive(:directory?).with("name").and_return(true)
+ allow(File).to receive(:realpath).and_return("name", "name/**/*_spec.rb")
+ expect(Dir).to receive(:[]).with("name/**/*_spec.rb").and_return(["dir1", "dir2"])
+ expect(@script.entries("name")).to eq(["dir1", "dir2"])
end
it "aborts if pattern cannot be resolved to a file nor a directory" do
- @script.should_receive(:abort)
+ expect(@script).to receive(:abort)
@script.entries("pattern")
end
@@ -382,47 +377,47 @@ describe MSpecScript, "#entries" do
it "returns the pattern in an array if it is a file" do
name = "#{@name}.rb"
- File.should_receive(:realpath).with(name).and_return(name)
- File.should_receive(:file?).with(name).and_return(true)
- @script.entries("name.rb").should == [name]
+ expect(File).to receive(:realpath).with(name).and_return(name)
+ expect(File).to receive(:file?).with(name).and_return(true)
+ expect(@script.entries("name.rb")).to eq([name])
end
it "returns Dir['pattern/**/*_spec.rb'] if pattern is a directory" do
- File.stub(:realpath).and_return(@name, @name+"/**/*_spec.rb")
- File.should_receive(:directory?).with(@name).and_return(true)
- Dir.should_receive(:[]).with(@name + "/**/*_spec.rb").and_return(["dir1", "dir2"])
- @script.entries("name").should == ["dir1", "dir2"]
+ allow(File).to receive(:realpath).and_return(@name, @name+"/**/*_spec.rb")
+ expect(File).to receive(:directory?).with(@name).and_return(true)
+ expect(Dir).to receive(:[]).with(@name + "/**/*_spec.rb").and_return(["dir1", "dir2"])
+ expect(@script.entries("name")).to eq(["dir1", "dir2"])
end
it "aborts if pattern cannot be resolved to a file nor a directory" do
- @script.should_receive(:abort)
+ expect(@script).to receive(:abort)
@script.entries("pattern")
end
end
end
-describe MSpecScript, "#files" do
+RSpec.describe MSpecScript, "#files" do
before :each do
@script = MSpecScript.new
end
it "accumulates the values returned by #entries" do
- @script.should_receive(:entries).and_return(["file1"], ["file2"])
- @script.files(["a", "b"]).should == ["file1", "file2"]
+ expect(@script).to receive(:entries).and_return(["file1"], ["file2"])
+ expect(@script.files(["a", "b"])).to eq(["file1", "file2"])
end
it "strips a leading '^' and removes the values returned by #entries" do
- @script.should_receive(:entries).and_return(["file1"], ["file2"], ["file1"])
- @script.files(["a", "b", "^a"]).should == ["file2"]
+ expect(@script).to receive(:entries).and_return(["file1"], ["file2"], ["file1"])
+ expect(@script.files(["a", "b", "^a"])).to eq(["file2"])
end
it "processes the array elements in order" do
- @script.should_receive(:entries).and_return(["file1"], ["file1"], ["file2"])
- @script.files(["^a", "a", "b"]).should == ["file1", "file2"]
+ expect(@script).to receive(:entries).and_return(["file1"], ["file1"], ["file2"])
+ expect(@script.files(["^a", "a", "b"])).to eq(["file1", "file2"])
end
end
-describe MSpecScript, "#files" do
+RSpec.describe MSpecScript, "#files" do
before :each do
MSpecScript.set :files, ["file1", "file2"]
@@ -434,21 +429,21 @@ describe MSpecScript, "#files" do
end
it "looks up items with leading ':' in the config object" do
- @script.should_receive(:entries).and_return(["file1"], ["file2"])
- @script.files([":files"]).should == ["file1", "file2"]
+ expect(@script).to receive(:entries).and_return(["file1"], ["file2"])
+ expect(@script.files([":files"])).to eq(["file1", "file2"])
end
it "aborts if the config key is not set" do
- @script.should_receive(:abort).with("Key :all_files not found in mspec config.")
+ expect(@script).to receive(:abort).with("Key :all_files not found in mspec config.")
@script.files([":all_files"])
end
end
-describe MSpecScript, "#setup_env" do
+RSpec.describe MSpecScript, "#setup_env" do
before :each do
@script = MSpecScript.new
@options, @config = new_option
- @script.stub(:config).and_return(@config)
+ allow(@script).to receive(:config).and_return(@config)
end
after :each do
@@ -457,19 +452,19 @@ describe MSpecScript, "#setup_env" do
it "sets MSPEC_RUNNER = '1' in the environment" do
ENV["MSPEC_RUNNER"] = "0"
@script.setup_env
- ENV["MSPEC_RUNNER"].should == "1"
+ expect(ENV["MSPEC_RUNNER"]).to eq("1")
end
it "sets RUBY_EXE = config[:target] in the environment" do
ENV["RUBY_EXE"] = nil
@script.setup_env
- ENV["RUBY_EXE"].should == @config[:target]
+ expect(ENV["RUBY_EXE"]).to eq(@config[:target])
end
it "sets RUBY_FLAGS = config[:flags] in the environment" do
ENV["RUBY_FLAGS"] = nil
@config[:flags] = ["-w", "-Q"]
@script.setup_env
- ENV["RUBY_FLAGS"].should == "-w -Q"
+ expect(ENV["RUBY_FLAGS"]).to eq("-w -Q")
end
end
diff --git a/spec/mspec/spec/utils/version_spec.rb b/spec/mspec/spec/utils/version_spec.rb
index 0b2d383c6d..ec367d2a1e 100644
--- a/spec/mspec/spec/utils/version_spec.rb
+++ b/spec/mspec/spec/utils/version_spec.rb
@@ -1,45 +1,45 @@
require 'spec_helper'
require 'mspec/utils/version'
-describe SpecVersion, "#to_s" do
+RSpec.describe SpecVersion, "#to_s" do
it "returns the string with which it was initialized" do
- SpecVersion.new("1.8").to_s.should == "1.8"
- SpecVersion.new("2.118.9").to_s.should == "2.118.9"
+ expect(SpecVersion.new("1.8").to_s).to eq("1.8")
+ expect(SpecVersion.new("2.118.9").to_s).to eq("2.118.9")
end
end
-describe SpecVersion, "#to_str" do
+RSpec.describe SpecVersion, "#to_str" do
it "returns the same string as #to_s" do
version = SpecVersion.new("2.118.9")
- version.to_str.should == version.to_s
+ expect(version.to_str).to eq(version.to_s)
end
end
-describe SpecVersion, "#to_i with ceil = false" do
+RSpec.describe SpecVersion, "#to_i with ceil = false" do
it "returns an integer representation of the version string" do
- SpecVersion.new("2.23.10").to_i.should == 1022310
+ expect(SpecVersion.new("2.23.10").to_i).to eq(1022310)
end
it "replaces missing version parts with zeros" do
- SpecVersion.new("1.8").to_i.should == 1010800
- SpecVersion.new("1.8.6").to_i.should == 1010806
+ expect(SpecVersion.new("1.8").to_i).to eq(1010800)
+ expect(SpecVersion.new("1.8.6").to_i).to eq(1010806)
end
end
-describe SpecVersion, "#to_i with ceil = true" do
+RSpec.describe SpecVersion, "#to_i with ceil = true" do
it "returns an integer representation of the version string" do
- SpecVersion.new("1.8.6", true).to_i.should == 1010806
+ expect(SpecVersion.new("1.8.6", true).to_i).to eq(1010806)
end
it "fills in 9s for missing tiny values" do
- SpecVersion.new("1.8", true).to_i.should == 1010899
- SpecVersion.new("1.8.6", true).to_i.should == 1010806
+ expect(SpecVersion.new("1.8", true).to_i).to eq(1010899)
+ expect(SpecVersion.new("1.8.6", true).to_i).to eq(1010806)
end
end
-describe SpecVersion, "#to_int" do
+RSpec.describe SpecVersion, "#to_int" do
it "returns the same value as #to_i" do
version = SpecVersion.new("4.16.87")
- version.to_int.should == version.to_i
+ expect(version.to_int).to eq(version.to_i)
end
end
diff --git a/spec/mspec/tool/check_require_spec_helper.rb b/spec/mspec/tool/check_require_spec_helper.rb
new file mode 100755
index 0000000000..07126e68dc
--- /dev/null
+++ b/spec/mspec/tool/check_require_spec_helper.rb
@@ -0,0 +1,34 @@
+#!/usr/bin/env ruby
+
+# This script is used to check that each *_spec.rb file has
+# a relative_require for spec_helper which should live higher
+# up in the ruby/spec repo directory tree.
+#
+# Prints errors to $stderr and returns a non-zero exit code when
+# errors are found.
+#
+# Related to https://github.com/ruby/spec/pull/992
+
+def check_file(fn)
+ File.foreach(fn) do |line|
+ return $1 if line =~ /^\s*require_relative\s*['"](.*spec_helper)['"]/
+ end
+ nil
+end
+
+rootdir = ARGV[0] || "."
+fglob = File.join(rootdir, "**", "*_spec.rb")
+specfiles = Dir.glob(fglob)
+raise "No spec files found in #{fglob.inspect}. Give an argument to specify the root-directory of ruby/spec" if specfiles.empty?
+
+errors = 0
+specfiles.sort.each do |fn|
+ result = check_file(fn)
+ if result.nil?
+ warn "Missing require_relative for *spec_helper for file: #{fn}"
+ errors += 1
+ end
+end
+
+puts "# Found #{errors} files with require_relative spec_helper issues."
+exit 1 if errors > 0
diff --git a/spec/mspec/tool/remove_old_guards.rb b/spec/mspec/tool/remove_old_guards.rb
index 718e351e11..bc5612c78d 100644..100755
--- a/spec/mspec/tool/remove_old_guards.rb
+++ b/spec/mspec/tool/remove_old_guards.rb
@@ -1,6 +1,23 @@
+#!/usr/bin/env ruby
+
# Removes old version guards in ruby/spec.
# Run it from the ruby/spec repository root.
# The argument is the new minimum supported version.
+#
+# cd spec
+# ../mspec/tool/remove_old_guards.rb <ruby-version>
+#
+# where <ruby-version> is a version guard with which should be removed
+#
+# Example:
+# tool/remove_old_guards.rb 3.1
+#
+# As a result guards like
+# ruby_version_is "3.1" do
+# # ...
+# end
+#
+# will be removed.
def dedent(line)
if line.start_with?(" ")
@@ -14,6 +31,12 @@ def each_spec_file(&block)
Dir["*/**/*.rb"].each(&block)
end
+def each_file(&block)
+ Dir["**/*"].each { |path|
+ yield path if File.file?(path)
+ }
+end
+
def remove_guards(guard, keep)
each_spec_file do |file|
contents = File.binread(file)
@@ -21,20 +44,24 @@ def remove_guards(guard, keep)
puts file
lines = contents.lines.to_a
while first = lines.find_index { |line| line =~ guard }
+ comment = first
+ while comment > 0 and lines[comment-1] =~ /^(\s*)#/
+ comment -= 1
+ end
indent = lines[first][/^(\s*)/, 1].length
last = (first+1...lines.size).find { |i|
space = lines[i][/^(\s*)end$/, 1] and space.length == indent
}
raise file unless last
if keep
- lines[first..last] = lines[first+1..last-1].map { |l| dedent(l) }
+ lines[comment..last] = lines[first+1..last-1].map { |l| dedent(l) }
else
- if first > 0 and lines[first-1] == "\n"
- first -= 1
+ if comment > 0 and lines[comment-1] == "\n"
+ comment -= 1
elsif lines[last+1] == "\n"
last += 1
end
- lines[first..last] = []
+ lines[comment..last] = []
end
end
File.binwrite file, lines.join
@@ -42,8 +69,53 @@ def remove_guards(guard, keep)
end
end
-def search(regexp)
+def remove_empty_files
each_spec_file do |file|
+ unless file.include?("fixtures/")
+ lines = File.readlines(file)
+ if lines.all? { |line| line.chomp.empty? or line.start_with?('require', '#') }
+ puts "Removing empty file #{file}"
+ File.delete(file)
+ end
+ end
+ end
+end
+
+def remove_unused_shared_specs
+ shared_groups = {}
+ # Dir["**/shared/**/*.rb"].each do |shared|
+ each_spec_file do |shared|
+ next if File.basename(shared) == 'constants.rb'
+ contents = File.binread(shared)
+ found = false
+ contents.scan(/^\s*describe (:[\w_?]+), shared: true do$/) {
+ shared_groups[$1] = 0
+ found = true
+ }
+ if !found and shared.include?('shared/') and !shared.include?('fixtures/') and !shared.end_with?('/constants.rb')
+ puts "no shared describe in #{shared} ?"
+ end
+ end
+
+ each_spec_file do |file|
+ contents = File.binread(file)
+ contents.scan(/(?:it_behaves_like|it_should_behave_like) (:[\w_?]+)[,\s]/) do
+ puts $1 unless shared_groups.key?($1)
+ shared_groups[$1] += 1
+ end
+ end
+
+ shared_groups.each_pair do |group, value|
+ if value == 0
+ puts "Shared describe #{group} seems unused"
+ elsif value == 1
+ puts "Shared describe #{group} seems used only once" if $VERBOSE
+ end
+ end
+end
+
+def search(regexp)
+ each_file do |file|
contents = File.binread(file)
if contents =~ regexp
puts file
@@ -56,11 +128,18 @@ def search(regexp)
end
end
+abort "usage: #{$0} <ruby-version>" if ARGV.empty?
+
version = Regexp.escape(ARGV.fetch(0))
version += "(?:\\.0)?" if version.count(".") < 2
remove_guards(/ruby_version_is (["'])#{version}\1 do/, true)
remove_guards(/ruby_version_is (["'])[0-9.]*\1 *... *(["'])#{version}\2 do/, false)
-remove_guards(/ruby_bug "#\d+", (["'])[0-9.]*\1 *... *(["'])#{version}\2 do/, true)
+remove_guards(/ruby_bug ["']#\d+["'], (["'])[0-9.]*\1 *... *(["'])#{version}\2 do/, true)
+
+remove_empty_files
+remove_unused_shared_specs
+puts "Search:"
search(/(["'])#{version}\1/)
search(/^\s*#.+#{version}/)
+search(/RUBY_VERSION_IS_#{version.tr('.', '_')}/)
diff --git a/spec/mspec/tool/sync/sync-rubyspec.rb b/spec/mspec/tool/sync/sync-rubyspec.rb
index 977bda3715..86c43d0dc8 100644
--- a/spec/mspec/tool/sync/sync-rubyspec.rb
+++ b/spec/mspec/tool/sync/sync-rubyspec.rb
@@ -1,6 +1,9 @@
+# This script is based on commands from the wiki:
+# https://github.com/ruby/spec/wiki/Merging-specs-from-JRuby-and-other-sources
+
IMPLS = {
truffleruby: {
- git: "https://github.com/oracle/truffleruby.git",
+ git: "https://github.com/truffleruby/truffleruby.git",
from_commit: "f10ab6988d",
},
jruby: {
@@ -17,9 +20,11 @@ IMPLS = {
MSPEC = ARGV.delete('--mspec')
-CHECK_LAST_MERGE = ENV['CHECK_LAST_MERGE'] != 'false'
+CHECK_LAST_MERGE = !MSPEC && ENV['CHECK_LAST_MERGE'] != 'false'
TEST_MASTER = ENV['TEST_MASTER'] != 'false'
+ONLY_FILTER = ENV['ONLY_FILTER'] == 'true'
+
MSPEC_REPO = File.expand_path("../../..", __FILE__)
raise MSPEC_REPO if !Dir.exist?(MSPEC_REPO) or !Dir.exist?("#{MSPEC_REPO}/.git")
@@ -29,6 +34,13 @@ raise RUBYSPEC_REPO unless Dir.exist?(RUBYSPEC_REPO)
SOURCE_REPO = MSPEC ? MSPEC_REPO : RUBYSPEC_REPO
+# LAST_MERGE is a commit of ruby/spec or ruby/mspec
+# which is the spec/mspec commit that was last imported in the Ruby implementation
+# (i.e. the commit in "Update to ruby/spec@commit").
+# It is normally automatically computed, but can be manually set when
+# e.g. the last update of specs wasn't merged in the Ruby implementation.
+LAST_MERGE = ENV["LAST_MERGE"]
+
NOW = Time.now
BRIGHT_RED = "\e[31;1m"
@@ -54,6 +66,10 @@ class RubyImplementation
File.basename(git_url, ".git")
end
+ def repo_path
+ "#{__dir__}/#{repo_name}"
+ end
+
def repo_org
File.basename(File.dirname(git_url))
end
@@ -133,8 +149,8 @@ def rebase_commits(impl)
else
sh "git", "checkout", impl.name
- if ENV["LAST_MERGE"]
- last_merge = `git log -n 1 --format='%H %ct' #{ENV["LAST_MERGE"]}`
+ if LAST_MERGE
+ last_merge = `git log -n 1 --format='%H %ct' #{LAST_MERGE}`
else
last_merge = `git log --grep='^#{impl.last_merge_message}' -n 1 --format='%H %ct'`
end
@@ -149,6 +165,11 @@ def rebase_commits(impl)
raise "#{days_since_last_merge.floor} days since last merge, probably wrong commit"
end
+ puts "Checking if the last merge is consistent with upstream files"
+ rubyspec_commit = `git log -n 1 --format='%s' #{last_merge}`.chomp.split('@', 2)[-1]
+ sh "git", "checkout", last_merge
+ sh "git", "diff", "--exit-code", rubyspec_commit, "--", ":!.github"
+
puts "Rebasing..."
sh "git", "branch", "-D", rebased if branch?(rebased)
sh "git", "checkout", "-b", rebased, impl.name
@@ -157,46 +178,40 @@ def rebase_commits(impl)
end
end
+def new_commits?(impl)
+ Dir.chdir(SOURCE_REPO) do
+ diff = `git diff master #{impl.rebased_branch}`
+ !diff.empty?
+ end
+end
+
def test_new_specs
require "yaml"
Dir.chdir(SOURCE_REPO) do
- diff = `git diff master`
- abort "#{BRIGHT_YELLOW}No new commits, aborting#{RESET}" if diff.empty?
-
workflow = YAML.load_file(".github/workflows/ci.yml")
job_name = MSPEC ? "test" : "specs"
- versions = workflow.dig("jobs", job_name, "strategy", "matrix", "ruby")
+ versions = workflow.dig("jobs", job_name, "strategy", "matrix", "ruby").map(&:to_s)
versions = versions.grep(/^\d+\./) # Test on MRI
min_version, max_version = versions.minmax
- test_command = MSPEC ? "bundle exec rspec" : "../mspec/bin/mspec -j"
+ test_command = MSPEC ? "bundle install && bundle exec rspec" : "../mspec/bin/mspec -j"
run_test = -> version {
- command = "chruby #{version} && #{test_command}"
+ command = "chruby ruby-#{version} && #{test_command}"
sh ENV["SHELL"], "-c", command
}
run_test[min_version]
run_test[max_version]
- run_test["ruby-master"] if TEST_MASTER
- end
-end
-
-def verify_commits(impl)
- puts
- Dir.chdir(SOURCE_REPO) do
- puts "Manually check commit messages:"
- print "Press enter >"
- STDIN.gets
- system "git", "log", "master..."
+ run_test["master"] if TEST_MASTER
end
end
def fast_forward_master(impl)
Dir.chdir(SOURCE_REPO) do
sh "git", "checkout", "master"
- sh "git", "merge", "--ff-only", "#{impl.name}-rebased"
- sh "git", "branch", "--delete", "#{impl.name}-rebased"
+ sh "git", "merge", "--ff-only", impl.rebased_branch
+ sh "git", "branch", "--delete", impl.rebased_branch
end
end
@@ -214,11 +229,17 @@ def main(impls)
impl = RubyImplementation.new(impl, data)
update_repo(impl)
filter_commits(impl)
- rebase_commits(impl)
- test_new_specs
- verify_commits(impl)
- fast_forward_master(impl)
- check_ci
+ unless ONLY_FILTER
+ rebase_commits(impl)
+ if new_commits?(impl)
+ test_new_specs
+ fast_forward_master(impl)
+ check_ci
+ else
+ STDERR.puts "#{BRIGHT_YELLOW}No new commits#{RESET}"
+ fast_forward_master(impl)
+ end
+ end
end
end
diff --git a/spec/mspec/tool/tag_from_output.rb b/spec/mspec/tool/tag_from_output.rb
index fba52ec26c..41aa70f932 100755
--- a/spec/mspec/tool/tag_from_output.rb
+++ b/spec/mspec/tool/tag_from_output.rb
@@ -3,6 +3,8 @@
# Adds tags based on error and failures output (e.g., from a CI log),
# without running any spec code.
+tag = ENV["TAG"] || "fails"
+
tags_dir = %w[
spec/tags
spec/tags/ruby
@@ -11,21 +13,40 @@ abort 'Could not find tags directory' unless tags_dir
output = ARGF.readlines
+# Automatically strip datetime of GitHub Actions
+if output.first =~ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z /
+ output = output.map { |line| line.split(' ', 2).last }
+end
+
NUMBER = /^\d+\)$/
ERROR_OR_FAILED = / (ERROR|FAILED)$/
-SPEC_FILE = /^(\/.+_spec\.rb)\:\d+/
+SPEC_FILE = /^((?:\/|[CD]:\/).+_spec\.rb)\:\d+/
-output.slice_before(NUMBER).select { |number, error_line, *rest|
- number =~ NUMBER and error_line =~ ERROR_OR_FAILED
-}.each { |number, error_line, *rest|
+output.slice_before(NUMBER).select { |number, *rest|
+ number =~ NUMBER and rest.any? { |line| line =~ ERROR_OR_FAILED }
+}.each { |number, *rest|
+ error_line = rest.find { |line| line =~ ERROR_OR_FAILED }
description = error_line.match(ERROR_OR_FAILED).pre_match
spec_file = rest.find { |line| line =~ SPEC_FILE }
- unless spec_file
- warn "Could not find file for:\n#{error_line}"
- next
+ if spec_file
+ spec_file = spec_file[SPEC_FILE, 1] or raise
+ else
+ if error_line =~ /^([\w:]+)[#\.](\w+) /
+ mod, method = $1, $2
+ file = "#{mod.downcase.gsub('::', '/')}/#{method}_spec.rb"
+ spec_file = ['spec/ruby/core', 'spec/ruby/library', *Dir.glob('spec/ruby/library/*')].find { |dir|
+ path = "#{dir}/#{file}"
+ break path if File.exist?(path)
+ }
+ end
+
+ unless spec_file
+ warn "Could not find file for:\n#{error_line}"
+ next
+ end
end
- spec_file = spec_file[SPEC_FILE, 1]
+
prefix = spec_file.index('spec/ruby/') || spec_file.index('spec/truffle/')
spec_file = spec_file[prefix..-1]
@@ -35,7 +56,7 @@ output.slice_before(NUMBER).select { |number, error_line, *rest|
dir = File.dirname(tags_file)
Dir.mkdir(dir) unless Dir.exist?(dir)
- tag_line = "fails:#{description}"
+ tag_line = "#{tag}:#{description}"
lines = File.exist?(tags_file) ? File.readlines(tags_file, chomp: true) : []
unless lines.include?(tag_line)
puts tags_file
diff --git a/spec/mspec/tool/wrap_with_guard.rb b/spec/mspec/tool/wrap_with_guard.rb
new file mode 100755
index 0000000000..5b1bf4d7f7
--- /dev/null
+++ b/spec/mspec/tool/wrap_with_guard.rb
@@ -0,0 +1,28 @@
+#!/usr/bin/env ruby
+# Wrap the passed the files with a guard (e.g., `ruby_version_is ""..."3.0"`).
+# Notably if some methods are removed, this is a convenient way to skip such file from a given version.
+# Example usage:
+# $ spec/mspec/tool/wrap_with_guard.rb 'ruby_version_is ""..."3.0"' spec/ruby/library/set/sortedset/**/*_spec.rb
+
+guard, *files = ARGV
+abort "Usage: #{$0} GUARD FILES..." if files.empty?
+
+files.each do |file|
+ contents = File.binread(file)
+ lines = contents.lines.to_a
+
+ lines = lines.map { |line| line.chomp.empty? ? line : " #{line}" }
+
+ version_line = "#{guard} do\n"
+ if lines[0] =~ /^\s*require.+spec_helper/
+ lines[0] = lines[0].sub(/^ /, '')
+ lines.insert 1, "\n", version_line
+ else
+ warn "Could not find 'require spec_helper' line in #{file}"
+ lines.insert 0, version_line
+ end
+
+ lines << "end\n"
+
+ File.binwrite file, lines.join
+end
diff --git a/spec/ruby/.mspec.constants b/spec/ruby/.mspec.constants
index 3e3bdbef8a..4da3633715 100644
--- a/spec/ruby/.mspec.constants
+++ b/spec/ruby/.mspec.constants
@@ -39,8 +39,11 @@ CodingUS_ASCII
CodingUTF_8
ComparisonTest
ConstantSpecsIncludedModule
+ConstantSpecsTwo
+ConstantSpecsThree
ConstantVisibility
Coverage
+CoverageSpecs
CustomArgumentError
DRb
DRbIdConv
@@ -72,6 +75,7 @@ EvalBindingProcA
Exception2MessageMapper
ExceptionForMatrix
Fcntl
+Fiddle
FileStat
FileUtils
Find
@@ -142,6 +146,7 @@ Prime
Private
ProcFromMethod
Psych
+RactorLocalSingleton
REXML
RUBY_SIGNALS
RbReadline
@@ -200,6 +205,7 @@ UserArray
UserCustomConstructorString
UserDefined
UserDefinedImmediate
+UserDefinedString
UserDefinedWithIvar
UserHash
UserHashInitParams
diff --git a/spec/ruby/.rubocop.yml b/spec/ruby/.rubocop.yml
index 5db9256572..0b5dcb80a2 100644
--- a/spec/ruby/.rubocop.yml
+++ b/spec/ruby/.rubocop.yml
@@ -1,11 +1,16 @@
inherit_from: .rubocop_todo.yml
AllCops:
- TargetRubyVersion: 2.5
+ TargetRubyVersion: 3.2
DisplayCopNames: true
Exclude:
- command_line/fixtures/bad_syntax.rb
+ - core/exception/fixtures/syntax_error.rb
DisabledByDefault: true
+ NewCops: disable
+
+Layout/IndentationConsistency:
+ Enabled: true
Layout/TrailingWhitespace:
Enabled: true
@@ -32,18 +37,29 @@ Lint/AssignmentInCondition:
Lint/BooleanSymbol:
Enabled: false
+Lint/DeprecatedOpenSSLConstant:
+ Exclude:
+ - library/openssl/digest/**/*.rb
+
Lint/InterpolationCheck:
Enabled: false
Lint/LiteralAsCondition:
Enabled: false
+# Required to support Ruby 3.0
Lint/RedundantRequireStatement:
- Enabled: false
+ Exclude:
+ - core/fiber/**/*.rb
+ - library/fiber/**/*.rb
+ - optional/capi/fiber_spec.rb
Lint/RedundantSplatExpansion:
Enabled: false
+Lint/RescueException:
+ Enabled: false
+
Lint/UnifiedInteger:
Enabled: false
@@ -56,12 +72,58 @@ Lint/UnusedMethodArgument:
Lint/UselessAssignment:
Enabled: false
-Lint/UselessComparison:
+Lint/BinaryOperatorWithIdenticalOperands:
Enabled: false
+Lint/EmptyConditionalBody:
+ Enabled: false # buggy
+
Lint/Void:
Enabled: false
+Lint/ConstantDefinitionInBlock:
+ Enabled: false
+
+Lint/RaiseException:
+ Enabled: false
+
+Lint/FloatComparison:
+ Enabled: false
+
+Lint/DeprecatedClassMethods:
+ Enabled: false
+
+Lint/UnreachableLoop:
+ Enabled: false
+
+Lint/MissingSuper:
+ Enabled: false
+
+Lint/UselessMethodDefinition:
+ Enabled: false
+
+Lint/UselessTimes:
+ Enabled: false
+
+Lint/MixedRegexpCaptureTypes:
+ Enabled: false
+
+Lint/DuplicateElsifCondition:
+ Enabled: false
+
+Lint/OutOfRangeRegexpRef:
+ Enabled: false
+
+Lint/InheritException:
+ Enabled: false
+
+Lint/SafeNavigationChain:
+ Enabled: false
+
+Lint/ElseLayout:
+ Exclude:
+ - 'language/if_spec.rb'
+
Lint/EmptyExpression:
Exclude:
- 'language/**/*.rb'
@@ -71,6 +133,10 @@ Lint/EmptyWhen:
- language/case_spec.rb
- optional/capi/spec_helper.rb
+Lint/ErbNewArguments:
+ Exclude:
+ - 'library/erb/new_spec.rb'
+
Lint/FormatParameterMismatch:
Exclude:
- 'core/kernel/shared/sprintf.rb'
@@ -95,6 +161,7 @@ Lint/UnreachableCode:
- 'core/kernel/raise_spec.rb'
- 'core/kernel/throw_spec.rb'
- 'language/break_spec.rb'
+ - 'language/optional_assignments_spec.rb'
- 'language/fixtures/break.rb'
- 'language/fixtures/break_lambda_toplevel.rb'
- 'language/fixtures/break_lambda_toplevel_block.rb'
@@ -113,6 +180,12 @@ Lint/Debugger:
Exclude:
- 'core/binding/fixtures/irb.rb'
+Lint/Loop:
+ Enabled: false
+
+Style/BlockComments:
+ Enabled: true
+
Style/Lambda:
Enabled: true
EnforcedStyle: literal
@@ -120,6 +193,7 @@ Style/Lambda:
- 'language/lambda_spec.rb'
- 'language/proc_spec.rb'
- 'language/numbered_parameters_spec.rb'
+ - 'language/it_parameter_spec.rb'
- 'core/kernel/lambda_spec.rb'
Style/EmptyLambdaParameter:
diff --git a/spec/ruby/.rubocop_todo.yml b/spec/ruby/.rubocop_todo.yml
index 7a5f9f74c5..f998002c6d 100644
--- a/spec/ruby/.rubocop_todo.yml
+++ b/spec/ruby/.rubocop_todo.yml
@@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
-# on 2019-12-12 22:16:26 +0900 using RuboCop version 0.77.0.
+# on 2026-05-29 08:10:07 UTC using RuboCop version 1.86.2.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
@@ -11,14 +11,20 @@ Lint/DuplicateCaseCondition:
Exclude:
- 'language/case_spec.rb'
-# Offense count: 6
+# Offense count: 20
Lint/DuplicateMethods:
Exclude:
- 'core/array/fixtures/encoded_strings.rb'
- 'core/method/fixtures/classes.rb'
+ - 'core/module/const_added_spec.rb'
+ - 'core/module/define_method_spec.rb'
- 'core/module/fixtures/classes.rb'
+ - 'core/module/method_added_spec.rb'
+ - 'core/module/name_spec.rb'
+ - 'core/proc/new_spec.rb'
- 'core/unboundmethod/fixtures/classes.rb'
- 'fixtures/class.rb'
+ - 'language/assignments_spec.rb'
# Offense count: 8
Lint/EnsureReturn:
@@ -38,9 +44,11 @@ Lint/FloatOutOfRange:
Exclude:
- 'core/string/modulo_spec.rb'
-# Offense count: 2
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
Lint/ImplicitStringConcatenation:
Exclude:
+ - 'core/string/chilled_string_spec.rb'
- 'language/string_spec.rb'
# Offense count: 4
@@ -50,23 +58,11 @@ Lint/IneffectiveAccessModifier:
- 'core/module/fixtures/classes.rb'
- 'language/fixtures/private.rb'
-# Offense count: 6
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: runtime_error, standard_error
-Lint/InheritException:
- Exclude:
- - 'core/enumerator/lazy/fixtures/classes.rb'
- - 'core/exception/fixtures/common.rb'
- - 'core/module/fixtures/autoload_ex1.rb'
- - 'shared/kernel/raise.rb'
-
-# Offense count: 72
-# Cop supports --auto-correct.
+# Offense count: 12
+# This cop supports safe autocorrection (--autocorrect).
Lint/LiteralInInterpolation:
Exclude:
- 'core/module/refine_spec.rb'
- - 'core/regexp/shared/new.rb'
- 'core/string/shared/to_sym.rb'
- 'language/alias_spec.rb'
- 'language/defined_spec.rb'
@@ -74,55 +70,41 @@ Lint/LiteralInInterpolation:
- 'language/string_spec.rb'
- 'language/symbol_spec.rb'
- 'language/undef_spec.rb'
- - 'library/net/ftp/connect_spec.rb'
-
-# Offense count: 16
-Lint/Loop:
- Exclude:
- - 'language/until_spec.rb'
- - 'language/while_spec.rb'
# Offense count: 8
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
Lint/MultipleComparison:
Exclude:
- 'language/precedence_spec.rb'
# Offense count: 9
+# This cop supports safe autocorrection (--autocorrect).
Lint/ParenthesesAsGroupedExpression:
Exclude:
- - 'core/string/fixtures/freeze_magic_comment.rb'
- 'language/block_spec.rb'
- - 'language/fixtures/send.rb'
- 'language/method_spec.rb'
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AllowedMethods, InferNonNilReceiver, AdditionalNilMethods.
+# AllowedMethods: instance_of?, kind_of?, is_a?, eql?, respond_to?, equal?
+# AdditionalNilMethods: present?, blank?, try, try!
+Lint/RedundantSafeNavigation:
+ Exclude:
+ - 'language/safe_navigator_spec.rb'
+ - 'language/fixtures/rescue_captures.rb'
+
# Offense count: 2
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
Lint/RedundantStringCoercion:
Exclude:
- 'core/io/print_spec.rb'
# Offense count: 1
-# Cop supports --auto-correct.
-Lint/RedundantWithIndex:
- Exclude:
- - 'core/enumerator/with_index_spec.rb'
-
-# Offense count: 22
-Lint/RescueException:
- Exclude:
- - 'command_line/fixtures/debug_info.rb'
- - 'core/dir/fileno_spec.rb'
- - 'core/exception/cause_spec.rb'
- - 'core/exception/no_method_error_spec.rb'
- - 'core/kernel/fixtures/autoload_frozen.rb'
- - 'core/kernel/raise_spec.rb'
- - 'core/module/autoload_spec.rb'
- - 'core/mutex/sleep_spec.rb'
- - 'core/thread/abort_on_exception_spec.rb'
- - 'core/thread/shared/exit.rb'
- - 'language/rescue_spec.rb'
- - 'library/erb/filename_spec.rb'
+# Configuration parameters: AllowRBSInlineAnnotation.
+Lint/SelfAssignment:
+ Exclude:
+ - 'core/gc/auto_compact_spec.rb'
# Offense count: 4
# Configuration parameters: IgnoreImplicitReferences.
@@ -130,8 +112,8 @@ Lint/ShadowedArgument:
Exclude:
- 'language/fixtures/super.rb'
-# Offense count: 39
-# Configuration parameters: AllowComments.
+# Offense count: 49
+# Configuration parameters: AllowComments, AllowNil.
Lint/SuppressedException:
Enabled: false
@@ -143,7 +125,8 @@ Lint/UnderscorePrefixedVariableName:
- 'core/io/popen_spec.rb'
- 'language/block_spec.rb'
-# Offense count: 7
+# Offense count: 9
+# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods.
Lint/UselessAccessModifier:
Exclude:
@@ -151,4 +134,5 @@ Lint/UselessAccessModifier:
- 'core/module/fixtures/classes.rb'
- 'core/module/module_function_spec.rb'
- 'core/module/private_class_method_spec.rb'
+ - 'language/fixtures/def.rb'
- 'language/fixtures/send.rb'
diff --git a/spec/ruby/CONTRIBUTING.md b/spec/ruby/CONTRIBUTING.md
index 9a2a341ae7..a474e205f0 100644
--- a/spec/ruby/CONTRIBUTING.md
+++ b/spec/ruby/CONTRIBUTING.md
@@ -8,17 +8,19 @@ Spec are grouped in 5 separate top-level groups:
* `command_line`: for the ruby executable command-line flags (`-v`, `-e`, etc)
* `language`: for the language keywords and syntax constructs (`if`, `def`, `A::B`, etc)
-* `core`: for the core methods (`Fixnum#+`, `String#upcase`, no need to require anything)
+* `core`: for the core methods (`Integer#+`, `String#upcase`, no need to require anything)
* `library`: for the standard libraries methods (`CSV.new`, `YAML.parse`, need to require the stdlib)
* `optional/capi`: for functions available to the Ruby C-extension API
The exact file for methods is decided by the `#owner` of a method, for instance for `#group_by`:
+
```ruby
> [].method(:group_by)
=> #<Method: Array(Enumerable)#group_by>
> [].method(:group_by).owner
=> Enumerable
```
+
Which should therefore be specified in `core/enumerable/group_by_spec.rb`.
### MkSpec - a tool to generate the spec structure
@@ -53,6 +55,11 @@ which indicates the file was generated but the method unspecified.
Here is a list of frequently-used matchers, which should be enough for most specs.
There are a few extra specific matchers used in the couple specs that need it.
+The general idea is: add `.should` just before the predicate you expect to be truthy, and done!
+This works for most needs and provides a great error when it fails.
+It's immediately clear which method is used and there no need to remember a mapping like in RSpec between e.g. `eq` and `==`.
+See [this blog post](https://eregon.me/blog/2019/10/07/a-new-should-syntax.html) for the motivation behind that syntax.
+
#### Comparison matchers
```ruby
@@ -81,43 +88,37 @@ File.should.equal?(File) # Calls #equal? (tests identity)
(0.1 + 0.2).should be_close(0.3, TOLERANCE) # (0.2-0.1).abs < TOLERANCE
(0.0/0.0).should.nan?
-(1.0/0.0).should be_positive_infinity
-(-1.0/0.0).should be_negative_infinity
-3.14.should be_an_instance_of(Float) # Calls #instance_of?
-3.14.should be_kind_of(Numeric) # Calls #is_a?
-Numeric.should be_ancestor_of(Float) # Float.ancestors.include?(Numeric)
+3.14.should.instance_of?(Float) # Calls #instance_of?
+3.14.should.is_a?(Numeric) # Calls #is_a?
3.14.should.respond_to?(:to_i)
-Fixnum.should have_instance_method(:+)
-Array.should have_method(:new)
+Integer.should.method_defined?(:+, false)
```
-Also `have_constant`, `have_private_instance_method`, `have_singleton_method`, etc.
-
#### Exception matchers
```ruby
-> {
raise "oops"
-}.should raise_error(RuntimeError, /oops/)
+}.should.raise(RuntimeError, /oops/)
-> {
raise "oops"
-}.should raise_error(RuntimeError) { |e|
+}.should.raise(RuntimeError) { |e|
# Custom checks on the Exception object
e.message.should.include?("oops")
e.cause.should == nil
}
```
-##### should_not raise_error
+##### `should_not.raise`
-**To avoid!** Instead, use an expectation testing what the code in the lambda does.
+**Avoid this!** Instead, use an expectation testing what the code in the lambda does.
If an exception is raised, it will fail the example anyway.
```ruby
--> { ... }.should_not raise_error
+-> { ... }.should_not.raise
```
#### Warning matcher
@@ -136,12 +137,12 @@ Here is a list of the most commonly-used guards:
#### Version guards
```ruby
-ruby_version_is ""..."2.6" do
- # Specs for RUBY_VERSION < 2.6
+ruby_version_is ""..."3.2" do
+ # Specs for RUBY_VERSION < 3.2
end
-ruby_version_is "2.6" do
- # Specs for RUBY_VERSION >= 2.6
+ruby_version_is "3.2" do
+ # Specs for RUBY_VERSION >= 3.2
end
```
@@ -162,7 +163,7 @@ end
platform_is_not :linux, :darwin do # Not Linux and not Darwin
end
-platform_is wordsize: 64 do
+platform_is pointer_size: 64 do
# 64-bit platform
end
@@ -173,12 +174,16 @@ end
#### Guard for bug
-In case there is a bug in MRI but the expected behavior is obvious.
+In case there is a bug in MRI and the fix will be backported to previous versions.
+If it is not backported or not likely, use `ruby_version_is` instead.
First, file a bug at https://bugs.ruby-lang.org/.
-It is better to use a `ruby_version_is` guard if there was a release with the fix.
+The problem is `ruby_bug` would make non-MRI implementations fail this spec while MRI itself does not pass it, so it should only be used if the bug is/will be fixed and backported.
+Otherwise, non-MRI implementations would have to choose between being incompatible with the latest release of MRI (which has the bug) to pass the spec, or behave the same as the latest release of MRI (which has the bug) and fail the spec, both which make no sense.
+
+IOW, `ruby_bug '#NN', ''...'X.Y' do` is equivalent to `guard_not { RUBY_ENGINE == "ruby" && ruby_version_is ''...'X.Y' } do`. So it skips tests on MRI on specified versions (where a bug is present) and runs tests on alternative implementations only.
```ruby
-ruby_bug '#13669', ''...'2.5' do
+ruby_bug '#13669', ''...'3.2' do
it "works like this" do
# Specify the expected behavior here, not the bug
end
@@ -188,11 +193,11 @@ end
#### Combining guards
```ruby
-guard -> { platform_is :windows and ruby_version_is ""..."2.6" } do
- # Windows and RUBY_VERSION < 2.6
+guard -> { platform_is :windows and ruby_version_is ""..."3.2" } do
+ # Windows and RUBY_VERSION < 3.2
end
-guard_not -> { platform_is :windows and ruby_version_is ""..."2.6" } do
+guard_not -> { platform_is :windows and ruby_version_is ""..."3.2" } do
# The opposite
end
```
@@ -220,11 +225,11 @@ If an implementation does not support some feature, simply tag the related specs
### Shared Specs
Often throughout Ruby, identical functionality is used by different methods and modules. In order
-to avoid duplication of specs, we have shared specs that are re-used in other specs. The use is a
+to avoid duplication of specs, we have shared specs that are re-used in other specs. The use is a
bit tricky however, so let's go over it.
Commonly, if a shared spec is only reused within its own module, the shared spec will live within a
-shared directory inside that module's directory. For example, the `core/hash/shared/key.rb` spec is
+shared directory inside that module's directory. For example, the `core/hash/shared/iteration.rb` spec is
only used by `Hash` specs, and so it lives inside `core/hash/shared/`.
When a shared spec is used across multiple modules or classes, it lives within the `shared/` directory.
@@ -232,54 +237,54 @@ An example of this is the `shared/file/socket.rb` which is used by `core/file/so
`core/filetest/socket_spec.rb`, and `core/file/state/socket_spec.rb` and so it lives in the root `shared/`.
Defining a shared spec involves adding a `shared: true` option to the top-level `describe` block. This
-will signal not to run the specs directly by the runner. Shared specs have access to two instance
+will signal not to run the specs directly by the runner. Shared specs have access to two instance
variables from the implementor spec: `@method` and `@object`, which the implementor spec will pass in.
Here's an example of a snippet of a shared spec and two specs which integrates it:
```ruby
-# core/hash/shared/key.rb
-describe :hash_key_p, shared: true do
- it "returns true if the key's matching value was false" do
- { xyz: false }.send(@method, :xyz).should == true
+# core/hash/shared/iteration.rb
+describe :hash_iteration_no_block, shared: true do
+ it "returns an Enumerator if called on a non-empty hash without a block" do
+ { 1 => 2 }.send(@method).should.instance_of?(Enumerator)
end
end
-# core/hash/key_spec.rb
-describe "Hash#key?" do
- it_behaves_like :hash_key_p, :key?
+# core/hash/select_spec.rb
+describe "Hash#select" do
+ it_behaves_like :hash_iteration_no_block, :select
end
-# core/hash/include_spec.rb
-describe "Hash#include?" do
- it_behaves_like :hash_key_p, :include?
+# core/hash/reject_spec.rb
+describe "Hash#reject" do
+ it_behaves_like :hash_iteration_no_block, :reject
end
```
-In the example, the first `describe` defines the shared spec `:hash_key_p`, which defines a spec that
-calls the `@method` method with an expectation. In the implementor spec, we use `it_behaves_like` to
-integrate the shared spec. `it_behaves_like` takes 3 parameters: the key of the shared spec, a method,
-and an object. These last two parameters are accessible via `@method` and `@object` in the shared spec.
+In the example, the first `describe` defines the shared spec `:hash_iteration_no_block`, which defines a spec that
+calls the `@method` method with an expectation. In the implementor spec, we use `it_behaves_like` to
+integrate the shared spec. `it_behaves_like` takes 3 parameters: the key of the shared spec, a method,
+and an object. These last two parameters are accessible via `@method` and `@object` in the shared spec.
Sometimes, shared specs require more context from the implementor class than a simple object. We can address
-this by passing a lambda as the method, which will have the scope of the implementor. Here's an example of
+this by passing a lambda as the method, which will have the scope of the implementor. Here's an example of
how this is used currently:
```ruby
describe :kernel_sprintf, shared: true do
it "raises TypeError exception if cannot convert to Integer" do
- -> { @method.call("%b", Object.new) }.should raise_error(TypeError)
+ -> { @method.call("%b", Object.new) }.should.raise(TypeError)
end
end
describe "Kernel#sprintf" do
- it_behaves_like :kernel_sprintf, -> (format, *args) {
+ it_behaves_like :kernel_sprintf, -> format, *args {
sprintf(format, *args)
}
end
describe "Kernel.sprintf" do
- it_behaves_like :kernel_sprintf, -> (format, *args) {
+ it_behaves_like :kernel_sprintf, -> format, *args {
Kernel.sprintf(format, *args)
}
end
diff --git a/spec/ruby/README.md b/spec/ruby/README.md
index df3b991b81..b259a97e21 100644
--- a/spec/ruby/README.md
+++ b/spec/ruby/README.md
@@ -1,10 +1,11 @@
# The Ruby Spec Suite
[![Actions Build Status](https://github.com/ruby/spec/workflows/CI/badge.svg)](https://github.com/ruby/spec/actions)
-[![Gitter](https://badges.gitter.im/ruby/spec.svg)](https://gitter.im/ruby/spec)
The Ruby Spec Suite, abbreviated `ruby/spec`, is a test suite for the behavior of the Ruby programming language.
+### Description and Motivation
+
It is not a standardized specification like the ISO one, and does not aim to become one.
Instead, it is a practical tool to describe and test the behavior of Ruby with code.
@@ -16,40 +17,54 @@ Every example code has a textual description, which presents several advantages:
The specs are written with syntax similar to RSpec 2.
They are run with MSpec, the purpose-built framework for running the Ruby Spec Suite.
-For more information, see the [MSpec](http://github.com/ruby/mspec) project.
+For more information, see the [MSpec](https://github.com/ruby/mspec) project.
The specs describe the [language syntax](language/), the [core library](core/), the [standard library](library/), the [C API for extensions](optional/capi) and the [command line flags](command_line/).
The language specs are grouped by keyword while the core and standard library specs are grouped by class and method.
ruby/spec is known to be tested in these implementations for every commit:
-* [MRI](http://rubyci.org/) on 30 platforms and 4 versions
+
+* [MRI](https://rubyci.org/) on 30 platforms and 4 versions
* [JRuby](https://github.com/jruby/jruby/tree/master/spec/ruby) for both 1.7 and 9.x
-* [TruffleRuby](https://github.com/oracle/truffleruby/tree/master/spec/ruby)
+* [TruffleRuby](https://github.com/truffleruby/truffleruby/tree/master/spec/ruby)
* [Opal](https://github.com/opal/opal/tree/master/spec)
+* [Artichoke](https://github.com/artichoke/spec/tree/artichoke-vendor)
-ruby/spec describes the behavior of Ruby 2.5 and more recent Ruby versions.
-More precisely, every latest stable MRI release should [pass](https://travis-ci.org/ruby/spec) all specs of ruby/spec (2.5.x, 2.6.x, 2.7.x, etc), and those are tested in TravisCI.
+ruby/spec describes the behavior of Ruby 3.3 and more recent Ruby versions.
+More precisely, every latest stable MRI release should [pass](https://github.com/ruby/spec/actions/workflows/ci.yml) all specs of ruby/spec (3.3.x, 3.4.x, etc), and those are tested in CI.
-The specs are synchronized both ways around once a month by @eregon between ruby/spec, MRI, JRuby and TruffleRuby.
+### Synchronization with Ruby Implementations
+
+The specs are synchronized both ways around once a month by @andrykonchin between ruby/spec, MRI, JRuby and TruffleRuby,
+using [this script](https://github.com/ruby/mspec/blob/master/tool/sync/sync-rubyspec.rb).
Each of these repositories has a full copy of the specs under `spec/ruby` to ease editing specs.
Any of these repositories can be used to add or edit specs, use what is most convenient for you.
-For *testing* a Ruby implementation, one should always test against the implementation's copy of the specs under `spec/ruby`, as that's what the Ruby implementation tests against in their CI.
+For *testing* the development version of a Ruby implementation, one should always test against that implementation's copy of the specs under `spec/ruby`, as that's what the Ruby implementation tests against in their CI.
Also, this repository doesn't always contain the latest spec changes from MRI (it's synchronized monthly), and does not contain tags (specs marked as failing on that Ruby implementation).
-Running specs in a Ruby implementation can be done with:
+Running specs on a Ruby implementation can be done with:
-```
+```console
$ cd ruby_implementation/spec/ruby
# Add ../ruby_implementation/bin in PATH, or pass -t /path/to/bin/ruby
$ ../mspec/bin/mspec
```
+### Specs for old Ruby versions
+
For older specs try these commits:
+
* Ruby 2.0.0-p647 - [Suite](https://github.com/ruby/spec/commit/245862558761d5abc676843ef74f86c9bcc8ea8d) using [MSpec](https://github.com/ruby/mspec/commit/f90efa068791064f955de7a843e96e2d7d3041c2) (may encounter 2 failures)
* Ruby 2.1.9 - [Suite](https://github.com/ruby/spec/commit/f029e65241374386077ac500add557ae65069b55) using [MSpec](https://github.com/ruby/mspec/commit/55568ea3918c6380e64db8c567d732fa5781efed)
* Ruby 2.2.10 - [Suite](https://github.com/ruby/spec/commit/cbaa0e412270c944df0c2532fc500c920dba0e92) using [MSpec](https://github.com/ruby/mspec/commit/d84d7668449e96856c5f6bac8cb1526b6d357ce3)
* Ruby 2.3.8 - [Suite](https://github.com/ruby/spec/commit/dc733114d8ae66a3368ba3a98422c50147a76ba5) using [MSpec](https://github.com/ruby/mspec/commit/4599bc195fb109f2a482a01c32a7d659518369ea)
* Ruby 2.4.10 - [Suite](https://github.com/ruby/spec/commit/bce4f2b81d6c31db67cf4d023a0625ceadde59bd) using [MSpec](https://github.com/ruby/mspec/commit/e7eb8aa4c26495b7b461e687d950b96eb08b3ff2)
+* Ruby 2.5.9 - [Suite](https://github.com/ruby/spec/commit/c503335d3d9f6ec6ef24de60a0716c34af69b64f) using [MSpec](https://github.com/ruby/mspec/commit/0091e8a62e954717cd54641f935eaf1403692041)
+* Ruby 2.6.10 - [Suite](https://github.com/ruby/spec/commit/aaf998fb8c92c4e63ad423a2e7ca6e6921818c6e) using [MSpec](https://github.com/ruby/mspec/commit/5e36c684e9e2b92b1187589bba1df22c640a8661)
+* Ruby 2.7.8 - [Suite](https://github.com/ruby/spec/commit/93787e6035c925b593a9c0c6fb0e7e07a6f1df1f) using [MSpec](https://github.com/ruby/mspec/commit/1d8cf64722d8a7529f7cd205be5f16a89b7a67fd)
+* Ruby 3.0.7 - [Suite](https://github.com/ruby/spec/commit/affef93d9940f615e4836f64b011da211f570913) using [MSpec](https://github.com/ruby/mspec/commit/0aabb3e548eb5ea6cad0125f8f46cee34542b6b7)
+* Ruby 3.1.6 - [Suite](https://github.com/ruby/spec/commit/ec960f2389d1c2265d32397fa8afa6d462014efc) using [MSpec](https://github.com/ruby/mspec/commit/484310dbed35b84c74484fd674602f88c42d063a)
+* Ruby 3.2.9 - [Suite](https://github.com/ruby/spec/commit/97f076242b7fc6e60703e6a6053365065cd6fc30) using [MSpec](https://github.com/ruby/mspec/commit/54704795e21128a930af2021c72c49cb87065134)
### Running the specs
@@ -61,7 +76,7 @@ Then move to it:
$ cd spec
-Clone [MSpec](http://github.com/ruby/mspec):
+Clone [MSpec](https://github.com/ruby/mspec):
$ git clone https://github.com/ruby/mspec.git ../mspec
@@ -116,16 +131,31 @@ MSpec can automatically add new top-level constants in this file with:
$ CHECK_LEAKS=save mspec ../mspec/bin/mspec file
+### Running Specs on S390x CPU Architecture
+
+Run the specs with `DFLTCC=0` if you see failing specs related to the zlib library on s390x CPU architecture. The failures can happen with the zlib library applying the patch madler/zlib#410 to enable the deflate algorithm producing a different compressed byte stream.
+
+ $ DFLTCC=0 ../mspec/bin/mspec
+
### Contributing and Writing Specs
See [CONTRIBUTING.md](https://github.com/ruby/spec/blob/master/CONTRIBUTING.md) for documentation about contributing and writing specs (guards, matchers, etc).
+### Dependencies
+
+These command-line executables are needed to run the specs.
+
+* `echo`
+* `stat` for `core/file/*time_spec.rb`
+* `find` for `core/file/fixtures/file_types.rb` (package `findutils`, not needed on Windows)
+
+The file `/etc/services` is required for socket specs (package `netbase` on Debian, not needed on Windows).
+
### Socket specs from rubysl-socket
-Most specs under `library/socket` were imported from [the rubysl-socket project](https://github.com/rubysl/rubysl-socket).
+Most specs under `library/socket` were imported from the rubysl-socket project (which is no longer on GitHub).
The 3 copyright holders of rubysl-socket, Yorick Peterse, Chuck Remes and
-Brian Shirai, [agreed to relicense those specs](https://github.com/rubysl/rubysl-socket/issues/15)
-under the MIT license in ruby/spec.
+Brian Shirai, agreed to relicense those specs under the MIT license in ruby/spec.
### History and RubySpec
@@ -133,5 +163,5 @@ This project was originally born from [Rubinius](https://github.com/rubinius/rub
The revision history of these specs is available [here](https://github.com/ruby/spec/blob/2b886623/CHANGES.before-2008-05-10).
These specs were later extracted to their own project, RubySpec, with a specific vision and principles.
At the end of 2014, Brian Shirai, the creator of RubySpec, decided to [end RubySpec](http://rubinius.com/2014/12/31/matz-s-ruby-developers-don-t-use-rubyspec/).
-A couple months later, the different repositories were merged and [the project was revived](http://eregon.github.io/rubyspec/2015/07/29/rubyspec-is-reborn.html).
+A couple months later, the different repositories were merged and [the project was revived](https://eregon.github.io/rubyspec/2015/07/29/rubyspec-is-reborn.html).
On 12 January 2016, the name was changed to "The Ruby Spec Suite" for clarity and to let the RubySpec ideology rest in peace.
diff --git a/spec/ruby/bin/rubocop b/spec/ruby/bin/rubocop
new file mode 100755
index 0000000000..0937c47906
--- /dev/null
+++ b/spec/ruby/bin/rubocop
@@ -0,0 +1,12 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require 'bundler/inline'
+
+gemfile do
+ source 'https://rubygems.org'
+
+ gem 'rubocop', '1.86.2'
+end
+
+exec(Gem.bin_path('rubocop', 'rubocop'), *ARGV)
diff --git a/spec/ruby/command_line/backtrace_limit_spec.rb b/spec/ruby/command_line/backtrace_limit_spec.rb
new file mode 100644
index 0000000000..4d57bc268b
--- /dev/null
+++ b/spec/ruby/command_line/backtrace_limit_spec.rb
@@ -0,0 +1,93 @@
+require_relative '../spec_helper'
+
+describe "The --backtrace-limit command line option" do
+ ruby_version_is ""..."3.4" do
+ it "limits top-level backtraces to a given number of entries" do
+ file = fixture(__FILE__ , "backtrace.rb")
+ out = ruby_exe(file, options: "--backtrace-limit=2", args: "top 2>&1", exit_status: 1)
+ out = out.gsub(__dir__, '')
+
+ out.should == <<-MSG
+top
+/fixtures/backtrace.rb:2:in `a': oops (RuntimeError)
+\tfrom /fixtures/backtrace.rb:6:in `b'
+\tfrom /fixtures/backtrace.rb:10:in `c'
+\t ... 2 levels...
+ MSG
+ end
+
+ it "affects Exception#full_message" do
+ file = fixture(__FILE__ , "backtrace.rb")
+ out = ruby_exe(file, options: "--backtrace-limit=2", args: "full_message 2>&1")
+ out = out.gsub(__dir__, '')
+
+ out.should == <<-MSG
+full_message
+/fixtures/backtrace.rb:2:in `a': oops (RuntimeError)
+\tfrom /fixtures/backtrace.rb:6:in `b'
+\tfrom /fixtures/backtrace.rb:10:in `c'
+\t ... 2 levels...
+ MSG
+ end
+
+ it "does not affect Exception#backtrace" do
+ file = fixture(__FILE__ , "backtrace.rb")
+ out = ruby_exe(file, options: "--backtrace-limit=2", args: "backtrace 2>&1")
+ out = out.gsub(__dir__, '')
+
+ out.should == <<-MSG
+backtrace
+/fixtures/backtrace.rb:2:in `a'
+/fixtures/backtrace.rb:6:in `b'
+/fixtures/backtrace.rb:10:in `c'
+/fixtures/backtrace.rb:14:in `d'
+/fixtures/backtrace.rb:29:in `<main>'
+ MSG
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "limits top-level backtraces to a given number of entries" do
+ file = fixture(__FILE__ , "backtrace.rb")
+ out = ruby_exe(file, options: "--backtrace-limit=2", args: "top 2>&1", exit_status: 1)
+ out = out.gsub(__dir__, '')
+
+ out.should == <<-MSG
+top
+/fixtures/backtrace.rb:2:in 'Object#a': oops (RuntimeError)
+\tfrom /fixtures/backtrace.rb:6:in 'Object#b'
+\tfrom /fixtures/backtrace.rb:10:in 'Object#c'
+\t ... 2 levels...
+ MSG
+ end
+
+ it "affects Exception#full_message" do
+ file = fixture(__FILE__ , "backtrace.rb")
+ out = ruby_exe(file, options: "--backtrace-limit=2", args: "full_message 2>&1")
+ out = out.gsub(__dir__, '')
+
+ out.should == <<-MSG
+full_message
+/fixtures/backtrace.rb:2:in 'Object#a': oops (RuntimeError)
+\tfrom /fixtures/backtrace.rb:6:in 'Object#b'
+\tfrom /fixtures/backtrace.rb:10:in 'Object#c'
+\t ... 2 levels...
+ MSG
+ end
+
+ it "does not affect Exception#backtrace" do
+ file = fixture(__FILE__ , "backtrace.rb")
+ out = ruby_exe(file, options: "--backtrace-limit=2", args: "backtrace 2>&1")
+ out = out.gsub(__dir__, '')
+
+ out.should == <<-MSG
+backtrace
+/fixtures/backtrace.rb:2:in 'Object#a'
+/fixtures/backtrace.rb:6:in 'Object#b'
+/fixtures/backtrace.rb:10:in 'Object#c'
+/fixtures/backtrace.rb:14:in 'Object#d'
+/fixtures/backtrace.rb:29:in '<main>'
+ MSG
+ end
+ end
+end
diff --git a/spec/ruby/command_line/dash_0_spec.rb b/spec/ruby/command_line/dash_0_spec.rb
new file mode 100755
index 0000000000..2ce4f49b5e
--- /dev/null
+++ b/spec/ruby/command_line/dash_0_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../spec_helper'
+
+describe "The -0 command line option" do
+ it "sets $/ and $-0" do
+ ruby_exe("puts $/, $-0", options: "-072").should == ":\n:\n"
+ end
+
+ ruby_version_is "4.0" do
+ it "sets $/ and $-0 as a frozen string" do
+ ruby_exe("puts $/.frozen?, $-0.frozen?", options: "-072").should == "true\ntrue\n"
+ end
+ end
+end
diff --git a/spec/ruby/command_line/dash_a_spec.rb b/spec/ruby/command_line/dash_a_spec.rb
index 9ea135dc76..43d923ce16 100644
--- a/spec/ruby/command_line/dash_a_spec.rb
+++ b/spec/ruby/command_line/dash_a_spec.rb
@@ -6,13 +6,13 @@ describe "The -a command line option" do
end
it "runs the code in loop conditional on Kernel.gets()" do
- ruby_exe("puts $F.last", options: "-n -a", escape: true,
+ ruby_exe("puts $F.last", options: "-n -a",
args: " < #{@names}").should ==
"jones\nfield\ngrey\n"
end
it "sets $-a" do
- ruby_exe("puts $-a", options: "-n -a", escape: true,
+ ruby_exe("puts $-a", options: "-n -a",
args: " < #{@names}").should ==
"true\ntrue\ntrue\n"
end
diff --git a/spec/ruby/command_line/dash_e_spec.rb b/spec/ruby/command_line/dash_e_spec.rb
index 9f600eb414..24ed34376d 100644
--- a/spec/ruby/command_line/dash_e_spec.rb
+++ b/spec/ruby/command_line/dash_e_spec.rb
@@ -23,7 +23,7 @@ describe "The -e command line option" do
#needs to test return => LocalJumpError
- describe "with -n and a Fixnum range" do
+ describe "with -n and an Integer range" do
before :each do
@script = "-ne 'print if %s' #{fixture(__FILE__, "conditional_range.txt")}"
end
diff --git a/spec/ruby/command_line/dash_encoding_spec.rb b/spec/ruby/command_line/dash_encoding_spec.rb
index 36ce55af5f..5803d328c1 100644
--- a/spec/ruby/command_line/dash_encoding_spec.rb
+++ b/spec/ruby/command_line/dash_encoding_spec.rb
@@ -25,6 +25,12 @@ describe "The --encoding command line option" do
end
it "does not accept a third encoding" do
- ruby_exe(@test_string, options: "--disable-gems --encoding big5:#{@enc2}:utf-32le", args: "2>&1").should =~ /extra argument for --encoding: utf-32le/
+ options = {
+ options: "--disable-gems --encoding big5:#{@enc2}:utf-32le",
+ args: "2>&1",
+ exit_status: 1
+ }
+
+ ruby_exe(@test_string, options).should =~ /extra argument for --encoding: utf-32le/
end
end
diff --git a/spec/ruby/command_line/dash_l_spec.rb b/spec/ruby/command_line/dash_l_spec.rb
index 5c1d3cf4cd..44a98445f3 100644
--- a/spec/ruby/command_line/dash_l_spec.rb
+++ b/spec/ruby/command_line/dash_l_spec.rb
@@ -6,25 +6,25 @@ describe "The -l command line option" do
end
it "chomps lines with default separator" do
- ruby_exe('puts $_.end_with?("\n")', options: "-n -l", escape: true,
+ ruby_exe('puts $_.end_with?("\n")', options: "-n -l",
args: " < #{@names}").should ==
"false\nfalse\nfalse\n"
end
it "chomps last line based on $/" do
- ruby_exe('BEGIN { $/ = "ones\n" }; puts $_', options: "-W0 -n -l", escape: true,
+ ruby_exe('BEGIN { $/ = "ones\n" }; puts $_', options: "-W0 -n -l",
args: " < #{@names}").should ==
"alice j\nbob field\njames grey\n"
end
it "sets $\\ to the value of $/" do
- ruby_exe("puts $\\ == $/", options: "-W0 -n -l", escape: true,
+ ruby_exe("puts $\\ == $/", options: "-W0 -n -l",
args: " < #{@names}").should ==
"true\ntrue\ntrue\n"
end
it "sets $-l" do
- ruby_exe("puts $-l", options: "-n -l", escape: true,
+ ruby_exe("puts $-l", options: "-n -l",
args: " < #{@names}").should ==
"true\ntrue\ntrue\n"
end
diff --git a/spec/ruby/command_line/dash_n_spec.rb b/spec/ruby/command_line/dash_n_spec.rb
index 9d331d6065..1dd9379259 100644
--- a/spec/ruby/command_line/dash_n_spec.rb
+++ b/spec/ruby/command_line/dash_n_spec.rb
@@ -6,19 +6,19 @@ describe "The -n command line option" do
end
it "runs the code in loop conditional on Kernel.gets()" do
- ruby_exe("puts $_", options: "-n", escape: true,
+ ruby_exe("puts $_", options: "-n",
args: " < #{@names}").should ==
"alice\nbob\njames\n"
end
it "only evaluates BEGIN blocks once" do
- ruby_exe("BEGIN { puts \"hi\" }; puts $_", options: "-n", escape: true,
+ ruby_exe("BEGIN { puts \"hi\" }; puts $_", options: "-n",
args: " < #{@names}").should ==
"hi\nalice\nbob\njames\n"
end
it "only evaluates END blocks once" do
- ruby_exe("puts $_; END {puts \"bye\"}", options: "-n", escape: true,
+ ruby_exe("puts $_; END {puts \"bye\"}", options: "-n",
args: " < #{@names}").should ==
"alice\nbob\njames\nbye\n"
end
@@ -29,7 +29,7 @@ describe "The -n command line option" do
$total += 1
END { puts $total }
script
- ruby_exe(script, options: "-n", escape: true,
+ ruby_exe(script, options: "-n",
args: " < #{@names}").should ==
"3\n"
end
diff --git a/spec/ruby/command_line/dash_p_spec.rb b/spec/ruby/command_line/dash_p_spec.rb
index 39827c3868..967e3796de 100644
--- a/spec/ruby/command_line/dash_p_spec.rb
+++ b/spec/ruby/command_line/dash_p_spec.rb
@@ -6,13 +6,13 @@ describe "The -p command line option" do
end
it "runs the code in loop conditional on Kernel.gets() and prints $_" do
- ruby_exe("$_ = $_.upcase", options: "-p", escape: true,
+ ruby_exe("$_ = $_.upcase", options: "-p",
args: " < #{@names}").should ==
"ALICE\nBOB\nJAMES\n"
end
it "sets $-p" do
- ruby_exe("$_ = $-p", options: "-p", escape: true,
+ ruby_exe("$_ = $-p", options: "-p",
args: " < #{@names}").should ==
"truetruetrue"
end
diff --git a/spec/ruby/command_line/dash_r_spec.rb b/spec/ruby/command_line/dash_r_spec.rb
index 46c000b9e7..0de9ba2e24 100644
--- a/spec/ruby/command_line/dash_r_spec.rb
+++ b/spec/ruby/command_line/dash_r_spec.rb
@@ -8,15 +8,15 @@ describe "The -r command line option" do
it "requires the specified file" do
out = ruby_exe(@script, options: "-r #{@test_file}")
- out.should include("REQUIRED")
- out.should include(@test_file + ".rb")
+ out.should.include?("REQUIRED")
+ out.should.include?(@test_file + ".rb")
end
it "requires the file before parsing the main script" do
- out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), options: "-r #{@test_file}", args: "2>&1")
+ out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), options: "-r #{@test_file}", args: "2>&1", exit_status: 1)
$?.should_not.success?
- out.should include("REQUIRED")
- out.should include("syntax error")
+ out.should.include?("REQUIRED")
+ out.should.include?("SyntaxError")
end
it "does not require the file if the main script file does not exist" do
diff --git a/spec/ruby/command_line/dash_upper_e_spec.rb b/spec/ruby/command_line/dash_upper_e_spec.rb
index b3c6ce262b..5a83962583 100644
--- a/spec/ruby/command_line/dash_upper_e_spec.rb
+++ b/spec/ruby/command_line/dash_upper_e_spec.rb
@@ -31,6 +31,7 @@ describe "ruby -E" do
it "raises a RuntimeError if used with -U" do
ruby_exe("p 1",
options: '-Eascii:ascii -U',
- args: '2>&1').should =~ /RuntimeError/
+ args: '2>&1',
+ exit_status: 1).should =~ /RuntimeError/
end
end
diff --git a/spec/ruby/command_line/dash_upper_f_spec.rb b/spec/ruby/command_line/dash_upper_f_spec.rb
index 967acc2ece..5c10a7140d 100644
--- a/spec/ruby/command_line/dash_upper_f_spec.rb
+++ b/spec/ruby/command_line/dash_upper_f_spec.rb
@@ -6,7 +6,7 @@ describe "the -F command line option" do
end
it "specifies the field separator pattern for -a" do
- ruby_exe("puts $F[0]", options: "-naF:", escape: true,
+ ruby_exe("puts $F[0]", options: "-naF:",
args: " < #{@passwd}").should ==
"nobody\nroot\ndaemon\n"
end
diff --git a/spec/ruby/command_line/dash_upper_i_spec.rb b/spec/ruby/command_line/dash_upper_i_spec.rb
index 4cafb724e3..4005a27d23 100644
--- a/spec/ruby/command_line/dash_upper_i_spec.rb
+++ b/spec/ruby/command_line/dash_upper_i_spec.rb
@@ -6,7 +6,7 @@ describe "The -I command line option" do
end
it "adds the path to the load path ($:)" do
- ruby_exe(@script, options: "-I fixtures").should include("fixtures")
+ ruby_exe(@script, options: "-I fixtures").should.include?("fixtures")
end
it "adds the path at the front of $LOAD_PATH" do
@@ -18,16 +18,16 @@ describe "The -I command line option" do
idx.should < 2
idx.should < lines.size-1
else
- lines[0].should include("fixtures")
+ lines[0].should.include?("fixtures")
end
end
it "adds the path expanded from CWD to $LOAD_PATH" do
- ruby_exe(@script, options: "-I fixtures").lines.should include "#{Dir.pwd}/fixtures\n"
+ ruby_exe(@script, options: "-I fixtures").lines.should.include? "#{Dir.pwd}/fixtures\n"
end
it "expands a path from CWD even if it does not exist" do
- ruby_exe(@script, options: "-I not_exist/not_exist").lines.should include "#{Dir.pwd}/not_exist/not_exist\n"
+ ruby_exe(@script, options: "-I not_exist/not_exist").lines.should.include? "#{Dir.pwd}/not_exist/not_exist\n"
end
end
@@ -45,7 +45,7 @@ platform_is_not :windows do
end
it "does not expand symlinks" do
- ruby_exe(@script, options: "-I #{@symlink}").lines.should include "#{@symlink}\n"
+ ruby_exe(@script, options: "-I #{@symlink}").lines.should.include? "#{@symlink}\n"
end
end
end
diff --git a/spec/ruby/command_line/dash_upper_k_spec.rb b/spec/ruby/command_line/dash_upper_k_spec.rb
index a060eab793..7e71532295 100644
--- a/spec/ruby/command_line/dash_upper_k_spec.rb
+++ b/spec/ruby/command_line/dash_upper_k_spec.rb
@@ -58,8 +58,8 @@ describe 'The -K command line option' do
end
it "ignores unknown codes" do
- locale = Encoding.find('locale')
+ external = Encoding.find('external')
ruby_exe(@test_string, options: '-KZ').should ==
- [Encoding::UTF_8.name, locale.name, nil].inspect
+ [Encoding::UTF_8.name, external.name, nil].inspect
end
end
diff --git a/spec/ruby/command_line/dash_upper_s_spec.rb b/spec/ruby/command_line/dash_upper_s_spec.rb
index 3a28fa2ad2..71c6016659 100644
--- a/spec/ruby/command_line/dash_upper_s_spec.rb
+++ b/spec/ruby/command_line/dash_upper_s_spec.rb
@@ -2,7 +2,8 @@ require_relative '../spec_helper'
describe 'The -S command line option' do
before :each do
- @path = [ENV['PATH'], fixture(__FILE__, "bin")].join(':')
+ @bin = fixture(__FILE__, "bin")
+ @path = [ENV['PATH'], @bin].join(File::PATH_SEPARATOR)
end
platform_is_not :windows do
@@ -10,20 +11,57 @@ describe 'The -S command line option' do
# and MRI shows warning when including world writable path in ENV['PATH'].
# This is why we are using /success$/ matching in the following cases.
+ it "runs launcher found in RUBYPATH, but only code after the first /\#!.*ruby.*/-ish line in target file" do
+ result = ruby_exe(nil, options: '-S hybrid_launcher.sh', env: { 'RUBYPATH' => @bin }, args: '2>&1')
+ result.should =~ /success$/
+ end
+
it "runs launcher found in PATH, but only code after the first /\#!.*ruby.*/-ish line in target file" do
result = ruby_exe(nil, options: '-S hybrid_launcher.sh', env: { 'PATH' => @path }, args: '2>&1')
result.should =~ /success$/
end
+ it "runs launcher found in RUBYPATH" do
+ result = ruby_exe(nil, options: '-S launcher.rb', env: { 'RUBYPATH' => @bin }, args: '2>&1')
+ result.should =~ /success$/
+ end
+
it "runs launcher found in PATH" do
result = ruby_exe(nil, options: '-S launcher.rb', env: { 'PATH' => @path }, args: '2>&1')
result.should =~ /success$/
end
+ it "runs launcher found in RUBYPATH and sets the exit status to 1 if it fails" do
+ result = ruby_exe(nil, options: '-S dash_s_fail', env: { 'RUBYPATH' => @bin }, args: '2>&1', exit_status: 1)
+ result.should =~ /\bdie\b/
+ $?.exitstatus.should == 1
+ end
+
it "runs launcher found in PATH and sets the exit status to 1 if it fails" do
- result = ruby_exe(nil, options: '-S dash_s_fail', env: { 'PATH' => @path }, args: '2>&1')
+ result = ruby_exe(nil, options: '-S dash_s_fail', env: { 'PATH' => @path }, args: '2>&1', exit_status: 1)
result.should =~ /\bdie\b/
$?.exitstatus.should == 1
end
+
+ ruby_version_is "4.1" do
+ describe "if the script name contains separator" do
+ before(:each) do
+ @bin = File.dirname(@bin)
+ @path = [ENV['PATH'], @bin].join(File::PATH_SEPARATOR)
+ end
+
+ it "does not search launcher in RUBYPATH" do
+ result = ruby_exe(nil, options: '-S bin/launcher.rb', env: { 'RUBYPATH' => @bin }, args: '2>&1', exit_status: 1)
+ result.should =~ /LoadError/
+ $?.should_not.success?
+ end
+
+ it "does not search launcher in PATH" do
+ result = ruby_exe(nil, options: '-S bin/launcher.rb', env: { 'PATH' => @path }, args: '2>&1', exit_status: 1)
+ result.should =~ /LoadError/
+ $?.should_not.success?
+ end
+ end
+ end
end
end
diff --git a/spec/ruby/command_line/dash_upper_u_spec.rb b/spec/ruby/command_line/dash_upper_u_spec.rb
index 2546b5b9f4..2c210eb603 100644
--- a/spec/ruby/command_line/dash_upper_u_spec.rb
+++ b/spec/ruby/command_line/dash_upper_u_spec.rb
@@ -2,42 +2,51 @@ require_relative '../spec_helper'
describe "ruby -U" do
it "sets Encoding.default_internal to UTF-8" do
- ruby_exe('print Encoding.default_internal.name',
- options: '-U').should == 'UTF-8'
+ ruby_exe('print Encoding.default_internal.name',
+ options: '-U').should == 'UTF-8'
+ end
+
+ it "sets Encoding.default_internal to UTF-8 when RUBYOPT is empty or only spaces" do
+ ruby_exe('p Encoding.default_internal',
+ options: '-U', env: { 'RUBYOPT' => '' }).should == "#<Encoding:UTF-8>\n"
+ ruby_exe('p Encoding.default_internal',
+ options: '-U', env: { 'RUBYOPT' => ' ' }).should == "#<Encoding:UTF-8>\n"
end
it "does nothing different if specified multiple times" do
- ruby_exe('print Encoding.default_internal.name',
- options: '-U -U').should == 'UTF-8'
+ ruby_exe('print Encoding.default_internal.name',
+ options: '-U -U').should == 'UTF-8'
end
it "is overruled by Encoding.default_internal=" do
- ruby_exe('Encoding.default_internal="ascii"; print Encoding.default_internal.name',
- options: '-U').should == 'US-ASCII'
+ ruby_exe('Encoding.default_internal="ascii"; print Encoding.default_internal.name',
+ options: '-U').should == 'US-ASCII'
end
it "does not affect the default external encoding" do
- ruby_exe('Encoding.default_external="ascii"; print Encoding.default_external.name',
- options: '-U').should == 'US-ASCII'
+ ruby_exe('Encoding.default_external="ascii"; print Encoding.default_external.name',
+ options: '-U').should == 'US-ASCII'
end
it "does not affect the source encoding" do
- ruby_exe("print __ENCODING__.name",
- options: '-U -KE').should == 'EUC-JP'
- ruby_exe("print __ENCODING__.name",
- options: '-KE -U').should == 'EUC-JP'
+ ruby_exe("print __ENCODING__.name",
+ options: '-U -KE').should == 'EUC-JP'
+ ruby_exe("print __ENCODING__.name",
+ options: '-KE -U').should == 'EUC-JP'
end
# I assume IO redirection will break on Windows...
it "raises a RuntimeError if used with -Eext:int" do
ruby_exe("p 1",
options: '-U -Eascii:ascii',
- args: '2>&1').should =~ /RuntimeError/
+ args: '2>&1',
+ exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError if used with -E:int" do
ruby_exe("p 1",
options: '-U -E:ascii',
- args: '2>&1').should =~ /RuntimeError/
+ args: '2>&1',
+ exit_status: 1).should =~ /RuntimeError/
end
end
diff --git a/spec/ruby/command_line/dash_upper_w_spec.rb b/spec/ruby/command_line/dash_upper_w_spec.rb
index 31bb976ad2..4019510d42 100644
--- a/spec/ruby/command_line/dash_upper_w_spec.rb
+++ b/spec/ruby/command_line/dash_upper_w_spec.rb
@@ -18,3 +18,27 @@ end
describe "The -W command line option with 2" do
it_behaves_like :command_line_verbose, "-W2"
end
+
+describe "The -W command line option with :deprecated" do
+ it "enables deprecation warnings" do
+ ruby_exe('p Warning[:deprecated]', options: '-W:deprecated').should == "true\n"
+ end
+end
+
+describe "The -W command line option with :no-deprecated" do
+ it "suppresses deprecation warnings" do
+ ruby_exe('p Warning[:deprecated]', options: '-w -W:no-deprecated').should == "false\n"
+ end
+end
+
+describe "The -W command line option with :experimental" do
+ it "enables experimental warnings" do
+ ruby_exe('p Warning[:experimental]', options: '-W:experimental').should == "true\n"
+ end
+end
+
+describe "The -W command line option with :no-experimental" do
+ it "suppresses experimental warnings" do
+ ruby_exe('p Warning[:experimental]', options: '-w -W:no-experimental').should == "false\n"
+ end
+end
diff --git a/spec/ruby/command_line/dash_v_spec.rb b/spec/ruby/command_line/dash_v_spec.rb
index 04d684fdad..6a4f7d3a15 100644
--- a/spec/ruby/command_line/dash_v_spec.rb
+++ b/spec/ruby/command_line/dash_v_spec.rb
@@ -6,7 +6,10 @@ describe "The -v command line option" do
describe "when used alone" do
it "prints version and ends" do
- ruby_exe(nil, args: '-v').should include(RUBY_DESCRIPTION)
- end
+ ruby_exe(nil, args: '-v').gsub("+PRISM ", "").should.include?(RUBY_DESCRIPTION.gsub("+PRISM ", ""))
+ end unless (defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?) ||
+ (defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?) ||
+ (ENV['RUBY_GC_LIBRARY'] && ENV['RUBY_GC_LIBRARY'].length > 0) ||
+ (ENV['RUBY_MN_THREADS'] == '1')
end
end
diff --git a/spec/ruby/command_line/dash_w_spec.rb b/spec/ruby/command_line/dash_w_spec.rb
index 1d93e0347b..f310dca3ed 100644
--- a/spec/ruby/command_line/dash_w_spec.rb
+++ b/spec/ruby/command_line/dash_w_spec.rb
@@ -3,4 +3,8 @@ require_relative 'shared/verbose'
describe "The -w command line option" do
it_behaves_like :command_line_verbose, "-w"
+
+ it "enables both deprecated and experimental warnings" do
+ ruby_exe('p Warning[:deprecated]; p Warning[:experimental]', options: '-w').should == "true\ntrue\n"
+ end
end
diff --git a/spec/ruby/command_line/dash_x_spec.rb b/spec/ruby/command_line/dash_x_spec.rb
index eb89db0144..38f97a5ab1 100644
--- a/spec/ruby/command_line/dash_x_spec.rb
+++ b/spec/ruby/command_line/dash_x_spec.rb
@@ -9,8 +9,8 @@ describe "The -x command line option" do
it "fails when /\#!.*ruby.*/-ish line in target file is not found" do
bad_embedded_ruby = fixture __FILE__, "bin/bad_embedded_ruby.txt"
- result = ruby_exe(bad_embedded_ruby, options: '-x', args: '2>&1')
- result.should include "no Ruby script found in input"
+ result = ruby_exe(bad_embedded_ruby, options: '-x', args: '2>&1', exit_status: 1)
+ result.should.include? "no Ruby script found in input"
end
it "behaves as -x was set when non-ruby shebang is encountered on first line" do
diff --git a/spec/ruby/command_line/error_message_spec.rb b/spec/ruby/command_line/error_message_spec.rb
index f3f7de4fd4..157cb8969c 100644
--- a/spec/ruby/command_line/error_message_spec.rb
+++ b/spec/ruby/command_line/error_message_spec.rb
@@ -2,10 +2,15 @@ require_relative '../spec_helper'
describe "The error message caused by an exception" do
it "is not printed to stdout" do
- out = ruby_exe("this_does_not_exist", args: "2> #{File::NULL}")
+ out = ruby_exe("this_does_not_exist", args: "2> #{File::NULL}", exit_status: 1)
out.chomp.should.empty?
- out = ruby_exe("end #syntax error", args: "2> #{File::NULL}")
+ out = ruby_exe("end #syntax error", args: "2> #{File::NULL}", exit_status: 1)
out.chomp.should.empty?
end
+
+ it "is not modified with extra escaping of control characters and backslashes" do
+ out = ruby_exe('raise "\e[31mRed\e[0m error\\\\message"', args: "2>&1", exit_status: 1)
+ out.chomp.should.include?("\e[31mRed\e[0m error\\message")
+ end
end
diff --git a/spec/ruby/command_line/feature_spec.rb b/spec/ruby/command_line/feature_spec.rb
index 02571ee8c6..0a3252b88d 100644
--- a/spec/ruby/command_line/feature_spec.rb
+++ b/spec/ruby/command_line/feature_spec.rb
@@ -1,6 +1,12 @@
require_relative '../spec_helper'
describe "The --enable and --disable flags" do
+ before :all do
+ # Since some specs disable reading RUBYOPT, we instead pass its contents as :options for those specs
+ rubyopt = [ENV["RUBYOPT"]]
+ rubyopt << ENV["#{RUBY_ENGINE.upcase}OPT"] unless RUBY_ENGINE == 'ruby'
+ @rubyopt = RUBY_ENGINE == "ruby" ? "" : rubyopt.compact.join(" ")
+ end
it "can be used with gems" do
ruby_exe("p defined?(Gem)", options: "--enable=gems").chomp.should == "\"constant\""
@@ -25,9 +31,9 @@ describe "The --enable and --disable flags" do
it "can be used with rubyopt" do
ruby_exe("p $VERBOSE", options: "--enable=rubyopt", env: {'RUBYOPT' => '-w'}).chomp.should == "true"
- ruby_exe("p $VERBOSE", options: "--disable=rubyopt", env: {'RUBYOPT' => '-w'}).chomp.should == "false"
+ ruby_exe("p $VERBOSE", options: "#{@rubyopt} --disable=rubyopt", env: {'RUBYOPT' => '-w'}).chomp.should == "false"
ruby_exe("p $VERBOSE", options: "--enable-rubyopt", env: {'RUBYOPT' => '-w'}).chomp.should == "true"
- ruby_exe("p $VERBOSE", options: "--disable-rubyopt", env: {'RUBYOPT' => '-w'}).chomp.should == "false"
+ ruby_exe("p $VERBOSE", options: "#{@rubyopt} --disable-rubyopt", env: {'RUBYOPT' => '-w'}).chomp.should == "false"
end
it "can be used with frozen-string-literal" do
@@ -37,20 +43,29 @@ describe "The --enable and --disable flags" do
ruby_exe("p 'foo'.frozen?", options: "--disable-frozen-string-literal").chomp.should == "false"
end
- it "can be used with all" do
+ # frequently hangs for >60s on GitHub Actions macos-latest
+ # MinGW's YJIT support seems broken
+ platform_is_not :darwin, :mingw do
+ it "can be used with all for enable" do
+ e = "p [defined?(Gem), defined?(DidYouMean), $VERBOSE, 'foo'.frozen?]"
+ env = {'RUBYOPT' => '-w'}
+ # Use a single variant here because it can be quite slow as it might enable jit, etc
+ ruby_exe(e, options: "--enable-all", env: env).chomp.should == "[\"constant\", \"constant\", true, true]"
+ end unless defined?(RubyVM::YJIT) && defined?(RubyVM::ZJIT) && RubyVM::ZJIT.enabled? # You're not supposed to enable YJIT with --enable-all when ZJIT options are passed.
+ end
+
+ it "can be used with all for disable" do
e = "p [defined?(Gem), defined?(DidYouMean), $VERBOSE, 'foo'.frozen?]"
env = {'RUBYOPT' => '-w'}
- ruby_exe(e, options: "--enable=all", env: env).chomp.should == "[\"constant\", \"constant\", true, true]"
- ruby_exe(e, options: "--enable-all", env: env).chomp.should == "[\"constant\", \"constant\", true, true]"
- ruby_exe(e, options: "--disable=all", env: env).chomp.should == "[nil, nil, false, false]"
- ruby_exe(e, options: "--disable-all", env: env).chomp.should == "[nil, nil, false, false]"
+ ruby_exe(e, options: "#{@rubyopt} --disable=all", env: env).chomp.should == "[nil, nil, false, false]"
+ ruby_exe(e, options: "#{@rubyopt} --disable-all", env: env).chomp.should == "[nil, nil, false, false]"
end
it "prints a warning for unknown features" do
- ruby_exe("p 14", options: "--enable=ruby-spec-feature-does-not-exist 2>&1").chomp.should include('warning: unknown argument for --enable')
- ruby_exe("p 14", options: "--disable=ruby-spec-feature-does-not-exist 2>&1").chomp.should include('warning: unknown argument for --disable')
- ruby_exe("p 14", options: "--enable-ruby-spec-feature-does-not-exist 2>&1").chomp.should include('warning: unknown argument for --enable')
- ruby_exe("p 14", options: "--disable-ruby-spec-feature-does-not-exist 2>&1").chomp.should include('warning: unknown argument for --disable')
+ ruby_exe("p 14", options: "--enable=ruby-spec-feature-does-not-exist 2>&1").chomp.should.include?('warning: unknown argument for --enable')
+ ruby_exe("p 14", options: "--disable=ruby-spec-feature-does-not-exist 2>&1").chomp.should.include?('warning: unknown argument for --disable')
+ ruby_exe("p 14", options: "--enable-ruby-spec-feature-does-not-exist 2>&1").chomp.should.include?('warning: unknown argument for --enable')
+ ruby_exe("p 14", options: "--disable-ruby-spec-feature-does-not-exist 2>&1").chomp.should.include?('warning: unknown argument for --disable')
end
end
diff --git a/spec/ruby/command_line/fixtures/backtrace.rb b/spec/ruby/command_line/fixtures/backtrace.rb
new file mode 100644
index 0000000000..99acae95c8
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/backtrace.rb
@@ -0,0 +1,35 @@
+def a
+ raise 'oops'
+end
+
+def b
+ a
+end
+
+def c
+ b
+end
+
+def d
+ c
+end
+
+arg = ARGV.first
+$stderr.puts arg
+
+case arg
+when 'full_message'
+ begin
+ d
+ rescue => exc
+ puts exc.full_message
+ end
+when 'backtrace'
+ begin
+ d
+ rescue => exc
+ puts exc.backtrace
+ end
+else
+ d
+end
diff --git a/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt b/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt
index a2b7ad085f..61b946977a 100644
--- a/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt
+++ b/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt
@@ -1,3 +1,3 @@
-@@@This line is not value Ruby
+@@@This line is not valid Ruby
#!rub_y
puts 'success'
diff --git a/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt b/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt
index c556bf0b71..0ec0f358db 100644
--- a/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt
+++ b/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt
@@ -1,3 +1,3 @@
-@@@This line is not value Ruby
+@@@This line is not valid Ruby
#!ruby
-puts 'success' \ No newline at end of file
+puts 'success'
diff --git a/spec/ruby/command_line/fixtures/debug_info.rb b/spec/ruby/command_line/fixtures/debug_info.rb
index ee607910c0..f02b041920 100644
--- a/spec/ruby/command_line/fixtures/debug_info.rb
+++ b/spec/ruby/command_line/fixtures/debug_info.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
a = 'string'
b = a
c = b
diff --git a/spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rb b/spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rb
index fa348d59e7..df4b952c46 100644
--- a/spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rb
+++ b/spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rb
Binary files differ
diff --git a/spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb b/spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb
index 074092c9d9..f5547a5bae 100644
--- a/spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb
+++ b/spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb
@@ -1 +1 @@
-p "abc".object_id == "abc".object_id
+p "abc".equal?("abc")
diff --git a/spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb b/spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb
new file mode 100644
index 0000000000..fb84b546c0
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+frozen = "test".frozen?
+interned = "test".equal?("test")
+puts "frozen:#{frozen} interned:#{interned}"
diff --git a/spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb b/spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb
new file mode 100644
index 0000000000..381a742001
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: false
+frozen = "test".frozen?
+interned = "test".equal?("test")
+puts "frozen:#{frozen} interned:#{interned}"
diff --git a/spec/ruby/command_line/fixtures/string_literal_raw.rb b/spec/ruby/command_line/fixtures/string_literal_raw.rb
new file mode 100644
index 0000000000..56b1841296
--- /dev/null
+++ b/spec/ruby/command_line/fixtures/string_literal_raw.rb
@@ -0,0 +1,3 @@
+frozen = "test".frozen?
+interned = "test".equal?("test")
+puts "frozen:#{frozen} interned:#{interned}"
diff --git a/spec/ruby/command_line/frozen_strings_spec.rb b/spec/ruby/command_line/frozen_strings_spec.rb
index 647b69daed..32ff7d0371 100644
--- a/spec/ruby/command_line/frozen_strings_spec.rb
+++ b/spec/ruby/command_line/frozen_strings_spec.rb
@@ -19,11 +19,76 @@ describe "The --enable-frozen-string-literal flag causes string literals to" do
end
end
+describe "The --disable-frozen-string-literal flag causes string literals to" do
+
+ it "produce a different object each time" do
+ ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb"), options: "--disable-frozen-string-literal").chomp.should == "false"
+ end
+
+end
+
+describe "With neither --enable-frozen-string-literal nor --disable-frozen-string-literal flag set" do
+ before do
+ # disable --enable-frozen-string-literal and --disable-frozen-string-literal passed in $RUBYOPT
+ @rubyopt = ENV["RUBYOPT"]
+ ENV["RUBYOPT"] = ""
+ end
+
+ after do
+ ENV["RUBYOPT"] = @rubyopt
+ end
+
+ it "produce a different object each time" do
+ ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb")).chomp.should == "false"
+ end
+
+ context "if file has no frozen_string_literal comment" do
+ it "produce different mutable strings each time" do
+ ruby_exe(fixture(__FILE__, "string_literal_raw.rb")).chomp.should == "frozen:false interned:false"
+ end
+
+ guard -> { ruby_version_is "3.4" and !"test".frozen? } do
+ it "complain about modification of produced mutable strings" do
+ -> { eval(<<~RUBY) }.should complain(/warning: literal string will be frozen in the future \(run with --debug-frozen-string-literal for more information\)/)
+ "test" << "!"
+ RUBY
+ end
+
+ it "does not complain about modification if Warning[:deprecated] is false" do
+ deprecated = Warning[:deprecated]
+ Warning[:deprecated] = false
+ -> { eval(<<~RUBY) }.should_not complain
+ "test" << "!"
+ RUBY
+ ensure
+ Warning[:deprecated] = deprecated
+ end
+ end
+ end
+
+ it "if file has frozen_string_literal:true comment produce same frozen strings each time" do
+ ruby_exe(fixture(__FILE__, "string_literal_frozen_comment.rb")).chomp.should == "frozen:true interned:true"
+ end
+
+ it "if file has frozen_string_literal:false comment produce different mutable strings each time" do
+ ruby_exe(fixture(__FILE__, "string_literal_mutable_comment.rb")).chomp.should == "frozen:false interned:false"
+ end
+end
+
describe "The --debug flag produces" do
it "debugging info on attempted frozen string modification" do
- error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '--debug', args: "2>&1")
- error_str.should include("can't modify frozen String")
- error_str.should include("created at")
- error_str.should include("command_line/fixtures/debug_info.rb:2")
+ error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '--enable-frozen-string-literal --debug', args: "2>&1")
+ error_str.should.include?("can't modify frozen String")
+ error_str.should.include?("created at")
+ error_str.should.include?("command_line/fixtures/debug_info.rb:1")
+ end
+
+ guard -> { ruby_version_is "3.4" and !"test".frozen? } do
+ it "debugging info on mutating chilled string" do
+ error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '-w --debug', args: "2>&1")
+ error_str.should.include?("literal string will be frozen in the future")
+ error_str.should.include?("the string was created here")
+ error_str.should.include?("command_line/fixtures/debug_info.rb:1")
+ end
end
end
diff --git a/spec/ruby/command_line/rubylib_spec.rb b/spec/ruby/command_line/rubylib_spec.rb
index b45919b997..1bedd146e3 100644
--- a/spec/ruby/command_line/rubylib_spec.rb
+++ b/spec/ruby/command_line/rubylib_spec.rb
@@ -14,15 +14,15 @@ describe "The RUBYLIB environment variable" do
dir = tmp("rubylib/incl")
ENV["RUBYLIB"] = @pre + dir
paths = ruby_exe("puts $LOAD_PATH").lines.map(&:chomp)
- paths.should include(dir)
+ paths.should.include?(dir)
end
it "adds a File::PATH_SEPARATOR-separated list of directories to $LOAD_PATH" do
dir1, dir2 = tmp("rubylib/incl1"), tmp("rubylib/incl2")
ENV["RUBYLIB"] = @pre + "#{dir1}#{File::PATH_SEPARATOR}#{dir2}"
paths = ruby_exe("puts $LOAD_PATH").lines.map(&:chomp)
- paths.should include(dir1)
- paths.should include(dir2)
+ paths.should.include?(dir1)
+ paths.should.include?(dir2)
paths.index(dir1).should < paths.index(dir2)
end
@@ -46,8 +46,8 @@ describe "The RUBYLIB environment variable" do
rubylib_dir = tmp("rubylib_include")
ENV["RUBYLIB"] = @pre + rubylib_dir
paths = ruby_exe("puts $LOAD_PATH", options: "-I #{dash_i_dir}").lines.map(&:chomp)
- paths.should include(dash_i_dir)
- paths.should include(rubylib_dir)
+ paths.should.include?(dash_i_dir)
+ paths.should.include?(rubylib_dir)
paths.index(dash_i_dir).should < paths.index(rubylib_dir)
end
@@ -56,14 +56,14 @@ describe "The RUBYLIB environment variable" do
rubylib_dir = tmp("rubylib_include")
ENV["RUBYLIB"] = @pre + rubylib_dir
paths = ruby_exe("puts $LOAD_PATH", env: { "RUBYOPT" => "-I#{rubyopt_dir}" }).lines.map(&:chomp)
- paths.should include(rubyopt_dir)
- paths.should include(rubylib_dir)
+ paths.should.include?(rubyopt_dir)
+ paths.should.include?(rubylib_dir)
paths.index(rubyopt_dir).should < paths.index(rubylib_dir)
end
it "keeps spaces in the value" do
ENV["RUBYLIB"] = @pre + " rubylib/incl "
out = ruby_exe("puts $LOAD_PATH")
- out.should include(" rubylib/incl ")
+ out.should.include?(" rubylib/incl ")
end
end
diff --git a/spec/ruby/command_line/rubyopt_spec.rb b/spec/ruby/command_line/rubyopt_spec.rb
index 2db42f77ef..eb297cd6fe 100644
--- a/spec/ruby/command_line/rubyopt_spec.rb
+++ b/spec/ruby/command_line/rubyopt_spec.rb
@@ -11,52 +11,70 @@ describe "Processing RUBYOPT" do
it "adds the -I path to $LOAD_PATH" do
ENV["RUBYOPT"] = "-Ioptrubyspecincl"
- result = ruby_exe("puts $LOAD_PATH.grep(/byspecin/)", escape: true)
+ result = ruby_exe("puts $LOAD_PATH.grep(/byspecin/)")
result.chomp[-15..-1].should == "optrubyspecincl"
end
it "sets $DEBUG to true for '-d'" do
ENV["RUBYOPT"] = '-d'
command = %[puts "value of $DEBUG is \#{$DEBUG}"]
- result = ruby_exe(command, escape: true, args: "2>&1")
+ result = ruby_exe(command, args: "2>&1")
result.should =~ /value of \$DEBUG is true/
end
- guard -> { not CROSS_COMPILING } do
+ guard -> { RbConfig::CONFIG["CROSS_COMPILING"] != "yes" } do
it "prints the version number for '-v'" do
ENV["RUBYOPT"] = '-v'
- ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION
+ ruby_exe("")[/\A.*/].gsub(/\s\+(YJIT( \w+)?|ZJIT( \w+)?|PRISM|GC(\[\w+\])?)(?=\s)/, "").should == RUBY_DESCRIPTION.gsub(/\s\+(YJIT( \w+)?|ZJIT( \w+)?|PRISM|GC(\[\w+\])?)(?=\s)/, "")
end
it "ignores whitespace around the option" do
ENV["RUBYOPT"] = ' -v '
- ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION
+ ruby_exe("")[/\A.*/].gsub(/\s\+(YJIT( \w+)?|ZJIT( \w+)?|PRISM|GC(\[\w+\])?)(?=\s)/, "").should == RUBY_DESCRIPTION.gsub(/\s\+(YJIT( \w+)?|ZJIT( \w+)?|PRISM|GC(\[\w+\])?)(?=\s)/, "")
end
end
it "sets $VERBOSE to true for '-w'" do
ENV["RUBYOPT"] = '-w'
- ruby_exe("p $VERBOSE", escape: true).chomp.should == "true"
+ ruby_exe("p $VERBOSE").chomp.should == "true"
end
it "sets $VERBOSE to true for '-W'" do
ENV["RUBYOPT"] = '-W'
- ruby_exe("p $VERBOSE", escape: true).chomp.should == "true"
+ ruby_exe("p $VERBOSE").chomp.should == "true"
end
it "sets $VERBOSE to nil for '-W0'" do
ENV["RUBYOPT"] = '-W0'
- ruby_exe("p $VERBOSE", escape: true).chomp.should == "nil"
+ ruby_exe("p $VERBOSE").chomp.should == "nil"
end
it "sets $VERBOSE to false for '-W1'" do
ENV["RUBYOPT"] = '-W1'
- ruby_exe("p $VERBOSE", escape: true).chomp.should == "false"
+ ruby_exe("p $VERBOSE").chomp.should == "false"
end
it "sets $VERBOSE to true for '-W2'" do
ENV["RUBYOPT"] = '-W2'
- ruby_exe("p $VERBOSE", escape: true).chomp.should == "true"
+ ruby_exe("p $VERBOSE").chomp.should == "true"
+ end
+
+ it "suppresses deprecation warnings for '-W:no-deprecated'" do
+ ENV["RUBYOPT"] = '-W:no-deprecated'
+ result = ruby_exe('$; = ""', args: '2>&1')
+ result.should == ""
+ end
+
+ it "suppresses experimental warnings for '-W:no-experimental'" do
+ ENV["RUBYOPT"] = '-W:no-experimental'
+ result = ruby_exe('case 0; in a; end', args: '2>&1')
+ result.should == ""
+ end
+
+ it "suppresses deprecation and experimental warnings for '-W:no-deprecated -W:no-experimental'" do
+ ENV["RUBYOPT"] = '-W:no-deprecated -W:no-experimental'
+ result = ruby_exe('case ($; = ""); in a; end', args: '2>&1')
+ result.should == ""
end
it "requires the file for '-r'" do
@@ -67,101 +85,101 @@ describe "Processing RUBYOPT" do
it "raises a RuntimeError for '-a'" do
ENV["RUBYOPT"] = '-a'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-p'" do
ENV["RUBYOPT"] = '-p'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-n'" do
ENV["RUBYOPT"] = '-n'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-y'" do
ENV["RUBYOPT"] = '-y'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-c'" do
ENV["RUBYOPT"] = '-c'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-s'" do
ENV["RUBYOPT"] = '-s'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-h'" do
ENV["RUBYOPT"] = '-h'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '--help'" do
ENV["RUBYOPT"] = '--help'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-l'" do
ENV["RUBYOPT"] = '-l'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-S'" do
ENV["RUBYOPT"] = '-S irb'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-e'" do
ENV["RUBYOPT"] = '-e0'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-i'" do
ENV["RUBYOPT"] = '-i.bak'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-x'" do
ENV["RUBYOPT"] = '-x'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-C'" do
ENV["RUBYOPT"] = '-C'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-X'" do
ENV["RUBYOPT"] = '-X.'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-F'" do
ENV["RUBYOPT"] = '-F'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '-0'" do
ENV["RUBYOPT"] = '-0'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '--copyright'" do
ENV["RUBYOPT"] = '--copyright'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '--version'" do
ENV["RUBYOPT"] = '--version'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
it "raises a RuntimeError for '--yydebug'" do
ENV["RUBYOPT"] = '--yydebug'
- ruby_exe("", args: '2>&1').should =~ /RuntimeError/
+ ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/
end
end
diff --git a/spec/ruby/command_line/syntax_error_spec.rb b/spec/ruby/command_line/syntax_error_spec.rb
index f61cfe928d..88864c048e 100644
--- a/spec/ruby/command_line/syntax_error_spec.rb
+++ b/spec/ruby/command_line/syntax_error_spec.rb
@@ -2,12 +2,12 @@ require_relative '../spec_helper'
describe "The interpreter" do
it "prints an error when given a file with invalid syntax" do
- out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), args: "2>&1")
- out.should include "syntax error"
+ out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), args: "2>&1", exit_status: 1)
+ out.should.include?("SyntaxError")
end
it "prints an error when given code via -e with invalid syntax" do
- out = ruby_exe(nil, args: "-e 'a{' 2>&1")
- out.should include "syntax error"
+ out = ruby_exe(nil, args: "-e 'a{' 2>&1", exit_status: 1)
+ out.should.include?("SyntaxError")
end
end
diff --git a/spec/ruby/core/argf/argf_spec.rb b/spec/ruby/core/argf/argf_spec.rb
index af67170b98..f9468539bb 100644
--- a/spec/ruby/core/argf/argf_spec.rb
+++ b/spec/ruby/core/argf/argf_spec.rb
@@ -2,10 +2,10 @@ require_relative '../../spec_helper'
describe "ARGF" do
it "is extended by the Enumerable module" do
- ARGF.should be_kind_of(Enumerable)
+ ARGF.should.is_a?(Enumerable)
end
it "is an instance of ARGF.class" do
- ARGF.should be_an_instance_of(ARGF.class)
+ ARGF.should.instance_of?(ARGF.class)
end
end
diff --git a/spec/ruby/core/argf/argv_spec.rb b/spec/ruby/core/argf/argv_spec.rb
index eab03c450f..77dfe78c21 100644
--- a/spec/ruby/core/argf/argv_spec.rb
+++ b/spec/ruby/core/argf/argv_spec.rb
@@ -7,7 +7,7 @@ describe "ARGF.argv" do
end
it "returns ARGV for the initial ARGF" do
- ARGF.argv.should equal ARGV
+ ARGF.argv.should.equal? ARGV
end
it "returns the remaining arguments to treat" do
diff --git a/spec/ruby/core/argf/binmode_spec.rb b/spec/ruby/core/argf/binmode_spec.rb
index e083a30a27..5288e3199d 100644
--- a/spec/ruby/core/argf/binmode_spec.rb
+++ b/spec/ruby/core/argf/binmode_spec.rb
@@ -9,7 +9,7 @@ describe "ARGF.binmode" do
it "returns self" do
argf [@bin_file] do
- @argf.binmode.should equal @argf
+ @argf.binmode.should.equal? @argf
end
end
diff --git a/spec/ruby/core/argf/bytes_spec.rb b/spec/ruby/core/argf/bytes_spec.rb
deleted file mode 100644
index 71d07fabcb..0000000000
--- a/spec/ruby/core/argf/bytes_spec.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'shared/each_byte'
-
-describe "ARGF.bytes" do
- it_behaves_like :argf_each_byte, :bytes
-end
diff --git a/spec/ruby/core/argf/chars_spec.rb b/spec/ruby/core/argf/chars_spec.rb
deleted file mode 100644
index ee79ea763b..0000000000
--- a/spec/ruby/core/argf/chars_spec.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'shared/each_char'
-
-describe "ARGF.chars" do
- it_behaves_like :argf_each_char, :chars
-end
diff --git a/spec/ruby/core/argf/close_spec.rb b/spec/ruby/core/argf/close_spec.rb
index d4d6a51e72..8ca7d71dc2 100644
--- a/spec/ruby/core/argf/close_spec.rb
+++ b/spec/ruby/core/argf/close_spec.rb
@@ -10,20 +10,20 @@ describe "ARGF.close" do
argf [@file1_name, @file2_name] do
io = @argf.to_io
@argf.close
- io.closed?.should be_true
+ io.closed?.should == true
end
end
it "returns self" do
argf [@file1_name, @file2_name] do
- @argf.close.should equal(@argf)
+ @argf.close.should.equal?(@argf)
end
end
it "doesn't raise an IOError if called on a closed stream" do
argf [@file1_name] do
- -> { @argf.close }.should_not raise_error
- -> { @argf.close }.should_not raise_error
+ -> { @argf.close }.should_not.raise
+ -> { @argf.close }.should_not.raise
end
end
end
diff --git a/spec/ruby/core/argf/closed_spec.rb b/spec/ruby/core/argf/closed_spec.rb
index e2dd6134e5..769381e8c3 100644
--- a/spec/ruby/core/argf/closed_spec.rb
+++ b/spec/ruby/core/argf/closed_spec.rb
@@ -11,7 +11,7 @@ describe "ARGF.closed?" do
stream = @argf.to_io
stream.close
- @argf.closed?.should be_true
+ @argf.closed?.should == true
stream.reopen(@argf.filename, 'r')
end
end
diff --git a/spec/ruby/core/argf/codepoints_spec.rb b/spec/ruby/core/argf/codepoints_spec.rb
deleted file mode 100644
index 7aa8a761fe..0000000000
--- a/spec/ruby/core/argf/codepoints_spec.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'shared/each_codepoint'
-
-describe "ARGF.codepoints" do
- it_behaves_like :argf_each_codepoint, :codepoints
-end
diff --git a/spec/ruby/core/argf/each_byte_spec.rb b/spec/ruby/core/argf/each_byte_spec.rb
index c5cce9f250..d9e4e7fe5b 100644
--- a/spec/ruby/core/argf/each_byte_spec.rb
+++ b/spec/ruby/core/argf/each_byte_spec.rb
@@ -1,6 +1,60 @@
require_relative '../../spec_helper'
-require_relative 'shared/each_byte'
describe "ARGF.each_byte" do
- it_behaves_like :argf_each_byte, :each_byte
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @bytes = []
+ File.read(@file1_name).each_byte { |b| @bytes << b }
+ File.read(@file2_name).each_byte { |b| @bytes << b }
+ end
+
+ it "yields each byte of all streams to the passed block" do
+ argf [@file1_name, @file2_name] do
+ bytes = []
+ @argf.each_byte { |b| bytes << b }
+ bytes.should == @bytes
+ end
+ end
+
+ it "returns self when passed a block" do
+ argf [@file1_name, @file2_name] do
+ @argf.each_byte {}.should.equal?(@argf)
+ end
+ end
+
+ it "returns an Enumerator when passed no block" do
+ argf [@file1_name, @file2_name] do
+ enum = @argf.each_byte
+ enum.should.instance_of?(Enumerator)
+
+ bytes = []
+ enum.each { |b| bytes << b }
+ bytes.should == @bytes
+ end
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ argf [@file1_name, @file2_name] do
+ enum = @argf.each_byte
+ enum.should.instance_of?(Enumerator)
+
+ bytes = []
+ enum.each { |b| bytes << b }
+ bytes.should == @bytes
+ end
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ argf [@file1_name, @file2_name] do
+ @argf.each_byte.size.should == nil
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/argf/each_char_spec.rb b/spec/ruby/core/argf/each_char_spec.rb
index 724e5e6e3e..62d19b6574 100644
--- a/spec/ruby/core/argf/each_char_spec.rb
+++ b/spec/ruby/core/argf/each_char_spec.rb
@@ -1,6 +1,60 @@
require_relative '../../spec_helper'
-require_relative 'shared/each_char'
describe "ARGF.each_char" do
- it_behaves_like :argf_each_char, :each_char
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @chars = []
+ File.read(@file1_name).each_char { |c| @chars << c }
+ File.read(@file2_name).each_char { |c| @chars << c }
+ end
+
+ it "yields each char of all streams to the passed block" do
+ argf [@file1_name, @file2_name] do
+ chars = []
+ @argf.each_char { |c| chars << c }
+ chars.should == @chars
+ end
+ end
+
+ it "returns self when passed a block" do
+ argf [@file1_name, @file2_name] do
+ @argf.each_char {}.should.equal?(@argf)
+ end
+ end
+
+ it "returns an Enumerator when passed no block" do
+ argf [@file1_name, @file2_name] do
+ enum = @argf.each_char
+ enum.should.instance_of?(Enumerator)
+
+ chars = []
+ enum.each { |c| chars << c }
+ chars.should == @chars
+ end
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ argf [@file1_name, @file2_name] do
+ enum = @argf.each_char
+ enum.should.instance_of?(Enumerator)
+
+ chars = []
+ enum.each { |c| chars << c }
+ chars.should == @chars
+ end
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ argf [@file1_name, @file2_name] do
+ @argf.each_char.size.should == nil
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/argf/each_codepoint_spec.rb b/spec/ruby/core/argf/each_codepoint_spec.rb
index 0bf8bf9764..ad55785cba 100644
--- a/spec/ruby/core/argf/each_codepoint_spec.rb
+++ b/spec/ruby/core/argf/each_codepoint_spec.rb
@@ -1,6 +1,60 @@
require_relative '../../spec_helper'
-require_relative 'shared/each_codepoint'
describe "ARGF.each_codepoint" do
- it_behaves_like :argf_each_codepoint, :each_codepoint
+ before :each do
+ file1_name = fixture __FILE__, "file1.txt"
+ file2_name = fixture __FILE__, "file2.txt"
+ @filenames = [file1_name, file2_name]
+
+ @codepoints = File.read(file1_name).codepoints
+ @codepoints.concat File.read(file2_name).codepoints
+ end
+
+ it "is a public method" do
+ argf @filenames do
+ @argf.public_methods(false).should.include?(:each_codepoint)
+ end
+ end
+
+ it "does not require arguments" do
+ argf @filenames do
+ @argf.method(:each_codepoint).arity.should == 0
+ end
+ end
+
+ it "returns self when passed a block" do
+ argf @filenames do
+ @argf.each_codepoint {}.should.equal?(@argf)
+ end
+ end
+
+ it "returns an Enumerator when passed no block" do
+ argf @filenames do
+ @argf.each_codepoint.should.instance_of?(Enumerator)
+ end
+ end
+
+ it "yields each codepoint of all streams" do
+ argf @filenames do
+ @argf.each_codepoint.to_a.should == @codepoints
+ end
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ argf @filenames do
+ @argf.each_codepoint.should.instance_of?(Enumerator)
+ end
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ argf @filenames do
+ @argf.each_codepoint.size.should == nil
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/argf/each_line_spec.rb b/spec/ruby/core/argf/each_line_spec.rb
index 52a7e5c411..fc4d6433b8 100644
--- a/spec/ruby/core/argf/each_line_spec.rb
+++ b/spec/ruby/core/argf/each_line_spec.rb
@@ -1,6 +1,64 @@
require_relative '../../spec_helper'
-require_relative 'shared/each_line'
describe "ARGF.each_line" do
- it_behaves_like :argf_each_line, :each_line
+ before :each do
+ @file1_name = fixture __FILE__, "file1.txt"
+ @file2_name = fixture __FILE__, "file2.txt"
+
+ @lines = File.readlines @file1_name
+ @lines += File.readlines @file2_name
+ end
+
+ it "is a public method" do
+ argf [@file1_name, @file2_name] do
+ @argf.public_methods(false).should.include?(:each_line)
+ end
+ end
+
+ it "requires multiple arguments" do
+ argf [@file1_name, @file2_name] do
+ @argf.method(:each_line).arity.should < 0
+ end
+ end
+
+ it "reads each line of files" do
+ argf [@file1_name, @file2_name] do
+ lines = []
+ @argf.each_line { |b| lines << b }
+ lines.should == @lines
+ end
+ end
+
+ it "returns self when passed a block" do
+ argf [@file1_name, @file2_name] do
+ @argf.each_line {}.should.equal?(@argf)
+ end
+ end
+
+ describe "with a separator" do
+ it "yields each separated section of all streams" do
+ argf [@file1_name, @file2_name] do
+ @argf.send(:each_line, '.').to_a.should ==
+ (File.readlines(@file1_name, '.') + File.readlines(@file2_name, '.'))
+ end
+ end
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ argf [@file1_name, @file2_name] do
+ @argf.each_line.should.instance_of?(Enumerator)
+ end
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ argf [@file1_name, @file2_name] do
+ @argf.each_line.size.should == nil
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/argf/each_spec.rb b/spec/ruby/core/argf/each_spec.rb
index 5742ba43bd..25f60b31d2 100644
--- a/spec/ruby/core/argf/each_spec.rb
+++ b/spec/ruby/core/argf/each_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/each_line'
describe "ARGF.each" do
- it_behaves_like :argf_each_line, :each
+ it "is an alias of ARGF.each_line" do
+ ARGF.method(:each).should == ARGF.method(:each_line)
+ end
end
diff --git a/spec/ruby/core/argf/eof_spec.rb b/spec/ruby/core/argf/eof_spec.rb
index 518f6e566e..940d104d69 100644
--- a/spec/ruby/core/argf/eof_spec.rb
+++ b/spec/ruby/core/argf/eof_spec.rb
@@ -1,10 +1,32 @@
require_relative '../../spec_helper'
-require_relative 'shared/eof'
describe "ARGF.eof" do
- it_behaves_like :argf_eof, :eof
+ it "is an alias of ARGF.eof?" do
+ ARGF.method(:eof).should == ARGF.method(:eof?)
+ end
end
describe "ARGF.eof?" do
- it_behaves_like :argf_eof, :eof?
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "returns true when reaching the end of a file" do
+ argf [@file1, @file2] do
+ result = []
+ while @argf.gets
+ result << @argf.eof?
+ end
+ result.should == [false, true, false, true]
+ end
+ end
+
+ it "raises IOError when called on a closed stream" do
+ argf [@file1] do
+ @argf.read
+ -> { @argf.eof? }.should.raise(IOError)
+ end
+ end
end
diff --git a/spec/ruby/core/argf/filename_spec.rb b/spec/ruby/core/argf/filename_spec.rb
index 7c0446269d..f4b6e922c6 100644
--- a/spec/ruby/core/argf/filename_spec.rb
+++ b/spec/ruby/core/argf/filename_spec.rb
@@ -1,6 +1,30 @@
require_relative '../../spec_helper'
-require_relative 'shared/filename'
describe "ARGF.filename" do
- it_behaves_like :argf_filename, :filename
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "returns the current file name on each file" do
+ argf [@file1, @file2] do
+ result = []
+ # returns first current file even when not yet open
+ result << @argf.filename
+ result << @argf.filename while @argf.gets
+ # returns last current file even when closed
+ result << @argf.filename
+
+ result.map! { |f| File.expand_path(f) }
+ result.should == [@file1, @file1, @file1, @file2, @file2, @file2]
+ end
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "sets the $FILENAME global variable with the current file name on each file" do
+ script = fixture __FILE__, "filename.rb"
+ out = ruby_exe(script, args: [@file1, @file2])
+ out.should == "#{@file1}\n#{@file1}\n#{@file2}\n#{@file2}\n#{@file2}\n"
+ end
end
diff --git a/spec/ruby/core/argf/fileno_spec.rb b/spec/ruby/core/argf/fileno_spec.rb
index 29d50c3582..99245f043c 100644
--- a/spec/ruby/core/argf/fileno_spec.rb
+++ b/spec/ruby/core/argf/fileno_spec.rb
@@ -1,6 +1,26 @@
require_relative '../../spec_helper'
-require_relative 'shared/fileno'
describe "ARGF.fileno" do
- it_behaves_like :argf_fileno, :fileno
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ # NOTE: this test assumes that fixtures files have two lines each
+ it "returns the current file number on each file" do
+ argf [@file1, @file2] do
+ result = []
+ # returns first current file even when not yet open
+ result << @argf.fileno while @argf.gets
+ # returns last current file even when closed
+ result.map { |d| d.class }.should == [Integer, Integer, Integer, Integer]
+ end
+ end
+
+ it "raises an ArgumentError when called on a closed stream" do
+ argf [@file1] do
+ @argf.read
+ -> { @argf.fileno }.should.raise(ArgumentError)
+ end
+ end
end
diff --git a/spec/ruby/core/argf/inspect_spec.rb b/spec/ruby/core/argf/inspect_spec.rb
new file mode 100644
index 0000000000..df0e3ba8dc
--- /dev/null
+++ b/spec/ruby/core/argf/inspect_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "ARGF.inspect" do
+ it "is an alias of ARGF.to_s" do
+ ARGF.method(:inspect).should == ARGF.method(:to_s)
+ end
+end
diff --git a/spec/ruby/core/argf/lines_spec.rb b/spec/ruby/core/argf/lines_spec.rb
deleted file mode 100644
index 6ca6ff1256..0000000000
--- a/spec/ruby/core/argf/lines_spec.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'shared/each_line'
-
-describe "ARGF.lines" do
- it_behaves_like :argf_each_line, :lines
-end
diff --git a/spec/ruby/core/argf/path_spec.rb b/spec/ruby/core/argf/path_spec.rb
index 7120f7d0e3..2f7b91999f 100644
--- a/spec/ruby/core/argf/path_spec.rb
+++ b/spec/ruby/core/argf/path_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/filename'
describe "ARGF.path" do
- it_behaves_like :argf_filename, :path
+ it "is an alias of ARGF.filename" do
+ ARGF.method(:path).should == ARGF.method(:filename)
+ end
end
diff --git a/spec/ruby/core/argf/pos_spec.rb b/spec/ruby/core/argf/pos_spec.rb
index fb3f25b945..1ff16e4f17 100644
--- a/spec/ruby/core/argf/pos_spec.rb
+++ b/spec/ruby/core/argf/pos_spec.rb
@@ -1,8 +1,35 @@
require_relative '../../spec_helper'
-require_relative 'shared/pos'
describe "ARGF.pos" do
- it_behaves_like :argf_pos, :pos
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+ end
+
+ it "gives the correct position for each read operation" do
+ argf [@file1, @file2] do
+ size1 = File.size(@file1)
+ size2 = File.size(@file2)
+
+ @argf.read(2)
+ @argf.pos.should == 2
+ @argf.read(size1-2)
+ @argf.pos.should == size1
+ @argf.read(6)
+ @argf.pos.should == 6
+ @argf.rewind
+ @argf.pos.should == 0
+ @argf.read(size2)
+ @argf.pos.should == size2
+ end
+ end
+
+ it "raises an ArgumentError when called on a closed stream" do
+ argf [@file1] do
+ @argf.read
+ -> { @argf.pos }.should.raise(ArgumentError)
+ end
+ end
end
describe "ARGF.pos=" do
diff --git a/spec/ruby/core/argf/read_nonblock_spec.rb b/spec/ruby/core/argf/read_nonblock_spec.rb
index 804a459a62..5c6bd52d80 100644
--- a/spec/ruby/core/argf/read_nonblock_spec.rb
+++ b/spec/ruby/core/argf/read_nonblock_spec.rb
@@ -66,7 +66,7 @@ platform_is_not :windows do
argf ['-'] do
-> {
@argf.read_nonblock(4)
- }.should raise_error(IO::EAGAINWaitReadable)
+ }.should.raise(IO::EAGAINWaitReadable)
end
end
diff --git a/spec/ruby/core/argf/readchar_spec.rb b/spec/ruby/core/argf/readchar_spec.rb
index 4eca2efcf7..63632721ec 100644
--- a/spec/ruby/core/argf/readchar_spec.rb
+++ b/spec/ruby/core/argf/readchar_spec.rb
@@ -13,7 +13,7 @@ describe "ARGF.readchar" do
it "raises EOFError when end of stream reached" do
argf [@file1, @file2] do
- -> { while @argf.readchar; end }.should raise_error(EOFError)
+ -> { while @argf.readchar; end }.should.raise(EOFError)
end
end
end
diff --git a/spec/ruby/core/argf/readline_spec.rb b/spec/ruby/core/argf/readline_spec.rb
index db53c499e9..8c23549b1b 100644
--- a/spec/ruby/core/argf/readline_spec.rb
+++ b/spec/ruby/core/argf/readline_spec.rb
@@ -17,7 +17,7 @@ describe "ARGF.readline" do
it "raises an EOFError when reaching end of files" do
argf [@file1, @file2] do
- -> { while @argf.readline; end }.should raise_error(EOFError)
+ -> { while @argf.readline; end }.should.raise(EOFError)
end
end
end
diff --git a/spec/ruby/core/argf/readlines_spec.rb b/spec/ruby/core/argf/readlines_spec.rb
index 30be936dab..156bb6a33f 100644
--- a/spec/ruby/core/argf/readlines_spec.rb
+++ b/spec/ruby/core/argf/readlines_spec.rb
@@ -1,6 +1,24 @@
require_relative '../../spec_helper'
-require_relative 'shared/readlines'
describe "ARGF.readlines" do
- it_behaves_like :argf_readlines, :readlines
+ before :each do
+ @file1 = fixture __FILE__, "file1.txt"
+ @file2 = fixture __FILE__, "file2.txt"
+
+ @lines = File.readlines(@file1)
+ @lines += File.readlines(@file2)
+ end
+
+ it "reads all lines of all files" do
+ argf [@file1, @file2] do
+ @argf.readlines.should == @lines
+ end
+ end
+
+ it "returns an empty Array when end of stream reached" do
+ argf [@file1, @file2] do
+ @argf.read
+ @argf.readlines.should == []
+ end
+ end
end
diff --git a/spec/ruby/core/argf/readpartial_spec.rb b/spec/ruby/core/argf/readpartial_spec.rb
index 5e284b3423..9f04e72cc2 100644
--- a/spec/ruby/core/argf/readpartial_spec.rb
+++ b/spec/ruby/core/argf/readpartial_spec.rb
@@ -16,7 +16,7 @@ describe "ARGF.readpartial" do
it "raises an ArgumentError if called without a maximum read length" do
argf [@file1_name] do
- -> { @argf.readpartial }.should raise_error(ArgumentError)
+ -> { @argf.readpartial }.should.raise(ArgumentError)
end
end
@@ -29,7 +29,7 @@ describe "ARGF.readpartial" do
it "clears output buffer even if EOFError is raised because @argf is at end" do
begin
- output = "to be cleared"
+ output = +"to be cleared"
argf [@file1_name] do
@argf.read
@@ -59,8 +59,8 @@ describe "ARGF.readpartial" do
@argf.readpartial(@file1.size)
@argf.readpartial(1)
@argf.readpartial(@file2.size)
- -> { @argf.readpartial(1) }.should raise_error(EOFError)
- -> { @argf.readpartial(1) }.should raise_error(EOFError)
+ -> { @argf.readpartial(1) }.should.raise(EOFError)
+ -> { @argf.readpartial(1) }.should.raise(EOFError)
end
end
@@ -69,7 +69,7 @@ describe "ARGF.readpartial" do
print ARGF.readpartial(#{@stdin.size})
ARGF.readpartial(1) rescue print $!.class
STR
- stdin = ruby_exe(ruby_str, args: "< #{@stdin_name}", escape: true)
+ stdin = ruby_exe(ruby_str, args: "< #{@stdin_name}")
stdin.should == @stdin + "EOFError"
end
end
diff --git a/spec/ruby/core/argf/rewind_spec.rb b/spec/ruby/core/argf/rewind_spec.rb
index b29f0b75b7..9255f790fe 100644
--- a/spec/ruby/core/argf/rewind_spec.rb
+++ b/spec/ruby/core/argf/rewind_spec.rb
@@ -33,7 +33,7 @@ describe "ARGF.rewind" do
it "raises an ArgumentError when end of stream reached" do
argf [@file1_name, @file2_name] do
@argf.read
- -> { @argf.rewind }.should raise_error(ArgumentError)
+ -> { @argf.rewind }.should.raise(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/argf/seek_spec.rb b/spec/ruby/core/argf/seek_spec.rb
index 2b73bd46fb..c1ea1ea438 100644
--- a/spec/ruby/core/argf/seek_spec.rb
+++ b/spec/ruby/core/argf/seek_spec.rb
@@ -57,7 +57,7 @@ describe "ARGF.seek" do
it "takes at least one argument (offset)" do
argf [@file1_name] do
- -> { @argf.seek }.should raise_error(ArgumentError)
+ -> { @argf.seek }.should.raise(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/argf/shared/each_byte.rb b/spec/ruby/core/argf/shared/each_byte.rb
deleted file mode 100644
index 6b1dc1dae2..0000000000
--- a/spec/ruby/core/argf/shared/each_byte.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-describe :argf_each_byte, shared: true do
- before :each do
- @file1_name = fixture __FILE__, "file1.txt"
- @file2_name = fixture __FILE__, "file2.txt"
-
- @bytes = []
- File.read(@file1_name).each_byte { |b| @bytes << b }
- File.read(@file2_name).each_byte { |b| @bytes << b }
- end
-
- it "yields each byte of all streams to the passed block" do
- argf [@file1_name, @file2_name] do
- bytes = []
- @argf.send(@method) { |b| bytes << b }
- bytes.should == @bytes
- end
- end
-
- it "returns self when passed a block" do
- argf [@file1_name, @file2_name] do
- @argf.send(@method) {}.should equal(@argf)
- end
- end
-
- it "returns an Enumerator when passed no block" do
- argf [@file1_name, @file2_name] do
- enum = @argf.send(@method)
- enum.should be_an_instance_of(Enumerator)
-
- bytes = []
- enum.each { |b| bytes << b }
- bytes.should == @bytes
- end
- end
-
- describe "when no block is given" do
- it "returns an Enumerator" do
- argf [@file1_name, @file2_name] do
- enum = @argf.send(@method)
- enum.should be_an_instance_of(Enumerator)
-
- bytes = []
- enum.each { |b| bytes << b }
- bytes.should == @bytes
- end
- end
-
- describe "returned Enumerator" do
- describe "size" do
- it "should return nil" do
- argf [@file1_name, @file2_name] do
- @argf.send(@method).size.should == nil
- end
- end
- end
- end
- end
-end
diff --git a/spec/ruby/core/argf/shared/each_char.rb b/spec/ruby/core/argf/shared/each_char.rb
deleted file mode 100644
index 9e333ecc5b..0000000000
--- a/spec/ruby/core/argf/shared/each_char.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-describe :argf_each_char, shared: true do
- before :each do
- @file1_name = fixture __FILE__, "file1.txt"
- @file2_name = fixture __FILE__, "file2.txt"
-
- @chars = []
- File.read(@file1_name).each_char { |c| @chars << c }
- File.read(@file2_name).each_char { |c| @chars << c }
- end
-
- it "yields each char of all streams to the passed block" do
- argf [@file1_name, @file2_name] do
- chars = []
- @argf.send(@method) { |c| chars << c }
- chars.should == @chars
- end
- end
-
- it "returns self when passed a block" do
- argf [@file1_name, @file2_name] do
- @argf.send(@method) {}.should equal(@argf)
- end
- end
-
- it "returns an Enumerator when passed no block" do
- argf [@file1_name, @file2_name] do
- enum = @argf.send(@method)
- enum.should be_an_instance_of(Enumerator)
-
- chars = []
- enum.each { |c| chars << c }
- chars.should == @chars
- end
- end
-
- describe "when no block is given" do
- it "returns an Enumerator" do
- argf [@file1_name, @file2_name] do
- enum = @argf.send(@method)
- enum.should be_an_instance_of(Enumerator)
-
- chars = []
- enum.each { |c| chars << c }
- chars.should == @chars
- end
- end
-
- describe "returned Enumerator" do
- describe "size" do
- it "should return nil" do
- argf [@file1_name, @file2_name] do
- @argf.send(@method).size.should == nil
- end
- end
- end
- end
- end
-end
diff --git a/spec/ruby/core/argf/shared/each_codepoint.rb b/spec/ruby/core/argf/shared/each_codepoint.rb
deleted file mode 100644
index e2a2dfff46..0000000000
--- a/spec/ruby/core/argf/shared/each_codepoint.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-describe :argf_each_codepoint, shared: true do
- before :each do
- file1_name = fixture __FILE__, "file1.txt"
- file2_name = fixture __FILE__, "file2.txt"
- @filenames = [file1_name, file2_name]
-
- @codepoints = File.read(file1_name).codepoints
- @codepoints.concat File.read(file2_name).codepoints
- end
-
- it "is a public method" do
- argf @filenames do
- @argf.public_methods(false).should include(@method)
- end
- end
-
- it "does not require arguments" do
- argf @filenames do
- @argf.method(@method).arity.should == 0
- end
- end
-
- it "returns self when passed a block" do
- argf @filenames do
- @argf.send(@method) {}.should equal(@argf)
- end
- end
-
- it "returns an Enumerator when passed no block" do
- argf @filenames do
- @argf.send(@method).should be_an_instance_of(Enumerator)
- end
- end
-
- it "yields each codepoint of all streams" do
- argf @filenames do
- @argf.send(@method).to_a.should == @codepoints
- end
- end
-
- describe "when no block is given" do
- it "returns an Enumerator" do
- argf @filenames do
- @argf.send(@method).should be_an_instance_of(Enumerator)
- end
- end
-
- describe "returned Enumerator" do
- describe "size" do
- it "should return nil" do
- argf @filenames do
- @argf.send(@method).size.should == nil
- end
- end
- end
- end
- end
-end
diff --git a/spec/ruby/core/argf/shared/each_line.rb b/spec/ruby/core/argf/shared/each_line.rb
deleted file mode 100644
index c0ef77dc54..0000000000
--- a/spec/ruby/core/argf/shared/each_line.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-describe :argf_each_line, shared: true do
- before :each do
- @file1_name = fixture __FILE__, "file1.txt"
- @file2_name = fixture __FILE__, "file2.txt"
-
- @lines = File.readlines @file1_name
- @lines += File.readlines @file2_name
- end
-
- it "is a public method" do
- argf [@file1_name, @file2_name] do
- @argf.public_methods(false).should include(@method)
- end
- end
-
- it "requires multiple arguments" do
- argf [@file1_name, @file2_name] do
- @argf.method(@method).arity.should < 0
- end
- end
-
- it "reads each line of files" do
- argf [@file1_name, @file2_name] do
- lines = []
- @argf.send(@method) { |b| lines << b }
- lines.should == @lines
- end
- end
-
- it "returns self when passed a block" do
- argf [@file1_name, @file2_name] do
- @argf.send(@method) {}.should equal(@argf)
- end
- end
-
- describe "with a separator" do
- it "yields each separated section of all streams" do
- argf [@file1_name, @file2_name] do
- @argf.send(@method, '.').to_a.should ==
- (File.readlines(@file1_name, '.') + File.readlines(@file2_name, '.'))
- end
- end
- end
-
- describe "when no block is given" do
- it "returns an Enumerator" do
- argf [@file1_name, @file2_name] do
- @argf.send(@method).should be_an_instance_of(Enumerator)
- end
- end
-
- describe "returned Enumerator" do
- describe "size" do
- it "should return nil" do
- argf [@file1_name, @file2_name] do
- @argf.send(@method).size.should == nil
- end
- end
- end
- end
- end
-end
diff --git a/spec/ruby/core/argf/shared/eof.rb b/spec/ruby/core/argf/shared/eof.rb
deleted file mode 100644
index 0e684f943f..0000000000
--- a/spec/ruby/core/argf/shared/eof.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-describe :argf_eof, shared: true do
- before :each do
- @file1 = fixture __FILE__, "file1.txt"
- @file2 = fixture __FILE__, "file2.txt"
- end
-
- # NOTE: this test assumes that fixtures files have two lines each
- it "returns true when reaching the end of a file" do
- argf [@file1, @file2] do
- result = []
- while @argf.gets
- result << @argf.send(@method)
- end
- result.should == [false, true, false, true]
- end
- end
-
- it "raises IOError when called on a closed stream" do
- argf [@file1] do
- @argf.read
- -> { @argf.send(@method) }.should raise_error(IOError)
- end
- end
-end
diff --git a/spec/ruby/core/argf/shared/filename.rb b/spec/ruby/core/argf/shared/filename.rb
deleted file mode 100644
index f47c673dc0..0000000000
--- a/spec/ruby/core/argf/shared/filename.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-describe :argf_filename, shared: true do
- before :each do
- @file1 = fixture __FILE__, "file1.txt"
- @file2 = fixture __FILE__, "file2.txt"
- end
-
- # NOTE: this test assumes that fixtures files have two lines each
- it "returns the current file name on each file" do
- argf [@file1, @file2] do
- result = []
- # returns first current file even when not yet open
- result << @argf.send(@method)
- result << @argf.send(@method) while @argf.gets
- # returns last current file even when closed
- result << @argf.send(@method)
-
- result.map! { |f| File.expand_path(f) }
- result.should == [@file1, @file1, @file1, @file2, @file2, @file2]
- end
- end
-
- # NOTE: this test assumes that fixtures files have two lines each
- it "sets the $FILENAME global variable with the current file name on each file" do
- script = fixture __FILE__, "filename.rb"
- out = ruby_exe(script, args: [@file1, @file2])
- out.should == "#{@file1}\n#{@file1}\n#{@file2}\n#{@file2}\n#{@file2}\n"
- end
-end
diff --git a/spec/ruby/core/argf/shared/fileno.rb b/spec/ruby/core/argf/shared/fileno.rb
deleted file mode 100644
index 5d674048e2..0000000000
--- a/spec/ruby/core/argf/shared/fileno.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-describe :argf_fileno, shared: true do
- before :each do
- @file1 = fixture __FILE__, "file1.txt"
- @file2 = fixture __FILE__, "file2.txt"
- end
-
- # NOTE: this test assumes that fixtures files have two lines each
- it "returns the current file number on each file" do
- argf [@file1, @file2] do
- result = []
- # returns first current file even when not yet open
- result << @argf.send(@method) while @argf.gets
- # returns last current file even when closed
- result.map { |d| d.class }.should == [Fixnum, Fixnum, Fixnum, Fixnum]
- end
- end
-
- it "raises an ArgumentError when called on a closed stream" do
- argf [@file1] do
- @argf.read
- -> { @argf.send(@method) }.should raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/ruby/core/argf/shared/getc.rb b/spec/ruby/core/argf/shared/getc.rb
index 8be39c60b6..d63372d9d7 100644
--- a/spec/ruby/core/argf/shared/getc.rb
+++ b/spec/ruby/core/argf/shared/getc.rb
@@ -9,7 +9,7 @@ describe :argf_getc, shared: true do
it "reads each char of files" do
argf [@file1, @file2] do
- chars = ""
+ chars = +""
@chars.size.times { chars << @argf.send(@method) }
chars.should == @chars
end
diff --git a/spec/ruby/core/argf/shared/pos.rb b/spec/ruby/core/argf/shared/pos.rb
deleted file mode 100644
index 9836d5f1e4..0000000000
--- a/spec/ruby/core/argf/shared/pos.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-describe :argf_pos, shared: true do
- before :each do
- @file1 = fixture __FILE__, "file1.txt"
- @file2 = fixture __FILE__, "file2.txt"
- end
-
- it "gives the correct position for each read operation" do
- argf [@file1, @file2] do
- size1 = File.size(@file1)
- size2 = File.size(@file2)
-
- @argf.read(2)
- @argf.send(@method).should == 2
- @argf.read(size1-2)
- @argf.send(@method).should == size1
- @argf.read(6)
- @argf.send(@method).should == 6
- @argf.rewind
- @argf.send(@method).should == 0
- @argf.read(size2)
- @argf.send(@method).should == size2
- end
- end
-
- it "raises an ArgumentError when called on a closed stream" do
- argf [@file1] do
- @argf.read
- -> { @argf.send(@method) }.should raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/ruby/core/argf/shared/read.rb b/spec/ruby/core/argf/shared/read.rb
index fe903983c0..e76d022139 100644
--- a/spec/ruby/core/argf/shared/read.rb
+++ b/spec/ruby/core/argf/shared/read.rb
@@ -15,7 +15,7 @@ describe :argf_read, shared: true do
it "treats second argument as an output buffer" do
argf [@file1_name] do
- buffer = ""
+ buffer = +""
@argf.send(@method, @file1.size, buffer)
buffer.should == @file1
end
@@ -23,7 +23,7 @@ describe :argf_read, shared: true do
it "clears output buffer before appending to it" do
argf [@file1_name] do
- buffer = "to be cleared"
+ buffer = +"to be cleared"
@argf.send(@method, @file1.size, buffer)
buffer.should == @file1
end
diff --git a/spec/ruby/core/argf/shared/readlines.rb b/spec/ruby/core/argf/shared/readlines.rb
deleted file mode 100644
index 505fa94acb..0000000000
--- a/spec/ruby/core/argf/shared/readlines.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-describe :argf_readlines, shared: true do
- before :each do
- @file1 = fixture __FILE__, "file1.txt"
- @file2 = fixture __FILE__, "file2.txt"
-
- @lines = File.readlines(@file1)
- @lines += File.readlines(@file2)
- end
-
- it "reads all lines of all files" do
- argf [@file1, @file2] do
- @argf.send(@method).should == @lines
- end
- end
-
- it "returns an empty Array when end of stream reached" do
- argf [@file1, @file2] do
- @argf.read
- @argf.send(@method).should == []
- end
- end
-end
diff --git a/spec/ruby/core/argf/skip_spec.rb b/spec/ruby/core/argf/skip_spec.rb
index 0181801c2d..bb1c0ae110 100644
--- a/spec/ruby/core/argf/skip_spec.rb
+++ b/spec/ruby/core/argf/skip_spec.rb
@@ -37,6 +37,6 @@ describe "ARGF.skip" do
# which as a side-effect calls argf.file which will initialize
# internals of ARGF enough for this to work.
it "has no effect when nothing has been processed yet" do
- -> { ARGF.class.new(@file1_name).skip }.should_not raise_error
+ -> { ARGF.class.new(@file1_name).skip }.should_not.raise
end
end
diff --git a/spec/ruby/core/argf/tell_spec.rb b/spec/ruby/core/argf/tell_spec.rb
index 16d9f29920..bb28df74a2 100644
--- a/spec/ruby/core/argf/tell_spec.rb
+++ b/spec/ruby/core/argf/tell_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/pos'
describe "ARGF.tell" do
- it_behaves_like :argf_pos, :tell
+ it "is an alias of ARGF.pos" do
+ ARGF.method(:tell).should == ARGF.method(:pos)
+ end
end
diff --git a/spec/ruby/core/argf/to_a_spec.rb b/spec/ruby/core/argf/to_a_spec.rb
index b17a93db33..d95dc732ec 100644
--- a/spec/ruby/core/argf/to_a_spec.rb
+++ b/spec/ruby/core/argf/to_a_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/readlines'
describe "ARGF.to_a" do
- it_behaves_like :argf_readlines, :to_a
+ it "is an alias of ARGF.readlines" do
+ ARGF.method(:to_a).should == ARGF.method(:readlines)
+ end
end
diff --git a/spec/ruby/core/argf/to_i_spec.rb b/spec/ruby/core/argf/to_i_spec.rb
index 2183de6cd4..e8df378f4e 100644
--- a/spec/ruby/core/argf/to_i_spec.rb
+++ b/spec/ruby/core/argf/to_i_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/fileno'
describe "ARGF.to_i" do
- it_behaves_like :argf_fileno, :to_i
+ it "is an alias of ARGF.fileno" do
+ ARGF.method(:to_i).should == ARGF.method(:fileno)
+ end
end
diff --git a/spec/ruby/core/argf/to_io_spec.rb b/spec/ruby/core/argf/to_io_spec.rb
index 062383d291..ab5de58bcf 100644
--- a/spec/ruby/core/argf/to_io_spec.rb
+++ b/spec/ruby/core/argf/to_io_spec.rb
@@ -15,7 +15,7 @@ describe "ARGF.to_io" do
result << @argf.to_io
end
- result.each { |io| io.should be_kind_of(IO) }
+ result.each { |io| io.should.is_a?(IO) }
result[0].should == result[1]
result[2].should == result[3]
end
diff --git a/spec/ruby/core/array/all_spec.rb b/spec/ruby/core/array/all_spec.rb
new file mode 100644
index 0000000000..680e8c26fa
--- /dev/null
+++ b/spec/ruby/core/array/all_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
+
+describe "Array#all?" do
+ @value_to_return = -> _ { true }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :all?
+
+ it "ignores the block if there is an argument" do
+ -> {
+ ['bar', 'foobar'].all?(/bar/) { false }.should == true
+ }.should complain(/given block not used/)
+ end
+end
diff --git a/spec/ruby/core/array/allocate_spec.rb b/spec/ruby/core/array/allocate_spec.rb
index 04f7c0d0ad..c9eceef590 100644
--- a/spec/ruby/core/array/allocate_spec.rb
+++ b/spec/ruby/core/array/allocate_spec.rb
@@ -3,7 +3,7 @@ require_relative '../../spec_helper'
describe "Array.allocate" do
it "returns an instance of Array" do
ary = Array.allocate
- ary.should be_an_instance_of(Array)
+ ary.should.instance_of?(Array)
end
it "returns a fully-formed instance of Array" do
@@ -14,6 +14,6 @@ describe "Array.allocate" do
end
it "does not accept any arguments" do
- -> { Array.allocate(1) }.should raise_error(ArgumentError)
+ -> { Array.allocate(1) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/array/any_spec.rb b/spec/ruby/core/array/any_spec.rb
index 09d949fe6e..b51ce62f0f 100644
--- a/spec/ruby/core/array/any_spec.rb
+++ b/spec/ruby/core/array/any_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#any?" do
describe 'with no block given (a default block of { |x| x } is implicit)' do
@@ -19,6 +20,9 @@ describe "Array#any?" do
end
describe 'with a block given' do
+ @value_to_return = -> _ { false }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :any?
+
it 'is false if the array is empty' do
empty_array = []
empty_array.any? {|v| 1 == 1 }.should == false
@@ -34,4 +38,12 @@ describe "Array#any?" do
array_with_members.any? {|v| v == 42 }.should == false
end
end
+
+ describe 'when given a pattern argument' do
+ it "ignores the block if there is an argument" do
+ -> {
+ ['bar', 'foobar'].any?(/bar/) { false }.should == true
+ }.should complain(/given block not used/)
+ end
+ end
end
diff --git a/spec/ruby/core/array/append_spec.rb b/spec/ruby/core/array/append_spec.rb
index c12473dc07..5480d9f65e 100644
--- a/spec/ruby/core/array/append_spec.rb
+++ b/spec/ruby/core/array/append_spec.rb
@@ -1,6 +1,5 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/push'
describe "Array#<<" do
it "pushes the object onto the end of the array" do
@@ -10,8 +9,8 @@ describe "Array#<<" do
it "returns self to allow chaining" do
a = []
b = a
- (a << 1).should equal(b)
- (a << 2 << 3).should equal(b)
+ (a << 1).should.equal?(b)
+ (a << 2 << 3).should.equal?(b)
end
it "correctly resizes the Array" do
@@ -31,10 +30,12 @@ describe "Array#<<" do
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array << 5 }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array << 5 }.should.raise(FrozenError)
end
end
describe "Array#append" do
- it_behaves_like :array_push, :append
+ it "is an alias of Array#push" do
+ Array.instance_method(:append).should == Array.instance_method(:push)
+ end
end
diff --git a/spec/ruby/core/array/assoc_spec.rb b/spec/ruby/core/array/assoc_spec.rb
index f8479d763c..a5026cf5d4 100644
--- a/spec/ruby/core/array/assoc_spec.rb
+++ b/spec/ruby/core/array/assoc_spec.rb
@@ -6,18 +6,18 @@ describe "Array#assoc" do
s1 = ["colors", "red", "blue", "green"]
s2 = [:letters, "a", "b", "c"]
s3 = [4]
- s4 = ["colors", "cyan", "yellow", "magenda"]
+ s4 = ["colors", "cyan", "yellow", "magenta"]
s5 = [:letters, "a", "i", "u"]
s_nil = [nil, nil]
a = [s1, s2, s3, s4, s5, s_nil]
- a.assoc(s1.first).should equal(s1)
- a.assoc(s2.first).should equal(s2)
- a.assoc(s3.first).should equal(s3)
- a.assoc(s4.first).should equal(s1)
- a.assoc(s5.first).should equal(s2)
- a.assoc(s_nil.first).should equal(s_nil)
- a.assoc(4).should equal(s3)
- a.assoc("key not in array").should be_nil
+ a.assoc(s1.first).should.equal?(s1)
+ a.assoc(s2.first).should.equal?(s2)
+ a.assoc(s3.first).should.equal?(s3)
+ a.assoc(s4.first).should.equal?(s1)
+ a.assoc(s5.first).should.equal?(s2)
+ a.assoc(s_nil.first).should.equal?(s_nil)
+ a.assoc(4).should.equal?(s3)
+ a.assoc("key not in array").should == nil
end
it "calls == on first element of each array" do
@@ -25,16 +25,28 @@ describe "Array#assoc" do
key2 = mock('key2')
items = [['not it', 1], [ArraySpecs::AssocKey.new, 2], ['na', 3]]
- items.assoc(key1).should equal(items[1])
- items.assoc(key2).should be_nil
+ items.assoc(key1).should.equal?(items[1])
+ items.assoc(key2).should == nil
end
it "ignores any non-Array elements" do
- [1, 2, 3].assoc(2).should be_nil
+ [1, 2, 3].assoc(2).should == nil
s1 = [4]
s2 = [5, 4, 3]
a = ["foo", [], s1, s2, nil, []]
- a.assoc(s1.first).should equal(s1)
- a.assoc(s2.first).should equal(s2)
+ a.assoc(s1.first).should.equal?(s1)
+ a.assoc(s2.first).should.equal?(s2)
+ end
+
+ it "calls to_ary on non-array elements" do
+ s1 = [1, 2]
+ s2 = ArraySpecs::ArrayConvertible.new(2, 3)
+ a = [s1, s2]
+
+ s1.should_not_receive(:to_ary)
+ a.assoc(s1.first).should.equal?(s1)
+
+ a.assoc(2).should == [2, 3]
+ s2.called.should.equal?(:to_ary)
end
end
diff --git a/spec/ruby/core/array/at_spec.rb b/spec/ruby/core/array/at_spec.rb
index 8bc789fef7..3c7c99fdff 100644
--- a/spec/ruby/core/array/at_spec.rb
+++ b/spec/ruby/core/array/at_spec.rb
@@ -47,10 +47,10 @@ describe "Array#at" do
end
it "raises a TypeError when the passed argument can't be coerced to Integer" do
- -> { [].at("cat") }.should raise_error(TypeError)
+ -> { [].at("cat") }.should.raise(TypeError)
end
it "raises an ArgumentError when 2 or more arguments are passed" do
- -> { [:a, :b].at(0,1) }.should raise_error(ArgumentError)
+ -> { [:a, :b].at(0,1) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/array/bsearch_index_spec.rb b/spec/ruby/core/array/bsearch_index_spec.rb
index aafded178d..e1d5eb66bb 100644
--- a/spec/ruby/core/array/bsearch_index_spec.rb
+++ b/spec/ruby/core/array/bsearch_index_spec.rb
@@ -8,11 +8,11 @@ describe "Array#bsearch_index" do
end
it "returns an Enumerator" do
- @enum.should be_an_instance_of(Enumerator)
+ @enum.should.instance_of?(Enumerator)
end
it "returns an Enumerator with unknown size" do
- @enum.size.should be_nil
+ @enum.size.should == nil
end
it "returns index of element when block condition is satisfied" do
@@ -21,11 +21,11 @@ describe "Array#bsearch_index" do
end
it "raises a TypeError when block returns a String" do
- -> { [1, 2, 3].bsearch_index { "not ok" } }.should raise_error(TypeError)
+ -> { [1, 2, 3].bsearch_index { "not ok" } }.should.raise(TypeError)
end
it "returns nil when block is empty" do
- [1, 2, 3].bsearch_index {}.should be_nil
+ [1, 2, 3].bsearch_index {}.should == nil
end
context "minimum mode" do
@@ -40,8 +40,8 @@ describe "Array#bsearch_index" do
end
it "returns nil when block condition is never satisfied" do
- @array.bsearch_index { false }.should be_nil
- @array.bsearch_index { |x| x >= 100 }.should be_nil
+ @array.bsearch_index { false }.should == nil
+ @array.bsearch_index { |x| x >= 100 }.should == nil
end
end
@@ -51,34 +51,30 @@ describe "Array#bsearch_index" do
end
it "returns the index of any matched elements where element is between 4 <= x < 8" do
- [1, 2].should include(@array.bsearch_index { |x| 1 - x / 4 })
+ [1, 2].should.include?(@array.bsearch_index { |x| 1 - x / 4 })
end
it "returns the index of any matched elements where element is between 8 <= x < 10" do
- @array.bsearch_index { |x| 4 - x / 2 }.should be_nil
+ @array.bsearch_index { |x| 4 - x / 2 }.should == nil
end
it "returns nil when block never returns 0" do
- @array.bsearch_index { |x| 1 }.should be_nil
- @array.bsearch_index { |x| -1 }.should be_nil
- end
-
- it "returns the middle element when block always returns zero" do
- @array.bsearch_index { |x| 0 }.should == 2
+ @array.bsearch_index { |x| 1 }.should == nil
+ @array.bsearch_index { |x| -1 }.should == nil
end
context "magnitude does not effect the result" do
it "returns the index of any matched elements where element is between 4n <= xn < 8n" do
- [1, 2].should include(@array.bsearch_index { |x| (1 - x / 4) * (2**100) })
+ [1, 2].should.include?(@array.bsearch_index { |x| (1 - x / 4) * (2**100) })
end
it "returns nil when block never returns 0" do
- @array.bsearch_index { |x| 1 * (2**100) }.should be_nil
- @array.bsearch_index { |x| (-1) * (2**100) }.should be_nil
+ @array.bsearch_index { |x| 1 * (2**100) }.should == nil
+ @array.bsearch_index { |x| (-1) * (2**100) }.should == nil
end
- it "handles values from Bignum#coerce" do
- [1, 2].should include(@array.bsearch_index { |x| (2**100).coerce((1 - x / 4) * (2**100)).first })
+ it "handles values from Integer#coerce" do
+ [1, 2].should.include?(@array.bsearch_index { |x| (2**100).coerce((1 - x / 4) * (2**100)).first })
end
end
end
diff --git a/spec/ruby/core/array/bsearch_spec.rb b/spec/ruby/core/array/bsearch_spec.rb
index 8fa6245dbf..12aec60654 100644
--- a/spec/ruby/core/array/bsearch_spec.rb
+++ b/spec/ruby/core/array/bsearch_spec.rb
@@ -3,26 +3,26 @@ require_relative '../enumerable/shared/enumeratorized'
describe "Array#bsearch" do
it "returns an Enumerator when not passed a block" do
- [1].bsearch.should be_an_instance_of(Enumerator)
+ [1].bsearch.should.instance_of?(Enumerator)
end
it_behaves_like :enumeratorized_with_unknown_size, :bsearch, [1,2,3]
it "raises a TypeError if the block returns an Object" do
- -> { [1].bsearch { Object.new } }.should raise_error(TypeError)
+ -> { [1].bsearch { Object.new } }.should.raise(TypeError)
end
it "raises a TypeError if the block returns a String" do
- -> { [1].bsearch { "1" } }.should raise_error(TypeError)
+ -> { [1].bsearch { "1" } }.should.raise(TypeError)
end
context "with a block returning true or false" do
it "returns nil if the block returns false for every element" do
- [0, 1, 2, 3].bsearch { |x| x > 3 }.should be_nil
+ [0, 1, 2, 3].bsearch { |x| x > 3 }.should == nil
end
it "returns nil if the block returns nil for every element" do
- [0, 1, 2, 3].bsearch { |x| nil }.should be_nil
+ [0, 1, 2, 3].bsearch { |x| nil }.should == nil
end
it "returns element at zero if the block returns true for every element" do
@@ -38,21 +38,21 @@ describe "Array#bsearch" do
context "with a block returning negative, zero, positive numbers" do
it "returns nil if the block returns less than zero for every element" do
- [0, 1, 2, 3].bsearch { |x| x <=> 5 }.should be_nil
+ [0, 1, 2, 3].bsearch { |x| x <=> 5 }.should == nil
end
it "returns nil if the block returns greater than zero for every element" do
- [0, 1, 2, 3].bsearch { |x| x <=> -1 }.should be_nil
+ [0, 1, 2, 3].bsearch { |x| x <=> -1 }.should == nil
end
it "returns nil if the block never returns zero" do
- [0, 1, 3, 4].bsearch { |x| x <=> 2 }.should be_nil
+ [0, 1, 3, 4].bsearch { |x| x <=> 2 }.should == nil
end
it "accepts (+/-)Float::INFINITY from the block" do
- [0, 1, 3, 4].bsearch { |x| Float::INFINITY }.should be_nil
- [0, 1, 3, 4].bsearch { |x| -Float::INFINITY }.should be_nil
+ [0, 1, 3, 4].bsearch { |x| Float::INFINITY }.should == nil
+ [0, 1, 3, 4].bsearch { |x| -Float::INFINITY }.should == nil
end
it "returns an element at an index for which block returns 0.0" do
@@ -62,17 +62,17 @@ describe "Array#bsearch" do
it "returns an element at an index for which block returns 0" do
result = [0, 1, 2, 3, 4].bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
- [1, 2].should include(result)
+ [1, 2].should.include?(result)
end
end
context "with a block that calls break" do
it "returns nil if break is called without a value" do
- ['a', 'b', 'c'].bsearch { |v| break }.should be_nil
+ ['a', 'b', 'c'].bsearch { |v| break }.should == nil
end
it "returns nil if break is called with a nil value" do
- ['a', 'b', 'c'].bsearch { |v| break nil }.should be_nil
+ ['a', 'b', 'c'].bsearch { |v| break nil }.should == nil
end
it "returns object if break is called with an object" do
diff --git a/spec/ruby/core/array/clear_spec.rb b/spec/ruby/core/array/clear_spec.rb
index bddc672d3b..15778f864f 100644
--- a/spec/ruby/core/array/clear_spec.rb
+++ b/spec/ruby/core/array/clear_spec.rb
@@ -4,13 +4,13 @@ require_relative 'fixtures/classes'
describe "Array#clear" do
it "removes all elements" do
a = [1, 2, 3, 4]
- a.clear.should equal(a)
+ a.clear.should.equal?(a)
a.should == []
end
it "returns self" do
a = [1]
- a.should equal a.clear
+ a.should.equal? a.clear
end
it "leaves the Array empty" do
@@ -20,33 +20,13 @@ describe "Array#clear" do
a.size.should == 0
end
- ruby_version_is ''...'2.7' do
- it "keeps tainted status" do
- a = [1]
- a.taint
- a.tainted?.should be_true
- a.clear
- a.tainted?.should be_true
- end
- end
-
it "does not accept any arguments" do
- -> { [1].clear(true) }.should raise_error(ArgumentError)
- end
-
- ruby_version_is ''...'2.7' do
- it "keeps untrusted status" do
- a = [1]
- a.untrust
- a.untrusted?.should be_true
- a.clear
- a.untrusted?.should be_true
- end
+ -> { [1].clear(true) }.should.raise(ArgumentError)
end
it "raises a FrozenError on a frozen array" do
a = [1]
a.freeze
- -> { a.clear }.should raise_error(FrozenError)
+ -> { a.clear }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/array/clone_spec.rb b/spec/ruby/core/array/clone_spec.rb
index e22a6c6d53..7ce9d40a81 100644
--- a/spec/ruby/core/array/clone_spec.rb
+++ b/spec/ruby/core/array/clone_spec.rb
@@ -23,9 +23,9 @@ describe "Array#clone" do
aa = a.clone
bb = b.clone
- a.respond_to?(:a_singleton_method).should be_true
- b.respond_to?(:a_singleton_method).should be_false
- aa.respond_to?(:a_singleton_method).should be_true
- bb.respond_to?(:a_singleton_method).should be_false
+ a.respond_to?(:a_singleton_method).should == true
+ b.respond_to?(:a_singleton_method).should == false
+ aa.respond_to?(:a_singleton_method).should == true
+ bb.respond_to?(:a_singleton_method).should == false
end
end
diff --git a/spec/ruby/core/array/collect_spec.rb b/spec/ruby/core/array/collect_spec.rb
index 0ad4c283b1..43a539f805 100644
--- a/spec/ruby/core/array/collect_spec.rb
+++ b/spec/ruby/core/array/collect_spec.rb
@@ -1,11 +1,143 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/collect'
+require_relative '../enumerable/shared/enumeratorized'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#collect" do
- it_behaves_like :array_collect, :collect
+ it "returns a copy of array with each element replaced by the value returned by block" do
+ a = ['a', 'b', 'c', 'd']
+ b = a.collect { |i| i + '!' }
+ b.should == ["a!", "b!", "c!", "d!"]
+ b.should_not.equal? a
+ end
+
+ it "does not return subclass instance" do
+ ArraySpecs::MyArray[1, 2, 3].collect { |x| x + 1 }.should.instance_of?(Array)
+ end
+
+ it "does not change self" do
+ a = ['a', 'b', 'c', 'd']
+ a.collect { |i| i + '!' }
+ a.should == ['a', 'b', 'c', 'd']
+ end
+
+ it "returns the evaluated value of block if it broke in the block" do
+ a = ['a', 'b', 'c', 'd']
+ b = a.collect {|i|
+ if i == 'c'
+ break 0
+ else
+ i + '!'
+ end
+ }
+ b.should == 0
+ end
+
+ it "returns an Enumerator when no block given" do
+ a = [1, 2, 3]
+ a.collect.should.instance_of?(Enumerator)
+ end
+
+ it "raises an ArgumentError when no block and with arguments" do
+ a = [1, 2, 3]
+ -> {
+ a.collect(:foo)
+ }.should.raise(ArgumentError)
+ end
+
+ before :each do
+ @object = [1, 2, 3, 4]
+ end
+ it_behaves_like :enumeratorized_with_origin_size, :collect
+
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :collect
end
describe "Array#collect!" do
- it_behaves_like :array_collect_b, :collect!
+ it "replaces each element with the value returned by block" do
+ a = [7, 9, 3, 5]
+ a.collect! { |i| i - 1 }.should.equal?(a)
+ a.should == [6, 8, 2, 4]
+ end
+
+ it "returns self" do
+ a = [1, 2, 3, 4, 5]
+ b = a.collect! {|i| i+1 }
+ a.should.equal? b
+ end
+
+ it "returns the evaluated value of block but its contents is partially modified, if it broke in the block" do
+ a = ['a', 'b', 'c', 'd']
+ b = a.collect! {|i|
+ if i == 'c'
+ break 0
+ else
+ i + '!'
+ end
+ }
+ b.should == 0
+ a.should == ['a!', 'b!', 'c', 'd']
+ end
+
+ it "returns an Enumerator when no block given, and the enumerator can modify the original array" do
+ a = [1, 2, 3]
+ enum = a.collect!
+ enum.should.instance_of?(Enumerator)
+ enum.each{|i| "#{i}!" }
+ a.should == ["1!", "2!", "3!"]
+ end
+
+ describe "when frozen" do
+ it "raises a FrozenError" do
+ -> { ArraySpecs.frozen_array.collect! {} }.should.raise(FrozenError)
+ end
+
+ it "raises a FrozenError when empty" do
+ -> { ArraySpecs.empty_frozen_array.collect! {} }.should.raise(FrozenError)
+ end
+
+ it "raises a FrozenError when calling #each on the returned Enumerator" do
+ enumerator = ArraySpecs.frozen_array.collect!
+ -> { enumerator.each {|x| x } }.should.raise(FrozenError)
+ end
+
+ it "raises a FrozenError when calling #each on the returned Enumerator when empty" do
+ enumerator = ArraySpecs.empty_frozen_array.collect!
+ -> { enumerator.each {|x| x } }.should.raise(FrozenError)
+ end
+ end
+
+ it "does not truncate the array is the block raises an exception" do
+ a = [1, 2, 3]
+ begin
+ a.collect! { raise StandardError, 'Oops' }
+ rescue
+ end
+
+ a.should == [1, 2, 3]
+ end
+
+ it "only changes elements before error is raised, keeping the element which raised an error." do
+ a = [1, 2, 3, 4]
+ begin
+ a.collect! do |e|
+ case e
+ when 1 then -1
+ when 2 then -2
+ when 3 then raise StandardError, 'Oops'
+ else 0
+ end
+ end
+ rescue StandardError
+ end
+
+ a.should == [-1, -2, 3, 4]
+ end
+
+ before :each do
+ @object = [1, 2, 3, 4]
+ end
+ it_behaves_like :enumeratorized_with_origin_size, :collect!
+
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :collect!
end
diff --git a/spec/ruby/core/array/combination_spec.rb b/spec/ruby/core/array/combination_spec.rb
index f16d6f98fc..ac570687ca 100644
--- a/spec/ruby/core/array/combination_spec.rb
+++ b/spec/ruby/core/array/combination_spec.rb
@@ -6,11 +6,11 @@ describe "Array#combination" do
end
it "returns an enumerator when no block is provided" do
- @array.combination(2).should be_an_instance_of(Enumerator)
+ @array.combination(2).should.instance_of?(Enumerator)
end
it "returns self when a block is given" do
- @array.combination(2){}.should equal(@array)
+ @array.combination(2){}.should.equal?(@array)
end
it "yields nothing for out of bounds length and return self" do
@@ -30,7 +30,7 @@ describe "Array#combination" do
it "yields a copy of self if the argument is the size of the receiver" do
r = @array.combination(4).to_a
r.should == [@array]
- r[0].should_not equal(@array)
+ r[0].should_not.equal?(@array)
end
it "yields [] when length is 0" do
diff --git a/spec/ruby/core/array/compact_spec.rb b/spec/ruby/core/array/compact_spec.rb
index aa3c1c0446..dbcd16da35 100644
--- a/spec/ruby/core/array/compact_spec.rb
+++ b/spec/ruby/core/array/compact_spec.rb
@@ -15,67 +15,37 @@ describe "Array#compact" do
it "does not return self" do
a = [1, 2, 3]
- a.compact.should_not equal(a)
+ a.compact.should_not.equal?(a)
end
it "does not return subclass instance for Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3, nil].compact.should be_an_instance_of(Array)
- end
-
- ruby_version_is ''...'2.7' do
- it "does not keep tainted status even if all elements are removed" do
- a = [nil, nil]
- a.taint
- a.compact.tainted?.should be_false
- end
-
- it "does not keep untrusted status even if all elements are removed" do
- a = [nil, nil]
- a.untrust
- a.compact.untrusted?.should be_false
- end
+ ArraySpecs::MyArray[1, 2, 3, nil].compact.should.instance_of?(Array)
end
end
describe "Array#compact!" do
it "removes all nil elements" do
a = ['a', nil, 'b', false, 'c']
- a.compact!.should equal(a)
+ a.compact!.should.equal?(a)
a.should == ["a", "b", false, "c"]
a = [nil, 'a', 'b', false, 'c']
- a.compact!.should equal(a)
+ a.compact!.should.equal?(a)
a.should == ["a", "b", false, "c"]
a = ['a', 'b', false, 'c', nil]
- a.compact!.should equal(a)
+ a.compact!.should.equal?(a)
a.should == ["a", "b", false, "c"]
end
it "returns self if some nil elements are removed" do
a = ['a', nil, 'b', false, 'c']
- a.compact!.should equal a
+ a.compact!.should.equal? a
end
it "returns nil if there are no nil elements to remove" do
[1, 2, false, 3].compact!.should == nil
end
- ruby_version_is ''...'2.7' do
- it "keeps tainted status even if all elements are removed" do
- a = [nil, nil]
- a.taint
- a.compact!
- a.tainted?.should be_true
- end
-
- it "keeps untrusted status even if all elements are removed" do
- a = [nil, nil]
- a.untrust
- a.compact!
- a.untrusted?.should be_true
- end
- end
-
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.compact! }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.compact! }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/array/comparison_spec.rb b/spec/ruby/core/array/comparison_spec.rb
index 5d1c3265f1..14e8931e5a 100644
--- a/spec/ruby/core/array/comparison_spec.rb
+++ b/spec/ruby/core/array/comparison_spec.rb
@@ -92,6 +92,6 @@ describe "Array#<=>" do
end
it "returns nil when the argument is not array-like" do
- ([] <=> false).should be_nil
+ ([] <=> false).should == nil
end
end
diff --git a/spec/ruby/core/array/concat_spec.rb b/spec/ruby/core/array/concat_spec.rb
index da6763bfd6..1e8d20c36c 100644
--- a/spec/ruby/core/array/concat_spec.rb
+++ b/spec/ruby/core/array/concat_spec.rb
@@ -4,12 +4,12 @@ require_relative 'fixtures/classes'
describe "Array#concat" do
it "returns the array itself" do
ary = [1,2,3]
- ary.concat([4,5,6]).equal?(ary).should be_true
+ ary.concat([4,5,6]).equal?(ary).should == true
end
it "appends the elements in the other array" do
ary = [1, 2, 3]
- ary.concat([9, 10, 11]).should equal(ary)
+ ary.concat([9, 10, 11]).should.equal?(ary)
ary.should == [1, 2, 3, 9, 10, 11]
ary.concat([])
ary.should == [1, 2, 3, 9, 10, 11]
@@ -33,70 +33,12 @@ describe "Array#concat" do
end
it "raises a FrozenError when Array is frozen and modification occurs" do
- -> { ArraySpecs.frozen_array.concat [1] }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.concat [1] }.should.raise(FrozenError)
end
# see [ruby-core:23666]
it "raises a FrozenError when Array is frozen and no modification occurs" do
- -> { ArraySpecs.frozen_array.concat([]) }.should raise_error(FrozenError)
- end
-
- ruby_version_is ''...'2.7' do
- it "keeps tainted status" do
- ary = [1, 2]
- ary.taint
- ary.concat([3])
- ary.tainted?.should be_true
- ary.concat([])
- ary.tainted?.should be_true
- end
-
- it "is not infected by the other" do
- ary = [1,2]
- other = [3]; other.taint
- ary.tainted?.should be_false
- ary.concat(other)
- ary.tainted?.should be_false
- end
-
- it "keeps the tainted status of elements" do
- ary = [ Object.new, Object.new, Object.new ]
- ary.each {|x| x.taint }
-
- ary.concat([ Object.new ])
- ary[0].tainted?.should be_true
- ary[1].tainted?.should be_true
- ary[2].tainted?.should be_true
- ary[3].tainted?.should be_false
- end
-
- it "keeps untrusted status" do
- ary = [1, 2]
- ary.untrust
- ary.concat([3])
- ary.untrusted?.should be_true
- ary.concat([])
- ary.untrusted?.should be_true
- end
-
- it "is not infected untrustedness by the other" do
- ary = [1,2]
- other = [3]; other.untrust
- ary.untrusted?.should be_false
- ary.concat(other)
- ary.untrusted?.should be_false
- end
-
- it "keeps the untrusted status of elements" do
- ary = [ Object.new, Object.new, Object.new ]
- ary.each {|x| x.untrust }
-
- ary.concat([ Object.new ])
- ary[0].untrusted?.should be_true
- ary[1].untrusted?.should be_true
- ary[2].untrusted?.should be_true
- ary[3].untrusted?.should be_false
- end
+ -> { ArraySpecs.frozen_array.concat([]) }.should.raise(FrozenError)
end
it "appends elements to an Array with enough capacity that has been shifted" do
@@ -126,7 +68,7 @@ describe "Array#concat" do
it "returns self when given no arguments" do
ary = [1, 2]
- ary.concat.should equal(ary)
+ ary.concat.should.equal?(ary)
ary.should == [1, 2]
end
end
diff --git a/spec/ruby/core/array/constructor_spec.rb b/spec/ruby/core/array/constructor_spec.rb
index 6f36074c45..c4398c535d 100644
--- a/spec/ruby/core/array/constructor_spec.rb
+++ b/spec/ruby/core/array/constructor_spec.rb
@@ -7,7 +7,7 @@ describe "Array.[]" do
Array.[](5, true, nil, 'a', "Ruby", obj).should == [5, true, nil, "a", "Ruby", obj]
a = ArraySpecs::MyArray.[](5, true, nil, 'a', "Ruby", obj)
- a.should be_an_instance_of(ArraySpecs::MyArray)
+ a.should.instance_of?(ArraySpecs::MyArray)
a.inspect.should == [5, true, nil, "a", "Ruby", obj].inspect
end
end
@@ -18,7 +18,7 @@ describe "Array[]" do
Array[5, true, nil, 'a', "Ruby", obj].should == Array.[](5, true, nil, "a", "Ruby", obj)
a = ArraySpecs::MyArray[5, true, nil, 'a', "Ruby", obj]
- a.should be_an_instance_of(ArraySpecs::MyArray)
+ a.should.instance_of?(ArraySpecs::MyArray)
a.inspect.should == [5, true, nil, "a", "Ruby", obj].inspect
end
end
diff --git a/spec/ruby/core/array/count_spec.rb b/spec/ruby/core/array/count_spec.rb
index eaf275aeb7..e778233c16 100644
--- a/spec/ruby/core/array/count_spec.rb
+++ b/spec/ruby/core/array/count_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#count" do
it "returns the number of elements" do
@@ -12,4 +13,14 @@ describe "Array#count" do
it "returns the number of element for which the block evaluates to true" do
[:a, :b, :c].count { |s| s != :b }.should == 2
end
+
+ it "ignores the block if there is an argument" do
+ -> {
+ [:a, :b, :b, :c].count(:b) { |e| e.size > 10 }.should == 2
+ }.should complain(/given block not used/)
+ end
+
+ context "when a block argument given" do
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :count
+ end
end
diff --git a/spec/ruby/core/array/cycle_spec.rb b/spec/ruby/core/array/cycle_spec.rb
index 7219b49883..29284257e9 100644
--- a/spec/ruby/core/array/cycle_spec.rb
+++ b/spec/ruby/core/array/cycle_spec.rb
@@ -10,17 +10,17 @@ describe "Array#cycle" do
end
it "does not yield and returns nil when the array is empty and passed value is an integer" do
- [].cycle(6, &@prc).should be_nil
+ [].cycle(6, &@prc).should == nil
ScratchPad.recorded.should == []
end
it "does not yield and returns nil when the array is empty and passed value is nil" do
- [].cycle(nil, &@prc).should be_nil
+ [].cycle(nil, &@prc).should == nil
ScratchPad.recorded.should == []
end
it "does not yield and returns nil when passed 0" do
- @array.cycle(0, &@prc).should be_nil
+ @array.cycle(0, &@prc).should == nil
ScratchPad.recorded.should == []
end
@@ -48,13 +48,13 @@ describe "Array#cycle" do
it "does not rescue StopIteration when not passed a count" do
-> do
@array.cycle { raise StopIteration }
- end.should raise_error(StopIteration)
+ end.should.raise(StopIteration)
end
it "does not rescue StopIteration when passed a count" do
-> do
@array.cycle(3) { raise StopIteration }
- end.should raise_error(StopIteration)
+ end.should.raise(StopIteration)
end
it "iterates the array Integer(count) times when passed a Float count" do
@@ -74,23 +74,23 @@ describe "Array#cycle" do
count = mock("cycle count 2")
count.should_receive(:to_int).and_return("2")
- -> { @array.cycle(count, &@prc) }.should raise_error(TypeError)
+ -> { @array.cycle(count, &@prc) }.should.raise(TypeError)
end
it "raises a TypeError if passed a String" do
- -> { @array.cycle("4") { } }.should raise_error(TypeError)
+ -> { @array.cycle("4") { } }.should.raise(TypeError)
end
it "raises a TypeError if passed an Object" do
- -> { @array.cycle(mock("cycle count")) { } }.should raise_error(TypeError)
+ -> { @array.cycle(mock("cycle count")) { } }.should.raise(TypeError)
end
it "raises a TypeError if passed true" do
- -> { @array.cycle(true) { } }.should raise_error(TypeError)
+ -> { @array.cycle(true) { } }.should.raise(TypeError)
end
it "raises a TypeError if passed false" do
- -> { @array.cycle(false) { } }.should raise_error(TypeError)
+ -> { @array.cycle(false) { } }.should.raise(TypeError)
end
before :all do
diff --git a/spec/ruby/core/array/deconstruct_spec.rb b/spec/ruby/core/array/deconstruct_spec.rb
index 2b07152dfc..11bb8e72c4 100644
--- a/spec/ruby/core/array/deconstruct_spec.rb
+++ b/spec/ruby/core/array/deconstruct_spec.rb
@@ -1,11 +1,9 @@
require_relative '../../spec_helper'
-ruby_version_is "2.7" do
- describe "Array#deconstruct" do
- it "returns self" do
- array = [1]
+describe "Array#deconstruct" do
+ it "returns self" do
+ array = [1]
- array.deconstruct.should equal array
- end
+ array.deconstruct.should.equal? array
end
end
diff --git a/spec/ruby/core/array/delete_at_spec.rb b/spec/ruby/core/array/delete_at_spec.rb
index fc225a03f1..1e298b6730 100644
--- a/spec/ruby/core/array/delete_at_spec.rb
+++ b/spec/ruby/core/array/delete_at_spec.rb
@@ -36,28 +36,6 @@ describe "Array#delete_at" do
end
it "raises a FrozenError on a frozen array" do
- -> { [1,2,3].freeze.delete_at(0) }.should raise_error(FrozenError)
- end
-
- ruby_version_is ''...'2.7' do
- it "keeps tainted status" do
- ary = [1, 2]
- ary.taint
- ary.tainted?.should be_true
- ary.delete_at(0)
- ary.tainted?.should be_true
- ary.delete_at(0) # now empty
- ary.tainted?.should be_true
- end
-
- it "keeps untrusted status" do
- ary = [1, 2]
- ary.untrust
- ary.untrusted?.should be_true
- ary.delete_at(0)
- ary.untrusted?.should be_true
- ary.delete_at(0) # now empty
- ary.untrusted?.should be_true
- end
+ -> { [1,2,3].freeze.delete_at(0) }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/array/delete_if_spec.rb b/spec/ruby/core/array/delete_if_spec.rb
index e1931220f5..701a612395 100644
--- a/spec/ruby/core/array/delete_if_spec.rb
+++ b/spec/ruby/core/array/delete_if_spec.rb
@@ -2,6 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/enumeratorize'
require_relative 'shared/delete_if'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized'
describe "Array#delete_if" do
@@ -16,7 +17,7 @@ describe "Array#delete_if" do
end
it "returns self" do
- @a.delete_if{ true }.equal?(@a).should be_true
+ @a.delete_if{ true }.equal?(@a).should == true
end
it_behaves_like :enumeratorize, :delete_if
@@ -24,45 +25,58 @@ describe "Array#delete_if" do
it "returns self when called on an Array emptied with #shift" do
array = [1]
array.shift
- array.delete_if { |x| true }.should equal(array)
+ array.delete_if { |x| true }.should.equal?(array)
end
it "returns an Enumerator if no block given, and the enumerator can modify the original array" do
enum = @a.delete_if
- enum.should be_an_instance_of(Enumerator)
- @a.should_not be_empty
+ enum.should.instance_of?(Enumerator)
+ @a.should_not.empty?
enum.each { true }
- @a.should be_empty
+ @a.should.empty?
end
it "returns an Enumerator if no block given, and the array is frozen" do
- @a.freeze.delete_if.should be_an_instance_of(Enumerator)
+ @a.freeze.delete_if.should.instance_of?(Enumerator)
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.delete_if {} }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.delete_if {} }.should.raise(FrozenError)
end
it "raises a FrozenError on an empty frozen array" do
- -> { ArraySpecs.empty_frozen_array.delete_if {} }.should raise_error(FrozenError)
+ -> { ArraySpecs.empty_frozen_array.delete_if {} }.should.raise(FrozenError)
end
- ruby_version_is ''...'2.7' do
- it "keeps tainted status" do
- @a.taint
- @a.tainted?.should be_true
- @a.delete_if{ true }
- @a.tainted?.should be_true
+ it "does not truncate the array is the block raises an exception" do
+ a = [1, 2, 3]
+ begin
+ a.delete_if { raise StandardError, 'Oops' }
+ rescue
end
- it "keeps untrusted status" do
- @a.untrust
- @a.untrusted?.should be_true
- @a.delete_if{ true }
- @a.untrusted?.should be_true
+ a.should == [1, 2, 3]
+ end
+
+ it "only removes elements for which the block returns true, keeping the element which raised an error." do
+ a = [1, 2, 3, 4]
+ begin
+ a.delete_if do |e|
+ case e
+ when 2 then true
+ when 3 then raise StandardError, 'Oops'
+ else false
+ end
+ end
+ rescue StandardError
end
+
+ a.should == [1, 3, 4]
end
it_behaves_like :enumeratorized_with_origin_size, :delete_if, [1,2,3]
it_behaves_like :delete_if, :delete_if
+
+ @value_to_return = -> _ { false }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :delete_if
end
diff --git a/spec/ruby/core/array/delete_spec.rb b/spec/ruby/core/array/delete_spec.rb
index 5d53c74e47..0d80b2839d 100644
--- a/spec/ruby/core/array/delete_spec.rb
+++ b/spec/ruby/core/array/delete_spec.rb
@@ -41,28 +41,6 @@ describe "Array#delete" do
end
it "raises a FrozenError on a frozen array" do
- -> { [1, 2, 3].freeze.delete(1) }.should raise_error(FrozenError)
- end
-
- ruby_version_is ''...'2.7' do
- it "keeps tainted status" do
- a = [1, 2]
- a.taint
- a.tainted?.should be_true
- a.delete(2)
- a.tainted?.should be_true
- a.delete(1) # now empty
- a.tainted?.should be_true
- end
-
- it "keeps untrusted status" do
- a = [1, 2]
- a.untrust
- a.untrusted?.should be_true
- a.delete(2)
- a.untrusted?.should be_true
- a.delete(1) # now empty
- a.untrusted?.should be_true
- end
+ -> { [1, 2, 3].freeze.delete(1) }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/array/difference_spec.rb b/spec/ruby/core/array/difference_spec.rb
index a357657967..63e32feca0 100644
--- a/spec/ruby/core/array/difference_spec.rb
+++ b/spec/ruby/core/array/difference_spec.rb
@@ -2,23 +2,21 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/difference'
-ruby_version_is "2.6" do
- describe "Array#difference" do
- it_behaves_like :array_binary_difference, :difference
+describe "Array#difference" do
+ it_behaves_like :array_binary_difference, :difference
- it "returns a copy when called without any parameter" do
- x = [1, 2, 3, 2]
- x.difference.should == x
- x.difference.should_not equal x
- end
+ it "returns a copy when called without any parameter" do
+ x = [1, 2, 3, 2]
+ x.difference.should == x
+ x.difference.should_not.equal? x
+ end
- it "does not return subclass instances for Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].difference.should be_an_instance_of(Array)
- end
+ it "does not return subclass instances for Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].difference.should.instance_of?(Array)
+ end
- it "accepts multiple arguments" do
- x = [1, 2, 3, 1]
- x.difference([], [0, 1], [3, 4], [3]).should == [2]
- end
+ it "accepts multiple arguments" do
+ x = [1, 2, 3, 1]
+ x.difference([], [0, 1], [3, 4], [3]).should == [2]
end
end
diff --git a/spec/ruby/core/array/dig_spec.rb b/spec/ruby/core/array/dig_spec.rb
index f2d8ff47fd..4166ff9f1f 100644
--- a/spec/ruby/core/array/dig_spec.rb
+++ b/spec/ruby/core/array/dig_spec.rb
@@ -4,7 +4,7 @@ describe "Array#dig" do
it "returns #at with one arg" do
['a'].dig(0).should == 'a'
- ['a'].dig(1).should be_nil
+ ['a'].dig(1).should == nil
end
it "recurses array elements" do
@@ -22,20 +22,20 @@ describe "Array#dig" do
it "raises a TypeError for a non-numeric index" do
-> {
['a'].dig(:first)
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
it "raises a TypeError if any intermediate step does not respond to #dig" do
a = [1, 2]
-> {
a.dig(0, 1)
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
it "raises an ArgumentError if no arguments provided" do
-> {
[10].dig()
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "returns nil if any intermediate step is nil" do
diff --git a/spec/ruby/core/array/drop_spec.rb b/spec/ruby/core/array/drop_spec.rb
index 84ea86b04c..c0e1c9edce 100644
--- a/spec/ruby/core/array/drop_spec.rb
+++ b/spec/ruby/core/array/drop_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Array#drop" do
it "removes the specified number of elements from the start of the array" do
@@ -6,7 +7,7 @@ describe "Array#drop" do
end
it "raises an ArgumentError if the number of elements specified is negative" do
- -> { [1, 2].drop(-3) }.should raise_error(ArgumentError)
+ -> { [1, 2].drop(-3) }.should.raise(ArgumentError)
end
it "returns an empty Array if all elements are dropped" do
@@ -39,13 +40,17 @@ describe "Array#drop" do
end
it "raises a TypeError when the passed argument can't be coerced to Integer" do
- -> { [1, 2].drop("cat") }.should raise_error(TypeError)
+ -> { [1, 2].drop("cat") }.should.raise(TypeError)
end
it "raises a TypeError when the passed argument isn't an integer and #to_int returns non-Integer" do
obj = mock("to_int")
obj.should_receive(:to_int).and_return("cat")
- -> { [1, 2].drop(obj) }.should raise_error(TypeError)
+ -> { [1, 2].drop(obj) }.should.raise(TypeError)
+ end
+
+ it 'returns a Array instance for Array subclasses' do
+ ArraySpecs::MyArray[1, 2, 3, 4, 5].drop(1).should.instance_of?(Array)
end
end
diff --git a/spec/ruby/core/array/drop_while_spec.rb b/spec/ruby/core/array/drop_while_spec.rb
index cfb6b1e267..4fead3ff06 100644
--- a/spec/ruby/core/array/drop_while_spec.rb
+++ b/spec/ruby/core/array/drop_while_spec.rb
@@ -1,6 +1,11 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#drop_while" do
+ @value_to_return = -> _ { true }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :drop_while
+
it "removes elements from the start of the array while the block evaluates to true" do
[1, 2, 3, 4].drop_while { |n| n < 4 }.should == [4]
end
@@ -12,4 +17,8 @@ describe "Array#drop_while" do
it "removes elements from the start of the array until the block returns false" do
[1, 2, 3, false, 5].drop_while { |n| n }.should == [false, 5]
end
+
+ it 'returns a Array instance for Array subclasses' do
+ ArraySpecs::MyArray[1, 2, 3, 4, 5].drop_while { |n| n < 4 }.should.instance_of?(Array)
+ end
end
diff --git a/spec/ruby/core/array/dup_spec.rb b/spec/ruby/core/array/dup_spec.rb
index 17f467d5fc..f14aeca3b5 100644
--- a/spec/ruby/core/array/dup_spec.rb
+++ b/spec/ruby/core/array/dup_spec.rb
@@ -12,8 +12,8 @@ describe "Array#dup" do
aa = a.dup
bb = b.dup
- aa.frozen?.should be_false
- bb.frozen?.should be_false
+ aa.frozen?.should == false
+ bb.frozen?.should == false
end
it "does not copy singleton methods" do
@@ -23,9 +23,9 @@ describe "Array#dup" do
aa = a.dup
bb = b.dup
- a.respond_to?(:a_singleton_method).should be_true
- b.respond_to?(:a_singleton_method).should be_false
- aa.respond_to?(:a_singleton_method).should be_false
- bb.respond_to?(:a_singleton_method).should be_false
+ a.respond_to?(:a_singleton_method).should == true
+ b.respond_to?(:a_singleton_method).should == false
+ aa.respond_to?(:a_singleton_method).should == false
+ bb.respond_to?(:a_singleton_method).should == false
end
end
diff --git a/spec/ruby/core/array/each_index_spec.rb b/spec/ruby/core/array/each_index_spec.rb
index 51af5842c4..b238a89d8a 100644
--- a/spec/ruby/core/array/each_index_spec.rb
+++ b/spec/ruby/core/array/each_index_spec.rb
@@ -5,7 +5,7 @@ require_relative '../enumerable/shared/enumeratorized'
# Modifying a collection while the contents are being iterated
# gives undefined behavior. See
-# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633
+# https://blade.ruby-lang.org/ruby-core/23633
describe "Array#each_index" do
before :each do
@@ -20,7 +20,7 @@ describe "Array#each_index" do
it "returns self" do
a = [:a, :b, :c]
- a.each_index { |i| }.should equal(a)
+ a.each_index { |i| }.should.equal?(a)
end
it "is not confused by removing elements from the front" do
@@ -40,3 +40,19 @@ describe "Array#each_index" do
it_behaves_like :enumeratorize, :each_index
it_behaves_like :enumeratorized_with_origin_size, :each_index, [1,2,3]
end
+
+describe "Array#each_index" do
+ it "tolerates increasing an array size during iteration" do
+ array = [:a, :b, :c]
+ ScratchPad.record []
+ i = 0
+
+ array.each_index do |index|
+ ScratchPad << index
+ array << i if i < 100
+ i += 1
+ end
+
+ ScratchPad.recorded.should == (0..102).to_a # element indices
+ end
+end
diff --git a/spec/ruby/core/array/each_spec.rb b/spec/ruby/core/array/each_spec.rb
index 256647d61e..73a4c36b17 100644
--- a/spec/ruby/core/array/each_spec.rb
+++ b/spec/ruby/core/array/each_spec.rb
@@ -1,20 +1,50 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/enumeratorize'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized'
-# Modifying a collection while the contents are being iterated
-# gives undefined behavior. See
-# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633
+# Mutating the array while it is being iterated is discouraged as it can result in confusing behavior.
+# Yet a Ruby implementation must not crash in such a case, and following the simple CRuby behavior makes sense.
+# CRuby simply reads the array storage and checks the size for every iteration;
+# like `i = 0; while i < size; yield self[i]; i += 1; end`
describe "Array#each" do
it "yields each element to the block" do
a = []
x = [1, 2, 3]
- x.each { |item| a << item }.should equal(x)
+ x.each { |item| a << item }.should.equal?(x)
a.should == [1, 2, 3]
end
+ it "yields each element to the block even if the array is changed during iteration" do
+ a = [1, 2, 3, 4, 5]
+ iterated = []
+ a.each { |x| iterated << x; a << x+5 if x.even? }
+ iterated.should == [1, 2, 3, 4, 5, 7, 9]
+ end
+
+ it "yields only elements that are still in the array" do
+ a = [0, 1, 2, 3, 4]
+ iterated = []
+ a.each { |x| iterated << x; a.pop if x.even? }
+ iterated.should == [0, 1, 2]
+ end
+
+ it "yields elements based on an internal index" do
+ a = [0, 1, 2, 3, 4]
+ iterated = []
+ a.each { |x| iterated << x; a.shift if x.even? }
+ iterated.should == [0, 2, 4]
+ end
+
+ it "yields the same element multiple times if inserting while iterating" do
+ a = [1, 2]
+ iterated = []
+ a.each { |x| iterated << x; a.unshift(0) if a.size == 2 }
+ iterated.should == [1, 1, 2]
+ end
+
it "yields each element to a block that takes multiple arguments" do
a = [[1, 2], :a, [3, 4]]
b = []
@@ -46,3 +76,7 @@ describe "Array#each" do
it_behaves_like :enumeratorize, :each
it_behaves_like :enumeratorized_with_origin_size, :each, [1,2,3]
end
+
+describe "Array#each" do
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :each
+end
diff --git a/spec/ruby/core/array/element_reference_spec.rb b/spec/ruby/core/array/element_reference_spec.rb
index 31e5578a09..d5f4b54961 100644
--- a/spec/ruby/core/array/element_reference_spec.rb
+++ b/spec/ruby/core/array/element_reference_spec.rb
@@ -1,9 +1,862 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/slice'
describe "Array#[]" do
- it_behaves_like :array_slice, :[]
+ it "returns the element at index with [index]" do
+ [ "a", "b", "c", "d", "e" ][1].should == "b"
+
+ a = [1, 2, 3, 4]
+
+ a[0].should == 1
+ a[1].should == 2
+ a[2].should == 3
+ a[3].should == 4
+ a[4].should == nil
+ a[10].should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns the element at index from the end of the array with [-index]" do
+ [ "a", "b", "c", "d", "e" ][-2].should == "d"
+
+ a = [1, 2, 3, 4]
+
+ a[-1].should == 4
+ a[-2].should == 3
+ a[-3].should == 2
+ a[-4].should == 1
+ a[-5].should == nil
+ a[-10].should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns count elements starting from index with [index, count]" do
+ [ "a", "b", "c", "d", "e" ][2, 3].should == ["c", "d", "e"]
+
+ a = [1, 2, 3, 4]
+
+ a[0, 0].should == []
+ a[0, 1].should == [1]
+ a[0, 2].should == [1, 2]
+ a[0, 4].should == [1, 2, 3, 4]
+ a[0, 6].should == [1, 2, 3, 4]
+ a[0, -1].should == nil
+ a[0, -2].should == nil
+ a[0, -4].should == nil
+
+ a[2, 0].should == []
+ a[2, 1].should == [3]
+ a[2, 2].should == [3, 4]
+ a[2, 4].should == [3, 4]
+ a[2, -1].should == nil
+
+ a[4, 0].should == []
+ a[4, 2].should == []
+ a[4, -1].should == nil
+
+ a[5, 0].should == nil
+ a[5, 2].should == nil
+ a[5, -1].should == nil
+
+ a[6, 0].should == nil
+ a[6, 2].should == nil
+ a[6, -1].should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns count elements starting at index from the end of array with [-index, count]" do
+ [ "a", "b", "c", "d", "e" ][-2, 2].should == ["d", "e"]
+
+ a = [1, 2, 3, 4]
+
+ a[-1, 0].should == []
+ a[-1, 1].should == [4]
+ a[-1, 2].should == [4]
+ a[-1, -1].should == nil
+
+ a[-2, 0].should == []
+ a[-2, 1].should == [3]
+ a[-2, 2].should == [3, 4]
+ a[-2, 4].should == [3, 4]
+ a[-2, -1].should == nil
+
+ a[-4, 0].should == []
+ a[-4, 1].should == [1]
+ a[-4, 2].should == [1, 2]
+ a[-4, 4].should == [1, 2, 3, 4]
+ a[-4, 6].should == [1, 2, 3, 4]
+ a[-4, -1].should == nil
+
+ a[-5, 0].should == nil
+ a[-5, 1].should == nil
+ a[-5, 10].should == nil
+ a[-5, -1].should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns the first count elements with [0, count]" do
+ [ "a", "b", "c", "d", "e" ][0, 3].should == ["a", "b", "c"]
+ end
+
+ it "returns the subarray which is independent to self with [index,count]" do
+ a = [1, 2, 3]
+ sub = a[1, 2]
+ sub.replace([:a, :b])
+ a.should == [1, 2, 3]
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.stub!(:to_int).and_return(2)
+
+ a = [1, 2, 3, 4]
+ a[obj].should == 3
+ a[obj, 1].should == [3]
+ a[obj, obj].should == [3, 4]
+ a[0, obj].should == [1, 2]
+ end
+
+ it "raises TypeError if to_int returns non-integer" do
+ from = mock('from')
+ to = mock('to')
+
+ # So we can construct a range out of them...
+ def from.<=>(o) 0 end
+ def to.<=>(o) 0 end
+
+ a = [1, 2, 3, 4, 5]
+
+ def from.to_int() 'cat' end
+ def to.to_int() -2 end
+
+ -> { a[from..to] }.should.raise(TypeError)
+
+ def from.to_int() 1 end
+ def to.to_int() 'cat' end
+
+ -> { a[from..to] }.should.raise(TypeError)
+ end
+
+ it "returns the elements specified by Range indexes with [m..n]" do
+ [ "a", "b", "c", "d", "e" ][1..3].should == ["b", "c", "d"]
+ [ "a", "b", "c", "d", "e" ][4..-1].should == ['e']
+ [ "a", "b", "c", "d", "e" ][3..3].should == ['d']
+ [ "a", "b", "c", "d", "e" ][3..-2].should == ['d']
+ ['a'][0..-1].should == ['a']
+
+ a = [1, 2, 3, 4]
+
+ a[0..-10].should == []
+ a[0..0].should == [1]
+ a[0..1].should == [1, 2]
+ a[0..2].should == [1, 2, 3]
+ a[0..3].should == [1, 2, 3, 4]
+ a[0..4].should == [1, 2, 3, 4]
+ a[0..10].should == [1, 2, 3, 4]
+
+ a[2..-10].should == []
+ a[2..0].should == []
+ a[2..2].should == [3]
+ a[2..3].should == [3, 4]
+ a[2..4].should == [3, 4]
+
+ a[3..0].should == []
+ a[3..3].should == [4]
+ a[3..4].should == [4]
+
+ a[4..0].should == []
+ a[4..4].should == []
+ a[4..5].should == []
+
+ a[5..0].should == nil
+ a[5..5].should == nil
+ a[5..6].should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns elements specified by Range indexes except the element at index n with [m...n]" do
+ [ "a", "b", "c", "d", "e" ][1...3].should == ["b", "c"]
+
+ a = [1, 2, 3, 4]
+
+ a[0...-10].should == []
+ a[0...0].should == []
+ a[0...1].should == [1]
+ a[0...2].should == [1, 2]
+ a[0...3].should == [1, 2, 3]
+ a[0...4].should == [1, 2, 3, 4]
+ a[0...10].should == [1, 2, 3, 4]
+
+ a[2...-10].should == []
+ a[2...0].should == []
+ a[2...2].should == []
+ a[2...3].should == [3]
+ a[2...4].should == [3, 4]
+
+ a[3...0].should == []
+ a[3...3].should == []
+ a[3...4].should == [4]
+
+ a[4...0].should == []
+ a[4...4].should == []
+ a[4...5].should == []
+
+ a[5...0].should == nil
+ a[5...5].should == nil
+ a[5...6].should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns elements that exist if range start is in the array but range end is not with [m..n]" do
+ [ "a", "b", "c", "d", "e" ][4..7].should == ["e"]
+ end
+
+ it "accepts Range instances having a negative m and both signs for n with [m..n] and [m...n]" do
+ a = [1, 2, 3, 4]
+
+ a[-1..-1].should == [4]
+ a[-1...-1].should == []
+ a[-1..3].should == [4]
+ a[-1...3].should == []
+ a[-1..4].should == [4]
+ a[-1...4].should == [4]
+ a[-1..10].should == [4]
+ a[-1...10].should == [4]
+ a[-1..0].should == []
+ a[-1..-4].should == []
+ a[-1...-4].should == []
+ a[-1..-6].should == []
+ a[-1...-6].should == []
+
+ a[-2..-2].should == [3]
+ a[-2...-2].should == []
+ a[-2..-1].should == [3, 4]
+ a[-2...-1].should == [3]
+ a[-2..10].should == [3, 4]
+ a[-2...10].should == [3, 4]
+
+ a[-4..-4].should == [1]
+ a[-4..-2].should == [1, 2, 3]
+ a[-4...-2].should == [1, 2]
+ a[-4..-1].should == [1, 2, 3, 4]
+ a[-4...-1].should == [1, 2, 3]
+ a[-4..3].should == [1, 2, 3, 4]
+ a[-4...3].should == [1, 2, 3]
+ a[-4..4].should == [1, 2, 3, 4]
+ a[-4...4].should == [1, 2, 3, 4]
+ a[-4...4].should == [1, 2, 3, 4]
+ a[-4..0].should == [1]
+ a[-4...0].should == []
+ a[-4..1].should == [1, 2]
+ a[-4...1].should == [1]
+
+ a[-5..-5].should == nil
+ a[-5...-5].should == nil
+ a[-5..-4].should == nil
+ a[-5..-1].should == nil
+ a[-5..10].should == nil
+
+ a.should == [1, 2, 3, 4]
+ end
+
+ it "returns the subarray which is independent to self with [m..n]" do
+ a = [1, 2, 3]
+ sub = a[1..2]
+ sub.replace([:a, :b])
+ a.should == [1, 2, 3]
+ end
+
+ it "tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]" do
+ from = mock('from')
+ to = mock('to')
+
+ # So we can construct a range out of them...
+ def from.<=>(o) 0 end
+ def to.<=>(o) 0 end
+
+ def from.to_int() 1 end
+ def to.to_int() -2 end
+
+ a = [1, 2, 3, 4]
+
+ a[from..to].should == [2, 3]
+ a[from...to].should == [2]
+ a[1..0].should == []
+ a[1...0].should == []
+
+ -> { a["a" .. "b"] }.should.raise(TypeError)
+ -> { a["a" ... "b"] }.should.raise(TypeError)
+ -> { a[from .. "b"] }.should.raise(TypeError)
+ -> { a[from ... "b"] }.should.raise(TypeError)
+ end
+
+ it "returns the same elements as [m..n] and [m...n] with Range subclasses" do
+ a = [1, 2, 3, 4]
+ range_incl = ArraySpecs::MyRange.new(1, 2)
+ range_excl = ArraySpecs::MyRange.new(-3, -1, true)
+
+ a[range_incl].should == [2, 3]
+ a[range_excl].should == [2, 3]
+ end
+
+ it "returns nil for a requested index not in the array with [index]" do
+ [ "a", "b", "c", "d", "e" ][5].should == nil
+ end
+
+ it "returns [] if the index is valid but length is zero with [index, length]" do
+ [ "a", "b", "c", "d", "e" ][0, 0].should == []
+ [ "a", "b", "c", "d", "e" ][2, 0].should == []
+ end
+
+ it "returns nil if length is zero but index is invalid with [index, length]" do
+ [ "a", "b", "c", "d", "e" ][100, 0].should == nil
+ [ "a", "b", "c", "d", "e" ][-50, 0].should == nil
+ end
+
+ # This is by design. It is in the official documentation.
+ it "returns [] if index == array.size with [index, length]" do
+ %w|a b c d e|[5, 2].should == []
+ end
+
+ it "returns nil if index > array.size with [index, length]" do
+ %w|a b c d e|[6, 2].should == nil
+ end
+
+ it "returns nil if length is negative with [index, length]" do
+ %w|a b c d e|[3, -1].should == nil
+ %w|a b c d e|[2, -2].should == nil
+ %w|a b c d e|[1, -100].should == nil
+ end
+
+ it "returns nil if no requested index is in the array with [m..n]" do
+ [ "a", "b", "c", "d", "e" ][6..10].should == nil
+ end
+
+ it "returns nil if range start is not in the array with [m..n]" do
+ [ "a", "b", "c", "d", "e" ][-10..2].should == nil
+ [ "a", "b", "c", "d", "e" ][10..12].should == nil
+ end
+
+ it "returns an empty array when m == n with [m...n]" do
+ [1, 2, 3, 4, 5][1...1].should == []
+ end
+
+ it "returns an empty array with [0...0]" do
+ [1, 2, 3, 4, 5][0...0].should == []
+ end
+
+ it "returns a subarray where m, n negatives and m < n with [m..n]" do
+ [ "a", "b", "c", "d", "e" ][-3..-2].should == ["c", "d"]
+ end
+
+ it "returns an array containing the first element with [0..0]" do
+ [1, 2, 3, 4, 5][0..0].should == [1]
+ end
+
+ it "returns the entire array with [0..-1]" do
+ [1, 2, 3, 4, 5][0..-1].should == [1, 2, 3, 4, 5]
+ end
+
+ it "returns all but the last element with [0...-1]" do
+ [1, 2, 3, 4, 5][0...-1].should == [1, 2, 3, 4]
+ end
+
+ it "returns [3] for [2..-1] out of [1, 2, 3]" do
+ [1,2,3][2..-1].should == [3]
+ end
+
+ it "returns an empty array when m > n and m, n are positive with [m..n]" do
+ [1, 2, 3, 4, 5][3..2].should == []
+ end
+
+ it "returns an empty array when m > n and m, n are negative with [m..n]" do
+ [1, 2, 3, 4, 5][-2..-3].should == []
+ end
+
+ it "does not expand array when the indices are outside of the array bounds" do
+ a = [1, 2]
+ a[4].should == nil
+ a.should == [1, 2]
+ a[4, 0].should == nil
+ a.should == [1, 2]
+ a[6, 1].should == nil
+ a.should == [1, 2]
+ a[8...8].should == nil
+ a.should == [1, 2]
+ a[10..10].should == nil
+ a.should == [1, 2]
+ end
+
+ describe "with a subclass of Array" do
+ before :each do
+ ScratchPad.clear
+
+ @array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
+ end
+
+ it "returns a Array instance with [n, m]" do
+ @array[0, 2].should.instance_of?(Array)
+ end
+
+ it "returns a Array instance with [-n, m]" do
+ @array[-3, 2].should.instance_of?(Array)
+ end
+
+ it "returns a Array instance with [n..m]" do
+ @array[1..3].should.instance_of?(Array)
+ end
+
+ it "returns a Array instance with [n...m]" do
+ @array[1...3].should.instance_of?(Array)
+ end
+
+ it "returns a Array instance with [-n..-m]" do
+ @array[-3..-1].should.instance_of?(Array)
+ end
+
+ it "returns a Array instance with [-n...-m]" do
+ @array[-3...-1].should.instance_of?(Array)
+ end
+
+ it "returns an empty array when m == n with [m...n]" do
+ @array[1...1].should == []
+ ScratchPad.recorded.should == nil
+ end
+
+ it "returns an empty array with [0...0]" do
+ @array[0...0].should == []
+ ScratchPad.recorded.should == nil
+ end
+
+ it "returns an empty array when m > n and m, n are positive with [m..n]" do
+ @array[3..2].should == []
+ ScratchPad.recorded.should == nil
+ end
+
+ it "returns an empty array when m > n and m, n are negative with [m..n]" do
+ @array[-2..-3].should == []
+ ScratchPad.recorded.should == nil
+ end
+
+ it "returns [] if index == array.size with [index, length]" do
+ @array[5, 2].should == []
+ ScratchPad.recorded.should == nil
+ end
+
+ it "returns [] if the index is valid but length is zero with [index, length]" do
+ @array[0, 0].should == []
+ @array[2, 0].should == []
+ ScratchPad.recorded.should == nil
+ end
+
+ it "does not call #initialize on the subclass instance" do
+ @array[0, 3].should == [1, 2, 3]
+ ScratchPad.recorded.should == nil
+ end
+ end
+
+ it "raises a RangeError when the start index is out of range of Fixnum" do
+ array = [1, 2, 3, 4, 5, 6]
+ obj = mock('large value')
+ obj.should_receive(:to_int).and_return(bignum_value)
+ -> { array[obj] }.should.raise(RangeError)
+
+ obj = 8e19
+ -> { array[obj] }.should.raise(RangeError)
+
+ # boundary value when longs are 64 bits
+ -> { array[2.0**63] }.should.raise(RangeError)
+
+ # just under the boundary value when longs are 64 bits
+ array[max_long.to_f.prev_float].should == nil
+ end
+
+ it "raises a RangeError when the length is out of range of Fixnum" do
+ array = [1, 2, 3, 4, 5, 6]
+ obj = mock('large value')
+ obj.should_receive(:to_int).and_return(bignum_value)
+ -> { array[1, obj] }.should.raise(RangeError)
+
+ obj = 8e19
+ -> { array[1, obj] }.should.raise(RangeError)
+ end
+
+ it "raises a type error if a range is passed with a length" do
+ ->{ [1, 2, 3][1..2, 1] }.should.raise(TypeError)
+ end
+
+ it "raises a RangeError if passed a range with a bound that is too large" do
+ array = [1, 2, 3, 4, 5, 6]
+ -> { array[bignum_value..(bignum_value + 1)] }.should.raise(RangeError)
+ -> { array[0..bignum_value] }.should.raise(RangeError)
+ end
+
+ it "can accept endless ranges" do
+ a = [0, 1, 2, 3, 4, 5]
+ a[eval("(2..)")].should == [2, 3, 4, 5]
+ a[eval("(2...)")].should == [2, 3, 4, 5]
+ a[eval("(-2..)")].should == [4, 5]
+ a[eval("(-2...)")].should == [4, 5]
+ a[eval("(9..)")].should == nil
+ a[eval("(9...)")].should == nil
+ a[eval("(-9..)")].should == nil
+ a[eval("(-9...)")].should == nil
+ end
+
+ describe "can be sliced with Enumerator::ArithmeticSequence" do
+ before :each do
+ @array = [0, 1, 2, 3, 4, 5]
+ end
+
+ it "has endless range and positive steps" do
+ @array[eval("(0..).step(1)")].should == [0, 1, 2, 3, 4, 5]
+ @array[eval("(0..).step(2)")].should == [0, 2, 4]
+ @array[eval("(0..).step(10)")].should == [0]
+
+ @array[eval("(2..).step(1)")].should == [2, 3, 4, 5]
+ @array[eval("(2..).step(2)")].should == [2, 4]
+ @array[eval("(2..).step(10)")].should == [2]
+
+ @array[eval("(-3..).step(1)")].should == [3, 4, 5]
+ @array[eval("(-3..).step(2)")].should == [3, 5]
+ @array[eval("(-3..).step(10)")].should == [3]
+ end
+
+ it "has beginless range and positive steps" do
+ # end with zero index
+ @array[(..0).step(1)].should == [0]
+ @array[(...0).step(1)].should == []
+
+ @array[(..0).step(2)].should == [0]
+ @array[(...0).step(2)].should == []
+
+ @array[(..0).step(10)].should == [0]
+ @array[(...0).step(10)].should == []
+
+ # end with positive index
+ @array[(..3).step(1)].should == [0, 1, 2, 3]
+ @array[(...3).step(1)].should == [0, 1, 2]
+
+ @array[(..3).step(2)].should == [0, 2]
+ @array[(...3).step(2)].should == [0, 2]
+
+ @array[(..3).step(10)].should == [0]
+ @array[(...3).step(10)].should == [0]
+
+ # end with negative index
+ @array[(..-2).step(1)].should == [0, 1, 2, 3, 4,]
+ @array[(...-2).step(1)].should == [0, 1, 2, 3]
+
+ @array[(..-2).step(2)].should == [0, 2, 4]
+ @array[(...-2).step(2)].should == [0, 2]
+
+ @array[(..-2).step(10)].should == [0]
+ @array[(...-2).step(10)].should == [0]
+ end
+
+ it "has endless range and negative steps" do
+ @array[eval("(0..).step(-1)")].should == [0]
+ @array[eval("(0..).step(-2)")].should == [0]
+ @array[eval("(0..).step(-10)")].should == [0]
+
+ @array[eval("(2..).step(-1)")].should == [2, 1, 0]
+ @array[eval("(2..).step(-2)")].should == [2, 0]
+
+ @array[eval("(-3..).step(-1)")].should == [3, 2, 1, 0]
+ @array[eval("(-3..).step(-2)")].should == [3, 1]
+ end
+
+ it "has closed range and positive steps" do
+ # start and end with 0
+ @array[eval("(0..0).step(1)")].should == [0]
+ @array[eval("(0...0).step(1)")].should == []
+
+ @array[eval("(0..0).step(2)")].should == [0]
+ @array[eval("(0...0).step(2)")].should == []
+
+ @array[eval("(0..0).step(10)")].should == [0]
+ @array[eval("(0...0).step(10)")].should == []
+
+ # start and end with positive index
+ @array[eval("(1..3).step(1)")].should == [1, 2, 3]
+ @array[eval("(1...3).step(1)")].should == [1, 2]
+
+ @array[eval("(1..3).step(2)")].should == [1, 3]
+ @array[eval("(1...3).step(2)")].should == [1]
+
+ @array[eval("(1..3).step(10)")].should == [1]
+ @array[eval("(1...3).step(10)")].should == [1]
+
+ # start with positive index, end with negative index
+ @array[eval("(1..-2).step(1)")].should == [1, 2, 3, 4]
+ @array[eval("(1...-2).step(1)")].should == [1, 2, 3]
+
+ @array[eval("(1..-2).step(2)")].should == [1, 3]
+ @array[eval("(1...-2).step(2)")].should == [1, 3]
+
+ @array[eval("(1..-2).step(10)")].should == [1]
+ @array[eval("(1...-2).step(10)")].should == [1]
+
+ # start with negative index, end with positive index
+ @array[eval("(-4..4).step(1)")].should == [2, 3, 4]
+ @array[eval("(-4...4).step(1)")].should == [2, 3]
+
+ @array[eval("(-4..4).step(2)")].should == [2, 4]
+ @array[eval("(-4...4).step(2)")].should == [2]
+
+ @array[eval("(-4..4).step(10)")].should == [2]
+ @array[eval("(-4...4).step(10)")].should == [2]
+
+ # start with negative index, end with negative index
+ @array[eval("(-4..-2).step(1)")].should == [2, 3, 4]
+ @array[eval("(-4...-2).step(1)")].should == [2, 3]
+
+ @array[eval("(-4..-2).step(2)")].should == [2, 4]
+ @array[eval("(-4...-2).step(2)")].should == [2]
+
+ @array[eval("(-4..-2).step(10)")].should == [2]
+ @array[eval("(-4...-2).step(10)")].should == [2]
+ end
+
+ it "has closed range and negative steps" do
+ # start and end with 0
+ @array[eval("(0..0).step(-1)")].should == [0]
+ @array[eval("(0...0).step(-1)")].should == []
+
+ @array[eval("(0..0).step(-2)")].should == [0]
+ @array[eval("(0...0).step(-2)")].should == []
+
+ @array[eval("(0..0).step(-10)")].should == [0]
+ @array[eval("(0...0).step(-10)")].should == []
+
+ # start and end with positive index
+ @array[eval("(1..3).step(-1)")].should == []
+ @array[eval("(1...3).step(-1)")].should == []
+
+ @array[eval("(1..3).step(-2)")].should == []
+ @array[eval("(1...3).step(-2)")].should == []
+
+ @array[eval("(1..3).step(-10)")].should == []
+ @array[eval("(1...3).step(-10)")].should == []
+
+ # start with positive index, end with negative index
+ @array[eval("(1..-2).step(-1)")].should == []
+ @array[eval("(1...-2).step(-1)")].should == []
+
+ @array[eval("(1..-2).step(-2)")].should == []
+ @array[eval("(1...-2).step(-2)")].should == []
+
+ @array[eval("(1..-2).step(-10)")].should == []
+ @array[eval("(1...-2).step(-10)")].should == []
+
+ # start with negative index, end with positive index
+ @array[eval("(-4..4).step(-1)")].should == []
+ @array[eval("(-4...4).step(-1)")].should == []
+
+ @array[eval("(-4..4).step(-2)")].should == []
+ @array[eval("(-4...4).step(-2)")].should == []
+
+ @array[eval("(-4..4).step(-10)")].should == []
+ @array[eval("(-4...4).step(-10)")].should == []
+
+ # start with negative index, end with negative index
+ @array[eval("(-4..-2).step(-1)")].should == []
+ @array[eval("(-4...-2).step(-1)")].should == []
+
+ @array[eval("(-4..-2).step(-2)")].should == []
+ @array[eval("(-4...-2).step(-2)")].should == []
+
+ @array[eval("(-4..-2).step(-10)")].should == []
+ @array[eval("(-4...-2).step(-10)")].should == []
+ end
+
+ it "has inverted closed range and positive steps" do
+ # start and end with positive index
+ @array[eval("(3..1).step(1)")].should == []
+ @array[eval("(3...1).step(1)")].should == []
+
+ @array[eval("(3..1).step(2)")].should == []
+ @array[eval("(3...1).step(2)")].should == []
+
+ @array[eval("(3..1).step(10)")].should == []
+ @array[eval("(3...1).step(10)")].should == []
+
+ # start with negative index, end with positive index
+ @array[eval("(-2..1).step(1)")].should == []
+ @array[eval("(-2...1).step(1)")].should == []
+
+ @array[eval("(-2..1).step(2)")].should == []
+ @array[eval("(-2...1).step(2)")].should == []
+
+ @array[eval("(-2..1).step(10)")].should == []
+ @array[eval("(-2...1).step(10)")].should == []
+
+ # start with positive index, end with negative index
+ @array[eval("(4..-4).step(1)")].should == []
+ @array[eval("(4...-4).step(1)")].should == []
+
+ @array[eval("(4..-4).step(2)")].should == []
+ @array[eval("(4...-4).step(2)")].should == []
+
+ @array[eval("(4..-4).step(10)")].should == []
+ @array[eval("(4...-4).step(10)")].should == []
+
+ # start with negative index, end with negative index
+ @array[eval("(-2..-4).step(1)")].should == []
+ @array[eval("(-2...-4).step(1)")].should == []
+
+ @array[eval("(-2..-4).step(2)")].should == []
+ @array[eval("(-2...-4).step(2)")].should == []
+
+ @array[eval("(-2..-4).step(10)")].should == []
+ @array[eval("(-2...-4).step(10)")].should == []
+ end
+
+ it "has range with bounds outside of array" do
+ # end is equal to array's length
+ @array[(0..6).step(1)].should == [0, 1, 2, 3, 4, 5]
+ -> { @array[(0..6).step(2)] }.should.raise(RangeError)
+
+ # end is greater than length with positive steps
+ @array[(1..6).step(2)].should == [1, 3, 5]
+ @array[(2..7).step(2)].should == [2, 4]
+ -> { @array[(2..8).step(2)] }.should.raise(RangeError)
+
+ # begin is greater than length with negative steps
+ @array[(6..1).step(-2)].should == [5, 3, 1]
+ @array[(7..2).step(-2)].should == [5, 3]
+ -> { @array[(8..2).step(-2)] }.should.raise(RangeError)
+ end
+
+ it "has endless range with start outside of array's bounds" do
+ @array[eval("(6..).step(1)")].should == []
+ @array[eval("(7..).step(1)")].should == nil
+
+ @array[eval("(6..).step(2)")].should == []
+ -> { @array[eval("(7..).step(2)")] }.should.raise(RangeError)
+ end
+ end
+
+ it "can accept beginless ranges" do
+ a = [0, 1, 2, 3, 4, 5]
+ a[(..3)].should == [0, 1, 2, 3]
+ a[(...3)].should == [0, 1, 2]
+ a[(..-3)].should == [0, 1, 2, 3]
+ a[(...-3)].should == [0, 1, 2]
+ a[(..0)].should == [0]
+ a[(...0)].should == []
+ a[(..9)].should == [0, 1, 2, 3, 4, 5]
+ a[(...9)].should == [0, 1, 2, 3, 4, 5]
+ a[(..-9)].should == []
+ a[(...-9)].should == []
+ end
+
+ describe "can be sliced with Enumerator::ArithmeticSequence" do
+ it "with infinite/inverted ranges and negative steps" do
+ array = [0, 1, 2, 3, 4, 5]
+ array[(2..).step(-1)].should == [2, 1, 0]
+ array[(2..).step(-2)].should == [2, 0]
+ array[(2..).step(-3)].should == [2]
+ array[(2..).step(-4)].should == [2]
+
+ array[(-3..).step(-1)].should == [3, 2, 1, 0]
+ array[(-3..).step(-2)].should == [3, 1]
+ array[(-3..).step(-3)].should == [3, 0]
+ array[(-3..).step(-4)].should == [3]
+ array[(-3..).step(-5)].should == [3]
+
+ array[(..0).step(-1)].should == [5, 4, 3, 2, 1, 0]
+ array[(..0).step(-2)].should == [5, 3, 1]
+ array[(..0).step(-3)].should == [5, 2]
+ array[(..0).step(-4)].should == [5, 1]
+ array[(..0).step(-5)].should == [5, 0]
+ array[(..0).step(-6)].should == [5]
+ array[(..0).step(-7)].should == [5]
+
+ array[(...0).step(-1)].should == [5, 4, 3, 2, 1]
+ array[(...0).step(-2)].should == [5, 3, 1]
+ array[(...0).step(-3)].should == [5, 2]
+ array[(...0).step(-4)].should == [5, 1]
+ array[(...0).step(-5)].should == [5]
+ array[(...0).step(-6)].should == [5]
+
+ array[(...1).step(-1)].should == [5, 4, 3, 2]
+ array[(...1).step(-2)].should == [5, 3]
+ array[(...1).step(-3)].should == [5, 2]
+ array[(...1).step(-4)].should == [5]
+ array[(...1).step(-5)].should == [5]
+
+ array[(..-5).step(-1)].should == [5, 4, 3, 2, 1]
+ array[(..-5).step(-2)].should == [5, 3, 1]
+ array[(..-5).step(-3)].should == [5, 2]
+ array[(..-5).step(-4)].should == [5, 1]
+ array[(..-5).step(-5)].should == [5]
+ array[(..-5).step(-6)].should == [5]
+
+ array[(...-5).step(-1)].should == [5, 4, 3, 2]
+ array[(...-5).step(-2)].should == [5, 3]
+ array[(...-5).step(-3)].should == [5, 2]
+ array[(...-5).step(-4)].should == [5]
+ array[(...-5).step(-5)].should == [5]
+
+ array[(4..1).step(-1)].should == [4, 3, 2, 1]
+ array[(4..1).step(-2)].should == [4, 2]
+ array[(4..1).step(-3)].should == [4, 1]
+ array[(4..1).step(-4)].should == [4]
+ array[(4..1).step(-5)].should == [4]
+
+ array[(4...1).step(-1)].should == [4, 3, 2]
+ array[(4...1).step(-2)].should == [4, 2]
+ array[(4...1).step(-3)].should == [4]
+ array[(4...1).step(-4)].should == [4]
+
+ array[(-2..1).step(-1)].should == [4, 3, 2, 1]
+ array[(-2..1).step(-2)].should == [4, 2]
+ array[(-2..1).step(-3)].should == [4, 1]
+ array[(-2..1).step(-4)].should == [4]
+ array[(-2..1).step(-5)].should == [4]
+
+ array[(-2...1).step(-1)].should == [4, 3, 2]
+ array[(-2...1).step(-2)].should == [4, 2]
+ array[(-2...1).step(-3)].should == [4]
+ array[(-2...1).step(-4)].should == [4]
+
+ array[(4..-5).step(-1)].should == [4, 3, 2, 1]
+ array[(4..-5).step(-2)].should == [4, 2]
+ array[(4..-5).step(-3)].should == [4, 1]
+ array[(4..-5).step(-4)].should == [4]
+ array[(4..-5).step(-5)].should == [4]
+
+ array[(4...-5).step(-1)].should == [4, 3, 2]
+ array[(4...-5).step(-2)].should == [4, 2]
+ array[(4...-5).step(-3)].should == [4]
+ array[(4...-5).step(-4)].should == [4]
+
+ array[(-2..-5).step(-1)].should == [4, 3, 2, 1]
+ array[(-2..-5).step(-2)].should == [4, 2]
+ array[(-2..-5).step(-3)].should == [4, 1]
+ array[(-2..-5).step(-4)].should == [4]
+ array[(-2..-5).step(-5)].should == [4]
+
+ array[(-2...-5).step(-1)].should == [4, 3, 2]
+ array[(-2...-5).step(-2)].should == [4, 2]
+ array[(-2...-5).step(-3)].should == [4]
+ array[(-2...-5).step(-4)].should == [4]
+ end
+ end
+
+ it "can accept nil...nil ranges" do
+ a = [0, 1, 2, 3, 4, 5]
+ a[eval("(nil...nil)")].should == a
+ a[(...nil)].should == a
+ a[eval("(nil..)")].should == a
+ end
end
describe "Array.[]" do
@@ -39,12 +892,12 @@ describe "Array.[]" do
end
it "returns an instance of the subclass" do
- ArraySpecs::MyArray[1, 2, 3].should be_an_instance_of(ArraySpecs::MyArray)
+ ArraySpecs::MyArray[1, 2, 3].should.instance_of?(ArraySpecs::MyArray)
end
it "does not call #initialize on the subclass instance" do
ArraySpecs::MyArray[1, 2, 3].should == [1, 2, 3]
- ScratchPad.recorded.should be_nil
+ ScratchPad.recorded.should == nil
end
end
end
diff --git a/spec/ruby/core/array/element_set_spec.rb b/spec/ruby/core/array/element_set_spec.rb
index 2e01e838e9..671e4338de 100644
--- a/spec/ruby/core/array/element_set_spec.rb
+++ b/spec/ruby/core/array/element_set_spec.rb
@@ -36,6 +36,7 @@ describe "Array#[]=" do
a[3, 2] = ['a', 'b', 'c', 'd']
a.should == [2, 2, 3, "a", "b", "c", "d", 6]
end
+
it "replaces the section defined by [start,length] with the given values" do
a = [1, 2, 3, 4, 5, 6]
a[3, 2] = 'a', 'b', 'c', 'd'
@@ -94,8 +95,8 @@ describe "Array#[]=" do
it "checks frozen before attempting to coerce arguments" do
a = [1,2,3,4].freeze
- -> {a[:foo] = 1}.should raise_error(FrozenError)
- -> {a[:foo, :bar] = 1}.should raise_error(FrozenError)
+ -> {a[:foo] = 1}.should.raise(FrozenError)
+ -> {a[:foo, :bar] = 1}.should.raise(FrozenError)
end
it "sets elements in the range arguments when passed ranges" do
@@ -169,6 +170,7 @@ describe "Array#[]=" do
ary[1...1] = []
ary.should == [1, 2, 3]
end
+
it "does nothing if the section defined by range has negative width and the rhs is an empty array" do
ary = [1, 2, 3, 4, 5]
ary[1...0] = []
@@ -195,25 +197,25 @@ describe "Array#[]=" do
a[to .. from] = ["x"]
a.should == [1, "a", "b", "x", "c", 4]
- -> { a["a" .. "b"] = [] }.should raise_error(TypeError)
- -> { a[from .. "b"] = [] }.should raise_error(TypeError)
+ -> { a["a" .. "b"] = [] }.should.raise(TypeError)
+ -> { a[from .. "b"] = [] }.should.raise(TypeError)
end
it "raises an IndexError when passed indexes out of bounds" do
a = [1, 2, 3, 4]
- -> { a[-5] = "" }.should raise_error(IndexError)
- -> { a[-5, -1] = "" }.should raise_error(IndexError)
- -> { a[-5, 0] = "" }.should raise_error(IndexError)
- -> { a[-5, 1] = "" }.should raise_error(IndexError)
- -> { a[-5, 2] = "" }.should raise_error(IndexError)
- -> { a[-5, 10] = "" }.should raise_error(IndexError)
-
- -> { a[-5..-5] = "" }.should raise_error(RangeError)
- -> { a[-5...-5] = "" }.should raise_error(RangeError)
- -> { a[-5..-4] = "" }.should raise_error(RangeError)
- -> { a[-5...-4] = "" }.should raise_error(RangeError)
- -> { a[-5..10] = "" }.should raise_error(RangeError)
- -> { a[-5...10] = "" }.should raise_error(RangeError)
+ -> { a[-5] = "" }.should.raise(IndexError)
+ -> { a[-5, -1] = "" }.should.raise(IndexError)
+ -> { a[-5, 0] = "" }.should.raise(IndexError)
+ -> { a[-5, 1] = "" }.should.raise(IndexError)
+ -> { a[-5, 2] = "" }.should.raise(IndexError)
+ -> { a[-5, 10] = "" }.should.raise(IndexError)
+
+ -> { a[-5..-5] = "" }.should.raise(RangeError)
+ -> { a[-5...-5] = "" }.should.raise(RangeError)
+ -> { a[-5..-4] = "" }.should.raise(RangeError)
+ -> { a[-5...-4] = "" }.should.raise(RangeError)
+ -> { a[-5..10] = "" }.should.raise(RangeError)
+ -> { a[-5...10] = "" }.should.raise(RangeError)
# ok
a[0..-9] = [1]
@@ -237,7 +239,7 @@ describe "Array#[]=" do
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array[0, 0] = [] }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array[0, 0] = [] }.should.raise(FrozenError)
end
end
@@ -284,6 +286,12 @@ describe "Array#[]= with [index, count]" do
(a[2, 3] = [4, 5]).should == [4, 5]
end
+ it "accepts a frozen String literal as RHS" do
+ a = ['a', 'b', 'c']
+ a[0, 2] = 'd'.freeze
+ a.should == ['d', 'c']
+ end
+
it "just sets the section defined by [start,length] to nil even if the rhs is nil" do
a = ['a', 'b', 'c', 'd', 'e']
a[1, 3] = nil
@@ -341,12 +349,12 @@ describe "Array#[]= with [index, count]" do
it "raises an IndexError when passed start and negative length" do
a = [1, 2, 3, 4]
- -> { a[-2, -1] = "" }.should raise_error(IndexError)
- -> { a[0, -1] = "" }.should raise_error(IndexError)
- -> { a[2, -1] = "" }.should raise_error(IndexError)
- -> { a[4, -1] = "" }.should raise_error(IndexError)
- -> { a[10, -1] = "" }.should raise_error(IndexError)
- -> { [1, 2, 3, 4, 5][2, -1] = [7, 8] }.should raise_error(IndexError)
+ -> { a[-2, -1] = "" }.should.raise(IndexError)
+ -> { a[0, -1] = "" }.should.raise(IndexError)
+ -> { a[2, -1] = "" }.should.raise(IndexError)
+ -> { a[4, -1] = "" }.should.raise(IndexError)
+ -> { a[10, -1] = "" }.should.raise(IndexError)
+ -> { [1, 2, 3, 4, 5][2, -1] = [7, 8] }.should.raise(IndexError)
end
end
@@ -437,42 +445,84 @@ describe "Array#[]= with [m..n]" do
end
end
-ruby_version_is "2.6" do
- describe "Array#[]= with [m..]" do
+describe "Array#[]= with [m..]" do
+ it "just sets the section defined by range to nil even if the rhs is nil" do
+ a = [1, 2, 3, 4, 5]
+ a[eval("(2..)")] = nil
+ a.should == [1, 2, nil]
+ end
- it "just sets the section defined by range to nil even if the rhs is nil" do
- a = [1, 2, 3, 4, 5]
- a[eval("(2..)")] = nil
- a.should == [1, 2, nil]
- end
+ it "just sets the section defined by range to nil if m and n < 0 and the rhs is nil" do
+ a = [1, 2, 3, 4, 5]
+ a[eval("(-3..)")] = nil
+ a.should == [1, 2, nil]
+ end
- it "just sets the section defined by range to nil if m and n < 0 and the rhs is nil" do
- a = [1, 2, 3, 4, 5]
- a[eval("(-3..)")] = nil
- a.should == [1, 2, nil]
- end
+ it "replaces the section defined by range" do
+ a = [6, 5, 4, 3, 2, 1]
+ a[eval("(3...)")] = 9
+ a.should == [6, 5, 4, 9]
+ a[eval("(2..)")] = [7, 7, 7]
+ a.should == [6, 5, 7, 7, 7]
+ end
- it "replaces the section defined by range" do
- a = [6, 5, 4, 3, 2, 1]
- a[eval("(3...)")] = 9
- a.should == [6, 5, 4, 9]
- a[eval("(2..)")] = [7, 7, 7]
- a.should == [6, 5, 7, 7, 7]
- end
+ it "replaces the section if m and n < 0" do
+ a = [1, 2, 3, 4, 5]
+ a[eval("(-3..)")] = [7, 8, 9]
+ a.should == [1, 2, 7, 8, 9]
+ end
- it "replaces the section if m and n < 0" do
- a = [1, 2, 3, 4, 5]
- a[eval("(-3..)")] = [7, 8, 9]
- a.should == [1, 2, 7, 8, 9]
- end
+ it "inserts at the end if m > the array size" do
+ a = [1, 2, 3]
+ a[eval("(3..)")] = [4]
+ a.should == [1, 2, 3, 4]
+ a[eval("(5..)")] = [6]
+ a.should == [1, 2, 3, 4, nil, 6]
+ end
+end
- it "inserts at the end if m > the array size" do
- a = [1, 2, 3]
- a[eval("(3..)")] = [4]
- a.should == [1, 2, 3, 4]
- a[eval("(5..)")] = [6]
- a.should == [1, 2, 3, 4, nil, 6]
- end
+describe "Array#[]= with [..n] and [...n]" do
+ it "just sets the section defined by range to nil even if the rhs is nil" do
+ a = [1, 2, 3, 4, 5]
+ a[(..2)] = nil
+ a.should == [nil, 4, 5]
+ a[(...2)] = nil
+ a.should == [nil, 5]
+ end
+
+ it "just sets the section defined by range to nil if n < 0 and the rhs is nil" do
+ a = [1, 2, 3, 4, 5]
+ a[(..-3)] = nil
+ a.should == [nil, 4, 5]
+ a[(...-1)] = [nil, 5]
+ end
+
+ it "replaces the section defined by range" do
+ a = [6, 5, 4, 3, 2, 1]
+ a[(...3)] = 9
+ a.should == [9, 3, 2, 1]
+ a[(..2)] = [7, 7, 7, 7, 7]
+ a.should == [7, 7, 7, 7, 7, 1]
+ end
+
+ it "replaces the section if n < 0" do
+ a = [1, 2, 3, 4, 5]
+ a[(..-2)] = [7, 8, 9]
+ a.should == [7, 8, 9, 5]
+ end
+
+ it "replaces everything if n > the array size" do
+ a = [1, 2, 3]
+ a[(...7)] = [4]
+ a.should == [4]
+ end
+
+ it "inserts at the beginning if n < negative the array size" do
+ a = [1, 2, 3]
+ a[(..-7)] = [4]
+ a.should == [4, 1, 2, 3]
+ a[(...-10)] = [6]
+ a.should == [6, 4, 1, 2, 3]
end
end
diff --git a/spec/ruby/core/array/eql_spec.rb b/spec/ruby/core/array/eql_spec.rb
index 8565b94c60..9a7447c2e8 100644
--- a/spec/ruby/core/array/eql_spec.rb
+++ b/spec/ruby/core/array/eql_spec.rb
@@ -6,7 +6,7 @@ describe "Array#eql?" do
it_behaves_like :array_eql, :eql?
it "returns false if any corresponding elements are not #eql?" do
- [1, 2, 3, 4].should_not eql([1, 2, 3, 4.0])
+ [1, 2, 3, 4].should_not.eql?([1, 2, 3, 4.0])
end
it "returns false if other is not a kind of Array" do
@@ -14,6 +14,6 @@ describe "Array#eql?" do
obj.should_not_receive(:to_ary)
obj.should_not_receive(:eql?)
- [1, 2, 3].should_not eql(obj)
+ [1, 2, 3].should_not.eql?(obj)
end
end
diff --git a/spec/ruby/core/array/equal_value_spec.rb b/spec/ruby/core/array/equal_value_spec.rb
index a82e07b218..4f7c0ce5e3 100644
--- a/spec/ruby/core/array/equal_value_spec.rb
+++ b/spec/ruby/core/array/equal_value_spec.rb
@@ -10,17 +10,17 @@ describe "Array#==" do
obj.should_receive(:respond_to?).at_least(1).with(:to_ary).and_return(true)
obj.should_receive(:==).with([1]).at_least(1).and_return(true)
- ([1] == obj).should be_true
- ([[1]] == [obj]).should be_true
- ([[[1], 3], 2] == [[obj, 3], 2]).should be_true
+ ([1] == obj).should == true
+ ([[1]] == [obj]).should == true
+ ([[[1], 3], 2] == [[obj, 3], 2]).should == true
# recursive arrays
arr1 = [[1]]
arr1 << arr1
arr2 = [obj]
arr2 << arr2
- (arr1 == arr2).should be_true
- (arr2 == arr1).should be_true
+ (arr1 == arr2).should == true
+ (arr2 == arr1).should == true
end
it "returns false if any corresponding elements are not #==" do
diff --git a/spec/ruby/core/array/fetch_spec.rb b/spec/ruby/core/array/fetch_spec.rb
index b81c0b48d7..ee94cfcbb2 100644
--- a/spec/ruby/core/array/fetch_spec.rb
+++ b/spec/ruby/core/array/fetch_spec.rb
@@ -12,9 +12,9 @@ describe "Array#fetch" do
end
it "raises an IndexError if there is no element at index" do
- -> { [1, 2, 3].fetch(3) }.should raise_error(IndexError)
- -> { [1, 2, 3].fetch(-4) }.should raise_error(IndexError)
- -> { [].fetch(0) }.should raise_error(IndexError)
+ -> { [1, 2, 3].fetch(3) }.should.raise(IndexError, "index 3 outside of array bounds: -3...3")
+ -> { [1, 2, 3].fetch(-4) }.should.raise(IndexError, "index -4 outside of array bounds: -3...3")
+ -> { [].fetch(0) }.should.raise(IndexError, "index 0 outside of array bounds: 0...0")
end
it "returns default if there is no element at index if passed a default value" do
@@ -33,7 +33,7 @@ describe "Array#fetch" do
o = mock('5')
def o.to_int(); 5; end
- [1, 2, 3].fetch(o) { |i| i }.should equal(o)
+ [1, 2, 3].fetch(o) { |i| i }.should.equal?(o)
end
it "gives precedence to the default block over the default argument" do
@@ -50,6 +50,6 @@ describe "Array#fetch" do
end
it "raises a TypeError when the passed argument can't be coerced to Integer" do
- -> { [].fetch("cat") }.should raise_error(TypeError)
+ -> { [].fetch("cat") }.should.raise(TypeError, "no implicit conversion of String into Integer")
end
end
diff --git a/spec/ruby/core/array/fetch_values_spec.rb b/spec/ruby/core/array/fetch_values_spec.rb
new file mode 100644
index 0000000000..237d6ad7f7
--- /dev/null
+++ b/spec/ruby/core/array/fetch_values_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Array#fetch_values" do
+ before :each do
+ @array = [:a, :b, :c]
+ end
+
+ ruby_version_is "3.4" do
+ describe "with matched indexes" do
+ it "returns the values for indexes" do
+ @array.fetch_values(0).should == [:a]
+ @array.fetch_values(0, 2).should == [:a, :c]
+ @array.fetch_values(-1).should == [:c]
+ end
+
+ it "returns the values for indexes ordered in the order of the requested indexes" do
+ @array.fetch_values(2, 0).should == [:c, :a]
+ end
+ end
+
+ describe "with unmatched indexes" do
+ it "raises a index error if no block is provided" do
+ -> { @array.fetch_values(0, 1, 44) }.should.raise(IndexError, "index 44 outside of array bounds: -3...3")
+ end
+
+ it "returns the default value from block" do
+ @array.fetch_values(44) { |index| "`#{index}' is not found" }.should == ["`44' is not found"]
+ @array.fetch_values(0, 44) { |index| "`#{index}' is not found" }.should == [:a, "`44' is not found"]
+ end
+ end
+
+ describe "without keys" do
+ it "returns an empty Array" do
+ @array.fetch_values.should == []
+ end
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+ @array.fetch_values(obj).should == [:c]
+ end
+
+ it "does not support a Range object as argument" do
+ -> {
+ @array.fetch_values(1..2)
+ }.should.raise(TypeError, "no implicit conversion of Range into Integer")
+ end
+
+ it "raises a TypeError when the passed argument can't be coerced to Integer" do
+ -> { [].fetch_values("cat") }.should.raise(TypeError, "no implicit conversion of String into Integer")
+ end
+ end
+end
diff --git a/spec/ruby/core/array/fill_spec.rb b/spec/ruby/core/array/fill_spec.rb
index 6745bc8d09..e4d51bd998 100644
--- a/spec/ruby/core/array/fill_spec.rb
+++ b/spec/ruby/core/array/fill_spec.rb
@@ -10,7 +10,7 @@ describe "Array#fill" do
it "returns self" do
ary = [1, 2, 3]
- ary.fill(:a).should equal(ary)
+ ary.fill(:a).should.equal?(ary)
end
it "is destructive" do
@@ -21,14 +21,14 @@ describe "Array#fill" do
it "does not replicate the filler" do
ary = [1, 2, 3, 4]
- str = "x"
+ str = +"x"
ary.fill(str).should == [str, str, str, str]
str << "y"
ary.should == [str, str, str, str]
- ary[0].should equal(str)
- ary[1].should equal(str)
- ary[2].should equal(str)
- ary[3].should equal(str)
+ ary[0].should.equal?(str)
+ ary[1].should.equal?(str)
+ ary[2].should.equal?(str)
+ ary[3].should.equal?(str)
end
it "replaces all elements in the array with the filler if not given a index nor a length" do
@@ -44,33 +44,71 @@ describe "Array#fill" do
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.fill('x') }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.fill('x') }.should.raise(FrozenError)
end
it "raises a FrozenError on an empty frozen array" do
- -> { ArraySpecs.empty_frozen_array.fill('x') }.should raise_error(FrozenError)
+ -> { ArraySpecs.empty_frozen_array.fill('x') }.should.raise(FrozenError)
end
it "raises an ArgumentError if 4 or more arguments are passed when no block given" do
- -> { [].fill('a') }.should_not raise_error(ArgumentError)
-
- -> { [].fill('a', 1) }.should_not raise_error(ArgumentError)
-
- -> { [].fill('a', 1, 2) }.should_not raise_error(ArgumentError)
- -> { [].fill('a', 1, 2, true) }.should raise_error(ArgumentError)
+ [].fill('a').should == []
+ [].fill('a', 1).should == []
+ [].fill('a', 1, 2).should == [nil, 'a', 'a']
+ -> { [].fill('a', 1, 2, true) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if no argument passed and no block given" do
- -> { [].fill }.should raise_error(ArgumentError)
+ -> { [].fill }.should.raise(ArgumentError)
end
it "raises an ArgumentError if 3 or more arguments are passed when a block given" do
- -> { [].fill() {|i|} }.should_not raise_error(ArgumentError)
+ [].fill() {|i|}.should == []
+ [].fill(1) {|i|}.should == []
+ [].fill(1, 2) {|i|}.should == [nil, nil, nil]
+ -> { [].fill(1, 2, true) {|i|} }.should.raise(ArgumentError)
+ end
+
+ it "does not truncate the array is the block raises an exception" do
+ a = [1, 2, 3]
+ begin
+ a.fill { raise StandardError, 'Oops' }
+ rescue
+ end
+
+ a.should == [1, 2, 3]
+ end
+
+ it "only changes elements before error is raised, keeping the element which raised an error." do
+ a = [1, 2, 3, 4]
+ begin
+ a.fill do |i|
+ case i
+ when 0 then -1
+ when 1 then -2
+ when 2 then raise StandardError, 'Oops'
+ else 0
+ end
+ end
+ rescue StandardError
+ end
- -> { [].fill(1) {|i|} }.should_not raise_error(ArgumentError)
+ a.should == [-1, -2, 3, 4]
+ end
+
+ it "tolerates increasing an array size during iteration" do
+ array = [:a, :b, :c]
+ ScratchPad.record []
+ i = 0
- -> { [].fill(1, 2) {|i|} }.should_not raise_error(ArgumentError)
- -> { [].fill(1, 2, true) {|i|} }.should raise_error(ArgumentError)
+ array.fill do |index|
+ ScratchPad << index
+ array << i if i < 100
+ i++
+ index
+ end
+
+ ScratchPad.recorded.should == [0, 1, 2]
end
end
@@ -169,25 +207,25 @@ describe "Array#fill with (filler, index, length)" do
[1, 2, 3, 4, 5].fill(-2, -2, &@never_passed).should == [1, 2, 3, 4, 5]
end
- # See: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/17481
+ # See: https://blade.ruby-lang.org/ruby-core/17481
it "does not raise an exception if the given length is negative and its absolute value does not exceed the index" do
- -> { [1, 2, 3, 4].fill('a', 3, -1)}.should_not raise_error(ArgumentError)
- -> { [1, 2, 3, 4].fill('a', 3, -2)}.should_not raise_error(ArgumentError)
- -> { [1, 2, 3, 4].fill('a', 3, -3)}.should_not raise_error(ArgumentError)
+ [1, 2, 3, 4].fill('a', 3, -1).should == [1, 2, 3, 4]
+ [1, 2, 3, 4].fill('a', 3, -2).should == [1, 2, 3, 4]
+ [1, 2, 3, 4].fill('a', 3, -3).should == [1, 2, 3, 4]
- -> { [1, 2, 3, 4].fill(3, -1, &@never_passed)}.should_not raise_error(ArgumentError)
- -> { [1, 2, 3, 4].fill(3, -2, &@never_passed)}.should_not raise_error(ArgumentError)
- -> { [1, 2, 3, 4].fill(3, -3, &@never_passed)}.should_not raise_error(ArgumentError)
+ [1, 2, 3, 4].fill(3, -1, &@never_passed).should == [1, 2, 3, 4]
+ [1, 2, 3, 4].fill(3, -2, &@never_passed).should == [1, 2, 3, 4]
+ [1, 2, 3, 4].fill(3, -3, &@never_passed).should == [1, 2, 3, 4]
end
it "does not raise an exception even if the given length is negative and its absolute value exceeds the index" do
- -> { [1, 2, 3, 4].fill('a', 3, -4)}.should_not raise_error(ArgumentError)
- -> { [1, 2, 3, 4].fill('a', 3, -5)}.should_not raise_error(ArgumentError)
- -> { [1, 2, 3, 4].fill('a', 3, -10000)}.should_not raise_error(ArgumentError)
+ [1, 2, 3, 4].fill('a', 3, -4).should == [1, 2, 3, 4]
+ [1, 2, 3, 4].fill('a', 3, -5).should == [1, 2, 3, 4]
+ [1, 2, 3, 4].fill('a', 3, -10000).should == [1, 2, 3, 4]
- -> { [1, 2, 3, 4].fill(3, -4, &@never_passed)}.should_not raise_error(ArgumentError)
- -> { [1, 2, 3, 4].fill(3, -5, &@never_passed)}.should_not raise_error(ArgumentError)
- -> { [1, 2, 3, 4].fill(3, -10000, &@never_passed)}.should_not raise_error(ArgumentError)
+ [1, 2, 3, 4].fill(3, -4, &@never_passed).should == [1, 2, 3, 4]
+ [1, 2, 3, 4].fill(3, -5, &@never_passed).should == [1, 2, 3, 4]
+ [1, 2, 3, 4].fill(3, -10000, &@never_passed).should == [1, 2, 3, 4]
end
it "tries to convert the second and third arguments to Integers using #to_int" do
@@ -199,18 +237,24 @@ describe "Array#fill with (filler, index, length)" do
end
it "raises a TypeError if the index is not numeric" do
- -> { [].fill 'a', true }.should raise_error(TypeError)
+ -> { [].fill 'a', true }.should.raise(TypeError)
obj = mock('nonnumeric')
- -> { [].fill('a', obj) }.should raise_error(TypeError)
+ -> { [].fill('a', obj) }.should.raise(TypeError)
+ end
+
+ it "raises a TypeError when the length is not numeric" do
+ -> { [1, 2, 3].fill("x", 1, "foo") }.should.raise(TypeError, /no implicit conversion of String into Integer/)
+ -> { [1, 2, 3].fill("x", 1, :"foo") }.should.raise(TypeError, /no implicit conversion of Symbol into Integer/)
+ -> { [1, 2, 3].fill("x", 1, Object.new) }.should.raise(TypeError, /no implicit conversion of Object into Integer/)
end
not_supported_on :opal do
it "raises an ArgumentError or RangeError for too-large sizes" do
error_types = [RangeError, ArgumentError]
arr = [1, 2, 3]
- -> { arr.fill(10, 1, fixnum_max) }.should raise_error { |err| error_types.should include(err.class) }
- -> { arr.fill(10, 1, bignum_value) }.should raise_error(RangeError)
+ -> { arr.fill(10, 1, fixnum_max) }.should.raise { |err| error_types.should.include?(err.class) }
+ -> { arr.fill(10, 1, bignum_value) }.should.raise(RangeError)
end
end
end
@@ -240,7 +284,7 @@ describe "Array#fill with (filler, range)" do
end
it "raises a TypeError with range and length argument" do
- -> { [].fill('x', 0 .. 2, 5) }.should raise_error(TypeError)
+ -> { [].fill('x', 0 .. 2, 5) }.should.raise(TypeError)
end
it "replaces elements between the (-m)th to the last and the (n+1)th from the first if given an range m..n where m < 0 and n >= 0" do
@@ -292,13 +336,13 @@ describe "Array#fill with (filler, range)" do
end
it "raises an exception if some of the given range lies before the first of the array" do
- -> { [1, 2, 3].fill('x', -5..-3) }.should raise_error(RangeError)
- -> { [1, 2, 3].fill('x', -5...-3) }.should raise_error(RangeError)
- -> { [1, 2, 3].fill('x', -5..-4) }.should raise_error(RangeError)
+ -> { [1, 2, 3].fill('x', -5..-3) }.should.raise(RangeError)
+ -> { [1, 2, 3].fill('x', -5...-3) }.should.raise(RangeError)
+ -> { [1, 2, 3].fill('x', -5..-4) }.should.raise(RangeError)
- -> { [1, 2, 3].fill(-5..-3, &@never_passed) }.should raise_error(RangeError)
- -> { [1, 2, 3].fill(-5...-3, &@never_passed) }.should raise_error(RangeError)
- -> { [1, 2, 3].fill(-5..-4, &@never_passed) }.should raise_error(RangeError)
+ -> { [1, 2, 3].fill(-5..-3, &@never_passed) }.should.raise(RangeError)
+ -> { [1, 2, 3].fill(-5...-3, &@never_passed) }.should.raise(RangeError)
+ -> { [1, 2, 3].fill(-5..-4, &@never_passed) }.should.raise(RangeError)
end
it "tries to convert the start and end of the passed range to Integers using #to_int" do
@@ -313,15 +357,18 @@ describe "Array#fill with (filler, range)" do
it "raises a TypeError if the start or end of the passed range is not numeric" do
obj = mock('nonnumeric')
def obj.<=>(rhs); rhs == self ? 0 : nil end
- -> { [].fill('a', obj..obj) }.should raise_error(TypeError)
+ -> { [].fill('a', obj..obj) }.should.raise(TypeError)
end
- ruby_version_is "2.6" do
- it "works with endless ranges" do
- [1, 2, 3, 4].fill('x', eval("(1..)")).should == [1, 'x', 'x', 'x']
- [1, 2, 3, 4].fill('x', eval("(3...)")).should == [1, 2, 3, 'x']
- [1, 2, 3, 4].fill(eval("(1..)")) { |x| x + 2 }.should == [1, 3, 4, 5]
- [1, 2, 3, 4].fill(eval("(3...)")) { |x| x + 2 }.should == [1, 2, 3, 5]
- end
+ it "works with endless ranges" do
+ [1, 2, 3, 4].fill('x', eval("(1..)")).should == [1, 'x', 'x', 'x']
+ [1, 2, 3, 4].fill('x', eval("(3...)")).should == [1, 2, 3, 'x']
+ [1, 2, 3, 4].fill(eval("(1..)")) { |x| x + 2 }.should == [1, 3, 4, 5]
+ [1, 2, 3, 4].fill(eval("(3...)")) { |x| x + 2 }.should == [1, 2, 3, 5]
+ end
+
+ it "works with beginless ranges" do
+ [1, 2, 3, 4].fill('x', (..2)).should == ["x", "x", "x", 4]
+ [1, 2, 3, 4].fill((...2)) { |x| x + 2 }.should == [2, 3, 3, 4]
end
end
diff --git a/spec/ruby/core/array/filter_spec.rb b/spec/ruby/core/array/filter_spec.rb
index ee4f71ca28..6ebda61069 100644
--- a/spec/ruby/core/array/filter_spec.rb
+++ b/spec/ruby/core/array/filter_spec.rb
@@ -1,16 +1,13 @@
require_relative '../../spec_helper'
-require_relative 'shared/select'
-ruby_version_is "2.6" do
- describe "Array#filter" do
- it_behaves_like :array_select, :filter
+describe "Array#filter" do
+ it "is an alias of Array#select" do
+ Array.instance_method(:filter).should == Array.instance_method(:select)
end
+end
- describe "Array#filter!" do
- it "returns nil if no changes were made in the array" do
- [1, 2, 3].filter! { true }.should be_nil
- end
-
- it_behaves_like :keep_if, :filter!
+describe "Array#filter!" do
+ it "is an alias of Array#select!" do
+ Array.instance_method(:filter!).should == Array.instance_method(:select!)
end
end
diff --git a/spec/ruby/core/array/find_index_spec.rb b/spec/ruby/core/array/find_index_spec.rb
index 759472024a..17ff6c04a7 100644
--- a/spec/ruby/core/array/find_index_spec.rb
+++ b/spec/ruby/core/array/find_index_spec.rb
@@ -1,6 +1,42 @@
require_relative '../../spec_helper'
-require_relative 'shared/index'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#find_index" do
- it_behaves_like :array_index, :find_index
+ it "returns the index of the first element == to object" do
+ x = mock('3')
+ def x.==(obj) 3 == obj; end
+
+ [2, x, 3, 1, 3, 1].find_index(3).should == 1
+ [2, 3.0, 3, x, 1, 3, 1].find_index(x).should == 1
+ end
+
+ it "returns 0 if first element == to object" do
+ [2, 1, 3, 2, 5].find_index(2).should == 0
+ end
+
+ it "returns size-1 if only last element == to object" do
+ [2, 1, 3, 1, 5].find_index(5).should == 4
+ end
+
+ it "returns nil if no element == to object" do
+ [2, 1, 1, 1, 1].find_index(3).should == nil
+ end
+
+ it "accepts a block instead of an argument" do
+ [4, 2, 1, 5, 1, 3].find_index {|x| x < 2}.should == 2
+ end
+
+ it "ignores the block if there is an argument" do
+ -> {
+ [4, 2, 1, 5, 1, 3].find_index(5) {|x| x < 2}.should == 3
+ }.should complain(/given block not used/)
+ end
+
+ describe "given no argument and no block" do
+ it "produces an Enumerator" do
+ [].find_index.should.instance_of?(Enumerator)
+ end
+ end
+
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :find_index
end
diff --git a/spec/ruby/core/array/first_spec.rb b/spec/ruby/core/array/first_spec.rb
index 66eeba6565..2c343ac8f6 100644
--- a/spec/ruby/core/array/first_spec.rb
+++ b/spec/ruby/core/array/first_spec.rb
@@ -30,11 +30,11 @@ describe "Array#first" do
end
it "raises an ArgumentError when count is negative" do
- -> { [1, 2].first(-1) }.should raise_error(ArgumentError)
+ -> { [1, 2].first(-1) }.should.raise(ArgumentError)
end
it "raises a RangeError when count is a Bignum" do
- -> { [].first(bignum_value) }.should raise_error(RangeError)
+ -> { [].first(bignum_value) }.should.raise(RangeError)
end
it "returns the entire array when count > length" do
@@ -53,10 +53,10 @@ describe "Array#first" do
it "properly handles recursive arrays" do
empty = ArraySpecs.empty_recursive_array
- empty.first.should equal(empty)
+ empty.first.should.equal?(empty)
ary = ArraySpecs.head_recursive_array
- ary.first.should equal(ary)
+ ary.first.should.equal?(ary)
end
it "tries to convert the passed argument to an Integer using #to_int" do
@@ -66,19 +66,19 @@ describe "Array#first" do
end
it "raises a TypeError if the passed argument is not numeric" do
- -> { [1,2].first(nil) }.should raise_error(TypeError)
- -> { [1,2].first("a") }.should raise_error(TypeError)
+ -> { [1,2].first(nil) }.should.raise(TypeError)
+ -> { [1,2].first("a") }.should.raise(TypeError)
obj = mock("nonnumeric")
- -> { [1,2].first(obj) }.should raise_error(TypeError)
+ -> { [1,2].first(obj) }.should.raise(TypeError)
end
it "does not return subclass instance when passed count on Array subclasses" do
- ArraySpecs::MyArray[].first(0).should be_an_instance_of(Array)
- ArraySpecs::MyArray[].first(2).should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2, 3].first(0).should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2, 3].first(1).should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2, 3].first(2).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[].first(0).should.instance_of?(Array)
+ ArraySpecs::MyArray[].first(2).should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2, 3].first(0).should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2, 3].first(1).should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2, 3].first(2).should.instance_of?(Array)
end
it "is not destructive" do
diff --git a/spec/ruby/core/array/fixtures/classes.rb b/spec/ruby/core/array/fixtures/classes.rb
index 2791a2eb5d..05283c0f74 100644
--- a/spec/ruby/core/array/fixtures/classes.rb
+++ b/spec/ruby/core/array/fixtures/classes.rb
@@ -40,6 +40,62 @@ module ArraySpecs
a
end
+ # Chi squared critical values for tests with n degrees of freedom at 99% confidence.
+ # Values obtained from NIST Engineering Statistic Handbook at
+ # https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm
+
+ CHI_SQUARED_CRITICAL_VALUES = [
+ 0,
+ 6.635, 9.210, 11.345, 13.277, 15.086, 16.812, 18.475, 20.090, 21.666, 23.209,
+ 24.725, 26.217, 27.688, 29.141, 30.578, 32.000, 33.409, 34.805, 36.191, 37.566,
+ 38.932, 40.289, 41.638, 42.980, 44.314, 45.642, 46.963, 48.278, 49.588, 50.892,
+ 52.191, 53.486, 54.776, 56.061, 57.342, 58.619, 59.893, 61.162, 62.428, 63.691,
+ 64.950, 66.206, 67.459, 68.710, 69.957, 71.201, 72.443, 73.683, 74.919, 76.154,
+ 77.386, 78.616, 79.843, 81.069, 82.292, 83.513, 84.733, 85.950, 87.166, 88.379,
+ 89.591, 90.802, 92.010, 93.217, 94.422, 95.626, 96.828, 98.028, 99.228, 100.425,
+ 101.621, 102.816, 104.010, 105.202, 106.393, 107.583, 108.771, 109.958, 111.144, 112.329,
+ 113.512, 114.695, 115.876, 117.057, 118.236, 119.414, 120.591, 121.767, 122.942, 124.116,
+ 125.289, 126.462, 127.633, 128.803, 129.973, 131.141, 132.309, 133.476, 134.642, 135.807,
+ ]
+
+ def self.measure_sample_fairness(size, samples, iters)
+ ary = Array.new(size) { |x| x }
+ expected = iters.fdiv size
+ (samples).times do |i|
+ chi_results = []
+ 3.times do
+ counts = Array.new(size, 0)
+ iters.times do
+ x = ary.sample(samples)[i]
+ counts[x] += 1
+ end
+ chi_squared = counts.sum {|count| (count - expected) ** 2} / expected
+ chi_results << chi_squared
+ break if chi_squared <= CHI_SQUARED_CRITICAL_VALUES[size]
+ end
+
+ chi_results.min.should <= CHI_SQUARED_CRITICAL_VALUES[size]
+ end
+ end
+
+ def self.measure_sample_fairness_large_sample_size(size, samples, iters)
+ ary = Array.new(size) { |x| x }
+ counts = Array.new(size, 0)
+ expected = (iters * samples).fdiv size
+ iters.times do
+ ary.sample(samples).each do |sample|
+ counts[sample] += 1
+ end
+ end
+ chi_squared = counts.sum {|count| (count - expected) ** 2} / expected
+
+ # Chi squared critical values for tests with 4 degrees of freedom
+ # Values obtained from NIST Engineering Statistic Handbook at
+ # https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm
+
+ chi_squared.should <= CHI_SQUARED_CRITICAL_VALUES[size]
+ end
+
class MyArray < Array
# The #initialize method has a different signature than Array to help
# catch places in the specs that do not assert the #initialize is not
@@ -98,6 +154,16 @@ module ArraySpecs
end
end
+ class ArrayMethodMissing
+ def initialize(*values, &block)
+ @values = values;
+ end
+
+ def method_missing(name, *args)
+ @values
+ end
+ end
+
class SortSame
def <=>(other); 0; end
def ==(other); true; end
@@ -126,7 +192,7 @@ module ArraySpecs
attr_accessor :order
end
- class ComparableWithFixnum
+ class ComparableWithInteger
include Comparable
def initialize(num)
@num = num
@@ -151,366 +217,370 @@ module ArraySpecs
obj
end
- LargeArray = ["test_create_table_with_force_true_does_not_drop_nonexisting_table",
- "test_add_table",
- "assert_difference",
- "assert_operator",
- "instance_variables",
- "class",
- "instance_variable_get",
- "__class__",
- "expects",
- "assert_no_difference",
- "name",
- "assert_blank",
- "assert_not_same",
- "is_a?",
- "test_add_table_with_decimals",
- "test_create_table_with_timestamps_should_create_datetime_columns",
- "assert_present",
- "assert_no_match",
- "__instance_of__",
- "assert_deprecated",
- "assert",
- "assert_throws",
- "kind_of?",
- "try",
- "__instance_variable_get__",
- "object_id",
- "timeout",
- "instance_variable_set",
- "assert_nothing_thrown",
- "__instance_variable_set__",
- "copy_object",
- "test_create_table_with_timestamps_should_create_datetime_columns_with_options",
- "assert_not_deprecated",
- "assert_in_delta",
- "id",
- "copy_metaclass",
- "test_create_table_without_a_block",
- "dup",
- "assert_not_nil",
- "send",
- "__instance_variables__",
- "to_sql",
- "mock",
- "assert_send",
- "instance_variable_defined?",
- "clone",
- "require",
- "test_migrator",
- "__instance_variable_defined_eh__",
- "frozen?",
- "test_add_column_not_null_with_default",
- "freeze",
- "test_migrator_one_up",
- "test_migrator_one_down",
- "singleton_methods",
- "method_exists?",
- "create_fixtures",
- "test_migrator_one_up_one_down",
- "test_native_decimal_insert_manual_vs_automatic",
- "instance_exec",
- "__is_a__",
- "test_migrator_double_up",
- "stub",
- "private_methods",
- "stubs",
- "test_migrator_double_down",
- "fixture_path",
- "private_singleton_methods",
- "stub_everything",
- "test_migrator_one_up_with_exception_and_rollback",
- "sequence",
- "protected_methods",
- "enum_for",
- "test_finds_migrations",
- "run_before_mocha",
- "states",
- "protected_singleton_methods",
- "to_json",
- "instance_values",
- "==",
- "mocha_setup",
- "public_methods",
- "test_finds_pending_migrations",
- "mocha_verify",
- "assert_kind_of",
- "===",
- "=~",
- "test_relative_migrations",
- "mocha_teardown",
- "gem",
- "mocha",
- "test_only_loads_pending_migrations",
- "test_add_column_with_precision_and_scale",
- "require_or_load",
- "eql?",
- "require_dependency",
- "test_native_types",
- "test_target_version_zero_should_run_only_once",
- "extend",
- "to_matcher",
- "unloadable",
- "require_association",
- "hash",
- "__id__",
- "load_dependency",
- "equals",
- "test_migrator_db_has_no_schema_migrations_table",
- "test_migrator_verbosity",
- "kind_of",
- "to_yaml",
- "to_bool",
- "test_migrator_verbosity_off",
- "taint",
- "test_migrator_going_down_due_to_version_target",
- "tainted?",
- "mocha_inspect",
- "test_migrator_rollback",
- "vim",
- "untaint",
- "taguri=",
- "test_migrator_forward",
- "test_schema_migrations_table_name",
- "test_proper_table_name",
- "all_of",
- "test_add_drop_table_with_prefix_and_suffix",
- "_setup_callbacks",
- "setup",
- "Not",
- "test_create_table_with_binary_column",
- "assert_not_equal",
- "enable_warnings",
- "acts_like?",
- "Rational",
- "_removed_setup_callbacks",
- "Table",
- "bind",
- "any_of",
- "__method__",
- "test_migrator_with_duplicates",
- "_teardown_callbacks",
- "method",
- "test_migrator_with_duplicate_names",
- "_removed_teardown_callbacks",
- "any_parameters",
- "test_migrator_with_missing_version_numbers",
- "test_add_remove_single_field_using_string_arguments",
- "test_create_table_with_custom_sequence_name",
- "test_add_remove_single_field_using_symbol_arguments",
- "_one_time_conditions_valid_14?",
- "_one_time_conditions_valid_16?",
- "run_callbacks",
- "anything",
- "silence_warnings",
- "instance_variable_names",
- "_fixture_path",
- "copy_instance_variables_from",
- "fixture_path?",
- "has_entry",
- "__marshal__",
- "_fixture_table_names",
- "__kind_of__",
- "fixture_table_names?",
- "test_add_rename",
- "assert_equal",
- "_fixture_class_names",
- "fixture_class_names?",
- "has_entries",
- "_use_transactional_fixtures",
- "people",
- "test_rename_column_using_symbol_arguments",
- "use_transactional_fixtures?",
- "instance_eval",
- "blank?",
- "with_warnings",
- "__nil__",
- "load",
- "metaclass",
- "_use_instantiated_fixtures",
- "has_key",
- "class_eval",
- "present?",
- "test_rename_column",
- "teardown",
- "use_instantiated_fixtures?",
- "method_name",
- "silence_stderr",
- "presence",
- "test_rename_column_preserves_default_value_not_null",
- "silence_stream",
- "_pre_loaded_fixtures",
- "__metaclass__",
- "__fixnum__",
- "pre_loaded_fixtures?",
- "has_value",
- "suppress",
- "to_yaml_properties",
- "test_rename_nonexistent_column",
- "test_add_index",
- "includes",
- "find_correlate_in",
- "equality_predicate_sql",
- "assert_nothing_raised",
- "let",
- "not_predicate_sql",
- "test_rename_column_with_sql_reserved_word",
- "singleton_class",
- "test_rename_column_with_an_index",
- "display",
- "taguri",
- "to_yaml_style",
- "test_remove_column_with_index",
- "size",
- "current_adapter?",
- "test_remove_column_with_multi_column_index",
- "respond_to?",
- "test_change_type_of_not_null_column",
- "is_a",
- "to_a",
- "test_rename_table_for_sqlite_should_work_with_reserved_words",
- "require_library_or_gem",
- "setup_fixtures",
- "equal?",
- "teardown_fixtures",
- "nil?",
- "fixture_table_names",
- "fixture_class_names",
- "test_create_table_without_id",
- "use_transactional_fixtures",
- "test_add_column_with_primary_key_attribute",
- "repair_validations",
- "use_instantiated_fixtures",
- "instance_of?",
- "test_create_table_adds_id",
- "test_rename_table",
- "pre_loaded_fixtures",
- "to_enum",
- "test_create_table_with_not_null_column",
- "instance_of",
- "test_change_column_nullability",
- "optionally",
- "test_rename_table_with_an_index",
- "run",
- "test_change_column",
- "default_test",
- "assert_raise",
- "test_create_table_with_defaults",
- "assert_nil",
- "flunk",
- "regexp_matches",
- "duplicable?",
- "reset_mocha",
- "stubba_method",
- "filter_backtrace",
- "test_create_table_with_limits",
- "responds_with",
- "stubba_object",
- "test_change_column_with_nil_default",
- "assert_block",
- "__show__",
- "assert_date_from_db",
- "__respond_to_eh__",
- "run_in_transaction?",
- "inspect",
- "assert_sql",
- "test_change_column_with_new_default",
- "yaml_equivalent",
- "build_message",
- "to_s",
- "test_change_column_default",
- "assert_queries",
- "pending",
- "as_json",
- "assert_no_queries",
- "test_change_column_quotes_column_names",
- "assert_match",
- "test_keeping_default_and_notnull_constraint_on_change",
- "methods",
- "connection_allow_concurrency_setup",
- "connection_allow_concurrency_teardown",
- "test_create_table_with_primary_key_prefix_as_table_name_with_underscore",
- "__send__",
- "make_connection",
- "assert_raises",
- "tap",
- "with_kcode",
- "assert_instance_of",
- "test_create_table_with_primary_key_prefix_as_table_name",
- "assert_respond_to",
- "test_change_column_default_to_null",
- "assert_same",
- "__extend__"]
-
- LargeTestArraySorted = ["test_add_column_not_null_with_default",
- "test_add_column_with_precision_and_scale",
- "test_add_column_with_primary_key_attribute",
- "test_add_drop_table_with_prefix_and_suffix",
- "test_add_index",
- "test_add_remove_single_field_using_string_arguments",
- "test_add_remove_single_field_using_symbol_arguments",
- "test_add_rename",
- "test_add_table",
- "test_add_table_with_decimals",
- "test_change_column",
- "test_change_column_default",
- "test_change_column_default_to_null",
- "test_change_column_nullability",
- "test_change_column_quotes_column_names",
- "test_change_column_with_new_default",
- "test_change_column_with_nil_default",
- "test_change_type_of_not_null_column",
- "test_create_table_adds_id",
- "test_create_table_with_binary_column",
- "test_create_table_with_custom_sequence_name",
- "test_create_table_with_defaults",
- "test_create_table_with_force_true_does_not_drop_nonexisting_table",
- "test_create_table_with_limits",
- "test_create_table_with_not_null_column",
- "test_create_table_with_primary_key_prefix_as_table_name",
- "test_create_table_with_primary_key_prefix_as_table_name_with_underscore",
- "test_create_table_with_timestamps_should_create_datetime_columns",
- "test_create_table_with_timestamps_should_create_datetime_columns_with_options",
- "test_create_table_without_a_block",
- "test_create_table_without_id",
- "test_finds_migrations",
- "test_finds_pending_migrations",
- "test_keeping_default_and_notnull_constraint_on_change",
- "test_migrator",
- "test_migrator_db_has_no_schema_migrations_table",
- "test_migrator_double_down",
- "test_migrator_double_up",
- "test_migrator_forward",
- "test_migrator_going_down_due_to_version_target",
- "test_migrator_one_down",
- "test_migrator_one_up",
- "test_migrator_one_up_one_down",
- "test_migrator_one_up_with_exception_and_rollback",
- "test_migrator_rollback",
- "test_migrator_verbosity",
- "test_migrator_verbosity_off",
- "test_migrator_with_duplicate_names",
- "test_migrator_with_duplicates",
- "test_migrator_with_missing_version_numbers",
- "test_native_decimal_insert_manual_vs_automatic",
- "test_native_types",
- "test_only_loads_pending_migrations",
- "test_proper_table_name",
- "test_relative_migrations",
- "test_remove_column_with_index",
- "test_remove_column_with_multi_column_index",
- "test_rename_column",
- "test_rename_column_preserves_default_value_not_null",
- "test_rename_column_using_symbol_arguments",
- "test_rename_column_with_an_index",
- "test_rename_column_with_sql_reserved_word",
- "test_rename_nonexistent_column",
- "test_rename_table",
- "test_rename_table_for_sqlite_should_work_with_reserved_words",
- "test_rename_table_with_an_index",
- "test_schema_migrations_table_name",
- "test_target_version_zero_should_run_only_once"]
+ LargeArray = [
+ "test_create_table_with_force_true_does_not_drop_nonexisting_table",
+ "test_add_table",
+ "assert_difference",
+ "assert_operator",
+ "instance_variables",
+ "class",
+ "instance_variable_get",
+ "__class__",
+ "expects",
+ "assert_no_difference",
+ "name",
+ "assert_blank",
+ "assert_not_same",
+ "is_a?",
+ "test_add_table_with_decimals",
+ "test_create_table_with_timestamps_should_create_datetime_columns",
+ "assert_present",
+ "assert_no_match",
+ "__instance_of__",
+ "assert_deprecated",
+ "assert",
+ "assert_throws",
+ "kind_of?",
+ "try",
+ "__instance_variable_get__",
+ "object_id",
+ "timeout",
+ "instance_variable_set",
+ "assert_nothing_thrown",
+ "__instance_variable_set__",
+ "copy_object",
+ "test_create_table_with_timestamps_should_create_datetime_columns_with_options",
+ "assert_not_deprecated",
+ "assert_in_delta",
+ "id",
+ "copy_metaclass",
+ "test_create_table_without_a_block",
+ "dup",
+ "assert_not_nil",
+ "send",
+ "__instance_variables__",
+ "to_sql",
+ "mock",
+ "assert_send",
+ "instance_variable_defined?",
+ "clone",
+ "require",
+ "test_migrator",
+ "__instance_variable_defined_eh__",
+ "frozen?",
+ "test_add_column_not_null_with_default",
+ "freeze",
+ "test_migrator_one_up",
+ "test_migrator_one_down",
+ "singleton_methods",
+ "method_exists?",
+ "create_fixtures",
+ "test_migrator_one_up_one_down",
+ "test_native_decimal_insert_manual_vs_automatic",
+ "instance_exec",
+ "__is_a__",
+ "test_migrator_double_up",
+ "stub",
+ "private_methods",
+ "stubs",
+ "test_migrator_double_down",
+ "fixture_path",
+ "private_singleton_methods",
+ "stub_everything",
+ "test_migrator_one_up_with_exception_and_rollback",
+ "sequence",
+ "protected_methods",
+ "enum_for",
+ "test_finds_migrations",
+ "run_before_mocha",
+ "states",
+ "protected_singleton_methods",
+ "to_json",
+ "instance_values",
+ "==",
+ "mocha_setup",
+ "public_methods",
+ "test_finds_pending_migrations",
+ "mocha_verify",
+ "assert_kind_of",
+ "===",
+ "=~",
+ "test_relative_migrations",
+ "mocha_teardown",
+ "gem",
+ "mocha",
+ "test_only_loads_pending_migrations",
+ "test_add_column_with_precision_and_scale",
+ "require_or_load",
+ "eql?",
+ "require_dependency",
+ "test_native_types",
+ "test_target_version_zero_should_run_only_once",
+ "extend",
+ "to_matcher",
+ "unloadable",
+ "require_association",
+ "hash",
+ "__id__",
+ "load_dependency",
+ "equals",
+ "test_migrator_db_has_no_schema_migrations_table",
+ "test_migrator_verbosity",
+ "kind_of",
+ "to_yaml",
+ "to_bool",
+ "test_migrator_verbosity_off",
+ "taint",
+ "test_migrator_going_down_due_to_version_target",
+ "tainted?",
+ "mocha_inspect",
+ "test_migrator_rollback",
+ "vim",
+ "untaint",
+ "taguri=",
+ "test_migrator_forward",
+ "test_schema_migrations_table_name",
+ "test_proper_table_name",
+ "all_of",
+ "test_add_drop_table_with_prefix_and_suffix",
+ "_setup_callbacks",
+ "setup",
+ "Not",
+ "test_create_table_with_binary_column",
+ "assert_not_equal",
+ "enable_warnings",
+ "acts_like?",
+ "Rational",
+ "_removed_setup_callbacks",
+ "Table",
+ "bind",
+ "any_of",
+ "__method__",
+ "test_migrator_with_duplicates",
+ "_teardown_callbacks",
+ "method",
+ "test_migrator_with_duplicate_names",
+ "_removed_teardown_callbacks",
+ "any_parameters",
+ "test_migrator_with_missing_version_numbers",
+ "test_add_remove_single_field_using_string_arguments",
+ "test_create_table_with_custom_sequence_name",
+ "test_add_remove_single_field_using_symbol_arguments",
+ "_one_time_conditions_valid_14?",
+ "_one_time_conditions_valid_16?",
+ "run_callbacks",
+ "anything",
+ "silence_warnings",
+ "instance_variable_names",
+ "_fixture_path",
+ "copy_instance_variables_from",
+ "fixture_path?",
+ "has_entry",
+ "__marshal__",
+ "_fixture_table_names",
+ "__kind_of__",
+ "fixture_table_names?",
+ "test_add_rename",
+ "assert_equal",
+ "_fixture_class_names",
+ "fixture_class_names?",
+ "has_entries",
+ "_use_transactional_fixtures",
+ "people",
+ "test_rename_column_using_symbol_arguments",
+ "use_transactional_fixtures?",
+ "instance_eval",
+ "blank?",
+ "with_warnings",
+ "__nil__",
+ "load",
+ "metaclass",
+ "_use_instantiated_fixtures",
+ "has_key",
+ "class_eval",
+ "present?",
+ "test_rename_column",
+ "teardown",
+ "use_instantiated_fixtures?",
+ "method_name",
+ "silence_stderr",
+ "presence",
+ "test_rename_column_preserves_default_value_not_null",
+ "silence_stream",
+ "_pre_loaded_fixtures",
+ "__metaclass__",
+ "__fixnum__",
+ "pre_loaded_fixtures?",
+ "has_value",
+ "suppress",
+ "to_yaml_properties",
+ "test_rename_nonexistent_column",
+ "test_add_index",
+ "includes",
+ "find_correlate_in",
+ "equality_predicate_sql",
+ "assert_nothing_raised",
+ "let",
+ "not_predicate_sql",
+ "test_rename_column_with_sql_reserved_word",
+ "singleton_class",
+ "test_rename_column_with_an_index",
+ "display",
+ "taguri",
+ "to_yaml_style",
+ "test_remove_column_with_index",
+ "size",
+ "current_adapter?",
+ "test_remove_column_with_multi_column_index",
+ "respond_to?",
+ "test_change_type_of_not_null_column",
+ "is_a",
+ "to_a",
+ "test_rename_table_for_sqlite_should_work_with_reserved_words",
+ "require_library_or_gem",
+ "setup_fixtures",
+ "equal?",
+ "teardown_fixtures",
+ "nil?",
+ "fixture_table_names",
+ "fixture_class_names",
+ "test_create_table_without_id",
+ "use_transactional_fixtures",
+ "test_add_column_with_primary_key_attribute",
+ "repair_validations",
+ "use_instantiated_fixtures",
+ "instance_of?",
+ "test_create_table_adds_id",
+ "test_rename_table",
+ "pre_loaded_fixtures",
+ "to_enum",
+ "test_create_table_with_not_null_column",
+ "instance_of",
+ "test_change_column_nullability",
+ "optionally",
+ "test_rename_table_with_an_index",
+ "run",
+ "test_change_column",
+ "default_test",
+ "assert_raise",
+ "test_create_table_with_defaults",
+ "assert_nil",
+ "flunk",
+ "regexp_matches",
+ "duplicable?",
+ "reset_mocha",
+ "stubba_method",
+ "filter_backtrace",
+ "test_create_table_with_limits",
+ "responds_with",
+ "stubba_object",
+ "test_change_column_with_nil_default",
+ "assert_block",
+ "__show__",
+ "assert_date_from_db",
+ "__respond_to_eh__",
+ "run_in_transaction?",
+ "inspect",
+ "assert_sql",
+ "test_change_column_with_new_default",
+ "yaml_equivalent",
+ "build_message",
+ "to_s",
+ "test_change_column_default",
+ "assert_queries",
+ "pending",
+ "as_json",
+ "assert_no_queries",
+ "test_change_column_quotes_column_names",
+ "assert_match",
+ "test_keeping_default_and_notnull_constraint_on_change",
+ "methods",
+ "connection_allow_concurrency_setup",
+ "connection_allow_concurrency_teardown",
+ "test_create_table_with_primary_key_prefix_as_table_name_with_underscore",
+ "__send__",
+ "make_connection",
+ "assert_raises",
+ "tap",
+ "with_kcode",
+ "assert_instance_of",
+ "test_create_table_with_primary_key_prefix_as_table_name",
+ "assert_respond_to",
+ "test_change_column_default_to_null",
+ "assert_same",
+ "__extend__",
+ ]
+
+ LargeTestArraySorted = [
+ "test_add_column_not_null_with_default",
+ "test_add_column_with_precision_and_scale",
+ "test_add_column_with_primary_key_attribute",
+ "test_add_drop_table_with_prefix_and_suffix",
+ "test_add_index",
+ "test_add_remove_single_field_using_string_arguments",
+ "test_add_remove_single_field_using_symbol_arguments",
+ "test_add_rename",
+ "test_add_table",
+ "test_add_table_with_decimals",
+ "test_change_column",
+ "test_change_column_default",
+ "test_change_column_default_to_null",
+ "test_change_column_nullability",
+ "test_change_column_quotes_column_names",
+ "test_change_column_with_new_default",
+ "test_change_column_with_nil_default",
+ "test_change_type_of_not_null_column",
+ "test_create_table_adds_id",
+ "test_create_table_with_binary_column",
+ "test_create_table_with_custom_sequence_name",
+ "test_create_table_with_defaults",
+ "test_create_table_with_force_true_does_not_drop_nonexisting_table",
+ "test_create_table_with_limits",
+ "test_create_table_with_not_null_column",
+ "test_create_table_with_primary_key_prefix_as_table_name",
+ "test_create_table_with_primary_key_prefix_as_table_name_with_underscore",
+ "test_create_table_with_timestamps_should_create_datetime_columns",
+ "test_create_table_with_timestamps_should_create_datetime_columns_with_options",
+ "test_create_table_without_a_block",
+ "test_create_table_without_id",
+ "test_finds_migrations",
+ "test_finds_pending_migrations",
+ "test_keeping_default_and_notnull_constraint_on_change",
+ "test_migrator",
+ "test_migrator_db_has_no_schema_migrations_table",
+ "test_migrator_double_down",
+ "test_migrator_double_up",
+ "test_migrator_forward",
+ "test_migrator_going_down_due_to_version_target",
+ "test_migrator_one_down",
+ "test_migrator_one_up",
+ "test_migrator_one_up_one_down",
+ "test_migrator_one_up_with_exception_and_rollback",
+ "test_migrator_rollback",
+ "test_migrator_verbosity",
+ "test_migrator_verbosity_off",
+ "test_migrator_with_duplicate_names",
+ "test_migrator_with_duplicates",
+ "test_migrator_with_missing_version_numbers",
+ "test_native_decimal_insert_manual_vs_automatic",
+ "test_native_types",
+ "test_only_loads_pending_migrations",
+ "test_proper_table_name",
+ "test_relative_migrations",
+ "test_remove_column_with_index",
+ "test_remove_column_with_multi_column_index",
+ "test_rename_column",
+ "test_rename_column_preserves_default_value_not_null",
+ "test_rename_column_using_symbol_arguments",
+ "test_rename_column_with_an_index",
+ "test_rename_column_with_sql_reserved_word",
+ "test_rename_nonexistent_column",
+ "test_rename_table",
+ "test_rename_table_for_sqlite_should_work_with_reserved_words",
+ "test_rename_table_with_an_index",
+ "test_schema_migrations_table_name",
+ "test_target_version_zero_should_run_only_once",
+ ]
class PrivateToAry
private
diff --git a/spec/ruby/core/array/fixtures/encoded_strings.rb b/spec/ruby/core/array/fixtures/encoded_strings.rb
index 5b85bd0e06..b5888d86ae 100644
--- a/spec/ruby/core/array/fixtures/encoded_strings.rb
+++ b/spec/ruby/core/array/fixtures/encoded_strings.rb
@@ -2,14 +2,14 @@
module ArraySpecs
def self.array_with_usascii_and_7bit_utf8_strings
[
- 'foo'.force_encoding('US-ASCII'),
+ 'foo'.dup.force_encoding('US-ASCII'),
'bar'
]
end
def self.array_with_usascii_and_utf8_strings
[
- 'foo'.force_encoding('US-ASCII'),
+ 'foo'.dup.force_encoding('US-ASCII'),
'báz'
]
end
@@ -17,7 +17,7 @@ module ArraySpecs
def self.array_with_7bit_utf8_and_usascii_strings
[
'bar',
- 'foo'.force_encoding('US-ASCII')
+ 'foo'.dup.force_encoding('US-ASCII')
]
end
@@ -25,13 +25,13 @@ module ArraySpecs
[
'báz',
'bar',
- 'foo'.force_encoding('US-ASCII')
+ 'foo'.dup.force_encoding('US-ASCII')
]
end
def self.array_with_usascii_and_utf8_strings
[
- 'foo'.force_encoding('US-ASCII'),
+ 'foo'.dup.force_encoding('US-ASCII'),
'bar',
'báz'
]
@@ -41,7 +41,7 @@ module ArraySpecs
[
'bar',
'báz',
- 'foo'.force_encoding('BINARY')
+ 'foo'.dup.force_encoding('BINARY')
]
end
@@ -55,14 +55,14 @@ module ArraySpecs
def self.array_with_usascii_and_7bit_binary_strings
[
- 'bar'.force_encoding('US-ASCII'),
- 'foo'.force_encoding('BINARY')
+ 'bar'.dup.force_encoding('US-ASCII'),
+ 'foo'.dup.force_encoding('BINARY')
]
end
def self.array_with_usascii_and_binary_strings
[
- 'bar'.force_encoding('US-ASCII'),
+ 'bar'.dup.force_encoding('US-ASCII'),
[255].pack('C').force_encoding('BINARY')
]
end
diff --git a/spec/ruby/core/array/flatten_spec.rb b/spec/ruby/core/array/flatten_spec.rb
index e7cd114b9b..272406b8f9 100644
--- a/spec/ruby/core/array/flatten_spec.rb
+++ b/spec/ruby/core/array/flatten_spec.rb
@@ -13,7 +13,7 @@ describe "Array#flatten" do
it "returns dup when the level of recursion is 0" do
a = [ 1, 2, [3, [4, 5] ] ]
a.flatten(0).should == a
- a.flatten(0).should_not equal(a)
+ a.flatten(0).should_not.equal?(a)
end
it "ignores negative levels" do
@@ -30,7 +30,7 @@ describe "Array#flatten" do
it "raises a TypeError when the passed Object can't be converted to an Integer" do
obj = mock("Not converted")
- -> { [ 1, 2, [3, [4, 5] ] ].flatten(obj) }.should raise_error(TypeError)
+ -> { [ 1, 2, [3, [4, 5] ] ].flatten(obj) }.should.raise(TypeError)
end
it "does not call flatten on elements" do
@@ -46,13 +46,13 @@ describe "Array#flatten" do
it "raises an ArgumentError on recursive arrays" do
x = []
x << x
- -> { x.flatten }.should raise_error(ArgumentError)
+ -> { x.flatten }.should.raise(ArgumentError)
x = []
y = []
x << y
y << x
- -> { x.flatten }.should raise_error(ArgumentError)
+ -> { x.flatten }.should.raise(ArgumentError)
end
it "flattens any element which responds to #to_ary, using the return value of said method" do
@@ -75,12 +75,12 @@ describe "Array#flatten" do
[[obj]].flatten(1)
end
- it "returns subclass instance for Array subclasses" do
- ArraySpecs::MyArray[].flatten.should be_an_instance_of(ArraySpecs::MyArray)
- ArraySpecs::MyArray[1, 2, 3].flatten.should be_an_instance_of(ArraySpecs::MyArray)
- ArraySpecs::MyArray[1, [2], 3].flatten.should be_an_instance_of(ArraySpecs::MyArray)
- ArraySpecs::MyArray[1, [2, 3], 4].flatten.should == ArraySpecs::MyArray[1, 2, 3, 4]
- [ArraySpecs::MyArray[1, 2, 3]].flatten.should be_an_instance_of(Array)
+ it "returns Array instance for Array subclasses" do
+ ArraySpecs::MyArray[].flatten.should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2, 3].flatten.should.instance_of?(Array)
+ ArraySpecs::MyArray[1, [2], 3].flatten.should.instance_of?(Array)
+ ArraySpecs::MyArray[1, [2, 3], 4].flatten.should == [1, 2, 3, 4]
+ [ArraySpecs::MyArray[1, 2, 3]].flatten.should.instance_of?(Array)
end
it "is not destructive" do
@@ -106,7 +106,7 @@ describe "Array#flatten" do
it "raises a TypeError if #to_ary does not return an Array" do
@obj.should_receive(:to_ary).and_return(1)
- -> { [@obj].flatten }.should raise_error(TypeError)
+ -> { [@obj].flatten }.should.raise(TypeError)
end
it "calls respond_to_missing?(:to_ary, true) to try coercing" do
@@ -125,7 +125,7 @@ describe "Array#flatten" do
it "calls #to_ary if not defined when #respond_to_missing? returns true" do
def @obj.respond_to_missing?(name, priv) ScratchPad << name; true end
- -> { [@obj].flatten }.should raise_error(NoMethodError)
+ -> { [@obj].flatten }.should.raise(NoMethodError)
ScratchPad.recorded.should == [:to_ary]
end
@@ -135,16 +135,6 @@ describe "Array#flatten" do
end
end
- ruby_version_is ''...'2.7' do
- it "returns a tainted array if self is tainted" do
- [].taint.flatten.tainted?.should be_true
- end
-
- it "returns an untrusted array if self is untrusted" do
- [].untrust.flatten.untrusted?.should be_true
- end
- end
-
it "performs respond_to? and method_missing-aware checks when coercing elements to array" do
bo = BasicObject.new
[bo].flatten.should == [bo]
@@ -178,7 +168,7 @@ describe "Array#flatten!" do
it "returns self if made some modifications" do
a = [[[1, [2, 3]],[2, 3, [4, [4, [5, 5]], [1, 2, 3]]], [4]], []]
- a.flatten!.should equal(a)
+ a.flatten!.should.equal?(a)
end
it "returns nil if no modifications took place" do
@@ -218,7 +208,7 @@ describe "Array#flatten!" do
it "raises a TypeError when the passed Object can't be converted to an Integer" do
obj = mock("Not converted")
- -> { [ 1, 2, [3, [4, 5] ] ].flatten!(obj) }.should raise_error(TypeError)
+ -> { [ 1, 2, [3, [4, 5] ] ].flatten!(obj) }.should.raise(TypeError)
end
it "does not call flatten! on elements" do
@@ -234,13 +224,13 @@ describe "Array#flatten!" do
it "raises an ArgumentError on recursive arrays" do
x = []
x << x
- -> { x.flatten! }.should raise_error(ArgumentError)
+ -> { x.flatten! }.should.raise(ArgumentError)
x = []
y = []
x << y
y << x
- -> { x.flatten! }.should raise_error(ArgumentError)
+ -> { x.flatten! }.should.raise(ArgumentError)
end
it "flattens any elements which responds to #to_ary, using the return value of said method" do
@@ -258,19 +248,19 @@ describe "Array#flatten!" do
ary = [ArraySpecs::MyArray[1, 2, 3]]
ary.flatten!
- ary.should be_an_instance_of(Array)
+ ary.should.instance_of?(Array)
ary.should == [1, 2, 3]
end
it "raises a FrozenError on frozen arrays when the array is modified" do
nested_ary = [1, 2, []]
nested_ary.freeze
- -> { nested_ary.flatten! }.should raise_error(FrozenError)
+ -> { nested_ary.flatten! }.should.raise(FrozenError)
end
# see [ruby-core:23663]
it "raises a FrozenError on frozen arrays when the array would not be modified" do
- -> { ArraySpecs.frozen_array.flatten! }.should raise_error(FrozenError)
- -> { ArraySpecs.empty_frozen_array.flatten! }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.flatten! }.should.raise(FrozenError)
+ -> { ArraySpecs.empty_frozen_array.flatten! }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/array/hash_spec.rb b/spec/ruby/core/array/hash_spec.rb
index 8392253ae4..3b7b6d5bed 100644
--- a/spec/ruby/core/array/hash_spec.rb
+++ b/spec/ruby/core/array/hash_spec.rb
@@ -7,16 +7,16 @@ describe "Array#hash" do
[[], [1, 2, 3]].each do |ary|
ary.hash.should == ary.dup.hash
- ary.hash.should be_an_instance_of(Fixnum)
+ ary.hash.should.instance_of?(Integer)
end
end
it "properly handles recursive arrays" do
empty = ArraySpecs.empty_recursive_array
- -> { empty.hash }.should_not raise_error
+ -> { empty.hash }.should_not.raise
array = ArraySpecs.recursive_array
- -> { array.hash }.should_not raise_error
+ -> { array.hash }.should_not.raise
end
it "returns the same hash for equal recursive arrays" do
@@ -74,7 +74,7 @@ describe "Array#hash" do
a.fill 'a', 0..3
b = %w|a a a a|
a.hash.should == b.hash
- a.should eql(b)
+ a.should.eql?(b)
end
it "produces different hashes for nested arrays with different values and empty terminator" do
diff --git a/spec/ruby/core/array/index_spec.rb b/spec/ruby/core/array/index_spec.rb
index 3acb7d0ef3..b66cb6eb53 100644
--- a/spec/ruby/core/array/index_spec.rb
+++ b/spec/ruby/core/array/index_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/index'
describe "Array#index" do
- it_behaves_like :array_index, :index
+ it "is an alias of Array#find_index" do
+ Array.instance_method(:index).should == Array.instance_method(:find_index)
+ end
end
diff --git a/spec/ruby/core/array/initialize_spec.rb b/spec/ruby/core/array/initialize_spec.rb
index a8deed2b84..19ee37825e 100644
--- a/spec/ruby/core/array/initialize_spec.rb
+++ b/spec/ruby/core/array/initialize_spec.rb
@@ -7,7 +7,7 @@ describe "Array#initialize" do
end
it "is private" do
- Array.should have_private_instance_method("initialize")
+ Array.private_instance_methods(false).should.include?(:initialize)
end
it "is called on subclasses" do
@@ -19,26 +19,26 @@ describe "Array#initialize" do
it "preserves the object's identity even when changing its value" do
a = [1, 2, 3]
- a.send(:initialize).should equal(a)
+ a.send(:initialize).should.equal?(a)
a.should_not == [1, 2, 3]
end
it "raises an ArgumentError if passed 3 or more arguments" do
-> do
[1, 2].send :initialize, 1, 'x', true
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
-> do
[1, 2].send(:initialize, 1, 'x', true) {}
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
end
it "raises a FrozenError on frozen arrays" do
-> do
ArraySpecs.frozen_array.send :initialize
- end.should raise_error(FrozenError)
+ end.should.raise(FrozenError)
-> do
ArraySpecs.frozen_array.send :initialize, ArraySpecs.frozen_array
- end.should raise_error(FrozenError)
+ end.should.raise(FrozenError)
end
it "calls #to_ary to convert the value to an array, even if it's private" do
@@ -49,11 +49,13 @@ end
describe "Array#initialize with no arguments" do
it "makes the array empty" do
- [1, 2, 3].send(:initialize).should be_empty
+ [1, 2, 3].send(:initialize).should.empty?
end
it "does not use the given block" do
- ->{ [1, 2, 3].send(:initialize) { raise } }.should_not raise_error
+ -> {
+ -> { [1, 2, 3].send(:initialize) { raise } }.should_not.raise
+ }.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true)
end
end
@@ -64,7 +66,7 @@ describe "Array#initialize with (array)" do
end
it "does not use the given block" do
- ->{ [1, 2, 3].send(:initialize) { raise } }.should_not raise_error
+ ->{ [1, 2, 3].send(:initialize) { raise } }.should_not.raise
end
it "calls #to_ary to convert the value to an array" do
@@ -81,7 +83,7 @@ describe "Array#initialize with (array)" do
end
it "raises a TypeError if an Array type argument and a default object" do
- -> { [].send(:initialize, [1, 2], 1) }.should raise_error(TypeError)
+ -> { [].send(:initialize, [1, 2], 1) }.should.raise(TypeError)
end
end
@@ -90,8 +92,8 @@ describe "Array#initialize with (size, object=nil)" do
a = []
obj = [3]
a.send(:initialize, 2, obj).should == [obj, obj]
- a[0].should equal(obj)
- a[1].should equal(obj)
+ a[0].should.equal?(obj)
+ a[1].should.equal?(obj)
b = []
b.send(:initialize, 3, 14).should == [14, 14, 14]
@@ -103,12 +105,12 @@ describe "Array#initialize with (size, object=nil)" do
end
it "raises an ArgumentError if size is negative" do
- -> { [].send(:initialize, -1, :a) }.should raise_error(ArgumentError)
- -> { [].send(:initialize, -1) }.should raise_error(ArgumentError)
+ -> { [].send(:initialize, -1, :a) }.should.raise(ArgumentError)
+ -> { [].send(:initialize, -1) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if size is too large" do
- -> { [].send(:initialize, fixnum_max+1) }.should raise_error(ArgumentError)
+ -> { [].send(:initialize, fixnum_max+1) }.should.raise(ArgumentError)
end
it "calls #to_int to convert the size argument to an Integer when object is given" do
@@ -126,7 +128,7 @@ describe "Array#initialize with (size, object=nil)" do
it "raises a TypeError if the size argument is not an Integer type" do
obj = mock('nonnumeric')
obj.stub!(:to_ary).and_return([1, 2])
- ->{ [].send(:initialize, obj, :a) }.should raise_error(TypeError)
+ ->{ [].send(:initialize, obj, :a) }.should.raise(TypeError)
end
it "yields the index of the element and sets the element to the value of the block" do
diff --git a/spec/ruby/core/array/insert_spec.rb b/spec/ruby/core/array/insert_spec.rb
index 9e1757f68b..38e132fd25 100644
--- a/spec/ruby/core/array/insert_spec.rb
+++ b/spec/ruby/core/array/insert_spec.rb
@@ -4,8 +4,8 @@ require_relative 'fixtures/classes'
describe "Array#insert" do
it "returns self" do
ary = []
- ary.insert(0).should equal(ary)
- ary.insert(0, :a).should equal(ary)
+ ary.insert(0).should.equal?(ary)
+ ary.insert(0, :a).should.equal?(ary)
end
it "inserts objects before the element at index for non-negative index" do
@@ -46,8 +46,8 @@ describe "Array#insert" do
end
it "raises an IndexError if the negative index is out of bounds" do
- -> { [].insert(-2, 1) }.should raise_error(IndexError)
- -> { [1].insert(-3, 2) }.should raise_error(IndexError)
+ -> { [].insert(-2, 1) }.should.raise(IndexError)
+ -> { [1].insert(-3, 2) }.should.raise(IndexError)
end
it "does nothing of no object is passed" do
@@ -64,15 +64,15 @@ describe "Array#insert" do
end
it "raises an ArgumentError if no argument passed" do
- -> { [].insert() }.should raise_error(ArgumentError)
+ -> { [].insert() }.should.raise(ArgumentError)
end
it "raises a FrozenError on frozen arrays when the array is modified" do
- -> { ArraySpecs.frozen_array.insert(0, 'x') }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.insert(0, 'x') }.should.raise(FrozenError)
end
# see [ruby-core:23666]
it "raises a FrozenError on frozen arrays when the array would not be modified" do
- -> { ArraySpecs.frozen_array.insert(0) }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.insert(0) }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/array/inspect_spec.rb b/spec/ruby/core/array/inspect_spec.rb
index 0832224f5a..e5dca82889 100644
--- a/spec/ruby/core/array/inspect_spec.rb
+++ b/spec/ruby/core/array/inspect_spec.rb
@@ -1,7 +1,108 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/inspect'
describe "Array#inspect" do
- it_behaves_like :array_inspect, :inspect
+ it "returns a string" do
+ [1, 2, 3].inspect.should.instance_of?(String)
+ end
+
+ it "returns '[]' for an empty Array" do
+ [].inspect.should == "[]"
+ end
+
+ it "calls inspect on its elements and joins the results with commas" do
+ items = Array.new(3) do |i|
+ obj = mock(i.to_s)
+ obj.should_receive(:inspect).and_return(i.to_s)
+ obj
+ end
+ items.inspect.should == "[0, 1, 2]"
+ end
+
+ it "does not call #to_s on a String returned from #inspect" do
+ str = +"abc"
+ str.should_not_receive(:to_s)
+
+ [str].inspect.should == '["abc"]'
+ end
+
+ it "calls #to_s on the object returned from #inspect if the Object isn't a String" do
+ obj = mock("Array#inspect/to_s calls #to_s")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_return("abc")
+
+ [obj].inspect.should == "[abc]"
+ end
+
+ it "does not call #to_str on the object returned from #inspect when it is not a String" do
+ obj = mock("Array#inspect/to_s does not call #to_str")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_not_receive(:to_str)
+
+ [obj].inspect.should =~ /^\[#<MockObject:0x[0-9a-f]+>\]$/
+ end
+
+ it "does not call #to_str on the object returned from #to_s when it is not a String" do
+ obj = mock("Array#inspect/to_s does not call #to_str on #to_s result")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_return(obj)
+ obj.should_not_receive(:to_str)
+
+ [obj].inspect.should =~ /^\[#<MockObject:0x[0-9a-f]+>\]$/
+ end
+
+ it "does not swallow exceptions raised by #to_s" do
+ obj = mock("Array#inspect/to_s does not swallow #to_s exceptions")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_raise(Exception)
+
+ -> { [obj].inspect }.should.raise(Exception)
+ end
+
+ it "represents a recursive element with '[...]'" do
+ ArraySpecs.recursive_array.inspect.should == "[1, \"two\", 3.0, [...], [...], [...], [...], [...]]"
+ ArraySpecs.head_recursive_array.inspect.should == "[[...], [...], [...], [...], [...], 1, \"two\", 3.0]"
+ ArraySpecs.empty_recursive_array.inspect.should == "[[...]]"
+ end
+
+ describe "with encoding" do
+ before :each do
+ @default_external_encoding = Encoding.default_external
+ end
+
+ after :each do
+ Encoding.default_external = @default_external_encoding
+ end
+
+ it "returns a US-ASCII string for an empty Array" do
+ [].inspect.encoding.should == Encoding::US_ASCII
+ end
+
+ it "use the default external encoding if it is ascii compatible" do
+ Encoding.default_external = Encoding.find('UTF-8')
+
+ utf8 = "utf8".encode("UTF-8")
+ jp = "jp".encode("EUC-JP")
+ array = [jp, utf8]
+
+ array.inspect.encoding.name.should == "UTF-8"
+ end
+
+ it "use US-ASCII encoding if the default external encoding is not ascii compatible" do
+ Encoding.default_external = Encoding.find('UTF-32')
+
+ utf8 = "utf8".encode("UTF-8")
+ jp = "jp".encode("EUC-JP")
+ array = [jp, utf8]
+
+ array.inspect.encoding.name.should == "US-ASCII"
+ end
+
+ it "does not raise if inspected result is not default external encoding" do
+ utf_16be = mock(+"utf_16be")
+ utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE))
+
+ [utf_16be].inspect.should == '["utf_16be \u3042"]'
+ end
+ end
end
diff --git a/spec/ruby/core/array/intersect_spec.rb b/spec/ruby/core/array/intersect_spec.rb
new file mode 100644
index 0000000000..456aa26c6e
--- /dev/null
+++ b/spec/ruby/core/array/intersect_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'Array#intersect?' do
+ describe 'when at least one element in two Arrays is the same' do
+ it 'returns true' do
+ [1, 2].intersect?([2, 3, 4]).should == true
+ [2, 3, 4].intersect?([1, 2]).should == true
+ end
+ end
+
+ describe 'when there are no elements in common between two Arrays' do
+ it 'returns false' do
+ [0, 1, 2].intersect?([3, 4]).should == false
+ [3, 4].intersect?([0, 1, 2]).should == false
+ [3, 4].intersect?([]).should == false
+ [].intersect?([0, 1, 2]).should == false
+ end
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('[1,2,3]')
+ obj.should_receive(:to_ary).and_return([1, 2, 3])
+
+ [1, 2].intersect?(obj).should == true
+ end
+
+ it "determines equivalence between elements in the sense of eql?" do
+ obj1 = mock('1')
+ obj2 = mock('2')
+ obj1.stub!(:hash).and_return(0)
+ obj2.stub!(:hash).and_return(0)
+ obj1.stub!(:eql?).and_return(true)
+ obj2.stub!(:eql?).and_return(true)
+
+ [obj1].intersect?([obj2]).should == true
+
+ obj1 = mock('3')
+ obj2 = mock('4')
+ obj1.stub!(:hash).and_return(0)
+ obj2.stub!(:hash).and_return(0)
+ obj1.stub!(:eql?).and_return(false)
+ obj2.stub!(:eql?).and_return(false)
+
+ [obj1].intersect?([obj2]).should == false
+ end
+
+ it "does not call to_ary on array subclasses" do
+ [5, 6].intersect?(ArraySpecs::ToAryArray[1, 2, 5, 6]).should == true
+ end
+
+ it "properly handles an identical item even when its #eql? isn't reflexive" do
+ x = mock('x')
+ x.stub!(:hash).and_return(42)
+ x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI.
+
+ [x].intersect?([x]).should == true
+ end
+
+ it "has semantic of !(a & b).empty?" do
+ [].intersect?([]).should == false
+ [nil].intersect?([nil]).should == true
+ end
+end
diff --git a/spec/ruby/core/array/intersection_spec.rb b/spec/ruby/core/array/intersection_spec.rb
index 27d90f1e44..e01a68d389 100644
--- a/spec/ruby/core/array/intersection_spec.rb
+++ b/spec/ruby/core/array/intersection_spec.rb
@@ -6,16 +6,14 @@ describe "Array#&" do
it_behaves_like :array_intersection, :&
end
-ruby_version_is "2.7" do
- describe "Array#intersection" do
- it_behaves_like :array_intersection, :intersection
+describe "Array#intersection" do
+ it_behaves_like :array_intersection, :intersection
- it "accepts multiple arguments" do
- [1, 2, 3, 4].intersection([1, 2, 3], [2, 3, 4]).should == [2, 3]
- end
+ it "accepts multiple arguments" do
+ [1, 2, 3, 4].intersection([1, 2, 3], [2, 3, 4]).should == [2, 3]
+ end
- it "preserves elements order from original array" do
- [1, 2, 3, 4].intersection([3, 2, 1]).should == [1, 2, 3]
- end
+ it "preserves elements order from original array" do
+ [1, 2, 3, 4].intersection([3, 2, 1]).should == [1, 2, 3]
end
end
diff --git a/spec/ruby/core/array/join_spec.rb b/spec/ruby/core/array/join_spec.rb
index e78ea6f9e1..3b4946a99f 100644
--- a/spec/ruby/core/array/join_spec.rb
+++ b/spec/ruby/core/array/join_spec.rb
@@ -4,7 +4,6 @@ require_relative 'shared/join'
describe "Array#join" do
it_behaves_like :array_join_with_string_separator, :join
- it_behaves_like :array_join_with_default_separator, :join
it "does not separate elements when the passed separator is nil" do
[1, 2, 3].join(nil).should == '123'
@@ -24,11 +23,108 @@ describe "Array#join" do
it "raises a TypeError if the separator cannot be coerced to a String by calling #to_str" do
obj = mock("not a string")
- -> { [1, 2].join(obj) }.should raise_error(TypeError)
+ -> { [1, 2].join(obj) }.should.raise(TypeError)
end
it "raises a TypeError if passed false as the separator" do
- -> { [1, 2].join(false) }.should raise_error(TypeError)
+ -> { [1, 2].join(false) }.should.raise(TypeError)
+ end
+end
+
+describe "Array#join with default separator" do
+ before :each do
+ @separator = $,
+ end
+
+ after :each do
+ $, = @separator
+ end
+
+ it "returns an empty string if the Array is empty" do
+ [].join.should == ''
+ end
+
+ it "returns a US-ASCII string for an empty Array" do
+ [].join.encoding.should == Encoding::US_ASCII
+ end
+
+ it "returns a string formed by concatenating each String element separated by $," do
+ suppress_warning {
+ $, = " | "
+ ["1", "2", "3"].join.should == "1 | 2 | 3"
+ }
+ end
+
+ it "attempts coercion via #to_str first" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).any_number_of_times.and_return("foo")
+ [obj].join.should == "foo"
+ end
+
+ it "attempts coercion via #to_ary second" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).any_number_of_times.and_return(nil)
+ obj.should_receive(:to_ary).any_number_of_times.and_return(["foo"])
+ [obj].join.should == "foo"
+ end
+
+ it "attempts coercion via #to_s third" do
+ obj = mock('foo')
+ obj.should_receive(:to_str).any_number_of_times.and_return(nil)
+ obj.should_receive(:to_ary).any_number_of_times.and_return(nil)
+ obj.should_receive(:to_s).any_number_of_times.and_return("foo")
+ [obj].join.should == "foo"
+ end
+
+ it "raises a NoMethodError if an element does not respond to #to_str, #to_ary, or #to_s" do
+ obj = mock('o')
+ class << obj; undef :to_s; end
+ -> { [1, obj].join }.should.raise(NoMethodError)
+ end
+
+ it "raises an ArgumentError when the Array is recursive" do
+ -> { ArraySpecs.recursive_array.join }.should.raise(ArgumentError)
+ -> { ArraySpecs.head_recursive_array.join }.should.raise(ArgumentError)
+ -> { ArraySpecs.empty_recursive_array.join }.should.raise(ArgumentError)
+ end
+
+ it "uses the first encoding when other strings are compatible" do
+ ary1 = ArraySpecs.array_with_7bit_utf8_and_usascii_strings
+ ary2 = ArraySpecs.array_with_usascii_and_7bit_utf8_strings
+ ary3 = ArraySpecs.array_with_utf8_and_7bit_binary_strings
+ ary4 = ArraySpecs.array_with_usascii_and_7bit_binary_strings
+
+ ary1.join.encoding.should == Encoding::UTF_8
+ ary2.join.encoding.should == Encoding::US_ASCII
+ ary3.join.encoding.should == Encoding::UTF_8
+ ary4.join.encoding.should == Encoding::US_ASCII
+ end
+
+ it "uses the widest common encoding when other strings are incompatible" do
+ ary1 = ArraySpecs.array_with_utf8_and_usascii_strings
+ ary2 = ArraySpecs.array_with_usascii_and_utf8_strings
+
+ ary1.join.encoding.should == Encoding::UTF_8
+ ary2.join.encoding.should == Encoding::UTF_8
+ end
+
+ it "fails for arrays with incompatibly-encoded strings" do
+ ary_utf8_bad_binary = ArraySpecs.array_with_utf8_and_binary_strings
+
+ -> { ary_utf8_bad_binary.join }.should.raise(EncodingError)
+ end
+
+ context "when $, is not nil" do
+ before do
+ suppress_warning do
+ $, = '*'
+ end
+ end
+
+ it "warns" do
+ -> { [].join }.should complain(/warning: \$, is set to non-nil value/)
+ -> { [].join(nil) }.should complain(/warning: \$, is set to non-nil value/)
+ end
end
end
diff --git a/spec/ruby/core/array/keep_if_spec.rb b/spec/ruby/core/array/keep_if_spec.rb
index bf2bdeaf91..62a65a04e8 100644
--- a/spec/ruby/core/array/keep_if_spec.rb
+++ b/spec/ruby/core/array/keep_if_spec.rb
@@ -1,9 +1,10 @@
+require_relative '../../spec_helper'
require_relative 'shared/keep_if'
describe "Array#keep_if" do
it "returns the same array if no changes were made" do
array = [1, 2, 3]
- array.keep_if { true }.should equal(array)
+ array.keep_if { true }.should.equal?(array)
end
it_behaves_like :keep_if, :keep_if
diff --git a/spec/ruby/core/array/last_spec.rb b/spec/ruby/core/array/last_spec.rb
index d6fefada09..ed417bcd2a 100644
--- a/spec/ruby/core/array/last_spec.rb
+++ b/spec/ruby/core/array/last_spec.rb
@@ -28,7 +28,7 @@ describe "Array#last" do
end
it "raises an ArgumentError when count is negative" do
- -> { [1, 2].last(-1) }.should raise_error(ArgumentError)
+ -> { [1, 2].last(-1) }.should.raise(ArgumentError)
end
it "returns the entire array when count > length" do
@@ -47,10 +47,10 @@ describe "Array#last" do
it "properly handles recursive arrays" do
empty = ArraySpecs.empty_recursive_array
- empty.last.should equal(empty)
+ empty.last.should.equal?(empty)
array = ArraySpecs.recursive_array
- array.last.should equal(array)
+ array.last.should.equal?(array)
end
it "tries to convert the passed argument to an Integer using #to_int" do
@@ -60,19 +60,19 @@ describe "Array#last" do
end
it "raises a TypeError if the passed argument is not numeric" do
- -> { [1,2].last(nil) }.should raise_error(TypeError)
- -> { [1,2].last("a") }.should raise_error(TypeError)
+ -> { [1,2].last(nil) }.should.raise(TypeError)
+ -> { [1,2].last("a") }.should.raise(TypeError)
obj = mock("nonnumeric")
- -> { [1,2].last(obj) }.should raise_error(TypeError)
+ -> { [1,2].last(obj) }.should.raise(TypeError)
end
it "does not return subclass instance on Array subclasses" do
- ArraySpecs::MyArray[].last(0).should be_an_instance_of(Array)
- ArraySpecs::MyArray[].last(2).should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2, 3].last(0).should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2, 3].last(1).should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2, 3].last(2).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[].last(0).should.instance_of?(Array)
+ ArraySpecs::MyArray[].last(2).should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2, 3].last(0).should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2, 3].last(1).should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2, 3].last(2).should.instance_of?(Array)
end
it "is not destructive" do
diff --git a/spec/ruby/core/array/length_spec.rb b/spec/ruby/core/array/length_spec.rb
index a90c001300..74b2eb3a08 100644
--- a/spec/ruby/core/array/length_spec.rb
+++ b/spec/ruby/core/array/length_spec.rb
@@ -1,7 +1,14 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/length'
describe "Array#length" do
- it_behaves_like :array_length, :length
+ it "returns the number of elements" do
+ [].length.should == 0
+ [1, 2, 3].length.should == 3
+ end
+
+ it "properly handles recursive arrays" do
+ ArraySpecs.empty_recursive_array.length.should == 1
+ ArraySpecs.recursive_array.length.should == 8
+ end
end
diff --git a/spec/ruby/core/array/map_spec.rb b/spec/ruby/core/array/map_spec.rb
index 0c7f3afa8c..f5e88c8624 100644
--- a/spec/ruby/core/array/map_spec.rb
+++ b/spec/ruby/core/array/map_spec.rb
@@ -1,11 +1,13 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/collect'
describe "Array#map" do
- it_behaves_like :array_collect, :map
+ it "is an alias of Array#collect" do
+ Array.instance_method(:map).should == Array.instance_method(:collect)
+ end
end
describe "Array#map!" do
- it_behaves_like :array_collect_b, :map!
+ it "is an alias of Array#collect!" do
+ Array.instance_method(:map!).should == Array.instance_method(:collect!)
+ end
end
diff --git a/spec/ruby/core/array/max_spec.rb b/spec/ruby/core/array/max_spec.rb
index d1c64519d0..868275a748 100644
--- a/spec/ruby/core/array/max_spec.rb
+++ b/spec/ruby/core/array/max_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "Array#max" do
it "is defined on Array" do
- [1].method(:max).owner.should equal Array
+ [1].method(:max).owner.should.equal? Array
end
it "returns nil with no values" do
@@ -70,16 +70,16 @@ describe "Array#max" do
it "raises a NoMethodError for elements without #<=>" do
-> do
[BasicObject.new, BasicObject.new].max
- end.should raise_error(NoMethodError)
+ end.should.raise(NoMethodError)
end
it "raises an ArgumentError for incomparable elements" do
-> do
[11,"22"].max
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
-> do
[11,12,22,33].max{|a, b| nil}
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
end
it "returns the maximum element (with block)" do
diff --git a/spec/ruby/core/array/min_spec.rb b/spec/ruby/core/array/min_spec.rb
index 3bdef0dd00..5913e08cf8 100644
--- a/spec/ruby/core/array/min_spec.rb
+++ b/spec/ruby/core/array/min_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "Array#min" do
it "is defined on Array" do
- [1].method(:max).owner.should equal Array
+ [1].method(:max).owner.should.equal? Array
end
it "returns nil with no values" do
@@ -64,22 +64,22 @@ describe "Array#min" do
end
it "returns nil for an empty Enumerable" do
- [].min.should be_nil
+ [].min.should == nil
end
it "raises a NoMethodError for elements without #<=>" do
-> do
[BasicObject.new, BasicObject.new].min
- end.should raise_error(NoMethodError)
+ end.should.raise(NoMethodError)
end
it "raises an ArgumentError for incomparable elements" do
-> do
[11,"22"].min
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
-> do
[11,12,22,33].min{|a, b| nil}
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
end
it "returns the minimum when using a block rule" do
diff --git a/spec/ruby/core/array/minmax_spec.rb b/spec/ruby/core/array/minmax_spec.rb
new file mode 100644
index 0000000000..e11fe63347
--- /dev/null
+++ b/spec/ruby/core/array/minmax_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/enumerable/minmax'
+
+describe "Array#minmax" do
+ before :each do
+ @enum = [6, 4, 5, 10, 8]
+ @empty_enum = []
+ @incomparable_enum = [BasicObject.new, BasicObject.new]
+ @incompatible_enum = [11, "22"]
+ @strs = ["333", "2", "60", "55555", "1010", "111"]
+ end
+
+ it_behaves_like :enumerable_minmax, :minmax
+end
diff --git a/spec/ruby/core/array/multiply_spec.rb b/spec/ruby/core/array/multiply_spec.rb
index 8ccec13d42..1ac14e1b09 100644
--- a/spec/ruby/core/array/multiply_spec.rb
+++ b/spec/ruby/core/array/multiply_spec.rb
@@ -17,7 +17,7 @@ describe "Array#*" do
it "raises a TypeError if the argument can neither be converted to a string nor an integer" do
obj = mock('not a string or integer')
- ->{ [1,2] * obj }.should raise_error(TypeError)
+ ->{ [1,2] * obj }.should.raise(TypeError)
end
it "converts the passed argument to a String rather than an Integer" do
@@ -28,15 +28,15 @@ describe "Array#*" do
end
it "raises a TypeError is the passed argument is nil" do
- ->{ [1,2] * nil }.should raise_error(TypeError)
+ ->{ [1,2] * nil }.should.raise(TypeError)
end
it "raises an ArgumentError when passed 2 or more arguments" do
- ->{ [1,2].send(:*, 1, 2) }.should raise_error(ArgumentError)
+ ->{ [1,2].send(:*, 1, 2) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when passed no arguments" do
- ->{ [1,2].send(:*) }.should raise_error(ArgumentError)
+ ->{ [1,2].send(:*) }.should.raise(ArgumentError)
end
end
@@ -50,7 +50,7 @@ describe "Array#* with an integer" do
it "does not return self even if the passed integer is 1" do
ary = [1, 2, 3]
- (ary * 1).should_not equal(ary)
+ (ary * 1).should_not.equal?(ary)
end
it "properly handles recursive arrays" do
@@ -65,8 +65,8 @@ describe "Array#* with an integer" do
end
it "raises an ArgumentError when passed a negative integer" do
- -> { [ 1, 2, 3 ] * -1 }.should raise_error(ArgumentError)
- -> { [] * -1 }.should raise_error(ArgumentError)
+ -> { [ 1, 2, 3 ] * -1 }.should.raise(ArgumentError)
+ -> { [] * -1 }.should.raise(ArgumentError)
end
describe "with a subclass of Array" do
@@ -76,55 +76,15 @@ describe "Array#* with an integer" do
@array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
end
- it "returns a subclass instance" do
- (@array * 0).should be_an_instance_of(ArraySpecs::MyArray)
- (@array * 1).should be_an_instance_of(ArraySpecs::MyArray)
- (@array * 2).should be_an_instance_of(ArraySpecs::MyArray)
+ it "returns an Array instance" do
+ (@array * 0).should.instance_of?(Array)
+ (@array * 1).should.instance_of?(Array)
+ (@array * 2).should.instance_of?(Array)
end
it "does not call #initialize on the subclass instance" do
(@array * 2).should == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
- ScratchPad.recorded.should be_nil
- end
- end
-
- ruby_version_is ''...'2.7' do
- it "copies the taint status of the original array even if the passed count is 0" do
- ary = [1, 2, 3]
- ary.taint
- (ary * 0).should.tainted?
- end
-
- it "copies the taint status of the original array even if the array is empty" do
- ary = []
- ary.taint
- (ary * 3).should.tainted?
- end
-
- it "copies the taint status of the original array if the passed count is not 0" do
- ary = [1, 2, 3]
- ary.taint
- (ary * 1).should.tainted?
- (ary * 2).should.tainted?
- end
-
- it "copies the untrusted status of the original array even if the passed count is 0" do
- ary = [1, 2, 3]
- ary.untrust
- (ary * 0).should.untrusted?
- end
-
- it "copies the untrusted status of the original array even if the array is empty" do
- ary = []
- ary.untrust
- (ary * 3).should.untrusted?
- end
-
- it "copies the untrusted status of the original array if the passed count is not 0" do
- ary = [1, 2, 3]
- ary.untrust
- (ary * 1).should.untrusted?
- (ary * 2).should.untrusted?
+ ScratchPad.recorded.should == nil
end
end
end
diff --git a/spec/ruby/core/array/new_spec.rb b/spec/ruby/core/array/new_spec.rb
index 96ec6b8198..b2f23e2f6b 100644
--- a/spec/ruby/core/array/new_spec.rb
+++ b/spec/ruby/core/array/new_spec.rb
@@ -3,30 +3,32 @@ require_relative 'fixtures/classes'
describe "Array.new" do
it "returns an instance of Array" do
- Array.new.should be_an_instance_of(Array)
+ Array.new.should.instance_of?(Array)
end
it "returns an instance of a subclass" do
- ArraySpecs::MyArray.new(1, 2).should be_an_instance_of(ArraySpecs::MyArray)
+ ArraySpecs::MyArray.new(1, 2).should.instance_of?(ArraySpecs::MyArray)
end
it "raises an ArgumentError if passed 3 or more arguments" do
-> do
[1, 2].send :initialize, 1, 'x', true
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
-> do
[1, 2].send(:initialize, 1, 'x', true) {}
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
end
end
describe "Array.new with no arguments" do
it "returns an empty array" do
- Array.new.should be_empty
+ Array.new.should.empty?
end
it "does not use the given block" do
- ->{ Array.new { raise } }.should_not raise_error
+ -> {
+ -> { Array.new { raise } }.should_not.raise
+ }.should complain(/warning: given block not used/, verbose: true)
end
end
@@ -37,7 +39,7 @@ describe "Array.new with (array)" do
end
it "does not use the given block" do
- ->{ Array.new([1, 2]) { raise } }.should_not raise_error
+ ->{ Array.new([1, 2]) { raise } }.should_not.raise
end
it "calls #to_ary to convert the value to an array" do
@@ -54,7 +56,7 @@ describe "Array.new with (array)" do
end
it "raises a TypeError if an Array type argument and a default object" do
- -> { Array.new([1, 2], 1) }.should raise_error(TypeError)
+ -> { Array.new([1, 2], 1) }.should.raise(TypeError)
end
end
@@ -63,8 +65,8 @@ describe "Array.new with (size, object=nil)" do
obj = [3]
a = Array.new(2, obj)
a.should == [obj, obj]
- a[0].should equal(obj)
- a[1].should equal(obj)
+ a[0].should.equal?(obj)
+ a[1].should.equal?(obj)
Array.new(3, 14).should == [14, 14, 14]
end
@@ -74,12 +76,12 @@ describe "Array.new with (size, object=nil)" do
end
it "raises an ArgumentError if size is negative" do
- -> { Array.new(-1, :a) }.should raise_error(ArgumentError)
- -> { Array.new(-1) }.should raise_error(ArgumentError)
+ -> { Array.new(-1, :a) }.should.raise(ArgumentError)
+ -> { Array.new(-1) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if size is too large" do
- -> { Array.new(fixnum_max+1) }.should raise_error(ArgumentError)
+ -> { Array.new(fixnum_max+1) }.should.raise(ArgumentError)
end
it "calls #to_int to convert the size argument to an Integer when object is given" do
@@ -97,7 +99,7 @@ describe "Array.new with (size, object=nil)" do
it "raises a TypeError if the size argument is not an Integer type" do
obj = mock('nonnumeric')
obj.stub!(:to_ary).and_return([1, 2])
- ->{ Array.new(obj, :a) }.should raise_error(TypeError)
+ ->{ Array.new(obj, :a) }.should.raise(TypeError)
end
it "yields the index of the element and sets the element to the value of the block" do
diff --git a/spec/ruby/core/array/none_spec.rb b/spec/ruby/core/array/none_spec.rb
new file mode 100644
index 0000000000..31cd8c46d6
--- /dev/null
+++ b/spec/ruby/core/array/none_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
+
+describe "Array#none?" do
+ @value_to_return = -> _ { false }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :none?
+
+ it "ignores the block if there is an argument" do
+ -> {
+ ['bar', 'foobar'].none?(/baz/) { true }.should == true
+ }.should complain(/given block not used/)
+ end
+end
diff --git a/spec/ruby/core/array/one_spec.rb b/spec/ruby/core/array/one_spec.rb
new file mode 100644
index 0000000000..0c61907881
--- /dev/null
+++ b/spec/ruby/core/array/one_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
+
+describe "Array#one?" do
+ @value_to_return = -> _ { false }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :one?
+
+ it "ignores the block if there is an argument" do
+ -> {
+ ['bar', 'foobar'].one?(/foo/) { false }.should == true
+ }.should complain(/given block not used/)
+ end
+end
diff --git a/spec/ruby/core/array/pack/a_spec.rb b/spec/ruby/core/array/pack/a_spec.rb
index 7af7a16c68..03bfd8214c 100644
--- a/spec/ruby/core/array/pack/a_spec.rb
+++ b/spec/ruby/core/array/pack/a_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/basic'
@@ -12,11 +12,22 @@ describe "Array#pack with format 'A'" do
it_behaves_like :array_pack_string, 'A'
it_behaves_like :array_pack_taint, 'A'
+ it "calls #to_str to convert an Object to a String" do
+ obj = mock("pack A string")
+ obj.should_receive(:to_str).and_return("``abcdef")
+ [obj].pack("A*").should == "``abcdef"
+ end
+
+ it "will not implicitly convert a number to a string" do
+ -> { [0].pack('A') }.should.raise(TypeError)
+ -> { [0].pack('a') }.should.raise(TypeError)
+ end
+
it "adds all the bytes to the output when passed the '*' modifier" do
["abc"].pack("A*").should == "abc"
end
- it "padds the output with spaces when the count exceeds the size of the String" do
+ it "pads the output with spaces when the count exceeds the size of the String" do
["abc"].pack("A6").should == "abc "
end
@@ -44,7 +55,7 @@ describe "Array#pack with format 'a'" do
["abc"].pack("a*").should == "abc"
end
- it "padds the output with NULL bytes when the count exceeds the size of the String" do
+ it "pads the output with NULL bytes when the count exceeds the size of the String" do
["abc"].pack("a6").should == "abc\x00\x00\x00"
end
diff --git a/spec/ruby/core/array/pack/at_spec.rb b/spec/ruby/core/array/pack/at_spec.rb
index 3942677913..bb9801440a 100644
--- a/spec/ruby/core/array/pack/at_spec.rb
+++ b/spec/ruby/core/array/pack/at_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/basic'
diff --git a/spec/ruby/core/array/pack/b_spec.rb b/spec/ruby/core/array/pack/b_spec.rb
index 872c1b88d5..f7576846ef 100644
--- a/spec/ruby/core/array/pack/b_spec.rb
+++ b/spec/ruby/core/array/pack/b_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/basic'
@@ -13,11 +13,16 @@ describe "Array#pack with format 'B'" do
it_behaves_like :array_pack_taint, 'B'
it "calls #to_str to convert an Object to a String" do
- obj = mock("pack H string")
+ obj = mock("pack B string")
obj.should_receive(:to_str).and_return("``abcdef")
[obj].pack("B*").should == "\x2a"
end
+ it "will not implicitly convert a number to a string" do
+ -> { [0].pack('B') }.should.raise(TypeError)
+ -> { [0].pack('b') }.should.raise(TypeError)
+ end
+
it "encodes one bit for each character starting with the most significant bit" do
[ [["0"], "\x00"],
[["1"], "\x80"]
diff --git a/spec/ruby/core/array/pack/buffer_spec.rb b/spec/ruby/core/array/pack/buffer_spec.rb
index ecb40bfd06..d104c80186 100644
--- a/spec/ruby/core/array/pack/buffer_spec.rb
+++ b/spec/ruby/core/array/pack/buffer_spec.rb
@@ -7,43 +7,53 @@ describe "Array#pack with :buffer option" do
n = [ 65, 66, 67 ]
buffer = " "*3
result = n.pack("ccc", buffer: buffer) #=> "ABC"
- result.should equal(buffer)
+ result.should.equal?(buffer)
end
it "adds result at the end of buffer content" do
n = [ 65, 66, 67 ] # result without buffer is "ABC"
- buffer = ""
+ buffer = +""
n.pack("ccc", buffer: buffer).should == "ABC"
- buffer = "123"
+ buffer = +"123"
n.pack("ccc", buffer: buffer).should == "123ABC"
- buffer = "12345"
+ buffer = +"12345"
n.pack("ccc", buffer: buffer).should == "12345ABC"
end
it "raises TypeError exception if buffer is not String" do
- -> { [65].pack("ccc", buffer: []) }.should raise_error(
+ -> { [65].pack("ccc", buffer: []) }.should.raise(
TypeError, "buffer must be String, not Array")
end
+ it "raise FrozenError if buffer is frozen" do
+ -> { [65].pack("c", buffer: "frozen-string".freeze) }.should.raise(FrozenError)
+ end
+
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ [65, 66, 67].pack("ccc", buffer: buffer)
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
+
context "offset (@) is specified" do
it 'keeps buffer content if it is longer than offset' do
n = [ 65, 66, 67 ]
- buffer = "123456"
+ buffer = +"123456"
n.pack("@3ccc", buffer: buffer).should == "123ABC"
end
it "fills the gap with \\0 if buffer content is shorter than offset" do
n = [ 65, 66, 67 ]
- buffer = "123"
+ buffer = +"123"
n.pack("@6ccc", buffer: buffer).should == "123\0\0\0ABC"
end
it 'does not keep buffer content if it is longer than offset + result' do
n = [ 65, 66, 67 ]
- buffer = "1234567890"
+ buffer = +"1234567890"
n.pack("@3ccc", buffer: buffer).should == "123ABC"
end
end
diff --git a/spec/ruby/core/array/pack/c_spec.rb b/spec/ruby/core/array/pack/c_spec.rb
index 7200830331..de06207a23 100644
--- a/spec/ruby/core/array/pack/c_spec.rb
+++ b/spec/ruby/core/array/pack/c_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
@@ -45,8 +45,10 @@ describe :array_pack_8bit, shared: true do
[1, 2, 3, 4, 5].pack(pack_format('*')).should == "\x01\x02\x03\x04\x05"
end
- it "ignores NULL bytes between directives" do
- [1, 2, 3].pack(pack_format("\000", 2)).should == "\x01\x02"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [1, 2, 3].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/pack/comment_spec.rb b/spec/ruby/core/array/pack/comment_spec.rb
index 254c827ccc..daf1cff06a 100644
--- a/spec/ruby/core/array/pack/comment_spec.rb
+++ b/spec/ruby/core/array/pack/comment_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
diff --git a/spec/ruby/core/array/pack/h_spec.rb b/spec/ruby/core/array/pack/h_spec.rb
index 85a875fc8b..1492d02b1f 100644
--- a/spec/ruby/core/array/pack/h_spec.rb
+++ b/spec/ruby/core/array/pack/h_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/basic'
@@ -18,6 +18,11 @@ describe "Array#pack with format 'H'" do
[obj].pack("H").should == "\xa0"
end
+ it "will not implicitly convert a number to a string" do
+ -> { [0].pack('H') }.should.raise(TypeError)
+ -> { [0].pack('h') }.should.raise(TypeError)
+ end
+
it "encodes the first character as the most significant nibble when passed no count modifier" do
["ab"].pack("H").should == "\xa0"
end
diff --git a/spec/ruby/core/array/pack/l_spec.rb b/spec/ruby/core/array/pack/l_spec.rb
index b446a7a36a..f6dfb1da83 100644
--- a/spec/ruby/core/array/pack/l_spec.rb
+++ b/spec/ruby/core/array/pack/l_spec.rb
@@ -29,7 +29,7 @@ describe "Array#pack with format 'L'" do
it_behaves_like :array_pack_32bit_be, 'L>'
end
- platform_is wordsize: 32 do
+ platform_is c_long_size: 32 do
describe "with modifier '<' and '_'" do
it_behaves_like :array_pack_32bit_le, 'L<_'
it_behaves_like :array_pack_32bit_le, 'L_<'
@@ -51,7 +51,7 @@ describe "Array#pack with format 'L'" do
end
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
describe "with modifier '<' and '_'" do
it_behaves_like :array_pack_64bit_le, 'L<_'
it_behaves_like :array_pack_64bit_le, 'L_<'
@@ -83,7 +83,7 @@ describe "Array#pack with format 'l'" do
it_behaves_like :array_pack_32bit_be, 'l>'
end
- platform_is wordsize: 32 do
+ platform_is c_long_size: 32 do
describe "with modifier '<' and '_'" do
it_behaves_like :array_pack_32bit_le, 'l<_'
it_behaves_like :array_pack_32bit_le, 'l_<'
@@ -105,7 +105,7 @@ describe "Array#pack with format 'l'" do
end
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
describe "with modifier '<' and '_'" do
it_behaves_like :array_pack_64bit_le, 'l<_'
it_behaves_like :array_pack_64bit_le, 'l_<'
@@ -137,7 +137,7 @@ little_endian do
it_behaves_like :array_pack_32bit_le, 'l'
end
- platform_is wordsize: 32 do
+ platform_is c_long_size: 32 do
describe "Array#pack with format 'L' with modifier '_'" do
it_behaves_like :array_pack_32bit_le, 'L_'
end
@@ -155,7 +155,7 @@ little_endian do
end
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
describe "Array#pack with format 'L' with modifier '_'" do
it_behaves_like :array_pack_64bit_le, 'L_'
end
@@ -183,7 +183,7 @@ big_endian do
it_behaves_like :array_pack_32bit_be, 'l'
end
- platform_is wordsize: 32 do
+ platform_is c_long_size: 32 do
describe "Array#pack with format 'L' with modifier '_'" do
it_behaves_like :array_pack_32bit_be, 'L_'
end
@@ -201,7 +201,7 @@ big_endian do
end
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
describe "Array#pack with format 'L' with modifier '_'" do
it_behaves_like :array_pack_64bit_be, 'L_'
end
diff --git a/spec/ruby/core/array/pack/m_spec.rb b/spec/ruby/core/array/pack/m_spec.rb
index 2b1a84abca..fb670d120e 100644
--- a/spec/ruby/core/array/pack/m_spec.rb
+++ b/spec/ruby/core/array/pack/m_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/basic'
@@ -80,8 +80,16 @@ describe "Array#pack with format 'M'" do
].should be_computed_by(:pack, "M")
end
- it "encodes a tab followed by a newline with an encoded newline" do
+ it "encodes a tab at the end of a line with an encoded newline" do
+ ["\t"].pack("M").should == "\t=\n"
["\t\n"].pack("M").should == "\t=\n\n"
+ ["abc\t\nxyz"].pack("M").should == "abc\t=\n\nxyz=\n"
+ end
+
+ it "encodes a space at the end of a line with an encoded newline" do
+ [" "].pack("M").should == " =\n"
+ [" \n"].pack("M").should == " =\n\n"
+ ["abc \nxyz"].pack("M").should == "abc =\n\nxyz=\n"
end
it "encodes 127..255 in hex format" do
@@ -147,7 +155,7 @@ describe "Array#pack with format 'M'" do
it "encodes a recursive array" do
empty = ArraySpecs.empty_recursive_array
- empty.pack('M').should be_an_instance_of(String)
+ empty.pack('M').should.instance_of?(String)
array = ArraySpecs.recursive_array
array.pack('M').should == "1=\n"
@@ -164,7 +172,7 @@ describe "Array#pack with format 'M'" do
obj = mock("pack M non-string")
obj.should_receive(:to_s).and_return(2)
- [obj].pack("M").should be_an_instance_of(String)
+ [obj].pack("M").should.instance_of?(String)
end
it "encodes a Symbol as a String" do
@@ -285,16 +293,16 @@ describe "Array#pack with format 'm'" do
it "raises a TypeError if #to_str does not return a String" do
obj = mock("pack m non-string")
- -> { [obj].pack("m") }.should raise_error(TypeError)
+ -> { [obj].pack("m") }.should.raise(TypeError)
end
it "raises a TypeError if passed nil" do
- -> { [nil].pack("m") }.should raise_error(TypeError)
+ -> { [nil].pack("m") }.should.raise(TypeError)
end
it "raises a TypeError if passed an Integer" do
- -> { [0].pack("m") }.should raise_error(TypeError)
- -> { [bignum_value].pack("m") }.should raise_error(TypeError)
+ -> { [0].pack("m") }.should.raise(TypeError)
+ -> { [bignum_value].pack("m") }.should.raise(TypeError)
end
it "does not emit a newline if passed zero as the count modifier" do
diff --git a/spec/ruby/core/array/pack/p_spec.rb b/spec/ruby/core/array/pack/p_spec.rb
index d7dff8a4da..b023bf9110 100644
--- a/spec/ruby/core/array/pack/p_spec.rb
+++ b/spec/ruby/core/array/pack/p_spec.rb
@@ -15,18 +15,6 @@ describe "Array#pack with format 'P'" do
["hello"].pack("P").unpack("P5").should == ["hello"]
end
- ruby_version_is ''...'2.7' do
- it "taints the input string" do
- input_string = "hello"
- [input_string].pack("P")
- input_string.tainted?.should be_true
- end
-
- it "does not taint the output string in normal cases" do
- ["hello"].pack("P").tainted?.should be_false
- end
- end
-
it "with nil gives a null pointer" do
[nil].pack("P").unpack("J").should == [0]
end
@@ -44,18 +32,6 @@ describe "Array#pack with format 'p'" do
["hello"].pack("p").unpack("p").should == ["hello"]
end
- ruby_version_is ''...'2.7' do
- it "taints the input string" do
- input_string = "hello"
- [input_string].pack("p")
- input_string.tainted?.should be_true
- end
-
- it "does not taint the output string in normal cases" do
- ["hello"].pack("p").tainted?.should be_false
- end
- end
-
it "with nil gives a null pointer" do
[nil].pack("p").unpack("J").should == [0]
end
diff --git a/spec/ruby/core/array/pack/percent_spec.rb b/spec/ruby/core/array/pack/percent_spec.rb
index 5d56dea5fe..29b119732a 100644
--- a/spec/ruby/core/array/pack/percent_spec.rb
+++ b/spec/ruby/core/array/pack/percent_spec.rb
@@ -2,6 +2,6 @@ require_relative '../../../spec_helper'
describe "Array#pack with format '%'" do
it "raises an Argument Error" do
- -> { [1].pack("%") }.should raise_error(ArgumentError)
+ -> { [1].pack("%") }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/array/pack/r_spec.rb b/spec/ruby/core/array/pack/r_spec.rb
new file mode 100644
index 0000000000..cefe1388d2
--- /dev/null
+++ b/spec/ruby/core/array/pack/r_spec.rb
@@ -0,0 +1,89 @@
+# encoding: binary
+require_relative '../../../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/basic'
+require_relative 'shared/numeric_basic'
+require_relative 'shared/integer'
+
+ruby_version_is "4.1" do
+ describe "Array#pack with format 'R'" do
+ it_behaves_like :array_pack_basic, 'R'
+ it_behaves_like :array_pack_basic_non_float, 'R'
+ it_behaves_like :array_pack_arguments, 'R'
+ it_behaves_like :array_pack_numeric_basic, 'R'
+ it_behaves_like :array_pack_integer, 'R'
+
+ it "encodes a ULEB128 integer" do
+ [ [[0], "\x00"],
+ [[1], "\x01"],
+ [[127], "\x7f"],
+ [[128], "\x80\x01"],
+ [[0x3fff], "\xff\x7f"],
+ [[0x4000], "\x80\x80\x01"],
+ [[0xffffffff], "\xff\xff\xff\xff\x0f"],
+ [[0x100000000], "\x80\x80\x80\x80\x10"],
+ [[0xffff_ffff_ffff_ffff], "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01"],
+ [[0xffff_ffff_ffff_ffff_ffff_ffff], "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x1f"],
+ ].should be_computed_by(:pack, "R")
+ end
+
+ it "encodes multiple values with '*' modifier" do
+ [1, 2].pack("R*").should == "\x01\x02"
+ [127, 128].pack("R*").should == "\x7f\x80\x01"
+ end
+
+ it "raises an ArgumentError when passed a negative value" do
+ -> { [-1].pack("R") }.should.raise(ArgumentError)
+ -> { [-100].pack("R") }.should.raise(ArgumentError)
+ end
+
+ it "round-trips values through pack and unpack" do
+ values = [0, 1, 127, 128, 0x3fff, 0x4000, 0xffffffff, 0x100000000]
+ values.pack("R*").unpack("R*").should == values
+ end
+ end
+
+ describe "Array#pack with format 'r'" do
+ it_behaves_like :array_pack_basic, 'r'
+ it_behaves_like :array_pack_basic_non_float, 'r'
+ it_behaves_like :array_pack_arguments, 'r'
+ it_behaves_like :array_pack_numeric_basic, 'r'
+ it_behaves_like :array_pack_integer, 'r'
+
+ it "encodes a SLEB128 integer" do
+ [ [[0], "\x00"],
+ [[1], "\x01"],
+ [[-1], "\x7f"],
+ [[-2], "\x7e"],
+ [[127], "\xff\x00"],
+ [[128], "\x80\x01"],
+ [[-127], "\x81\x7f"],
+ [[-128], "\x80\x7f"],
+ ].should be_computed_by(:pack, "r")
+ end
+
+ it "encodes larger positive numbers" do
+ [0x3fff].pack("r").should == "\xff\xff\x00"
+ [0x4000].pack("r").should == "\x80\x80\x01"
+ end
+
+ it "encodes larger negative numbers" do
+ [-0x3fff].pack("r").should == "\x81\x80\x7f"
+ [-0x4000].pack("r").should == "\x80\x80\x7f"
+ end
+
+ it "encodes very large numbers" do
+ [0xffff_ffff_ffff_ffff_ffff_ffff].pack("r").should == "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x1F"
+ [-0xffff_ffff_ffff_ffff_ffff_ffff].pack("r").should == "\x81\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x60"
+ end
+
+ it "encodes multiple values with '*' modifier" do
+ [0, 1, -1].pack("r*").should == "\x00\x01\x7f"
+ end
+
+ it "round-trips values through pack and unpack" do
+ values = [0, 1, -1, 127, -127, 128, -128, 0x3fff, -0x3fff, 0x4000, -0x4000]
+ values.pack("r*").unpack("r*").should == values
+ end
+ end
+end
diff --git a/spec/ruby/core/array/pack/shared/basic.rb b/spec/ruby/core/array/pack/shared/basic.rb
index 9061273ad6..2894369c71 100644
--- a/spec/ruby/core/array/pack/shared/basic.rb
+++ b/spec/ruby/core/array/pack/shared/basic.rb
@@ -1,6 +1,6 @@
describe :array_pack_arguments, shared: true do
it "raises an ArgumentError if there are fewer elements than the format requires" do
- -> { [].pack(pack_format(1)) }.should raise_error(ArgumentError)
+ -> { [].pack(pack_format(1)) }.should.raise(ArgumentError)
end
end
@@ -10,11 +10,11 @@ describe :array_pack_basic, shared: true do
end
it "raises a TypeError when passed nil" do
- -> { [@obj].pack(nil) }.should raise_error(TypeError)
+ -> { [@obj].pack(nil) }.should.raise(TypeError)
end
it "raises a TypeError when passed an Integer" do
- -> { [@obj].pack(1) }.should raise_error(TypeError)
+ -> { [@obj].pack(1) }.should.raise(TypeError)
end
end
@@ -24,46 +24,50 @@ describe :array_pack_basic_non_float, shared: true do
end
it "ignores whitespace in the format string" do
- [@obj, @obj].pack("a \t\n\v\f\r"+pack_format).should be_an_instance_of(String)
+ [@obj, @obj].pack("a \t\n\v\f\r"+pack_format).should.instance_of?(String)
+ end
+
+ it "ignores comments in the format string" do
+ # 2 additional directives ('a') are required for the X directive
+ [@obj, @obj, @obj, @obj].pack("aa #{pack_format} # some comment \n#{pack_format}").should.instance_of?(String)
+ end
+
+ it "raise ArgumentError when a directive is unknown" do
+ # additional directive ('a') is required for the X directive
+ -> { [@obj, @obj].pack("a K" + pack_format) }.should.raise(ArgumentError, /unknown pack directive 'K'/)
+ -> { [@obj, @obj].pack("a 0" + pack_format) }.should.raise(ArgumentError, /unknown pack directive '0'/)
+ -> { [@obj, @obj].pack("a :" + pack_format) }.should.raise(ArgumentError, /unknown pack directive ':'/)
end
it "calls #to_str to coerce the directives string" do
d = mock("pack directive")
d.should_receive(:to_str).and_return("x"+pack_format)
- [@obj, @obj].pack(d).should be_an_instance_of(String)
- end
-
- ruby_version_is ''...'2.7' do
- it "taints the output string if the format string is tainted" do
- [@obj, @obj].pack("x"+pack_format.taint).tainted?.should be_true
- end
+ [@obj, @obj].pack(d).should.instance_of?(String)
end
end
describe :array_pack_basic_float, shared: true do
it "ignores whitespace in the format string" do
- [9.3, 4.7].pack(" \t\n\v\f\r"+pack_format).should be_an_instance_of(String)
+ [9.3, 4.7].pack(" \t\n\v\f\r"+pack_format).should.instance_of?(String)
+ end
+
+ it "ignores comments in the format string" do
+ [9.3, 4.7].pack(pack_format + "# some comment \n" + pack_format).should.instance_of?(String)
end
it "calls #to_str to coerce the directives string" do
d = mock("pack directive")
d.should_receive(:to_str).and_return("x"+pack_format)
- [1.2, 4.7].pack(d).should be_an_instance_of(String)
- end
-
- ruby_version_is ''...'2.7' do
- it "taints the output string if the format string is tainted" do
- [3.2, 2.8].pack("x"+pack_format.taint).tainted?.should be_true
- end
+ [1.2, 4.7].pack(d).should.instance_of?(String)
end
end
describe :array_pack_no_platform, shared: true do
it "raises ArgumentError when the format modifier is '_'" do
- ->{ [1].pack(pack_format("_")) }.should raise_error(ArgumentError)
+ ->{ [1].pack(pack_format("_")) }.should.raise(ArgumentError)
end
it "raises ArgumentError when the format modifier is '!'" do
- ->{ [1].pack(pack_format("!")) }.should raise_error(ArgumentError)
+ ->{ [1].pack(pack_format("!")) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/array/pack/shared/encodings.rb b/spec/ruby/core/array/pack/shared/encodings.rb
index 6b7ffac764..0b5a5cc8a0 100644
--- a/spec/ruby/core/array/pack/shared/encodings.rb
+++ b/spec/ruby/core/array/pack/shared/encodings.rb
@@ -5,12 +5,12 @@ describe :array_pack_hex, shared: true do
it "raises a TypeError if the object does not respond to #to_str" do
obj = mock("pack hex non-string")
- -> { [obj].pack(pack_format) }.should raise_error(TypeError)
+ -> { [obj].pack(pack_format) }.should.raise(TypeError)
end
it "raises a TypeError if #to_str does not return a String" do
obj = mock("pack hex non-string")
obj.should_receive(:to_str).and_return(1)
- -> { [obj].pack(pack_format) }.should raise_error(TypeError)
+ -> { [obj].pack(pack_format) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/array/pack/shared/float.rb b/spec/ruby/core/array/pack/shared/float.rb
index c6b194007f..c1efcd7677 100644
--- a/spec/ruby/core/array/pack/shared/float.rb
+++ b/spec/ruby/core/array/pack/shared/float.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
describe :array_pack_float_le, shared: true do
it "encodes a positive Float" do
@@ -14,7 +14,7 @@ describe :array_pack_float_le, shared: true do
end
it "raises a TypeError if passed a String representation of a floating point number" do
- -> { ["13"].pack(pack_format) }.should raise_error(TypeError)
+ -> { ["13"].pack(pack_format) }.should.raise(TypeError)
end
it "encodes the number of array elements specified by the count modifier" do
@@ -25,8 +25,10 @@ describe :array_pack_float_le, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "\x9a\x999@33\xb3?33\x03A"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "\x9a\x99\xa9@33\x13A"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -43,7 +45,7 @@ describe :array_pack_float_le, shared: true do
it "encodes NaN" do
nans = ["\x00\x00\xc0\xff", "\x00\x00\xc0\x7f", "\xFF\xFF\xFF\x7F"]
- nans.should include([nan_value].pack(pack_format))
+ nans.should.include?([nan_value].pack(pack_format))
end
it "encodes a positive Float outside the range of a single precision float" do
@@ -53,6 +55,14 @@ describe :array_pack_float_le, shared: true do
it "encodes a negative Float outside the range of a single precision float" do
[-1e150].pack(pack_format).should == "\x00\x00\x80\xff"
end
+
+ it "encodes a bignum as a float" do
+ [2 ** 65].pack(pack_format).should == [(2 ** 65).to_f].pack(pack_format)
+ end
+
+ it "encodes a rational as a float" do
+ [Rational(3, 4)].pack(pack_format).should == [Rational(3, 4).to_f].pack(pack_format)
+ end
end
describe :array_pack_float_be, shared: true do
@@ -66,10 +76,15 @@ describe :array_pack_float_be, shared: true do
it "converts an Integer to a Float" do
[8].pack(pack_format).should == "A\x00\x00\x00"
+ [bignum_value].pack(pack_format).should == "_\x80\x00\x00"
+ end
+
+ it "converts a Rational to a Float" do
+ [Rational(8)].pack(pack_format).should == "A\x00\x00\x00"
end
it "raises a TypeError if passed a String representation of a floating point number" do
- -> { ["13"].pack(pack_format) }.should raise_error(TypeError)
+ -> { ["13"].pack(pack_format) }.should.raise(TypeError)
end
it "encodes the number of array elements specified by the count modifier" do
@@ -80,8 +95,10 @@ describe :array_pack_float_be, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "@9\x99\x9a?\xb333A\x0333"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\xa9\x99\x9aA\x1333"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -98,7 +115,7 @@ describe :array_pack_float_be, shared: true do
it "encodes NaN" do
nans = ["\xff\xc0\x00\x00", "\x7f\xc0\x00\x00", "\x7F\xFF\xFF\xFF"]
- nans.should include([nan_value].pack(pack_format))
+ nans.should.include?([nan_value].pack(pack_format))
end
it "encodes a positive Float outside the range of a single precision float" do
@@ -121,10 +138,15 @@ describe :array_pack_double_le, shared: true do
it "converts an Integer to a Float" do
[8].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\x20@"
+ [bignum_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xF0C"
+ end
+
+ it "converts a Rational to a Float" do
+ [Rational(8)].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00 @"
end
it "raises a TypeError if passed a String representation of a floating point number" do
- -> { ["13"].pack(pack_format) }.should raise_error(TypeError)
+ -> { ["13"].pack(pack_format) }.should.raise(TypeError)
end
it "encodes the number of array elements specified by the count modifier" do
@@ -135,8 +157,10 @@ describe :array_pack_double_le, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "333333\x07@ffffff\xf6?ffffff\x20@"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "333333\x15@ffffff\x22@"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -157,7 +181,7 @@ describe :array_pack_double_le, shared: true do
"\x00\x00\x00\x00\x00\x00\xf8\x7f",
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F"
]
- nans.should include([nan_value].pack(pack_format))
+ nans.should.include?([nan_value].pack(pack_format))
end
it "encodes a positive Float outside the range of a single precision float" do
@@ -183,7 +207,7 @@ describe :array_pack_double_be, shared: true do
end
it "raises a TypeError if passed a String representation of a floating point number" do
- -> { ["13"].pack(pack_format) }.should raise_error(TypeError)
+ -> { ["13"].pack(pack_format) }.should.raise(TypeError)
end
it "encodes the number of array elements specified by the count modifier" do
@@ -194,8 +218,10 @@ describe :array_pack_double_be, shared: true do
[2.9, 1.4, 8.2].pack(pack_format("*")).should == "@\x07333333?\xf6ffffff@\x20ffffff"
end
- it "ignores NULL bytes between directives" do
- [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\x15333333@\x22ffffff"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [5.3, 9.2].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -216,7 +242,7 @@ describe :array_pack_double_be, shared: true do
"\x7f\xf8\x00\x00\x00\x00\x00\x00",
"\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
]
- nans.should include([nan_value].pack(pack_format))
+ nans.should.include?([nan_value].pack(pack_format))
end
it "encodes a positive Float outside the range of a single precision float" do
diff --git a/spec/ruby/core/array/pack/shared/integer.rb b/spec/ruby/core/array/pack/shared/integer.rb
index 6592f85022..1cdd386cc1 100644
--- a/spec/ruby/core/array/pack/shared/integer.rb
+++ b/spec/ruby/core/array/pack/shared/integer.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
describe :array_pack_16bit_le, shared: true do
it "encodes the least significant 16 bits of a positive number" do
@@ -41,9 +41,10 @@ describe :array_pack_16bit_le, shared: true do
str.should == "\x78\x65\xcd\xab\x21\x43"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x78\x65\xcd\xab"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -93,9 +94,10 @@ describe :array_pack_16bit_be, shared: true do
str.should == "\x65\x78\xab\xcd\x43\x21"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x65\x78\xab\xcd"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -145,9 +147,10 @@ describe :array_pack_32bit_le, shared: true do
str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde\x21\x43\x65\x78"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -197,9 +200,10 @@ describe :array_pack_32bit_be, shared: true do
str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd\x78\x65\x43\x21"
end
- it "ignores NULL bytes between directives" do
- str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
- str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -225,7 +229,7 @@ describe :array_pack_32bit_le_platform, shared: true do
str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde\x21\x43\x65\x78"
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
it "encodes the least significant 32 bits of a number that is greater than 32 bits" do
[ [[0xff_7865_4321], "\x21\x43\x65\x78"],
[[-0xff_7865_4321], "\xdf\xbc\x9a\x87"]
@@ -251,7 +255,7 @@ describe :array_pack_32bit_be_platform, shared: true do
str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd\x78\x65\x43\x21"
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
it "encodes the least significant 32 bits of a number that is greater than 32 bits" do
[ [[0xff_7865_4321], "\x78\x65\x43\x21"],
[[-0xff_7865_4321], "\x87\x9a\xbc\xdf"]
@@ -309,9 +313,10 @@ describe :array_pack_64bit_le, shared: true do
str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78"
end
- it "ignores NULL bytes between directives" do
- str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
- str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -369,9 +374,10 @@ describe :array_pack_64bit_be, shared: true do
str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0"
end
- it "ignores NULL bytes between directives" do
- str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
- str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2))
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
diff --git a/spec/ruby/core/array/pack/shared/numeric_basic.rb b/spec/ruby/core/array/pack/shared/numeric_basic.rb
index 7c36ba4a32..6594914933 100644
--- a/spec/ruby/core/array/pack/shared/numeric_basic.rb
+++ b/spec/ruby/core/array/pack/shared/numeric_basic.rb
@@ -4,15 +4,15 @@ describe :array_pack_numeric_basic, shared: true do
end
it "raises a TypeError when passed nil" do
- -> { [nil].pack(pack_format) }.should raise_error(TypeError)
+ -> { [nil].pack(pack_format) }.should.raise(TypeError)
end
it "raises a TypeError when passed true" do
- -> { [true].pack(pack_format) }.should raise_error(TypeError)
+ -> { [true].pack(pack_format) }.should.raise(TypeError)
end
it "raises a TypeError when passed false" do
- -> { [false].pack(pack_format) }.should raise_error(TypeError)
+ -> { [false].pack(pack_format) }.should.raise(TypeError)
end
it "returns a binary string" do
@@ -24,21 +24,27 @@ end
describe :array_pack_integer, shared: true do
it "raises a TypeError when the object does not respond to #to_int" do
obj = mock('not an integer')
- -> { [obj].pack(pack_format) }.should raise_error(TypeError)
+ -> { [obj].pack(pack_format) }.should.raise(TypeError)
end
it "raises a TypeError when passed a String" do
- -> { ["5"].pack(pack_format) }.should raise_error(TypeError)
+ -> { ["5"].pack(pack_format) }.should.raise(TypeError)
end
end
describe :array_pack_float, shared: true do
it "raises a TypeError if a String does not represent a floating point number" do
- -> { ["a"].pack(pack_format) }.should raise_error(TypeError)
+ -> { ["a"].pack(pack_format) }.should.raise(TypeError)
end
- it "raises a TypeError when the object does not respond to #to_f" do
- obj = mock('not an float')
- -> { [obj].pack(pack_format) }.should raise_error(TypeError)
+ it "raises a TypeError when the object is not Numeric" do
+ obj = Object.new
+ -> { [obj].pack(pack_format) }.should.raise(TypeError, /can't convert Object into Float/)
+ end
+
+ it "raises a TypeError when the Numeric object does not respond to #to_f" do
+ klass = Class.new(Numeric)
+ obj = klass.new
+ -> { [obj].pack(pack_format) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/array/pack/shared/string.rb b/spec/ruby/core/array/pack/shared/string.rb
index 8c82e8c617..b02257059f 100644
--- a/spec/ruby/core/array/pack/shared/string.rb
+++ b/spec/ruby/core/array/pack/shared/string.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
describe :array_pack_string, shared: true do
it "adds count bytes of a String to the output" do
["abc"].pack(pack_format(2)).should == "ab"
@@ -17,11 +17,11 @@ describe :array_pack_string, shared: true do
end
it "raises an ArgumentError when the Array is empty" do
- -> { [].pack(pack_format) }.should raise_error(ArgumentError)
+ -> { [].pack(pack_format) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the Array has too few elements" do
- -> { ["a"].pack(pack_format(nil, 2)) }.should raise_error(ArgumentError)
+ -> { ["a"].pack(pack_format(nil, 2)) }.should.raise(ArgumentError)
end
it "calls #to_str to convert the element to a String" do
@@ -33,14 +33,14 @@ describe :array_pack_string, shared: true do
it "raises a TypeError when the object does not respond to #to_str" do
obj = mock("not a string")
- -> { [obj].pack(pack_format) }.should raise_error(TypeError)
+ -> { [obj].pack(pack_format) }.should.raise(TypeError)
end
it "returns a string in encoding of common to the concatenated results" do
f = pack_format("*")
[ [["\u{3042 3044 3046 3048}", 0x2000B].pack(f+"U"), Encoding::BINARY],
[["abcde\xd1", "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::BINARY],
- [["a".force_encoding("ascii"), "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::BINARY],
+ [["a".dup.force_encoding("ascii"), "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::BINARY],
# under discussion [ruby-dev:37294]
[["\u{3042 3044 3046 3048}", 1].pack(f+"N"), Encoding::BINARY]
].should be_computed_by(:encoding)
diff --git a/spec/ruby/core/array/pack/shared/taint.rb b/spec/ruby/core/array/pack/shared/taint.rb
index 565f04b8b9..2c2b011c34 100644
--- a/spec/ruby/core/array/pack/shared/taint.rb
+++ b/spec/ruby/core/array/pack/shared/taint.rb
@@ -1,35 +1,2 @@
describe :array_pack_taint, shared: true do
- ruby_version_is ''...'2.7' do
- it "returns a tainted string when a pack argument is tainted" do
- ["abcd".taint, 0x20].pack(pack_format("3C")).tainted?.should be_true
- end
-
- it "does not return a tainted string when the array is tainted" do
- ["abcd", 0x20].taint.pack(pack_format("3C")).tainted?.should be_false
- end
-
- it "returns a tainted string when the format is tainted" do
- ["abcd", 0x20].pack(pack_format("3C").taint).tainted?.should be_true
- end
-
- it "returns a tainted string when an empty format is tainted" do
- ["abcd", 0x20].pack("".taint).tainted?.should be_true
- end
-
- it "returns a untrusted string when the format is untrusted" do
- ["abcd", 0x20].pack(pack_format("3C").untrust).untrusted?.should be_true
- end
-
- it "returns a untrusted string when the empty format is untrusted" do
- ["abcd", 0x20].pack("".untrust).untrusted?.should be_true
- end
-
- it "returns a untrusted string when a pack argument is untrusted" do
- ["abcd".untrust, 0x20].pack(pack_format("3C")).untrusted?.should be_true
- end
-
- it "returns a trusted string when the array is untrusted" do
- ["abcd", 0x20].untrust.pack(pack_format("3C")).untrusted?.should be_false
- end
- end
end
diff --git a/spec/ruby/core/array/pack/shared/unicode.rb b/spec/ruby/core/array/pack/shared/unicode.rb
index dd0f8b38aa..58ba8a8b23 100644
--- a/spec/ruby/core/array/pack/shared/unicode.rb
+++ b/spec/ruby/core/array/pack/shared/unicode.rb
@@ -26,7 +26,7 @@ describe :array_pack_unicode, shared: true do
it "constructs strings with valid encodings" do
str = [0x85].pack("U*")
str.should == "\xc2\x85"
- str.valid_encoding?.should be_true
+ str.valid_encoding?.should == true
end
it "encodes values larger than UTF-8 max codepoints" do
@@ -64,11 +64,13 @@ describe :array_pack_unicode, shared: true do
it "raises a TypeError if #to_int does not return an Integer" do
obj = mock('to_int')
obj.should_receive(:to_int).and_return("5")
- -> { [obj].pack("U") }.should raise_error(TypeError)
+ -> { [obj].pack("U") }.should.raise(TypeError)
end
- it "ignores NULL bytes between directives" do
- [1, 2, 3].pack("U\x00U").should == "\x01\x02"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [1, 2, 3].pack("U\x00U")
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -76,11 +78,11 @@ describe :array_pack_unicode, shared: true do
end
it "raises a RangeError if passed a negative number" do
- -> { [-1].pack("U") }.should raise_error(RangeError)
+ -> { [-1].pack("U") }.should.raise(RangeError)
end
it "raises a RangeError if passed a number larger than an unsigned 32-bit integer" do
- -> { [2**32].pack("U") }.should raise_error(RangeError)
+ -> { [2**32].pack("U") }.should.raise(RangeError)
end
it "sets the output string to UTF-8 encoding" do
diff --git a/spec/ruby/core/array/pack/u_spec.rb b/spec/ruby/core/array/pack/u_spec.rb
index fe969cbb2d..c6a0d77eb2 100644
--- a/spec/ruby/core/array/pack/u_spec.rb
+++ b/spec/ruby/core/array/pack/u_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/basic'
@@ -18,6 +18,16 @@ describe "Array#pack with format 'u'" do
it_behaves_like :array_pack_arguments, 'u'
it_behaves_like :array_pack_taint, 'u'
+ it "calls #to_str to convert an Object to a String" do
+ obj = mock("pack u string")
+ obj.should_receive(:to_str).and_return("``abcdef")
+ [obj].pack("u*").should == "(8&!A8F-D968`\n"
+ end
+
+ it "will not implicitly convert a number to a string" do
+ -> { [0].pack('u') }.should.raise(TypeError)
+ end
+
it "encodes an empty string as an empty string" do
[""].pack("u").should == ""
end
@@ -112,16 +122,16 @@ describe "Array#pack with format 'u'" do
it "raises a TypeError if #to_str does not return a String" do
obj = mock("pack m non-string")
- -> { [obj].pack("u") }.should raise_error(TypeError)
+ -> { [obj].pack("u") }.should.raise(TypeError)
end
it "raises a TypeError if passed nil" do
- -> { [nil].pack("u") }.should raise_error(TypeError)
+ -> { [nil].pack("u") }.should.raise(TypeError)
end
it "raises a TypeError if passed an Integer" do
- -> { [0].pack("u") }.should raise_error(TypeError)
- -> { [bignum_value].pack("u") }.should raise_error(TypeError)
+ -> { [0].pack("u") }.should.raise(TypeError)
+ -> { [bignum_value].pack("u") }.should.raise(TypeError)
end
it "sets the output string to US-ASCII encoding" do
diff --git a/spec/ruby/core/array/pack/w_spec.rb b/spec/ruby/core/array/pack/w_spec.rb
index 439fa02198..263e2a2288 100644
--- a/spec/ruby/core/array/pack/w_spec.rb
+++ b/spec/ruby/core/array/pack/w_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/basic'
@@ -24,8 +24,10 @@ describe "Array#pack with format 'w'" do
[obj].pack("w").should == "\x05"
end
- it "ignores NULL bytes between directives" do
- [1, 2, 3].pack("w\x00w").should == "\x01\x02"
+ it "raise ArgumentError for NULL bytes between directives" do
+ -> {
+ [1, 2, 3].pack("w\x00w")
+ }.should.raise(ArgumentError, /unknown pack directive/)
end
it "ignores spaces between directives" do
@@ -33,7 +35,7 @@ describe "Array#pack with format 'w'" do
end
it "raises an ArgumentError when passed a negative value" do
- -> { [-1].pack("w") }.should raise_error(ArgumentError)
+ -> { [-1].pack("w") }.should.raise(ArgumentError)
end
it "returns a binary string" do
diff --git a/spec/ruby/core/array/pack/x_spec.rb b/spec/ruby/core/array/pack/x_spec.rb
index a28dd0bf21..7ff587a01e 100644
--- a/spec/ruby/core/array/pack/x_spec.rb
+++ b/spec/ruby/core/array/pack/x_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/basic'
@@ -30,6 +30,7 @@ describe "Array#pack with format 'x'" do
it "does not add a NULL byte when passed the '*' modifier" do
[].pack("x*").should == ""
+ [1, 2].pack("Cx*C").should == "\x01\x02"
end
end
@@ -55,10 +56,10 @@ describe "Array#pack with format 'X'" do
end
it "raises an ArgumentError if the output string is empty" do
- -> { [1, 2, 3].pack("XC") }.should raise_error(ArgumentError)
+ -> { [1, 2, 3].pack("XC") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if the count modifier is greater than the bytes in the string" do
- -> { [1, 2, 3].pack("C2X3") }.should raise_error(ArgumentError)
+ -> { [1, 2, 3].pack("C2X3") }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/array/pack/z_spec.rb b/spec/ruby/core/array/pack/z_spec.rb
index 82ce7b4a1c..5cd084c825 100644
--- a/spec/ruby/core/array/pack/z_spec.rb
+++ b/spec/ruby/core/array/pack/z_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
require_relative '../fixtures/classes'
require_relative 'shared/basic'
@@ -12,11 +12,21 @@ describe "Array#pack with format 'Z'" do
it_behaves_like :array_pack_string, 'Z'
it_behaves_like :array_pack_taint, 'Z'
+ it "calls #to_str to convert an Object to a String" do
+ obj = mock("pack Z string")
+ obj.should_receive(:to_str).and_return("``abcdef")
+ [obj].pack("Z*").should == "``abcdef\x00"
+ end
+
+ it "will not implicitly convert a number to a string" do
+ -> { [0].pack('Z') }.should.raise(TypeError)
+ end
+
it "adds all the bytes and appends a NULL byte when passed the '*' modifier" do
["abc"].pack("Z*").should == "abc\x00"
end
- it "padds the output with NULL bytes when the count exceeds the size of the String" do
+ it "pads the output with NULL bytes when the count exceeds the size of the String" do
["abc"].pack("Z6").should == "abc\x00\x00\x00"
end
diff --git a/spec/ruby/core/array/partition_spec.rb b/spec/ruby/core/array/partition_spec.rb
index be36fffcab..bd3f3a6b6f 100644
--- a/spec/ruby/core/array/partition_spec.rb
+++ b/spec/ruby/core/array/partition_spec.rb
@@ -36,8 +36,8 @@ describe "Array#partition" do
it "does not return subclass instances on Array subclasses" do
result = ArraySpecs::MyArray[1, 2, 3].partition { |x| x % 2 == 0 }
- result.should be_an_instance_of(Array)
- result[0].should be_an_instance_of(Array)
- result[1].should be_an_instance_of(Array)
+ result.should.instance_of?(Array)
+ result[0].should.instance_of?(Array)
+ result[1].should.instance_of?(Array)
end
end
diff --git a/spec/ruby/core/array/permutation_spec.rb b/spec/ruby/core/array/permutation_spec.rb
index f15bd76639..b5df84b52b 100644
--- a/spec/ruby/core/array/permutation_spec.rb
+++ b/spec/ruby/core/array/permutation_spec.rb
@@ -11,7 +11,7 @@ describe "Array#permutation" do
it "returns an Enumerator of all permutations when called without a block or arguments" do
enum = @numbers.permutation
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.to_a.sort.should == [
[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]
].sort
@@ -19,13 +19,13 @@ describe "Array#permutation" do
it "returns an Enumerator of permutations of given length when called with an argument but no block" do
enum = @numbers.permutation(1)
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.to_a.sort.should == [[1],[2],[3]]
end
it "yields all permutations to the block then returns self when called with block but no arguments" do
array = @numbers.permutation {|n| @yielded << n}
- array.should be_an_instance_of(Array)
+ array.should.instance_of?(Array)
array.sort.should == @numbers.sort
@yielded.sort.should == [
[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]
@@ -34,7 +34,7 @@ describe "Array#permutation" do
it "yields all permutations of given length to the block then returns self when called with block and argument" do
array = @numbers.permutation(2) {|n| @yielded << n}
- array.should be_an_instance_of(Array)
+ array.should.instance_of?(Array)
array.sort.should == @numbers.sort
@yielded.sort.should == [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]].sort
end
@@ -78,7 +78,7 @@ describe "Array#permutation" do
[3, 1], [3, 2], [3, [4, 5]],
[[4, 5], 1], [[4, 5], 2], [[4, 5], 3]
]
- expected.each {|e| got.include?(e).should be_true}
+ expected.each {|e| got.include?(e).should == true}
got.size.should == expected.size
end
diff --git a/spec/ruby/core/array/plus_spec.rb b/spec/ruby/core/array/plus_spec.rb
index 45f8438208..7ead927fc0 100644
--- a/spec/ruby/core/array/plus_spec.rb
+++ b/spec/ruby/core/array/plus_spec.rb
@@ -14,10 +14,23 @@ describe "Array#+" do
(ary + ary).should == [1, 2, 3, 1, 2, 3]
end
- it "tries to convert the passed argument to an Array using #to_ary" do
- obj = mock('["x", "y"]')
- obj.should_receive(:to_ary).and_return(["x", "y"])
- ([1, 2, 3] + obj).should == [1, 2, 3, "x", "y"]
+ describe "converts the passed argument to an Array using #to_ary" do
+ it "successfully concatenates the resulting array from the #to_ary call" do
+ obj = mock('["x", "y"]')
+ obj.should_receive(:to_ary).and_return(["x", "y"])
+ ([1, 2, 3] + obj).should == [1, 2, 3, "x", "y"]
+ end
+
+ it "raises a TypeError if the given argument can't be converted to an array" do
+ -> { [1, 2, 3] + nil }.should.raise(TypeError)
+ -> { [1, 2, 3] + "abc" }.should.raise(TypeError)
+ end
+
+ it "raises a NoMethodError if the given argument raises a NoMethodError during type coercion to an Array" do
+ obj = mock("hello")
+ obj.should_receive(:to_ary).and_raise(NoMethodError)
+ -> { [1, 2, 3] + obj }.should.raise(NoMethodError)
+ end
end
it "properly handles recursive arrays" do
@@ -32,28 +45,12 @@ describe "Array#+" do
end
it "does return subclass instances with Array subclasses" do
- (ArraySpecs::MyArray[1, 2, 3] + []).should be_an_instance_of(Array)
- (ArraySpecs::MyArray[1, 2, 3] + ArraySpecs::MyArray[]).should be_an_instance_of(Array)
- ([1, 2, 3] + ArraySpecs::MyArray[]).should be_an_instance_of(Array)
+ (ArraySpecs::MyArray[1, 2, 3] + []).should.instance_of?(Array)
+ (ArraySpecs::MyArray[1, 2, 3] + ArraySpecs::MyArray[]).should.instance_of?(Array)
+ ([1, 2, 3] + ArraySpecs::MyArray[]).should.instance_of?(Array)
end
it "does not call to_ary on array subclasses" do
([5, 6] + ArraySpecs::ToAryArray[1, 2]).should == [5, 6, 1, 2]
end
-
- ruby_version_is ''...'2.7' do
- it "does not get infected even if an original array is tainted" do
- ([1, 2] + [3, 4]).tainted?.should be_false
- ([1, 2].taint + [3, 4]).tainted?.should be_false
- ([1, 2] + [3, 4].taint).tainted?.should be_false
- ([1, 2].taint + [3, 4].taint).tainted?.should be_false
- end
-
- it "does not infected even if an original array is untrusted" do
- ([1, 2] + [3, 4]).untrusted?.should be_false
- ([1, 2].untrust + [3, 4]).untrusted?.should be_false
- ([1, 2] + [3, 4].untrust).untrusted?.should be_false
- ([1, 2].untrust + [3, 4].untrust).untrusted?.should be_false
- end
- end
end
diff --git a/spec/ruby/core/array/pop_spec.rb b/spec/ruby/core/array/pop_spec.rb
index 96ef78da32..069083331c 100644
--- a/spec/ruby/core/array/pop_spec.rb
+++ b/spec/ruby/core/array/pop_spec.rb
@@ -30,32 +30,12 @@ describe "Array#pop" do
array.pop.should == [1, 'two', 3.0, array, array, array, array]
end
- ruby_version_is ''...'2.7' do
- it "keeps taint status" do
- a = [1, 2].taint
- a.pop
- a.tainted?.should be_true
- a.pop
- a.tainted?.should be_true
- end
- end
-
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.pop }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.pop }.should.raise(FrozenError)
end
it "raises a FrozenError on an empty frozen array" do
- -> { ArraySpecs.empty_frozen_array.pop }.should raise_error(FrozenError)
- end
-
- ruby_version_is ''...'2.7' do
- it "keeps untrusted status" do
- a = [1, 2].untrust
- a.pop
- a.untrusted?.should be_true
- a.pop
- a.untrusted?.should be_true
- end
+ -> { ArraySpecs.empty_frozen_array.pop }.should.raise(FrozenError)
end
describe "passed a number n as an argument" do
@@ -91,7 +71,7 @@ describe "Array#pop" do
popped2.should == []
a.should == []
- popped1.should_not equal(popped2)
+ popped1.should_not.equal?(popped2)
end
it "returns whole elements if n exceeds size of the array" do
@@ -102,14 +82,14 @@ describe "Array#pop" do
it "does not return self even when it returns whole elements" do
a = [1, 2, 3, 4, 5]
- a.pop(5).should_not equal(a)
+ a.pop(5).should_not.equal?(a)
a = [1, 2, 3, 4, 5]
- a.pop(6).should_not equal(a)
+ a.pop(6).should_not.equal?(a)
end
it "raises an ArgumentError if n is negative" do
- ->{ [1, 2, 3].pop(-1) }.should raise_error(ArgumentError)
+ ->{ [1, 2, 3].pop(-1) }.should.raise(ArgumentError)
end
it "tries to convert n to an Integer using #to_int" do
@@ -124,53 +104,21 @@ describe "Array#pop" do
end
it "raises a TypeError when the passed n cannot be coerced to Integer" do
- ->{ [1, 2].pop("cat") }.should raise_error(TypeError)
- ->{ [1, 2].pop(nil) }.should raise_error(TypeError)
+ ->{ [1, 2].pop("cat") }.should.raise(TypeError)
+ ->{ [1, 2].pop(nil) }.should.raise(TypeError)
end
it "raises an ArgumentError if more arguments are passed" do
- ->{ [1, 2].pop(1, 2) }.should raise_error(ArgumentError)
+ ->{ [1, 2].pop(1, 2) }.should.raise(ArgumentError)
end
it "does not return subclass instances with Array subclass" do
- ArraySpecs::MyArray[1, 2, 3].pop(2).should be_an_instance_of(Array)
- end
-
- ruby_version_is ''...'2.7' do
- it "returns an untainted array even if the array is tainted" do
- ary = [1, 2].taint
- ary.pop(2).tainted?.should be_false
- ary.pop(0).tainted?.should be_false
- end
-
- it "keeps taint status" do
- a = [1, 2].taint
- a.pop(2)
- a.tainted?.should be_true
- a.pop(2)
- a.tainted?.should be_true
- end
-
- it "returns a trusted array even if the array is untrusted" do
- ary = [1, 2].untrust
- ary.pop(2).untrusted?.should be_false
- ary.pop(0).untrusted?.should be_false
- end
+ ArraySpecs::MyArray[1, 2, 3].pop(2).should.instance_of?(Array)
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.pop(2) }.should raise_error(FrozenError)
- -> { ArraySpecs.frozen_array.pop(0) }.should raise_error(FrozenError)
- end
-
- ruby_version_is ''...'2.7' do
- it "keeps untrusted status" do
- a = [1, 2].untrust
- a.pop(2)
- a.untrusted?.should be_true
- a.pop(2)
- a.untrusted?.should be_true
- end
+ -> { ArraySpecs.frozen_array.pop(2) }.should.raise(FrozenError)
+ -> { ArraySpecs.frozen_array.pop(0) }.should.raise(FrozenError)
end
end
end
diff --git a/spec/ruby/core/array/prepend_spec.rb b/spec/ruby/core/array/prepend_spec.rb
index 368b8dcfcd..2d0ce31c71 100644
--- a/spec/ruby/core/array/prepend_spec.rb
+++ b/spec/ruby/core/array/prepend_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/unshift'
describe "Array#prepend" do
- it_behaves_like :array_unshift, :prepend
+ it "is an alias of Array#unshift" do
+ Array.instance_method(:prepend).should == Array.instance_method(:unshift)
+ end
end
diff --git a/spec/ruby/core/array/product_spec.rb b/spec/ruby/core/array/product_spec.rb
index 07d2880a96..837f0eaf34 100644
--- a/spec/ruby/core/array/product_spec.rb
+++ b/spec/ruby/core/array/product_spec.rb
@@ -3,12 +3,17 @@ require_relative 'fixtures/classes'
describe "Array#product" do
it "returns converted arguments using :to_ary" do
- ->{ [1].product(2..3) }.should raise_error(TypeError)
+ ->{ [1].product(2..3) }.should.raise(TypeError)
ar = ArraySpecs::ArrayConvertible.new(2,3)
[1].product(ar).should == [[1,2],[1,3]]
ar.called.should == :to_ary
end
+ it "returns converted arguments using :method_missing" do
+ ar = ArraySpecs::ArrayMethodMissing.new(2,3)
+ [1].product(ar).should == [[1,2],[1,3]]
+ end
+
it "returns the expected result" do
[1,2].product([3,4,5],[6,8]).should == [[1, 3, 6], [1, 3, 8], [1, 4, 6], [1, 4, 8], [1, 5, 6], [1, 5, 8],
[2, 3, 6], [2, 3, 8], [2, 4, 6], [2, 4, 8], [2, 5, 6], [2, 5, 8]]
@@ -26,7 +31,7 @@ describe "Array#product" do
a = (0..100).to_a
-> do
a.product(a, a, a, a, a, a, a, a, a, a)
- end.should raise_error(RangeError)
+ end.should.raise(RangeError)
end
describe "when given a block" do
@@ -38,7 +43,7 @@ describe "Array#product" do
acc = []
[1,2].product([3,4,5],[],[6,8]){|array| acc << array}
- acc.should be_empty
+ acc.should.empty?
end
it "returns self" do
@@ -51,18 +56,18 @@ describe "Array#product" do
a = (0..100).to_a
-> do
a.product(a, a, a, a, a, a, a, a, a, a)
- end.should raise_error(RangeError)
+ end.should.raise(RangeError)
end
end
describe "when given an empty block" do
it "returns self" do
arr = [1,2]
- arr.product([3,4,5],[6,8]){}.should equal(arr)
+ arr.product([3,4,5],[6,8]){}.should.equal?(arr)
arr = []
- arr.product([3,4,5],[6,8]){}.should equal(arr)
+ arr.product([3,4,5],[6,8]){}.should.equal?(arr)
arr = [1,2]
- arr.product([]){}.should equal(arr)
+ arr.product([]){}.should.equal?(arr)
end
end
end
diff --git a/spec/ruby/core/array/push_spec.rb b/spec/ruby/core/array/push_spec.rb
index 607cbc7b4d..6255a84371 100644
--- a/spec/ruby/core/array/push_spec.rb
+++ b/spec/ruby/core/array/push_spec.rb
@@ -1,7 +1,36 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/push'
describe "Array#push" do
- it_behaves_like :array_push, :push
+ it "appends the arguments to the array" do
+ a = [ "a", "b", "c" ]
+ a.push("d", "e", "f").should.equal?(a)
+ a.push.should == ["a", "b", "c", "d", "e", "f"]
+ a.push(5)
+ a.should == ["a", "b", "c", "d", "e", "f", 5]
+
+ a = [0, 1]
+ a.push(2)
+ a.should == [0, 1, 2]
+ end
+
+ it "isn't confused by previous shift" do
+ a = [ "a", "b", "c" ]
+ a.shift
+ a.push("foo")
+ a.should == ["b", "c", "foo"]
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.push(:last).should == [empty, :last]
+
+ array = ArraySpecs.recursive_array
+ array.push(:last).should == [1, 'two', 3.0, array, array, array, array, array, :last]
+ end
+
+ it "raises a FrozenError on a frozen array" do
+ -> { ArraySpecs.frozen_array.push(1) }.should.raise(FrozenError)
+ -> { ArraySpecs.frozen_array.push }.should.raise(FrozenError)
+ end
end
diff --git a/spec/ruby/core/array/rassoc_spec.rb b/spec/ruby/core/array/rassoc_spec.rb
index 62fbd40611..95e4ed1892 100644
--- a/spec/ruby/core/array/rassoc_spec.rb
+++ b/spec/ruby/core/array/rassoc_spec.rb
@@ -12,11 +12,11 @@ describe "Array#rassoc" do
it "properly handles recursive arrays" do
empty = ArraySpecs.empty_recursive_array
- empty.rassoc([]).should be_nil
+ empty.rassoc([]).should == nil
[[empty, empty]].rassoc(empty).should == [empty, empty]
array = ArraySpecs.recursive_array
- array.rassoc(array).should be_nil
+ array.rassoc(array).should == nil
[[empty, array]].rassoc(array).should == [empty, array]
end
@@ -35,4 +35,16 @@ describe "Array#rassoc" do
[[1, :foobar, o], [2, o, 1], [3, mock('foo')]].rassoc(key).should == [2, o, 1]
end
+
+ it "calls to_ary on non-array elements" do
+ s1 = [1, 2]
+ s2 = ArraySpecs::ArrayConvertible.new(2, 3)
+ a = [s1, s2]
+
+ s1.should_not_receive(:to_ary)
+ a.rassoc(2).should.equal?(s1)
+
+ a.rassoc(3).should == [2, 3]
+ s2.called.should.equal?(:to_ary)
+ end
end
diff --git a/spec/ruby/core/array/reject_spec.rb b/spec/ruby/core/array/reject_spec.rb
index fcf43fabde..8d237b3a75 100644
--- a/spec/ruby/core/array/reject_spec.rb
+++ b/spec/ruby/core/array/reject_spec.rb
@@ -2,6 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/enumeratorize'
require_relative 'shared/delete_if'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized'
describe "Array#reject" do
@@ -9,9 +10,9 @@ describe "Array#reject" do
ary = [1, 2, 3, 4, 5]
ary.reject { true }.should == []
ary.reject { false }.should == ary
- ary.reject { false }.should_not equal ary
+ ary.reject { false }.should_not.equal? ary
ary.reject { nil }.should == ary
- ary.reject { nil }.should_not equal ary
+ ary.reject { nil }.should_not.equal? ary
ary.reject { 5 }.should == []
ary.reject { |i| i < 3 }.should == [3, 4, 5]
ary.reject { |i| i % 2 == 0 }.should == [1, 3, 5]
@@ -34,7 +35,7 @@ describe "Array#reject" do
end
it "does not return subclass instance on Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].reject { |x| x % 2 == 0 }.should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].reject { |x| x % 2 == 0 }.should.instance_of?(Array)
end
it "does not retain instance variables" do
@@ -47,10 +48,14 @@ describe "Array#reject" do
it_behaves_like :enumeratorized_with_origin_size, :reject, [1,2,3]
end
+describe "Array#reject" do
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :reject
+end
+
describe "Array#reject!" do
it "removes elements for which block is true" do
a = [3, 4, 5, 6, 7, 8, 9, 10, 11]
- a.reject! { |i| i % 2 == 0 }.should equal(a)
+ a.reject! { |i| i % 2 == 0 }.should.equal?(a)
a.should == [3, 5, 7, 9, 11]
a.reject! { |i| i > 8 }
a.should == [3, 5, 7]
@@ -100,15 +105,20 @@ describe "Array#reject!" do
end
it "returns an Enumerator if no block given, and the array is frozen" do
- ArraySpecs.frozen_array.reject!.should be_an_instance_of(Enumerator)
+ ArraySpecs.frozen_array.reject!.should.instance_of?(Enumerator)
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.reject! {} }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.reject! {} }.should.raise(FrozenError)
end
it "raises a FrozenError on an empty frozen array" do
- -> { ArraySpecs.empty_frozen_array.reject! {} }.should raise_error(FrozenError)
+ -> { ArraySpecs.empty_frozen_array.reject! {} }.should.raise(FrozenError)
+ end
+
+ it "raises a FrozenError on a frozen array only during iteration if called without a block" do
+ enum = ArraySpecs.frozen_array.reject!
+ -> { enum.each {} }.should.raise(FrozenError)
end
it "does not truncate the array is the block raises an exception" do
@@ -141,3 +151,8 @@ describe "Array#reject!" do
it_behaves_like :enumeratorized_with_origin_size, :reject!, [1,2,3]
it_behaves_like :delete_if, :reject!
end
+
+describe "Array#reject!" do
+ @value_to_return = -> _ { false }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :reject!
+end
diff --git a/spec/ruby/core/array/repeated_combination_spec.rb b/spec/ruby/core/array/repeated_combination_spec.rb
index b62382024a..a714f05f54 100644
--- a/spec/ruby/core/array/repeated_combination_spec.rb
+++ b/spec/ruby/core/array/repeated_combination_spec.rb
@@ -6,16 +6,16 @@ describe "Array#repeated_combination" do
end
it "returns an enumerator when no block is provided" do
- @array.repeated_combination(2).should be_an_instance_of(Enumerator)
+ @array.repeated_combination(2).should.instance_of?(Enumerator)
end
it "returns self when a block is given" do
- @array.repeated_combination(2){}.should equal(@array)
+ @array.repeated_combination(2){}.should.equal?(@array)
end
it "yields nothing for negative length and return self" do
- @array.repeated_combination(-1){ fail }.should equal(@array)
- @array.repeated_combination(-10){ fail }.should equal(@array)
+ @array.repeated_combination(-1){ fail }.should.equal?(@array)
+ @array.repeated_combination(-10){ fail }.should.equal?(@array)
end
it "yields the expected repeated_combinations" do
diff --git a/spec/ruby/core/array/repeated_permutation_spec.rb b/spec/ruby/core/array/repeated_permutation_spec.rb
index a165fda09e..c54a8c0c2b 100644
--- a/spec/ruby/core/array/repeated_permutation_spec.rb
+++ b/spec/ruby/core/array/repeated_permutation_spec.rb
@@ -10,13 +10,13 @@ describe "Array#repeated_permutation" do
it "returns an Enumerator of all repeated permutations of given length when called without a block" do
enum = @numbers.repeated_permutation(2)
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.to_a.sort.should == @permutations
end
it "yields all repeated_permutations to the block then returns self when called with block but no arguments" do
yielded = []
- @numbers.repeated_permutation(2) {|n| yielded << n}.should equal(@numbers)
+ @numbers.repeated_permutation(2) {|n| yielded << n}.should.equal?(@numbers)
yielded.sort.should == @permutations
end
diff --git a/spec/ruby/core/array/replace_spec.rb b/spec/ruby/core/array/replace_spec.rb
index 2f53338f5e..ee6a98a646 100644
--- a/spec/ruby/core/array/replace_spec.rb
+++ b/spec/ruby/core/array/replace_spec.rb
@@ -1,7 +1,63 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/replace'
describe "Array#replace" do
- it_behaves_like :array_replace, :replace
+ it "replaces the elements with elements from other array" do
+ a = [1, 2, 3, 4, 5]
+ b = ['a', 'b', 'c']
+ a.replace(b).should.equal?(a)
+ a.should == b
+ a.should_not.equal?(b)
+
+ a.replace([4] * 10)
+ a.should == [4] * 10
+
+ a.replace([])
+ a.should == []
+ end
+
+ it "properly handles recursive arrays" do
+ orig = [1, 2, 3]
+ empty = ArraySpecs.empty_recursive_array
+ orig.replace(empty)
+ orig.should == empty
+
+ array = ArraySpecs.recursive_array
+ orig.replace(array)
+ orig.should == array
+ end
+
+ it "returns self" do
+ ary = [1, 2, 3]
+ other = [:a, :b, :c]
+ ary.replace(other).should.equal?(ary)
+ end
+
+ it "does not make self dependent to the original array" do
+ ary = [1, 2, 3]
+ other = [:a, :b, :c]
+ ary.replace(other)
+ ary.should == [:a, :b, :c]
+ ary << :d
+ ary.should == [:a, :b, :c, :d]
+ other.should == [:a, :b, :c]
+ end
+
+ it "tries to convert the passed argument to an Array using #to_ary" do
+ obj = mock('to_ary')
+ obj.stub!(:to_ary).and_return([1, 2, 3])
+ [].replace(obj).should == [1, 2, 3]
+ end
+
+ it "does not call #to_ary on Array subclasses" do
+ obj = ArraySpecs::ToAryArray[5, 6, 7]
+ obj.should_not_receive(:to_ary)
+ [].replace(ArraySpecs::ToAryArray[5, 6, 7]).should == [5, 6, 7]
+ end
+
+ it "raises a FrozenError on a frozen array" do
+ -> {
+ ArraySpecs.frozen_array.replace(ArraySpecs.frozen_array)
+ }.should.raise(FrozenError)
+ end
end
diff --git a/spec/ruby/core/array/reverse_each_spec.rb b/spec/ruby/core/array/reverse_each_spec.rb
index 28b8bfcb34..8fa5ce6da1 100644
--- a/spec/ruby/core/array/reverse_each_spec.rb
+++ b/spec/ruby/core/array/reverse_each_spec.rb
@@ -5,7 +5,7 @@ require_relative '../enumerable/shared/enumeratorized'
# Modifying a collection while the contents are being iterated
# gives undefined behavior. See
-# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633
+# https://blade.ruby-lang.org/ruby-core/23633
describe "Array#reverse_each" do
before :each do
@@ -19,7 +19,7 @@ describe "Array#reverse_each" do
it "returns self" do
a = [:a, :b, :c]
- a.reverse_each { |x| }.should equal(a)
+ a.reverse_each { |x| }.should.equal?(a)
end
it "yields only the top level element of an empty recursive arrays" do
@@ -38,6 +38,20 @@ describe "Array#reverse_each" do
[1, 2, 3].reverse_each.size.should == 3
end
+ it "tolerates increasing an array size during iteration" do
+ array = [:a, :b, :c]
+ ScratchPad.record []
+ i = 0
+
+ array.reverse_each do |e|
+ ScratchPad << e
+ array.prepend i if i < 100
+ i += 1
+ end
+
+ ScratchPad.recorded.should == [:c, :a, 1]
+ end
+
it_behaves_like :enumeratorize, :reverse_each
it_behaves_like :enumeratorized_with_origin_size, :reverse_each, [1,2,3]
end
diff --git a/spec/ruby/core/array/reverse_spec.rb b/spec/ruby/core/array/reverse_spec.rb
index 05dbd2efcf..f25a484be8 100644
--- a/spec/ruby/core/array/reverse_spec.rb
+++ b/spec/ruby/core/array/reverse_spec.rb
@@ -16,14 +16,14 @@ describe "Array#reverse" do
end
it "does not return subclass instance on Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].reverse.should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].reverse.should.instance_of?(Array)
end
end
describe "Array#reverse!" do
it "reverses the elements in place" do
a = [6, 3, 4, 2, 1]
- a.reverse!.should equal(a)
+ a.reverse!.should.equal?(a)
a.should == [1, 2, 4, 3, 6]
[].reverse!.should == []
end
@@ -37,6 +37,6 @@ describe "Array#reverse!" do
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.reverse! }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.reverse! }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/array/rindex_spec.rb b/spec/ruby/core/array/rindex_spec.rb
index 175c7bcfe2..858c39dc92 100644
--- a/spec/ruby/core/array/rindex_spec.rb
+++ b/spec/ruby/core/array/rindex_spec.rb
@@ -4,7 +4,7 @@ require_relative '../enumerable/shared/enumeratorized'
# Modifying a collection while the contents are being iterated
# gives undefined behavior. See
-# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633
+# https://blade.ruby-lang.org/ruby-core/23633
describe "Array#rindex" do
it "returns the first index backwards from the end where element == to object" do
@@ -41,7 +41,7 @@ describe "Array#rindex" do
it "properly handles empty recursive arrays" do
empty = ArraySpecs.empty_recursive_array
empty.rindex(empty).should == 0
- empty.rindex(1).should be_nil
+ empty.rindex(1).should == nil
end
it "properly handles recursive arrays" do
@@ -68,10 +68,25 @@ describe "Array#rindex" do
seen.should == [3]
end
+ it "tolerates increasing an array size during iteration" do
+ array = [:a, :b, :c]
+ ScratchPad.record []
+ i = 0
+
+ array.rindex do |e|
+ ScratchPad << e
+ array.prepend i if i < 100
+ i += 1
+ false
+ end
+
+ ScratchPad.recorded.should == [:c, :a, 1]
+ end
+
describe "given no argument and no block" do
it "produces an Enumerator" do
enum = [4, 2, 1, 5, 1, 3].rindex
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.each { |x| x < 2 }.should == 4
end
end
diff --git a/spec/ruby/core/array/rotate_spec.rb b/spec/ruby/core/array/rotate_spec.rb
index 60dcc8b113..009ce5ed49 100644
--- a/spec/ruby/core/array/rotate_spec.rb
+++ b/spec/ruby/core/array/rotate_spec.rb
@@ -29,10 +29,10 @@ describe "Array#rotate" do
it "raises a TypeError if not passed an integer-like argument" do
-> {
[1, 2].rotate(nil)
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
-> {
[1, 2].rotate("4")
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
end
@@ -50,18 +50,18 @@ describe "Array#rotate" do
[].freeze.rotate
[2].freeze.rotate(2)
[1,2,3].freeze.rotate(-3)
- }.should_not raise_error
+ }.should_not.raise
end
it "does not return self" do
a = [1, 2, 3]
- a.rotate.should_not equal(a)
+ a.rotate.should_not.equal?(a)
a = []
- a.rotate(0).should_not equal(a)
+ a.rotate(0).should_not.equal?(a)
end
it "does not return subclass instance for Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].rotate.should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].rotate.should.instance_of?(Array)
end
end
@@ -69,7 +69,7 @@ describe "Array#rotate!" do
describe "when passed no argument" do
it "moves the first element to the end and returns self" do
a = [1, 2, 3, 4, 5]
- a.rotate!.should equal(a)
+ a.rotate!.should.equal?(a)
a.should == [2, 3, 4, 5, 1]
end
end
@@ -77,11 +77,11 @@ describe "Array#rotate!" do
describe "with an argument n" do
it "moves the first (n % size) elements at the end and returns self" do
a = [1, 2, 3, 4, 5]
- a.rotate!(2).should equal(a)
+ a.rotate!(2).should.equal?(a)
a.should == [3, 4, 5, 1, 2]
- a.rotate!(-12).should equal(a)
+ a.rotate!(-12).should.equal?(a)
a.should == [1, 2, 3, 4, 5]
- a.rotate!(13).should equal(a)
+ a.rotate!(13).should.equal?(a)
a.should == [4, 5, 1, 2, 3]
end
@@ -96,34 +96,34 @@ describe "Array#rotate!" do
it "raises a TypeError if not passed an integer-like argument" do
-> {
[1, 2].rotate!(nil)
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
-> {
[1, 2].rotate!("4")
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
end
it "does nothing and returns self when the length is zero or one" do
a = [1]
- a.rotate!.should equal(a)
+ a.rotate!.should.equal?(a)
a.should == [1]
- a.rotate!(2).should equal(a)
+ a.rotate!(2).should.equal?(a)
a.should == [1]
- a.rotate!(-21).should equal(a)
+ a.rotate!(-21).should.equal?(a)
a.should == [1]
a = []
- a.rotate!.should equal(a)
+ a.rotate!.should.equal?(a)
a.should == []
- a.rotate!(2).should equal(a)
+ a.rotate!(2).should.equal?(a)
a.should == []
- a.rotate!(-21).should equal(a)
+ a.rotate!(-21).should.equal?(a)
a.should == []
end
it "raises a FrozenError on a frozen array" do
- -> { [1, 2, 3].freeze.rotate!(0) }.should raise_error(FrozenError)
- -> { [1].freeze.rotate!(42) }.should raise_error(FrozenError)
- -> { [].freeze.rotate! }.should raise_error(FrozenError)
+ -> { [1, 2, 3].freeze.rotate!(0) }.should.raise(FrozenError)
+ -> { [1].freeze.rotate!(42) }.should.raise(FrozenError)
+ -> { [].freeze.rotate! }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/array/sample_spec.rb b/spec/ruby/core/array/sample_spec.rb
index 73b6bdf480..fd443b47de 100644
--- a/spec/ruby/core/array/sample_spec.rb
+++ b/spec/ruby/core/array/sample_spec.rb
@@ -3,24 +3,34 @@ require_relative 'fixtures/classes'
describe "Array#sample" do
it "samples evenly" do
- ary = [0, 1, 2, 3]
- 3.times do |i|
- counts = [0, 0, 0, 0]
- 4000.times do
- counts[ary.sample(3)[i]] += 1
- end
- counts.each do |count|
- (800..1200).should include(count)
- end
- end
+ ArraySpecs.measure_sample_fairness(4, 1, 400)
+ ArraySpecs.measure_sample_fairness(4, 2, 400)
+ ArraySpecs.measure_sample_fairness(4, 3, 400)
+ ArraySpecs.measure_sample_fairness(40, 3, 400)
+ ArraySpecs.measure_sample_fairness(40, 4, 400)
+ ArraySpecs.measure_sample_fairness(40, 8, 400)
+ ArraySpecs.measure_sample_fairness(40, 16, 400)
+ ArraySpecs.measure_sample_fairness_large_sample_size(100, 80, 4000)
end
it "returns nil for an empty Array" do
- [].sample.should be_nil
+ [].sample.should == nil
+ end
+
+ it "returns nil for an empty array when called without n and a Random is given" do
+ [].sample(random: Random.new(42)).should == nil
end
it "returns a single value when not passed a count" do
- [4].sample.should equal(4)
+ [4].sample.should.equal?(4)
+ end
+
+ it "returns a single value when not passed a count and a Random is given" do
+ [4].sample(random: Random.new(42)).should.equal?(4)
+ end
+
+ it "returns a single value when not passed a count and a Random class is given" do
+ [4].sample(random: Random).should.equal?(4)
end
it "returns an empty Array when passed zero" do
@@ -28,12 +38,12 @@ describe "Array#sample" do
end
it "returns an Array of elements when passed a count" do
- [1, 2, 3, 4].sample(3).should be_an_instance_of(Array)
+ [1, 2, 3, 4].sample(3).should.instance_of?(Array)
end
it "returns elements from the Array" do
array = [1, 2, 3, 4]
- array.sample(3).all? { |x| array.should include(x) }
+ array.sample(3).all? { |x| array.should.include?(x) }
end
it "returns at most the number of elements in the Array" do
@@ -57,11 +67,11 @@ describe "Array#sample" do
end
it "raises ArgumentError when passed a negative count" do
- -> { [1, 2].sample(-1) }.should raise_error(ArgumentError)
+ -> { [1, 2].sample(-1) }.should.raise(ArgumentError)
end
it "does not return subclass instances with Array subclass" do
- ArraySpecs::MyArray[1, 2, 3].sample(2).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].sample(2).should.instance_of?(Array)
end
describe "with options" do
@@ -69,17 +79,17 @@ describe "Array#sample" do
obj = mock("array_sample_random")
obj.should_receive(:rand).and_return(0.5)
- [1, 2].sample(random: obj).should be_an_instance_of(Fixnum)
+ [1, 2].sample(random: obj).should.instance_of?(Integer)
end
it "raises a NoMethodError if an object passed for the RNG does not define #rand" do
obj = BasicObject.new
- -> { [1, 2].sample(random: obj) }.should raise_error(NoMethodError)
+ -> { [1, 2].sample(random: obj) }.should.raise(NoMethodError)
end
- describe "when the object returned by #rand is a Fixnum" do
- it "uses the fixnum as index" do
+ describe "when the object returned by #rand is an Integer" do
+ it "uses the integer as index" do
random = mock("array_sample_random_ret")
random.should_receive(:rand).and_return(0)
@@ -95,19 +105,26 @@ describe "Array#sample" do
random = mock("array_sample_random")
random.should_receive(:rand).and_return(-1)
- -> { [1, 2].sample(random: random) }.should raise_error(RangeError)
+ -> { [1, 2].sample(random: random) }.should.raise(RangeError)
end
it "raises a RangeError if the value is equal to the Array size" do
random = mock("array_sample_random")
random.should_receive(:rand).and_return(2)
- -> { [1, 2].sample(random: random) }.should raise_error(RangeError)
+ -> { [1, 2].sample(random: random) }.should.raise(RangeError)
+ end
+
+ it "raises a RangeError if the value is greater than the Array size" do
+ random = mock("array_sample_random")
+ random.should_receive(:rand).and_return(3)
+
+ -> { [1, 2].sample(random: random) }.should.raise(RangeError)
end
end
end
- describe "when the object returned by #rand is not a Fixnum but responds to #to_int" do
+ describe "when the object returned by #rand is not an Integer but responds to #to_int" do
it "calls #to_int on the Object" do
value = mock("array_sample_random_value")
value.should_receive(:to_int).and_return(1)
@@ -123,7 +140,7 @@ describe "Array#sample" do
random = mock("array_sample_random")
random.should_receive(:rand).and_return(value)
- -> { [1, 2].sample(random: random) }.should raise_error(RangeError)
+ -> { [1, 2].sample(random: random) }.should.raise(RangeError)
end
it "raises a RangeError if the value is equal to the Array size" do
@@ -132,7 +149,7 @@ describe "Array#sample" do
random = mock("array_sample_random")
random.should_receive(:rand).and_return(value)
- -> { [1, 2].sample(random: random) }.should raise_error(RangeError)
+ -> { [1, 2].sample(random: random) }.should.raise(RangeError)
end
end
end
diff --git a/spec/ruby/core/array/select_spec.rb b/spec/ruby/core/array/select_spec.rb
index 298b591744..57ec0b2540 100644
--- a/spec/ruby/core/array/select_spec.rb
+++ b/spec/ruby/core/array/select_spec.rb
@@ -1,13 +1,42 @@
require_relative '../../spec_helper'
-require_relative 'shared/select'
+require_relative '../enumerable/shared/enumeratorized'
+require_relative 'fixtures/classes'
+require_relative 'shared/enumeratorize'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
+require_relative 'shared/keep_if'
describe "Array#select" do
- it_behaves_like :array_select, :select
+ it_behaves_like :enumeratorize, :select
+
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :select
+
+ before :each do
+ @object = [1,2,3]
+ end
+ it_behaves_like :enumeratorized_with_origin_size, :select
+
+ it "returns a new array of elements for which block is true" do
+ [1, 3, 4, 5, 6, 9].select { |i| i % ((i + 1) / 2) == 0}.should == [1, 4, 6]
+ end
+
+ it "does not return subclass instance on Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].select { true }.should.instance_of?(Array)
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.select { true }.should == empty
+ empty.select { false }.should == []
+
+ array = ArraySpecs.recursive_array
+ array.select { true }.should == [1, 'two', 3.0, array, array, array, array, array]
+ array.select { false }.should == []
+ end
end
describe "Array#select!" do
it "returns nil if no changes were made in the array" do
- [1, 2, 3].select! { true }.should be_nil
+ [1, 2, 3].select! { true }.should == nil
end
it_behaves_like :keep_if, :select!
diff --git a/spec/ruby/core/array/shared/clone.rb b/spec/ruby/core/array/shared/clone.rb
index 3c17b1f10f..1a45c2fe2c 100644
--- a/spec/ruby/core/array/shared/clone.rb
+++ b/spec/ruby/core/array/shared/clone.rb
@@ -1,14 +1,14 @@
describe :array_clone, shared: true do
it "returns an Array or a subclass instance" do
- [].send(@method).should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2].send(@method).should be_an_instance_of(ArraySpecs::MyArray)
+ [].send(@method).should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2].send(@method).should.instance_of?(ArraySpecs::MyArray)
end
it "produces a shallow copy where the references are directly copied" do
a = [mock('1'), mock('2')]
b = a.send @method
- b.first.should equal a.first
- b.last.should equal a.last
+ b.first.should.equal? a.first
+ b.last.should.equal? a.last
end
it "creates a new array containing all elements or the original" do
@@ -17,28 +17,4 @@ describe :array_clone, shared: true do
b.should == a
b.__id__.should_not == a.__id__
end
-
- ruby_version_is ''...'2.7' do
- it "copies taint status from the original" do
- a = [1, 2, 3, 4]
- b = [1, 2, 3, 4]
- a.taint
- aa = a.send @method
- bb = b.send @method
-
- aa.should.tainted?
- bb.should_not.tainted?
- end
-
- it "copies untrusted status from the original" do
- a = [1, 2, 3, 4]
- b = [1, 2, 3, 4]
- a.untrust
- aa = a.send @method
- bb = b.send @method
-
- aa.should.untrusted?
- bb.should_not.untrusted?
- end
- end
end
diff --git a/spec/ruby/core/array/shared/collect.rb b/spec/ruby/core/array/shared/collect.rb
deleted file mode 100644
index d84432734a..0000000000
--- a/spec/ruby/core/array/shared/collect.rb
+++ /dev/null
@@ -1,140 +0,0 @@
-require_relative '../../enumerable/shared/enumeratorized'
-
-describe :array_collect, shared: true do
- it "returns a copy of array with each element replaced by the value returned by block" do
- a = ['a', 'b', 'c', 'd']
- b = a.send(@method) { |i| i + '!' }
- b.should == ["a!", "b!", "c!", "d!"]
- b.should_not equal a
- end
-
- it "does not return subclass instance" do
- ArraySpecs::MyArray[1, 2, 3].send(@method) { |x| x + 1 }.should be_an_instance_of(Array)
- end
-
- it "does not change self" do
- a = ['a', 'b', 'c', 'd']
- a.send(@method) { |i| i + '!' }
- a.should == ['a', 'b', 'c', 'd']
- end
-
- it "returns the evaluated value of block if it broke in the block" do
- a = ['a', 'b', 'c', 'd']
- b = a.send(@method) {|i|
- if i == 'c'
- break 0
- else
- i + '!'
- end
- }
- b.should == 0
- end
-
- it "returns an Enumerator when no block given" do
- a = [1, 2, 3]
- a.send(@method).should be_an_instance_of(Enumerator)
- end
-
- it "raises an ArgumentError when no block and with arguments" do
- a = [1, 2, 3]
- -> {
- a.send(@method, :foo)
- }.should raise_error(ArgumentError)
- end
-
- ruby_version_is ''...'2.7' do
- it "does not copy tainted status" do
- a = [1, 2, 3]
- a.taint
- a.send(@method){|x| x}.tainted?.should be_false
- end
-
- it "does not copy untrusted status" do
- a = [1, 2, 3]
- a.untrust
- a.send(@method){|x| x}.untrusted?.should be_false
- end
- end
-
- before :all do
- @object = [1, 2, 3, 4]
- end
- it_should_behave_like :enumeratorized_with_origin_size
-end
-
-describe :array_collect_b, shared: true do
- it "replaces each element with the value returned by block" do
- a = [7, 9, 3, 5]
- a.send(@method) { |i| i - 1 }.should equal(a)
- a.should == [6, 8, 2, 4]
- end
-
- it "returns self" do
- a = [1, 2, 3, 4, 5]
- b = a.send(@method) {|i| i+1 }
- a.should equal b
- end
-
- it "returns the evaluated value of block but its contents is partially modified, if it broke in the block" do
- a = ['a', 'b', 'c', 'd']
- b = a.send(@method) {|i|
- if i == 'c'
- break 0
- else
- i + '!'
- end
- }
- b.should == 0
- a.should == ['a!', 'b!', 'c', 'd']
- end
-
- it "returns an Enumerator when no block given, and the enumerator can modify the original array" do
- a = [1, 2, 3]
- enum = a.send(@method)
- enum.should be_an_instance_of(Enumerator)
- enum.each{|i| "#{i}!" }
- a.should == ["1!", "2!", "3!"]
- end
-
- ruby_version_is ''...'2.7' do
- it "keeps tainted status" do
- a = [1, 2, 3]
- a.taint
- a.tainted?.should be_true
- a.send(@method){|x| x}
- a.tainted?.should be_true
- end
-
- it "keeps untrusted status" do
- a = [1, 2, 3]
- a.untrust
- a.send(@method){|x| x}
- a.untrusted?.should be_true
- end
- end
-
- describe "when frozen" do
- it "raises a FrozenError" do
- -> { ArraySpecs.frozen_array.send(@method) {} }.should raise_error(FrozenError)
- end
-
- it "raises a FrozenError when empty" do
- -> { ArraySpecs.empty_frozen_array.send(@method) {} }.should raise_error(FrozenError)
- end
-
- it "raises a FrozenError when calling #each on the returned Enumerator" do
- enumerator = ArraySpecs.frozen_array.send(@method)
- -> { enumerator.each {|x| x } }.should raise_error(FrozenError)
- end
-
- it "raises a FrozenError when calling #each on the returned Enumerator when empty" do
- enumerator = ArraySpecs.empty_frozen_array.send(@method)
- -> { enumerator.each {|x| x } }.should raise_error(FrozenError)
- end
- end
-
- before :all do
- @object = [1, 2, 3, 4]
- end
- it_should_behave_like :enumeratorized_with_origin_size
-end
diff --git a/spec/ruby/core/array/shared/difference.rb b/spec/ruby/core/array/shared/difference.rb
index 3e69050d82..3fe22331bd 100644
--- a/spec/ruby/core/array/shared/difference.rb
+++ b/spec/ruby/core/array/shared/difference.rb
@@ -27,13 +27,13 @@ describe :array_binary_difference, shared: true do
it "raises a TypeError if the argument cannot be coerced to an Array by calling #to_ary" do
obj = mock('not an array')
- -> { [1, 2, 3].send(@method, obj) }.should raise_error(TypeError)
+ -> { [1, 2, 3].send(@method, obj) }.should.raise(TypeError)
end
it "does not return subclass instance for Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].send(@method, []).should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[]).should be_an_instance_of(Array)
- [1, 2, 3].send(@method, ArraySpecs::MyArray[]).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].send(@method, []).should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[]).should.instance_of?(Array)
+ [1, 2, 3].send(@method, ArraySpecs::MyArray[]).should.instance_of?(Array)
end
it "does not call to_ary on array subclasses" do
diff --git a/spec/ruby/core/array/shared/enumeratorize.rb b/spec/ruby/core/array/shared/enumeratorize.rb
index a19a5d3b9b..5beab5c4c4 100644
--- a/spec/ruby/core/array/shared/enumeratorize.rb
+++ b/spec/ruby/core/array/shared/enumeratorize.rb
@@ -1,5 +1,5 @@
describe :enumeratorize, shared: true do
it "returns an Enumerator if no block given" do
- [1,2].send(@method).should be_an_instance_of(Enumerator)
+ [1,2].send(@method).should.instance_of?(Enumerator)
end
end
diff --git a/spec/ruby/core/array/shared/eql.rb b/spec/ruby/core/array/shared/eql.rb
index b5d9128434..5e770bf167 100644
--- a/spec/ruby/core/array/shared/eql.rb
+++ b/spec/ruby/core/array/shared/eql.rb
@@ -1,59 +1,59 @@
describe :array_eql, shared: true do
it "returns true if other is the same array" do
a = [1]
- a.send(@method, a).should be_true
+ a.send(@method, a).should == true
end
it "returns true if corresponding elements are #eql?" do
- [].send(@method, []).should be_true
- [1, 2, 3, 4].send(@method, [1, 2, 3, 4]).should be_true
+ [].send(@method, []).should == true
+ [1, 2, 3, 4].send(@method, [1, 2, 3, 4]).should == true
end
it "returns false if other is shorter than self" do
- [1, 2, 3, 4].send(@method, [1, 2, 3]).should be_false
+ [1, 2, 3, 4].send(@method, [1, 2, 3]).should == false
end
it "returns false if other is longer than self" do
- [1, 2, 3, 4].send(@method, [1, 2, 3, 4, 5]).should be_false
+ [1, 2, 3, 4].send(@method, [1, 2, 3, 4, 5]).should == false
end
it "returns false immediately when sizes of the arrays differ" do
obj = mock('1')
obj.should_not_receive(@method)
- [] .send(@method, [obj] ).should be_false
- [obj] .send(@method, [] ).should be_false
+ [] .send(@method, [obj] ).should == false
+ [obj] .send(@method, [] ).should == false
end
it "handles well recursive arrays" do
a = ArraySpecs.empty_recursive_array
- a .send(@method, [a] ).should be_true
- a .send(@method, [[a]] ).should be_true
- [a] .send(@method, a ).should be_true
- [[a]] .send(@method, a ).should be_true
+ a .send(@method, [a] ).should == true
+ a .send(@method, [[a]] ).should == true
+ [a] .send(@method, a ).should == true
+ [[a]] .send(@method, a ).should == true
# These may be surprising, but no difference can be
# found between these arrays, so they are ==.
# There is no "path" that will lead to a difference
# (contrary to other examples below)
a2 = ArraySpecs.empty_recursive_array
- a .send(@method, a2 ).should be_true
- a .send(@method, [a2] ).should be_true
- a .send(@method, [[a2]] ).should be_true
- [a] .send(@method, a2 ).should be_true
- [[a]] .send(@method, a2 ).should be_true
+ a .send(@method, a2 ).should == true
+ a .send(@method, [a2] ).should == true
+ a .send(@method, [[a2]] ).should == true
+ [a] .send(@method, a2 ).should == true
+ [[a]] .send(@method, a2 ).should == true
back = []
forth = [back]; back << forth;
- back .send(@method, a ).should be_true
+ back .send(@method, a ).should == true
x = []; x << x << x
- x .send(@method, a ).should be_false # since x.size != a.size
- x .send(@method, [a, a] ).should be_false # since x[0].size != [a, a][0].size
- x .send(@method, [x, a] ).should be_false # since x[1].size != [x, a][1].size
- [x, a] .send(@method, [a, x] ).should be_false # etc...
- x .send(@method, [x, x] ).should be_true
- x .send(@method, [[x, x], [x, x]] ).should be_true
+ x .send(@method, a ).should == false # since x.size != a.size
+ x .send(@method, [a, a] ).should == false # since x[0].size != [a, a][0].size
+ x .send(@method, [x, a] ).should == false # since x[1].size != [x, a][1].size
+ [x, a] .send(@method, [a, x] ).should == false # etc...
+ x .send(@method, [x, x] ).should == true
+ x .send(@method, [[x, x], [x, x]] ).should == true
tree = [];
branch = []; branch << tree << tree; tree << branch
@@ -62,31 +62,31 @@ describe :array_eql, shared: true do
forest = [tree, branch, :bird, a]; forest << forest
forest2 = [tree2, branch2, :bird, a2]; forest2 << forest2
- forest .send(@method, forest2 ).should be_true
- forest .send(@method, [tree2, branch, :bird, a, forest2]).should be_true
+ forest .send(@method, forest2 ).should == true
+ forest .send(@method, [tree2, branch, :bird, a, forest2]).should == true
diffforest = [branch2, tree2, :bird, a2]; diffforest << forest2
- forest .send(@method, diffforest ).should be_false # since forest[0].size == 1 != 3 == diffforest[0]
- forest .send(@method, [nil] ).should be_false
- forest .send(@method, [forest] ).should be_false
+ forest .send(@method, diffforest ).should == false # since forest[0].size == 1 != 3 == diffforest[0]
+ forest .send(@method, [nil] ).should == false
+ forest .send(@method, [forest] ).should == false
end
it "does not call #to_ary on its argument" do
obj = mock('to_ary')
obj.should_not_receive(:to_ary)
- [1, 2, 3].send(@method, obj).should be_false
+ [1, 2, 3].send(@method, obj).should == false
end
it "does not call #to_ary on Array subclasses" do
ary = ArraySpecs::ToAryArray[5, 6, 7]
ary.should_not_receive(:to_ary)
- [5, 6, 7].send(@method, ary).should be_true
+ [5, 6, 7].send(@method, ary).should == true
end
it "ignores array class differences" do
- ArraySpecs::MyArray[1, 2, 3].send(@method, [1, 2, 3]).should be_true
- ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_true
- [1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_true
+ ArraySpecs::MyArray[1, 2, 3].send(@method, [1, 2, 3]).should == true
+ ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should == true
+ [1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should == true
end
end
diff --git a/spec/ruby/core/array/shared/index.rb b/spec/ruby/core/array/shared/index.rb
deleted file mode 100644
index a9896554f2..0000000000
--- a/spec/ruby/core/array/shared/index.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-describe :array_index, shared: true do
- it "returns the index of the first element == to object" do
- x = mock('3')
- def x.==(obj) 3 == obj; end
-
- [2, x, 3, 1, 3, 1].send(@method, 3).should == 1
- [2, 3.0, 3, x, 1, 3, 1].send(@method, x).should == 1
- end
-
- it "returns 0 if first element == to object" do
- [2, 1, 3, 2, 5].send(@method, 2).should == 0
- end
-
- it "returns size-1 if only last element == to object" do
- [2, 1, 3, 1, 5].send(@method, 5).should == 4
- end
-
- it "returns nil if no element == to object" do
- [2, 1, 1, 1, 1].send(@method, 3).should == nil
- end
-
- it "accepts a block instead of an argument" do
- [4, 2, 1, 5, 1, 3].send(@method) {|x| x < 2}.should == 2
- end
-
- it "ignores the block if there is an argument" do
- -> {
- [4, 2, 1, 5, 1, 3].send(@method, 5) {|x| x < 2}.should == 3
- }.should complain(/given block not used/)
- end
-
- describe "given no argument and no block" do
- it "produces an Enumerator" do
- [].send(@method).should be_an_instance_of(Enumerator)
- end
- end
-end
diff --git a/spec/ruby/core/array/shared/inspect.rb b/spec/ruby/core/array/shared/inspect.rb
deleted file mode 100644
index 736f8d946b..0000000000
--- a/spec/ruby/core/array/shared/inspect.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-require_relative '../fixtures/encoded_strings'
-
-describe :array_inspect, shared: true do
- it "returns a string" do
- [1, 2, 3].send(@method).should be_an_instance_of(String)
- end
-
- it "returns '[]' for an empty Array" do
- [].send(@method).should == "[]"
- end
-
- it "calls inspect on its elements and joins the results with commas" do
- items = Array.new(3) do |i|
- obj = mock(i.to_s)
- obj.should_receive(:inspect).and_return(i.to_s)
- obj
- end
- items.send(@method).should == "[0, 1, 2]"
- end
-
- it "does not call #to_s on a String returned from #inspect" do
- str = "abc"
- str.should_not_receive(:to_s)
-
- [str].send(@method).should == '["abc"]'
- end
-
- it "calls #to_s on the object returned from #inspect if the Object isn't a String" do
- obj = mock("Array#inspect/to_s calls #to_s")
- obj.should_receive(:inspect).and_return(obj)
- obj.should_receive(:to_s).and_return("abc")
-
- [obj].send(@method).should == "[abc]"
- end
-
- it "does not call #to_str on the object returned from #inspect when it is not a String" do
- obj = mock("Array#inspect/to_s does not call #to_str")
- obj.should_receive(:inspect).and_return(obj)
- obj.should_not_receive(:to_str)
-
- [obj].send(@method).should =~ /^\[#<MockObject:0x[0-9a-f]+>\]$/
- end
-
- it "does not call #to_str on the object returned from #to_s when it is not a String" do
- obj = mock("Array#inspect/to_s does not call #to_str on #to_s result")
- obj.should_receive(:inspect).and_return(obj)
- obj.should_receive(:to_s).and_return(obj)
- obj.should_not_receive(:to_str)
-
- [obj].send(@method).should =~ /^\[#<MockObject:0x[0-9a-f]+>\]$/
- end
-
- it "does not swallow exceptions raised by #to_s" do
- obj = mock("Array#inspect/to_s does not swallow #to_s exceptions")
- obj.should_receive(:inspect).and_return(obj)
- obj.should_receive(:to_s).and_raise(Exception)
-
- -> { [obj].send(@method) }.should raise_error(Exception)
- end
-
- it "represents a recursive element with '[...]'" do
- ArraySpecs.recursive_array.send(@method).should == "[1, \"two\", 3.0, [...], [...], [...], [...], [...]]"
- ArraySpecs.head_recursive_array.send(@method).should == "[[...], [...], [...], [...], [...], 1, \"two\", 3.0]"
- ArraySpecs.empty_recursive_array.send(@method).should == "[[...]]"
- end
-
- ruby_version_is ''...'2.7' do
- it "taints the result if the Array is non-empty and tainted" do
- [1, 2].taint.send(@method).tainted?.should be_true
- end
-
- it "does not taint the result if the Array is tainted but empty" do
- [].taint.send(@method).tainted?.should be_false
- end
-
- it "taints the result if an element is tainted" do
- ["str".taint].send(@method).tainted?.should be_true
- end
-
- it "untrusts the result if the Array is untrusted" do
- [1, 2].untrust.send(@method).untrusted?.should be_true
- end
-
- it "does not untrust the result if the Array is untrusted but empty" do
- [].untrust.send(@method).untrusted?.should be_false
- end
-
- it "untrusts the result if an element is untrusted" do
- ["str".untrust].send(@method).untrusted?.should be_true
- end
- end
-
- describe "with encoding" do
- before :each do
- @default_external_encoding = Encoding.default_external
- end
-
- after :each do
- Encoding.default_external = @default_external_encoding
- end
-
- it "returns a US-ASCII string for an empty Array" do
- [].send(@method).encoding.should == Encoding::US_ASCII
- end
-
- it "use the default external encoding if it is ascii compatible" do
- Encoding.default_external = Encoding.find('UTF-8')
-
- utf8 = "utf8".encode("UTF-8")
- jp = "jp".encode("EUC-JP")
- array = [jp, utf8]
-
- array.send(@method).encoding.name.should == "UTF-8"
- end
-
- it "use US-ASCII encoding if the default external encoding is not ascii compatible" do
- Encoding.default_external = Encoding.find('UTF-32')
-
- utf8 = "utf8".encode("UTF-8")
- jp = "jp".encode("EUC-JP")
- array = [jp, utf8]
-
- array.send(@method).encoding.name.should == "US-ASCII"
- end
-
- it "does not raise if inspected result is not default external encoding" do
- utf_16be = mock("utf_16be")
- utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE))
-
- [utf_16be].send(@method).should == '["utf_16be \u3042"]'
- end
- end
-end
diff --git a/spec/ruby/core/array/shared/intersection.rb b/spec/ruby/core/array/shared/intersection.rb
index 49849b08c2..dda72e8bd7 100644
--- a/spec/ruby/core/array/shared/intersection.rb
+++ b/spec/ruby/core/array/shared/intersection.rb
@@ -11,7 +11,8 @@ describe :array_intersection, shared: true do
end
it "creates an array with elements in order they are first encountered" do
- [ 1, 2, 3, 2, 5 ].send(@method, [ 5, 2, 3, 4 ]).should == [2, 3, 5]
+ [ 1, 2, 3, 2, 5, 6, 7, 8 ].send(@method, [ 5, 2, 3, 4 ]).should == [2, 3, 5] # array > other
+ [ 5, 2, 3, 4 ].send(@method, [ 1, 2, 3, 2, 5, 6, 7, 8 ]).should == [5, 2, 3] # array < other
end
it "does not modify the original Array" do
@@ -65,9 +66,9 @@ describe :array_intersection, shared: true do
end
it "does return subclass instances for Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].send(@method, []).should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
- [].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].send(@method, []).should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should.instance_of?(Array)
+ [].send(@method, ArraySpecs::MyArray[1, 2, 3]).should.instance_of?(Array)
end
it "does not call to_ary on array subclasses" do
diff --git a/spec/ruby/core/array/shared/iterable_and_tolerating_size_increasing.rb b/spec/ruby/core/array/shared/iterable_and_tolerating_size_increasing.rb
new file mode 100644
index 0000000000..3e73bad44b
--- /dev/null
+++ b/spec/ruby/core/array/shared/iterable_and_tolerating_size_increasing.rb
@@ -0,0 +1,25 @@
+describe :array_iterable_and_tolerating_size_increasing, shared: true do
+ before do
+ @value_to_return ||= -> _ { nil }
+ end
+
+ it "tolerates increasing an array size during iteration" do
+ # The goal is to trigger potential reallocation of internal array storage, so we:
+ # - use elements of different types, starting with the less generic (Integer)
+ # - add reasonably big number of new elements (~ 100)
+ array = [1, 2, 3] # to test some methods we need several uniq elements
+ array_to_join = [:a, :b, :c] + (4..100).to_a
+
+ ScratchPad.record []
+ i = 0
+
+ array.send(@method) do |e|
+ ScratchPad << e
+ array << array_to_join[i] if i < array_to_join.size
+ i += 1
+ @value_to_return.call(e)
+ end
+
+ ScratchPad.recorded.should == [1, 2, 3] + array_to_join
+ end
+end
diff --git a/spec/ruby/core/array/shared/join.rb b/spec/ruby/core/array/shared/join.rb
index c443d9a628..93d5329ee3 100644
--- a/spec/ruby/core/array/shared/join.rb
+++ b/spec/ruby/core/array/shared/join.rb
@@ -1,134 +1,6 @@
require_relative '../fixtures/classes'
require_relative '../fixtures/encoded_strings'
-describe :array_join_with_default_separator, shared: true do
- before :each do
- @separator = $,
- end
-
- after :each do
- $, = @separator
- end
-
- it "returns an empty string if the Array is empty" do
- [].send(@method).should == ''
- end
-
- it "returns a US-ASCII string for an empty Array" do
- [].send(@method).encoding.should == Encoding::US_ASCII
- end
-
- it "returns a string formed by concatenating each String element separated by $," do
- suppress_warning {
- $, = " | "
- ["1", "2", "3"].send(@method).should == "1 | 2 | 3"
- }
- end
-
- it "attempts coercion via #to_str first" do
- obj = mock('foo')
- obj.should_receive(:to_str).any_number_of_times.and_return("foo")
- [obj].send(@method).should == "foo"
- end
-
- it "attempts coercion via #to_ary second" do
- obj = mock('foo')
- obj.should_receive(:to_str).any_number_of_times.and_return(nil)
- obj.should_receive(:to_ary).any_number_of_times.and_return(["foo"])
- [obj].send(@method).should == "foo"
- end
-
- it "attempts coercion via #to_s third" do
- obj = mock('foo')
- obj.should_receive(:to_str).any_number_of_times.and_return(nil)
- obj.should_receive(:to_ary).any_number_of_times.and_return(nil)
- obj.should_receive(:to_s).any_number_of_times.and_return("foo")
- [obj].send(@method).should == "foo"
- end
-
- it "raises a NoMethodError if an element does not respond to #to_str, #to_ary, or #to_s" do
- obj = mock('o')
- class << obj; undef :to_s; end
- -> { [1, obj].send(@method) }.should raise_error(NoMethodError)
- end
-
- it "raises an ArgumentError when the Array is recursive" do
- -> { ArraySpecs.recursive_array.send(@method) }.should raise_error(ArgumentError)
- -> { ArraySpecs.head_recursive_array.send(@method) }.should raise_error(ArgumentError)
- -> { ArraySpecs.empty_recursive_array.send(@method) }.should raise_error(ArgumentError)
- end
-
- ruby_version_is ''...'2.7' do
- it "taints the result if the Array is tainted and non-empty" do
- [1, 2].taint.send(@method).tainted?.should be_true
- end
-
- it "does not taint the result if the Array is tainted but empty" do
- [].taint.send(@method).tainted?.should be_false
- end
-
- it "taints the result if the result of coercing an element is tainted" do
- s = mock("taint")
- s.should_receive(:to_s).and_return("str".taint)
- [s].send(@method).tainted?.should be_true
- end
-
- it "untrusts the result if the Array is untrusted and non-empty" do
- [1, 2].untrust.send(@method).untrusted?.should be_true
- end
-
- it "does not untrust the result if the Array is untrusted but empty" do
- [].untrust.send(@method).untrusted?.should be_false
- end
-
- it "untrusts the result if the result of coercing an element is untrusted" do
- s = mock("untrust")
- s.should_receive(:to_s).and_return("str".untrust)
- [s].send(@method).untrusted?.should be_true
- end
- end
-
- it "uses the first encoding when other strings are compatible" do
- ary1 = ArraySpecs.array_with_7bit_utf8_and_usascii_strings
- ary2 = ArraySpecs.array_with_usascii_and_7bit_utf8_strings
- ary3 = ArraySpecs.array_with_utf8_and_7bit_binary_strings
- ary4 = ArraySpecs.array_with_usascii_and_7bit_binary_strings
-
- ary1.send(@method).encoding.should == Encoding::UTF_8
- ary2.send(@method).encoding.should == Encoding::US_ASCII
- ary3.send(@method).encoding.should == Encoding::UTF_8
- ary4.send(@method).encoding.should == Encoding::US_ASCII
- end
-
- it "uses the widest common encoding when other strings are incompatible" do
- ary1 = ArraySpecs.array_with_utf8_and_usascii_strings
- ary2 = ArraySpecs.array_with_usascii_and_utf8_strings
-
- ary1.send(@method).encoding.should == Encoding::UTF_8
- ary2.send(@method).encoding.should == Encoding::UTF_8
- end
-
- it "fails for arrays with incompatibly-encoded strings" do
- ary_utf8_bad_binary = ArraySpecs.array_with_utf8_and_binary_strings
-
- -> { ary_utf8_bad_binary.send(@method) }.should raise_error(EncodingError)
- end
-
- ruby_version_is "2.7" do
- context "when $, is not nil" do
- before do
- suppress_warning do
- $, = '*'
- end
- end
-
- it "warns" do
- -> { [].join }.should complain(/warning: \$, is set to non-nil value/)
- end
- end
- end
-end
-
describe :array_join_with_string_separator, shared: true do
it "returns a string formed by concatenating each element.to_str separated by separator" do
obj = mock('foo')
@@ -140,42 +12,4 @@ describe :array_join_with_string_separator, shared: true do
[1, [2, [3, 4], 5], 6].send(@method, ":").should == "1:2:3:4:5:6"
[1, [2, ArraySpecs::MyArray[3, 4], 5], 6].send(@method, ":").should == "1:2:3:4:5:6"
end
-
- ruby_version_is ''...'2.7' do
- describe "with a tainted separator" do
- before :each do
- @sep = ":".taint
- end
-
- it "does not taint the result if the array is empty" do
- [].send(@method, @sep).tainted?.should be_false
- end
-
- it "does not taint the result if the array has only one element" do
- [1].send(@method, @sep).tainted?.should be_false
- end
-
- it "taints the result if the array has two or more elements" do
- [1, 2].send(@method, @sep).tainted?.should be_true
- end
- end
-
- describe "with an untrusted separator" do
- before :each do
- @sep = ":".untrust
- end
-
- it "does not untrust the result if the array is empty" do
- [].send(@method, @sep).untrusted?.should be_false
- end
-
- it "does not untrust the result if the array has only one element" do
- [1].send(@method, @sep).untrusted?.should be_false
- end
-
- it "untrusts the result if the array has two or more elements" do
- [1, 2].send(@method, @sep).untrusted?.should be_true
- end
- end
- end
end
diff --git a/spec/ruby/core/array/shared/keep_if.rb b/spec/ruby/core/array/shared/keep_if.rb
index f26aff028c..44625eebd1 100644
--- a/spec/ruby/core/array/shared/keep_if.rb
+++ b/spec/ruby/core/array/shared/keep_if.rb
@@ -1,14 +1,15 @@
require_relative '../../enumerable/shared/enumeratorized'
+require_relative '../shared/iterable_and_tolerating_size_increasing'
describe :keep_if, shared: true do
it "deletes elements for which the block returns a false value" do
array = [1, 2, 3, 4, 5]
- array.send(@method) {|item| item > 3 }.should equal(array)
+ array.send(@method) {|item| item > 3 }.should.equal?(array)
array.should == [4, 5]
end
it "returns an enumerator if no block is given" do
- [1, 2, 3].send(@method).should be_an_instance_of(Enumerator)
+ [1, 2, 3].send(@method).should.instance_of?(Enumerator)
end
it "updates the receiver after all blocks" do
@@ -32,29 +33,63 @@ describe :keep_if, shared: true do
end
it "returns an Enumerator if no block is given" do
- @frozen.send(@method).should be_an_instance_of(Enumerator)
+ @frozen.send(@method).should.instance_of?(Enumerator)
end
describe "with truthy block" do
it "keeps elements after any exception" do
- -> { @frozen.send(@method) { true } }.should raise_error(Exception)
+ -> { @frozen.send(@method) { true } }.should.raise(Exception)
@frozen.should == @origin
end
it "raises a FrozenError" do
- -> { @frozen.send(@method) { true } }.should raise_error(FrozenError)
+ -> { @frozen.send(@method) { true } }.should.raise(FrozenError)
end
end
describe "with falsy block" do
it "keeps elements after any exception" do
- -> { @frozen.send(@method) { false } }.should raise_error(Exception)
+ -> { @frozen.send(@method) { false } }.should.raise(Exception)
@frozen.should == @origin
end
it "raises a FrozenError" do
- -> { @frozen.send(@method) { false } }.should raise_error(FrozenError)
+ -> { @frozen.send(@method) { false } }.should.raise(FrozenError)
end
end
+
+ it "raises a FrozenError on a frozen array only during iteration if called without a block" do
+ enum = @frozen.send(@method)
+ -> { enum.each {} }.should.raise(FrozenError)
+ end
+ end
+
+ it "does not truncate the array is the block raises an exception" do
+ a = [1, 2, 3]
+ begin
+ a.send(@method) { raise StandardError, 'Oops' }
+ rescue
+ end
+
+ a.should == [1, 2, 3]
end
+
+ it "only changes elements before error is raised, keeping the element which raised an error." do
+ a = [1, 2, 3, 4]
+ begin
+ a.send(@method) do |e|
+ case e
+ when 2 then false
+ when 3 then raise StandardError, 'Oops'
+ else true
+ end
+ end
+ rescue StandardError
+ end
+
+ a.should == [1, 3, 4]
+ end
+
+ @value_to_return = -> _ { true }
+ it_should_behave_like :array_iterable_and_tolerating_size_increasing
end
diff --git a/spec/ruby/core/array/shared/length.rb b/spec/ruby/core/array/shared/length.rb
deleted file mode 100644
index f84966d0ba..0000000000
--- a/spec/ruby/core/array/shared/length.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-describe :array_length, shared: true do
- it "returns the number of elements" do
- [].send(@method).should == 0
- [1, 2, 3].send(@method).should == 3
- end
-
- it "properly handles recursive arrays" do
- ArraySpecs.empty_recursive_array.send(@method).should == 1
- ArraySpecs.recursive_array.send(@method).should == 8
- end
-end
diff --git a/spec/ruby/core/array/shared/push.rb b/spec/ruby/core/array/shared/push.rb
deleted file mode 100644
index ac790fb6a4..0000000000
--- a/spec/ruby/core/array/shared/push.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-describe :array_push, shared: true do
- it "appends the arguments to the array" do
- a = [ "a", "b", "c" ]
- a.send(@method, "d", "e", "f").should equal(a)
- a.send(@method).should == ["a", "b", "c", "d", "e", "f"]
- a.send(@method, 5)
- a.should == ["a", "b", "c", "d", "e", "f", 5]
-
- a = [0, 1]
- a.send(@method, 2)
- a.should == [0, 1, 2]
- end
-
- it "isn't confused by previous shift" do
- a = [ "a", "b", "c" ]
- a.shift
- a.send(@method, "foo")
- a.should == ["b", "c", "foo"]
- end
-
- it "properly handles recursive arrays" do
- empty = ArraySpecs.empty_recursive_array
- empty.send(@method, :last).should == [empty, :last]
-
- array = ArraySpecs.recursive_array
- array.send(@method, :last).should == [1, 'two', 3.0, array, array, array, array, array, :last]
- end
-
- it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.send(@method, 1) }.should raise_error(FrozenError)
- -> { ArraySpecs.frozen_array.send(@method) }.should raise_error(FrozenError)
- end
-end
diff --git a/spec/ruby/core/array/shared/replace.rb b/spec/ruby/core/array/shared/replace.rb
deleted file mode 100644
index 9a6e60c1b0..0000000000
--- a/spec/ruby/core/array/shared/replace.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-describe :array_replace, shared: true do
- it "replaces the elements with elements from other array" do
- a = [1, 2, 3, 4, 5]
- b = ['a', 'b', 'c']
- a.send(@method, b).should equal(a)
- a.should == b
- a.should_not equal(b)
-
- a.send(@method, [4] * 10)
- a.should == [4] * 10
-
- a.send(@method, [])
- a.should == []
- end
-
- it "properly handles recursive arrays" do
- orig = [1, 2, 3]
- empty = ArraySpecs.empty_recursive_array
- orig.send(@method, empty)
- orig.should == empty
-
- array = ArraySpecs.recursive_array
- orig.send(@method, array)
- orig.should == array
- end
-
- it "returns self" do
- ary = [1, 2, 3]
- other = [:a, :b, :c]
- ary.send(@method, other).should equal(ary)
- end
-
- it "does not make self dependent to the original array" do
- ary = [1, 2, 3]
- other = [:a, :b, :c]
- ary.send(@method, other)
- ary.should == [:a, :b, :c]
- ary << :d
- ary.should == [:a, :b, :c, :d]
- other.should == [:a, :b, :c]
- end
-
- it "tries to convert the passed argument to an Array using #to_ary" do
- obj = mock('to_ary')
- obj.stub!(:to_ary).and_return([1, 2, 3])
- [].send(@method, obj).should == [1, 2, 3]
- end
-
- it "does not call #to_ary on Array subclasses" do
- obj = ArraySpecs::ToAryArray[5, 6, 7]
- obj.should_not_receive(:to_ary)
- [].send(@method, ArraySpecs::ToAryArray[5, 6, 7]).should == [5, 6, 7]
- end
-
- it "raises a FrozenError on a frozen array" do
- -> {
- ArraySpecs.frozen_array.send(@method, ArraySpecs.frozen_array)
- }.should raise_error(FrozenError)
- end
-end
diff --git a/spec/ruby/core/array/shared/select.rb b/spec/ruby/core/array/shared/select.rb
deleted file mode 100644
index 09101e8ab5..0000000000
--- a/spec/ruby/core/array/shared/select.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative '../../../spec_helper'
-require_relative '../fixtures/classes'
-require_relative '../shared/enumeratorize'
-require_relative '../shared/keep_if'
-require_relative '../../enumerable/shared/enumeratorized'
-
-describe :array_select, shared: true do
- it_should_behave_like :enumeratorize
-
- before :each do
- @object = [1,2,3]
- end
- it_should_behave_like :enumeratorized_with_origin_size
-
- it "returns a new array of elements for which block is true" do
- [1, 3, 4, 5, 6, 9].send(@method) { |i| i % ((i + 1) / 2) == 0}.should == [1, 4, 6]
- end
-
- it "does not return subclass instance on Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].send(@method) { true }.should be_an_instance_of(Array)
- end
-
- it "properly handles recursive arrays" do
- empty = ArraySpecs.empty_recursive_array
- empty.send(@method) { true }.should == empty
- empty.send(@method) { false }.should == []
-
- array = ArraySpecs.recursive_array
- array.send(@method) { true }.should == [1, 'two', 3.0, array, array, array, array, array]
- array.send(@method) { false }.should == []
- end
-end
diff --git a/spec/ruby/core/array/shared/slice.rb b/spec/ruby/core/array/shared/slice.rb
deleted file mode 100644
index f36890fa4e..0000000000
--- a/spec/ruby/core/array/shared/slice.rb
+++ /dev/null
@@ -1,495 +0,0 @@
-describe :array_slice, shared: true do
- it "returns the element at index with [index]" do
- [ "a", "b", "c", "d", "e" ].send(@method, 1).should == "b"
-
- a = [1, 2, 3, 4]
-
- a.send(@method, 0).should == 1
- a.send(@method, 1).should == 2
- a.send(@method, 2).should == 3
- a.send(@method, 3).should == 4
- a.send(@method, 4).should == nil
- a.send(@method, 10).should == nil
-
- a.should == [1, 2, 3, 4]
- end
-
- it "returns the element at index from the end of the array with [-index]" do
- [ "a", "b", "c", "d", "e" ].send(@method, -2).should == "d"
-
- a = [1, 2, 3, 4]
-
- a.send(@method, -1).should == 4
- a.send(@method, -2).should == 3
- a.send(@method, -3).should == 2
- a.send(@method, -4).should == 1
- a.send(@method, -5).should == nil
- a.send(@method, -10).should == nil
-
- a.should == [1, 2, 3, 4]
- end
-
- it "returns count elements starting from index with [index, count]" do
- [ "a", "b", "c", "d", "e" ].send(@method, 2, 3).should == ["c", "d", "e"]
-
- a = [1, 2, 3, 4]
-
- a.send(@method, 0, 0).should == []
- a.send(@method, 0, 1).should == [1]
- a.send(@method, 0, 2).should == [1, 2]
- a.send(@method, 0, 4).should == [1, 2, 3, 4]
- a.send(@method, 0, 6).should == [1, 2, 3, 4]
- a.send(@method, 0, -1).should == nil
- a.send(@method, 0, -2).should == nil
- a.send(@method, 0, -4).should == nil
-
- a.send(@method, 2, 0).should == []
- a.send(@method, 2, 1).should == [3]
- a.send(@method, 2, 2).should == [3, 4]
- a.send(@method, 2, 4).should == [3, 4]
- a.send(@method, 2, -1).should == nil
-
- a.send(@method, 4, 0).should == []
- a.send(@method, 4, 2).should == []
- a.send(@method, 4, -1).should == nil
-
- a.send(@method, 5, 0).should == nil
- a.send(@method, 5, 2).should == nil
- a.send(@method, 5, -1).should == nil
-
- a.send(@method, 6, 0).should == nil
- a.send(@method, 6, 2).should == nil
- a.send(@method, 6, -1).should == nil
-
- a.should == [1, 2, 3, 4]
- end
-
- it "returns count elements starting at index from the end of array with [-index, count]" do
- [ "a", "b", "c", "d", "e" ].send(@method, -2, 2).should == ["d", "e"]
-
- a = [1, 2, 3, 4]
-
- a.send(@method, -1, 0).should == []
- a.send(@method, -1, 1).should == [4]
- a.send(@method, -1, 2).should == [4]
- a.send(@method, -1, -1).should == nil
-
- a.send(@method, -2, 0).should == []
- a.send(@method, -2, 1).should == [3]
- a.send(@method, -2, 2).should == [3, 4]
- a.send(@method, -2, 4).should == [3, 4]
- a.send(@method, -2, -1).should == nil
-
- a.send(@method, -4, 0).should == []
- a.send(@method, -4, 1).should == [1]
- a.send(@method, -4, 2).should == [1, 2]
- a.send(@method, -4, 4).should == [1, 2, 3, 4]
- a.send(@method, -4, 6).should == [1, 2, 3, 4]
- a.send(@method, -4, -1).should == nil
-
- a.send(@method, -5, 0).should == nil
- a.send(@method, -5, 1).should == nil
- a.send(@method, -5, 10).should == nil
- a.send(@method, -5, -1).should == nil
-
- a.should == [1, 2, 3, 4]
- end
-
- it "returns the first count elements with [0, count]" do
- [ "a", "b", "c", "d", "e" ].send(@method, 0, 3).should == ["a", "b", "c"]
- end
-
- it "returns the subarray which is independent to self with [index,count]" do
- a = [1, 2, 3]
- sub = a.send(@method, 1,2)
- sub.replace([:a, :b])
- a.should == [1, 2, 3]
- end
-
- it "tries to convert the passed argument to an Integer using #to_int" do
- obj = mock('to_int')
- obj.stub!(:to_int).and_return(2)
-
- a = [1, 2, 3, 4]
- a.send(@method, obj).should == 3
- a.send(@method, obj, 1).should == [3]
- a.send(@method, obj, obj).should == [3, 4]
- a.send(@method, 0, obj).should == [1, 2]
- end
-
- it "raises TypeError if to_int returns non-integer" do
- from = mock('from')
- to = mock('to')
-
- # So we can construct a range out of them...
- def from.<=>(o) 0 end
- def to.<=>(o) 0 end
-
- a = [1, 2, 3, 4, 5]
-
- def from.to_int() 'cat' end
- def to.to_int() -2 end
-
- -> { a.send(@method, from..to) }.should raise_error(TypeError)
-
- def from.to_int() 1 end
- def to.to_int() 'cat' end
-
- -> { a.send(@method, from..to) }.should raise_error(TypeError)
- end
-
- it "returns the elements specified by Range indexes with [m..n]" do
- [ "a", "b", "c", "d", "e" ].send(@method, 1..3).should == ["b", "c", "d"]
- [ "a", "b", "c", "d", "e" ].send(@method, 4..-1).should == ['e']
- [ "a", "b", "c", "d", "e" ].send(@method, 3..3).should == ['d']
- [ "a", "b", "c", "d", "e" ].send(@method, 3..-2).should == ['d']
- ['a'].send(@method, 0..-1).should == ['a']
-
- a = [1, 2, 3, 4]
-
- a.send(@method, 0..-10).should == []
- a.send(@method, 0..0).should == [1]
- a.send(@method, 0..1).should == [1, 2]
- a.send(@method, 0..2).should == [1, 2, 3]
- a.send(@method, 0..3).should == [1, 2, 3, 4]
- a.send(@method, 0..4).should == [1, 2, 3, 4]
- a.send(@method, 0..10).should == [1, 2, 3, 4]
-
- a.send(@method, 2..-10).should == []
- a.send(@method, 2..0).should == []
- a.send(@method, 2..2).should == [3]
- a.send(@method, 2..3).should == [3, 4]
- a.send(@method, 2..4).should == [3, 4]
-
- a.send(@method, 3..0).should == []
- a.send(@method, 3..3).should == [4]
- a.send(@method, 3..4).should == [4]
-
- a.send(@method, 4..0).should == []
- a.send(@method, 4..4).should == []
- a.send(@method, 4..5).should == []
-
- a.send(@method, 5..0).should == nil
- a.send(@method, 5..5).should == nil
- a.send(@method, 5..6).should == nil
-
- a.should == [1, 2, 3, 4]
- end
-
- it "returns elements specified by Range indexes except the element at index n with [m...n]" do
- [ "a", "b", "c", "d", "e" ].send(@method, 1...3).should == ["b", "c"]
-
- a = [1, 2, 3, 4]
-
- a.send(@method, 0...-10).should == []
- a.send(@method, 0...0).should == []
- a.send(@method, 0...1).should == [1]
- a.send(@method, 0...2).should == [1, 2]
- a.send(@method, 0...3).should == [1, 2, 3]
- a.send(@method, 0...4).should == [1, 2, 3, 4]
- a.send(@method, 0...10).should == [1, 2, 3, 4]
-
- a.send(@method, 2...-10).should == []
- a.send(@method, 2...0).should == []
- a.send(@method, 2...2).should == []
- a.send(@method, 2...3).should == [3]
- a.send(@method, 2...4).should == [3, 4]
-
- a.send(@method, 3...0).should == []
- a.send(@method, 3...3).should == []
- a.send(@method, 3...4).should == [4]
-
- a.send(@method, 4...0).should == []
- a.send(@method, 4...4).should == []
- a.send(@method, 4...5).should == []
-
- a.send(@method, 5...0).should == nil
- a.send(@method, 5...5).should == nil
- a.send(@method, 5...6).should == nil
-
- a.should == [1, 2, 3, 4]
- end
-
- it "returns elements that exist if range start is in the array but range end is not with [m..n]" do
- [ "a", "b", "c", "d", "e" ].send(@method, 4..7).should == ["e"]
- end
-
- it "accepts Range instances having a negative m and both signs for n with [m..n] and [m...n]" do
- a = [1, 2, 3, 4]
-
- a.send(@method, -1..-1).should == [4]
- a.send(@method, -1...-1).should == []
- a.send(@method, -1..3).should == [4]
- a.send(@method, -1...3).should == []
- a.send(@method, -1..4).should == [4]
- a.send(@method, -1...4).should == [4]
- a.send(@method, -1..10).should == [4]
- a.send(@method, -1...10).should == [4]
- a.send(@method, -1..0).should == []
- a.send(@method, -1..-4).should == []
- a.send(@method, -1...-4).should == []
- a.send(@method, -1..-6).should == []
- a.send(@method, -1...-6).should == []
-
- a.send(@method, -2..-2).should == [3]
- a.send(@method, -2...-2).should == []
- a.send(@method, -2..-1).should == [3, 4]
- a.send(@method, -2...-1).should == [3]
- a.send(@method, -2..10).should == [3, 4]
- a.send(@method, -2...10).should == [3, 4]
-
- a.send(@method, -4..-4).should == [1]
- a.send(@method, -4..-2).should == [1, 2, 3]
- a.send(@method, -4...-2).should == [1, 2]
- a.send(@method, -4..-1).should == [1, 2, 3, 4]
- a.send(@method, -4...-1).should == [1, 2, 3]
- a.send(@method, -4..3).should == [1, 2, 3, 4]
- a.send(@method, -4...3).should == [1, 2, 3]
- a.send(@method, -4..4).should == [1, 2, 3, 4]
- a.send(@method, -4...4).should == [1, 2, 3, 4]
- a.send(@method, -4...4).should == [1, 2, 3, 4]
- a.send(@method, -4..0).should == [1]
- a.send(@method, -4...0).should == []
- a.send(@method, -4..1).should == [1, 2]
- a.send(@method, -4...1).should == [1]
-
- a.send(@method, -5..-5).should == nil
- a.send(@method, -5...-5).should == nil
- a.send(@method, -5..-4).should == nil
- a.send(@method, -5..-1).should == nil
- a.send(@method, -5..10).should == nil
-
- a.should == [1, 2, 3, 4]
- end
-
- it "returns the subarray which is independent to self with [m..n]" do
- a = [1, 2, 3]
- sub = a.send(@method, 1..2)
- sub.replace([:a, :b])
- a.should == [1, 2, 3]
- end
-
- it "tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]" do
- from = mock('from')
- to = mock('to')
-
- # So we can construct a range out of them...
- def from.<=>(o) 0 end
- def to.<=>(o) 0 end
-
- def from.to_int() 1 end
- def to.to_int() -2 end
-
- a = [1, 2, 3, 4]
-
- a.send(@method, from..to).should == [2, 3]
- a.send(@method, from...to).should == [2]
- a.send(@method, 1..0).should == []
- a.send(@method, 1...0).should == []
-
- -> { a.send(@method, "a" .. "b") }.should raise_error(TypeError)
- -> { a.send(@method, "a" ... "b") }.should raise_error(TypeError)
- -> { a.send(@method, from .. "b") }.should raise_error(TypeError)
- -> { a.send(@method, from ... "b") }.should raise_error(TypeError)
- end
-
- it "returns the same elements as [m..n] and [m...n] with Range subclasses" do
- a = [1, 2, 3, 4]
- range_incl = ArraySpecs::MyRange.new(1, 2)
- range_excl = ArraySpecs::MyRange.new(-3, -1, true)
-
- a.send(@method, range_incl).should == [2, 3]
- a.send(@method, range_excl).should == [2, 3]
- end
-
- it "returns nil for a requested index not in the array with [index]" do
- [ "a", "b", "c", "d", "e" ].send(@method, 5).should == nil
- end
-
- it "returns [] if the index is valid but length is zero with [index, length]" do
- [ "a", "b", "c", "d", "e" ].send(@method, 0, 0).should == []
- [ "a", "b", "c", "d", "e" ].send(@method, 2, 0).should == []
- end
-
- it "returns nil if length is zero but index is invalid with [index, length]" do
- [ "a", "b", "c", "d", "e" ].send(@method, 100, 0).should == nil
- [ "a", "b", "c", "d", "e" ].send(@method, -50, 0).should == nil
- end
-
- # This is by design. It is in the official documentation.
- it "returns [] if index == array.size with [index, length]" do
- %w|a b c d e|.send(@method, 5, 2).should == []
- end
-
- it "returns nil if index > array.size with [index, length]" do
- %w|a b c d e|.send(@method, 6, 2).should == nil
- end
-
- it "returns nil if length is negative with [index, length]" do
- %w|a b c d e|.send(@method, 3, -1).should == nil
- %w|a b c d e|.send(@method, 2, -2).should == nil
- %w|a b c d e|.send(@method, 1, -100).should == nil
- end
-
- it "returns nil if no requested index is in the array with [m..n]" do
- [ "a", "b", "c", "d", "e" ].send(@method, 6..10).should == nil
- end
-
- it "returns nil if range start is not in the array with [m..n]" do
- [ "a", "b", "c", "d", "e" ].send(@method, -10..2).should == nil
- [ "a", "b", "c", "d", "e" ].send(@method, 10..12).should == nil
- end
-
- it "returns an empty array when m == n with [m...n]" do
- [1, 2, 3, 4, 5].send(@method, 1...1).should == []
- end
-
- it "returns an empty array with [0...0]" do
- [1, 2, 3, 4, 5].send(@method, 0...0).should == []
- end
-
- it "returns a subarray where m, n negatives and m < n with [m..n]" do
- [ "a", "b", "c", "d", "e" ].send(@method, -3..-2).should == ["c", "d"]
- end
-
- it "returns an array containing the first element with [0..0]" do
- [1, 2, 3, 4, 5].send(@method, 0..0).should == [1]
- end
-
- it "returns the entire array with [0..-1]" do
- [1, 2, 3, 4, 5].send(@method, 0..-1).should == [1, 2, 3, 4, 5]
- end
-
- it "returns all but the last element with [0...-1]" do
- [1, 2, 3, 4, 5].send(@method, 0...-1).should == [1, 2, 3, 4]
- end
-
- it "returns [3] for [2..-1] out of [1, 2, 3]" do
- [1,2,3].send(@method, 2..-1).should == [3]
- end
-
- it "returns an empty array when m > n and m, n are positive with [m..n]" do
- [1, 2, 3, 4, 5].send(@method, 3..2).should == []
- end
-
- it "returns an empty array when m > n and m, n are negative with [m..n]" do
- [1, 2, 3, 4, 5].send(@method, -2..-3).should == []
- end
-
- it "does not expand array when the indices are outside of the array bounds" do
- a = [1, 2]
- a.send(@method, 4).should == nil
- a.should == [1, 2]
- a.send(@method, 4, 0).should == nil
- a.should == [1, 2]
- a.send(@method, 6, 1).should == nil
- a.should == [1, 2]
- a.send(@method, 8...8).should == nil
- a.should == [1, 2]
- a.send(@method, 10..10).should == nil
- a.should == [1, 2]
- end
-
- describe "with a subclass of Array" do
- before :each do
- ScratchPad.clear
-
- @array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
- end
-
- it "returns a subclass instance with [n, m]" do
- @array.send(@method, 0, 2).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [-n, m]" do
- @array.send(@method, -3, 2).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [n..m]" do
- @array.send(@method, 1..3).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [n...m]" do
- @array.send(@method, 1...3).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [-n..-m]" do
- @array.send(@method, -3..-1).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns a subclass instance with [-n...-m]" do
- @array.send(@method, -3...-1).should be_an_instance_of(ArraySpecs::MyArray)
- end
-
- it "returns an empty array when m == n with [m...n]" do
- @array.send(@method, 1...1).should == []
- ScratchPad.recorded.should be_nil
- end
-
- it "returns an empty array with [0...0]" do
- @array.send(@method, 0...0).should == []
- ScratchPad.recorded.should be_nil
- end
-
- it "returns an empty array when m > n and m, n are positive with [m..n]" do
- @array.send(@method, 3..2).should == []
- ScratchPad.recorded.should be_nil
- end
-
- it "returns an empty array when m > n and m, n are negative with [m..n]" do
- @array.send(@method, -2..-3).should == []
- ScratchPad.recorded.should be_nil
- end
-
- it "returns [] if index == array.size with [index, length]" do
- @array.send(@method, 5, 2).should == []
- ScratchPad.recorded.should be_nil
- end
-
- it "returns [] if the index is valid but length is zero with [index, length]" do
- @array.send(@method, 0, 0).should == []
- @array.send(@method, 2, 0).should == []
- ScratchPad.recorded.should be_nil
- end
-
- it "does not call #initialize on the subclass instance" do
- @array.send(@method, 0, 3).should == [1, 2, 3]
- ScratchPad.recorded.should be_nil
- end
- end
-
- it "raises a RangeError when the start index is out of range of Fixnum" do
- array = [1, 2, 3, 4, 5, 6]
- obj = mock('large value')
- obj.should_receive(:to_int).and_return(bignum_value)
- -> { array.send(@method, obj) }.should raise_error(RangeError)
-
- obj = 8e19
- -> { array.send(@method, obj) }.should raise_error(RangeError)
-
- # boundary value when longs are 64 bits
- -> { array.send(@method, 2.0**63) }.should raise_error(RangeError)
-
- # just under the boundary value when longs are 64 bits
- array.send(@method, max_long.to_f.prev_float).should == nil
- end
-
- it "raises a RangeError when the length is out of range of Fixnum" do
- array = [1, 2, 3, 4, 5, 6]
- obj = mock('large value')
- obj.should_receive(:to_int).and_return(bignum_value)
- -> { array.send(@method, 1, obj) }.should raise_error(RangeError)
-
- obj = 8e19
- -> { array.send(@method, 1, obj) }.should raise_error(RangeError)
- end
-
- it "raises a type error if a range is passed with a length" do
- ->{ [1, 2, 3].send(@method, 1..2, 1) }.should raise_error(TypeError)
- end
-
- it "raises a RangeError if passed a range with a bound that is too large" do
- -> { "hello".send(@method, bignum_value..(bignum_value + 1)) }.should raise_error(RangeError)
- -> { "hello".send(@method, 0..bignum_value) }.should raise_error(RangeError)
- end
-end
diff --git a/spec/ruby/core/array/shared/union.rb b/spec/ruby/core/array/shared/union.rb
index 12a98cc9fe..0b225b9a31 100644
--- a/spec/ruby/core/array/shared/union.rb
+++ b/spec/ruby/core/array/shared/union.rb
@@ -31,7 +31,7 @@ describe :array_binary_union, shared: true do
[0].send(@method, obj).should == ([0] | [1, 2, 3])
end
- # MRI follows hashing semantics here, so doesn't actually call eql?/hash for Fixnum/Symbol
+ # MRI follows hashing semantics here, so doesn't actually call eql?/hash for Integer/Symbol
it "acts as if using an intermediate hash to collect values" do
not_supported_on :opal do
[5.0, 4.0].send(@method, [5, 4]).should == [5.0, 4.0, 5, 4]
@@ -60,9 +60,9 @@ describe :array_binary_union, shared: true do
end
it "does not return subclass instances for Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].send(@method, []).should be_an_instance_of(Array)
- ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
- [].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].send(@method, []).should.instance_of?(Array)
+ ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should.instance_of?(Array)
+ [].send(@method, ArraySpecs::MyArray[1, 2, 3]).should.instance_of?(Array)
end
it "does not call to_ary on array subclasses" do
diff --git a/spec/ruby/core/array/shared/unshift.rb b/spec/ruby/core/array/shared/unshift.rb
deleted file mode 100644
index fc82e19e2a..0000000000
--- a/spec/ruby/core/array/shared/unshift.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-describe :array_unshift, shared: true do
- it "prepends object to the original array" do
- a = [1, 2, 3]
- a.send(@method, "a").should equal(a)
- a.should == ['a', 1, 2, 3]
- a.send(@method).should equal(a)
- a.should == ['a', 1, 2, 3]
- a.send(@method, 5, 4, 3)
- a.should == [5, 4, 3, 'a', 1, 2, 3]
-
- # shift all but one element
- a = [1, 2]
- a.shift
- a.send(@method, 3, 4)
- a.should == [3, 4, 2]
-
- # now shift all elements
- a.shift
- a.shift
- a.shift
- a.send(@method, 3, 4)
- a.should == [3, 4]
- end
-
- it "quietly ignores unshifting nothing" do
- [].send(@method).should == []
- end
-
- it "properly handles recursive arrays" do
- empty = ArraySpecs.empty_recursive_array
- empty.send(@method, :new).should == [:new, empty]
-
- array = ArraySpecs.recursive_array
- array.send(@method, :new)
- array[0..5].should == [:new, 1, 'two', 3.0, array, array]
- end
-
- it "raises a FrozenError on a frozen array when the array is modified" do
- -> { ArraySpecs.frozen_array.send(@method, 1) }.should raise_error(FrozenError)
- end
-
- # see [ruby-core:23666]
- it "raises a FrozenError on a frozen array when the array would not be modified" do
- -> { ArraySpecs.frozen_array.send(@method) }.should raise_error(FrozenError)
- end
-end
diff --git a/spec/ruby/core/array/shift_spec.rb b/spec/ruby/core/array/shift_spec.rb
index b998e45d28..09dfa79c45 100644
--- a/spec/ruby/core/array/shift_spec.rb
+++ b/spec/ruby/core/array/shift_spec.rb
@@ -31,10 +31,10 @@ describe "Array#shift" do
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.shift }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.shift }.should.raise(FrozenError)
end
it "raises a FrozenError on an empty frozen array" do
- -> { ArraySpecs.empty_frozen_array.shift }.should raise_error(FrozenError)
+ -> { ArraySpecs.empty_frozen_array.shift }.should.raise(FrozenError)
end
describe "passed a number n as an argument" do
@@ -72,7 +72,7 @@ describe "Array#shift" do
popped2.should == []
a.should == []
- popped1.should_not equal(popped2)
+ popped1.should_not.equal?(popped2)
end
it "returns whole elements if n exceeds size of the array" do
@@ -83,14 +83,14 @@ describe "Array#shift" do
it "does not return self even when it returns whole elements" do
a = [1, 2, 3, 4, 5]
- a.shift(5).should_not equal(a)
+ a.shift(5).should_not.equal?(a)
a = [1, 2, 3, 4, 5]
- a.shift(6).should_not equal(a)
+ a.shift(6).should_not.equal?(a)
end
it "raises an ArgumentError if n is negative" do
- ->{ [1, 2, 3].shift(-1) }.should raise_error(ArgumentError)
+ ->{ [1, 2, 3].shift(-1) }.should.raise(ArgumentError)
end
it "tries to convert n to an Integer using #to_int" do
@@ -105,32 +105,16 @@ describe "Array#shift" do
end
it "raises a TypeError when the passed n cannot be coerced to Integer" do
- ->{ [1, 2].shift("cat") }.should raise_error(TypeError)
- ->{ [1, 2].shift(nil) }.should raise_error(TypeError)
+ ->{ [1, 2].shift("cat") }.should.raise(TypeError)
+ ->{ [1, 2].shift(nil) }.should.raise(TypeError)
end
it "raises an ArgumentError if more arguments are passed" do
- ->{ [1, 2].shift(1, 2) }.should raise_error(ArgumentError)
+ ->{ [1, 2].shift(1, 2) }.should.raise(ArgumentError)
end
it "does not return subclass instances with Array subclass" do
- ArraySpecs::MyArray[1, 2, 3].shift(2).should be_an_instance_of(Array)
- end
-
- ruby_version_is ''...'2.7' do
- it "returns an untainted array even if the array is tainted" do
- ary = [1, 2].taint
- ary.shift(2).tainted?.should be_false
- ary.shift(0).tainted?.should be_false
- end
-
- it "keeps taint status" do
- a = [1, 2].taint
- a.shift(2)
- a.tainted?.should be_true
- a.shift(2)
- a.tainted?.should be_true
- end
+ ArraySpecs::MyArray[1, 2, 3].shift(2).should.instance_of?(Array)
end
end
end
diff --git a/spec/ruby/core/array/shuffle_spec.rb b/spec/ruby/core/array/shuffle_spec.rb
index b255147c75..9bc9df73ac 100644
--- a/spec/ruby/core/array/shuffle_spec.rb
+++ b/spec/ruby/core/array/shuffle_spec.rb
@@ -10,7 +10,7 @@ describe "Array#shuffle" do
s.sort.should == a
different ||= (a != s)
end
- different.should be_true # Will fail once in a blue moon (4!^10)
+ different.should == true # Will fail once in a blue moon (4!^10)
end
it "is not destructive" do
@@ -22,7 +22,7 @@ describe "Array#shuffle" do
end
it "does not return subclass instances with Array subclass" do
- ArraySpecs::MyArray[1, 2, 3].shuffle.should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].shuffle.should.instance_of?(Array)
end
it "calls #rand on the Object passed by the :random key in the arguments Hash" do
@@ -31,20 +31,24 @@ describe "Array#shuffle" do
result = [1, 2].shuffle(random: obj)
result.size.should == 2
- result.should include(1, 2)
+ result.sort.should == [1, 2]
end
it "raises a NoMethodError if an object passed for the RNG does not define #rand" do
obj = BasicObject.new
- -> { [1, 2].shuffle(random: obj) }.should raise_error(NoMethodError)
+ -> { [1, 2].shuffle(random: obj) }.should.raise(NoMethodError)
end
it "accepts a Float for the value returned by #rand" do
random = mock("array_shuffle_random")
random.should_receive(:rand).at_least(1).times.and_return(0.3)
- [1, 2].shuffle(random: random).should be_an_instance_of(Array)
+ [1, 2].shuffle(random: random).should.instance_of?(Array)
+ end
+
+ it "accepts a Random class for the value for random: argument" do
+ [1, 2].shuffle(random: Random).should.instance_of?(Array)
end
it "calls #to_int on the Object returned by #rand" do
@@ -53,7 +57,7 @@ describe "Array#shuffle" do
random = mock("array_shuffle_random")
random.should_receive(:rand).at_least(1).times.and_return(value)
- [1, 2].shuffle(random: random).should be_an_instance_of(Array)
+ [1, 2].shuffle(random: random).should.instance_of?(Array)
end
it "raises a RangeError if the value is less than zero" do
@@ -62,16 +66,25 @@ describe "Array#shuffle" do
random = mock("array_shuffle_random")
random.should_receive(:rand).and_return(value)
- -> { [1, 2].shuffle(random: random) }.should raise_error(RangeError)
+ -> { [1, 2].shuffle(random: random) }.should.raise(RangeError)
+ end
+
+ it "raises a RangeError if the value is equal to the Array size" do
+ value = mock("array_shuffle_random_value")
+ value.should_receive(:to_int).at_least(1).times.and_return(2)
+ random = mock("array_shuffle_random")
+ random.should_receive(:rand).at_least(1).times.and_return(value)
+
+ -> { [1, 2].shuffle(random: random) }.should.raise(RangeError)
end
- it "raises a RangeError if the value is equal to one" do
+ it "raises a RangeError if the value is greater than the Array size" do
value = mock("array_shuffle_random_value")
- value.should_receive(:to_int).at_least(1).times.and_return(1)
+ value.should_receive(:to_int).at_least(1).times.and_return(3)
random = mock("array_shuffle_random")
random.should_receive(:rand).at_least(1).times.and_return(value)
- -> { [1, 2].shuffle(random: random) }.should raise_error(RangeError)
+ -> { [1, 2].shuffle(random: random) }.should.raise(RangeError)
end
end
@@ -85,12 +98,22 @@ describe "Array#shuffle!" do
a.sort.should == [1, 2, 3, 4]
different ||= (a != [1, 2, 3, 4])
end
- different.should be_true # Will fail once in a blue moon (4!^10)
- a.should equal(original)
+ different.should == true # Will fail once in a blue moon (4!^10)
+ a.should.equal?(original)
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.shuffle! }.should raise_error(FrozenError)
- -> { ArraySpecs.empty_frozen_array.shuffle! }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.shuffle! }.should.raise(FrozenError)
+ -> { ArraySpecs.empty_frozen_array.shuffle! }.should.raise(FrozenError)
+ end
+
+ it "matches CRuby with random:" do
+ %w[a b c].shuffle(random: Random.new(1)).should == %w[a c b]
+ (0..10).to_a.shuffle(random: Random.new(10)).should == [2, 6, 8, 5, 7, 10, 3, 1, 0, 4, 9]
+ end
+
+ it "matches CRuby with srand" do
+ srand(123)
+ %w[a b c d e f g h i j k].shuffle.should == %w[a e f h i j d b g k c]
end
end
diff --git a/spec/ruby/core/array/size_spec.rb b/spec/ruby/core/array/size_spec.rb
index d68f956a83..83e8969012 100644
--- a/spec/ruby/core/array/size_spec.rb
+++ b/spec/ruby/core/array/size_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/length'
describe "Array#size" do
- it_behaves_like :array_length, :size
+ it "is an alias of Array#length" do
+ Array.instance_method(:size).should == Array.instance_method(:length)
+ end
end
diff --git a/spec/ruby/core/array/slice_spec.rb b/spec/ruby/core/array/slice_spec.rb
index 3829794938..230d1dc5d1 100644
--- a/spec/ruby/core/array/slice_spec.rb
+++ b/spec/ruby/core/array/slice_spec.rb
@@ -1,6 +1,5 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/slice'
describe "Array#slice!" do
it "removes and return the element at index" do
@@ -116,8 +115,8 @@ describe "Array#slice!" do
a.slice!(from .. to).should == [2, 3, 4]
a.should == [1, 5]
- -> { a.slice!("a" .. "b") }.should raise_error(TypeError)
- -> { a.slice!(from .. "b") }.should raise_error(TypeError)
+ -> { a.slice!("a" .. "b") }.should.raise(TypeError)
+ -> { a.slice!(from .. "b") }.should.raise(TypeError)
end
it "returns last element for consecutive calls at zero index" do
@@ -151,22 +150,70 @@ describe "Array#slice!" do
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.slice!(0, 0) }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.slice!(0, 0) }.should.raise(FrozenError)
end
- ruby_version_is "2.6" do
- it "works with endless ranges" do
- a = [1, 2, 3]
- a.slice!(eval("(1..)")).should == [2, 3]
- a.should == [1]
+ it "works with endless ranges" do
+ a = [1, 2, 3]
+ a.slice!(eval("(1..)")).should == [2, 3]
+ a.should == [1]
+
+ a = [1, 2, 3]
+ a.slice!(eval("(2...)")).should == [3]
+ a.should == [1, 2]
+
+ a = [1, 2, 3]
+ a.slice!(eval("(-2..)")).should == [2, 3]
+ a.should == [1]
+
+ a = [1, 2, 3]
+ a.slice!(eval("(-1...)")).should == [3]
+ a.should == [1, 2]
+ end
+
+ it "works with beginless ranges" do
+ a = [0,1,2,3,4]
+ a.slice!((..3)).should == [0, 1, 2, 3]
+ a.should == [4]
+
+ a = [0,1,2,3,4]
+ a.slice!((...-2)).should == [0, 1, 2]
+ a.should == [3, 4]
+ end
+
+ describe "with a subclass of Array" do
+ before :each do
+ @array = ArraySpecs::MyArray[1, 2, 3, 4, 5]
+ end
+
+ it "returns a Array instance with [n, m]" do
+ @array.slice!(0, 2).should.instance_of?(Array)
+ end
+
+ it "returns a Array instance with [-n, m]" do
+ @array.slice!(-3, 2).should.instance_of?(Array)
+ end
- a = [1, 2, 3]
- a.slice!(eval("(2...)")).should == [3]
- a.should == [1, 2]
+ it "returns a Array instance with [n..m]" do
+ @array.slice!(1..3).should.instance_of?(Array)
+ end
+
+ it "returns a Array instance with [n...m]" do
+ @array.slice!(1...3).should.instance_of?(Array)
+ end
+
+ it "returns a Array instance with [-n..-m]" do
+ @array.slice!(-3..-1).should.instance_of?(Array)
+ end
+
+ it "returns a Array instance with [-n...-m]" do
+ @array.slice!(-3...-1).should.instance_of?(Array)
end
end
end
describe "Array#slice" do
- it_behaves_like :array_slice, :slice
+ it "is an alias of Array#[]" do
+ Array.instance_method(:slice).should == Array.instance_method(:[])
+ end
end
diff --git a/spec/ruby/core/array/sort_by_spec.rb b/spec/ruby/core/array/sort_by_spec.rb
index 7cea6ec6d3..132abb028a 100644
--- a/spec/ruby/core/array/sort_by_spec.rb
+++ b/spec/ruby/core/array/sort_by_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
require_relative '../enumerable/shared/enumeratorized'
describe "Array#sort_by!" do
@@ -10,25 +11,30 @@ describe "Array#sort_by!" do
end
it "returns an Enumerator if not given a block" do
- (1..10).to_a.sort_by!.should be_an_instance_of(Enumerator)
+ (1..10).to_a.sort_by!.should.instance_of?(Enumerator)
end
it "completes when supplied a block that always returns the same result" do
a = [2, 3, 5, 1, 4]
a.sort_by!{ 1 }
- a.should be_an_instance_of(Array)
+ a.should.instance_of?(Array)
a.sort_by!{ 0 }
- a.should be_an_instance_of(Array)
+ a.should.instance_of?(Array)
a.sort_by!{ -1 }
- a.should be_an_instance_of(Array)
+ a.should.instance_of?(Array)
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.sort_by! {}}.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.sort_by! {}}.should.raise(FrozenError)
end
it "raises a FrozenError on an empty frozen array" do
- -> { ArraySpecs.empty_frozen_array.sort_by! {}}.should raise_error(FrozenError)
+ -> { ArraySpecs.empty_frozen_array.sort_by! {}}.should.raise(FrozenError)
+ end
+
+ it "raises a FrozenError on a frozen array only during iteration if called without a block" do
+ enum = ArraySpecs.frozen_array.sort_by!
+ -> { enum.each {} }.should.raise(FrozenError)
end
it "returns the specified value when it would break in the given block" do
@@ -41,12 +47,39 @@ describe "Array#sort_by!" do
ary.sort_by!{|x,y| break if x==i; x<=>y}
ary
}
- partially_sorted.any?{|ary| ary != [1, 2, 3, 4, 5]}.should be_true
+ partially_sorted.any?{|ary| ary != [1, 2, 3, 4, 5]}.should == true
end
it "changes nothing when called on a single element array" do
[1].sort_by!(&:to_s).should == [1]
end
+ it "does not truncate the array is the block raises an exception" do
+ a = [1, 2, 3]
+ begin
+ a.sort_by! { raise StandardError, 'Oops' }
+ rescue
+ end
+
+ a.should == [1, 2, 3]
+ end
+
+ it "doesn't change array if error is raised" do
+ a = [4, 3, 2, 1]
+ begin
+ a.sort_by! do |e|
+ raise StandardError, 'Oops' if e == 1
+ e
+ end
+ rescue StandardError
+ end
+
+ a.should == [4, 3, 2, 1]
+ end
+
it_behaves_like :enumeratorized_with_origin_size, :sort_by!, [1,2,3]
end
+
+describe "Array#sort_by!" do
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :sort_by!
+end
diff --git a/spec/ruby/core/array/sort_spec.rb b/spec/ruby/core/array/sort_spec.rb
index 0c5ecdcbbf..27300c3385 100644
--- a/spec/ruby/core/array/sort_spec.rb
+++ b/spec/ruby/core/array/sort_spec.rb
@@ -42,7 +42,7 @@ describe "Array#sort" do
a = [1, 2, 3]
sorted = a.sort
sorted.should == a
- sorted.should_not equal(a)
+ sorted.should_not.equal?(a)
end
it "properly handles recursive arrays" do
@@ -68,7 +68,7 @@ describe "Array#sort" do
-> {
[o, 1].sort
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "may take a block which is used to determine the order of objects a and b described as -1, 0 or +1" do
@@ -78,28 +78,28 @@ describe "Array#sort" do
end
it "raises an error when a given block returns nil" do
- -> { [1, 2].sort {} }.should raise_error(ArgumentError)
+ -> { [1, 2].sort {} }.should.raise(ArgumentError)
end
it "does not call #<=> on contained objects when invoked with a block" do
a = Array.new(25)
(0...25).each {|i| a[i] = ArraySpecs::UFOSceptic.new }
- a.sort { -1 }.should be_an_instance_of(Array)
+ a.sort { -1 }.should.instance_of?(Array)
end
it "does not call #<=> on elements when invoked with a block even if Array is large (Rubinius #412)" do
a = Array.new(1500)
(0...1500).each {|i| a[i] = ArraySpecs::UFOSceptic.new }
- a.sort { -1 }.should be_an_instance_of(Array)
+ a.sort { -1 }.should.instance_of?(Array)
end
it "completes when supplied a block that always returns the same result" do
a = [2, 3, 5, 1, 4]
- a.sort { 1 }.should be_an_instance_of(Array)
- a.sort { 0 }.should be_an_instance_of(Array)
- a.sort { -1 }.should be_an_instance_of(Array)
+ a.sort { 1 }.should.instance_of?(Array)
+ a.sort { 0 }.should.instance_of?(Array)
+ a.sort { -1 }.should.instance_of?(Array)
end
it "does not freezes self during being sorted" do
@@ -111,10 +111,10 @@ describe "Array#sort" do
[1, 2, 3].sort{ break :a }.should == :a
end
- it "uses the sign of Bignum block results as the sort result" do
+ it "uses the sign of Integer block results as the sort result" do
a = [1, 2, 5, 10, 7, -4, 12]
begin
- class Bignum;
+ class Integer
alias old_spaceship <=>
def <=>(other)
raise
@@ -122,7 +122,7 @@ describe "Array#sort" do
end
a.sort {|n, m| (n - m) * (2 ** 200)}.should == [-4, 1, 2, 5, 7, 10, 12]
ensure
- class Bignum
+ class Integer
alias <=> old_spaceship
end
end
@@ -132,11 +132,11 @@ describe "Array#sort" do
a = [1, 2, 5, 10, 7, -4, 12]
a.sort { |n, m| n - m }.should == [-4, 1, 2, 5, 7, 10, 12]
a.sort { |n, m|
- ArraySpecs::ComparableWithFixnum.new(n-m)
+ ArraySpecs::ComparableWithInteger.new(n-m)
}.should == [-4, 1, 2, 5, 7, 10, 12]
-> {
a.sort { |n, m| (n - m).to_s }
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "sorts an array that has a value shifted off without a block" do
@@ -155,7 +155,7 @@ describe "Array#sort" do
it "raises an error if objects can't be compared" do
a=[ArraySpecs::Uncomparable.new, ArraySpecs::Uncomparable.new]
- -> {a.sort}.should raise_error(ArgumentError)
+ -> {a.sort}.should.raise(ArgumentError)
end
# From a strange Rubinius bug
@@ -166,7 +166,7 @@ describe "Array#sort" do
it "does not return subclass instance on Array subclasses" do
ary = ArraySpecs::MyArray[1, 2, 3]
- ary.sort.should be_an_instance_of(Array)
+ ary.sort.should.instance_of?(Array)
end
end
@@ -184,13 +184,13 @@ describe "Array#sort!" do
it "returns self if the order of elements changed" do
a = [6, 7, 2, 3, 7]
- a.sort!.should equal(a)
+ a.sort!.should.equal?(a)
a.should == [2, 3, 6, 7, 7]
end
it "returns self even if makes no modification" do
a = [1, 2, 3, 4, 5]
- a.sort!.should equal(a)
+ a.sort!.should.equal?(a)
a.should == [1, 2, 3, 4, 5]
end
@@ -216,25 +216,25 @@ describe "Array#sort!" do
a = Array.new(25)
(0...25).each {|i| a[i] = ArraySpecs::UFOSceptic.new }
- a.sort! { -1 }.should be_an_instance_of(Array)
+ a.sort! { -1 }.should.instance_of?(Array)
end
it "does not call #<=> on elements when invoked with a block even if Array is large (Rubinius #412)" do
a = Array.new(1500)
(0...1500).each {|i| a[i] = ArraySpecs::UFOSceptic.new }
- a.sort! { -1 }.should be_an_instance_of(Array)
+ a.sort! { -1 }.should.instance_of?(Array)
end
it "completes when supplied a block that always returns the same result" do
a = [2, 3, 5, 1, 4]
- a.sort!{ 1 }.should be_an_instance_of(Array)
- a.sort!{ 0 }.should be_an_instance_of(Array)
- a.sort!{ -1 }.should be_an_instance_of(Array)
+ a.sort!{ 1 }.should.instance_of?(Array)
+ a.sort!{ 0 }.should.instance_of?(Array)
+ a.sort!{ -1 }.should.instance_of?(Array)
end
it "raises a FrozenError on a frozen array" do
- -> { ArraySpecs.frozen_array.sort! }.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.sort! }.should.raise(FrozenError)
end
it "returns the specified value when it would break in the given block" do
@@ -247,6 +247,6 @@ describe "Array#sort!" do
ary.sort!{|x,y| break if x==i; x<=>y}
ary
}
- partially_sorted.any?{|ary| ary != [1, 2, 3, 4, 5]}.should be_true
+ partially_sorted.any?{|ary| ary != [1, 2, 3, 4, 5]}.should == true
end
end
diff --git a/spec/ruby/core/array/sum_spec.rb b/spec/ruby/core/array/sum_spec.rb
index 39c769d328..cd4ba4c2d8 100644
--- a/spec/ruby/core/array/sum_spec.rb
+++ b/spec/ruby/core/array/sum_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#sum" do
it "returns the sum of elements" do
@@ -9,6 +10,39 @@ describe "Array#sum" do
[1, 2, 3].sum { |i| i * 10 }.should == 60
end
+ it "doesn't apply the block init" do
+ [1, 2, 3].sum(1) { |i| i * 10 }.should == 61
+ end
+
+ # https://bugs.ruby-lang.org/issues/12217
+ # https://github.com/ruby/ruby/blob/master/doc/ChangeLog/ChangeLog-2.4.0#L6208-L6214
+ it "uses Kahan's compensated summation algorithm for precise sum of float numbers" do
+ floats = [2.7800000000000002, 5.0, 2.5, 4.44, 3.89, 3.89, 4.44, 7.78, 5.0, 2.7800000000000002, 5.0, 2.5]
+ naive_sum = floats.reduce { |sum, e| sum + e }
+ naive_sum.should == 50.00000000000001
+ floats.sum.should == 50.0
+ end
+
+ it "handles infinite values and NaN" do
+ [1.0, Float::INFINITY].sum.should == Float::INFINITY
+ [1.0, -Float::INFINITY].sum.should == -Float::INFINITY
+ [1.0, Float::NAN].sum.should.nan?
+
+ [Float::INFINITY, 1.0].sum.should == Float::INFINITY
+ [-Float::INFINITY, 1.0].sum.should == -Float::INFINITY
+ [Float::NAN, 1.0].sum.should.nan?
+
+ [Float::NAN, Float::INFINITY].sum.should.nan?
+ [Float::INFINITY, Float::NAN].sum.should.nan?
+
+ [Float::INFINITY, -Float::INFINITY].sum.should.nan?
+ [-Float::INFINITY, Float::INFINITY].sum.should.nan?
+
+ [Float::INFINITY, Float::INFINITY].sum.should == Float::INFINITY
+ [-Float::INFINITY, -Float::INFINITY].sum.should == -Float::INFINITY
+ [Float::NAN, Float::NAN].sum.should.nan?
+ end
+
it "returns init value if array is empty" do
[].sum(-1).should == -1
end
@@ -26,11 +60,11 @@ describe "Array#sum" do
end
it 'raises TypeError if any element are not numeric' do
- -> { ["a"].sum }.should raise_error(TypeError)
+ -> { ["a"].sum }.should.raise(TypeError)
end
it 'raises TypeError if any element cannot be added to init value' do
- -> { [1].sum([]) }.should raise_error(TypeError)
+ -> { [1].sum([]) }.should.raise(TypeError)
end
it "calls + to sum the elements" do
@@ -39,4 +73,16 @@ describe "Array#sum" do
a.should_receive(:+).with(b).and_return(42)
[b].sum(a).should == 42
end
+
+ it "calls + on the init value" do
+ a = mock("a")
+ b = mock("b")
+ a.should_receive(:+).with(42).and_return(b)
+ [42].sum(a).should == b
+ end
+end
+
+describe "Array#sum" do
+ @value_to_return = -> _ { 1 }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :sum
end
diff --git a/spec/ruby/core/array/take_spec.rb b/spec/ruby/core/array/take_spec.rb
index 0de99b0a7e..837c734b77 100644
--- a/spec/ruby/core/array/take_spec.rb
+++ b/spec/ruby/core/array/take_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Array#take" do
it "returns the first specified number of elements" do
@@ -22,6 +23,10 @@ describe "Array#take" do
end
it "raises an ArgumentError when the argument is negative" do
- ->{ [1].take(-3) }.should raise_error(ArgumentError)
+ ->{ [1].take(-3) }.should.raise(ArgumentError)
+ end
+
+ it 'returns a Array instance for Array subclasses' do
+ ArraySpecs::MyArray[1, 2, 3, 4, 5].take(1).should.instance_of?(Array)
end
end
diff --git a/spec/ruby/core/array/take_while_spec.rb b/spec/ruby/core/array/take_while_spec.rb
index f159e6f251..7811edab9e 100644
--- a/spec/ruby/core/array/take_while_spec.rb
+++ b/spec/ruby/core/array/take_while_spec.rb
@@ -1,4 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#take_while" do
it "returns all elements until the block returns false" do
@@ -12,4 +14,13 @@ describe "Array#take_while" do
it "returns all elements until the block returns false" do
[1, 2, false, 4].take_while{ |element| element }.should == [1, 2]
end
+
+ it 'returns a Array instance for Array subclasses' do
+ ArraySpecs::MyArray[1, 2, 3, 4, 5].take_while { |n| n < 4 }.should.instance_of?(Array)
+ end
+end
+
+describe "Array#take_while" do
+ @value_to_return = -> _ { true }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :take_while
end
diff --git a/spec/ruby/core/array/to_a_spec.rb b/spec/ruby/core/array/to_a_spec.rb
index 49d0a4782e..078de1638a 100644
--- a/spec/ruby/core/array/to_a_spec.rb
+++ b/spec/ruby/core/array/to_a_spec.rb
@@ -5,12 +5,12 @@ describe "Array#to_a" do
it "returns self" do
a = [1, 2, 3]
a.to_a.should == [1, 2, 3]
- a.should equal(a.to_a)
+ a.should.equal?(a.to_a)
end
it "does not return subclass instance on Array subclasses" do
e = ArraySpecs::MyArray.new(1, 2)
- e.to_a.should be_an_instance_of(Array)
+ e.to_a.should.instance_of?(Array)
e.to_a.should == [1, 2]
end
diff --git a/spec/ruby/core/array/to_ary_spec.rb b/spec/ruby/core/array/to_ary_spec.rb
index 314699b709..dc5193158d 100644
--- a/spec/ruby/core/array/to_ary_spec.rb
+++ b/spec/ruby/core/array/to_ary_spec.rb
@@ -4,9 +4,9 @@ require_relative 'fixtures/classes'
describe "Array#to_ary" do
it "returns self" do
a = [1, 2, 3]
- a.should equal(a.to_ary)
+ a.should.equal?(a.to_ary)
a = ArraySpecs::MyArray[1, 2, 3]
- a.should equal(a.to_ary)
+ a.should.equal?(a.to_ary)
end
it "properly handles recursive arrays" do
diff --git a/spec/ruby/core/array/to_h_spec.rb b/spec/ruby/core/array/to_h_spec.rb
index 46a79ba58b..1d626763c2 100644
--- a/spec/ruby/core/array/to_h_spec.rb
+++ b/spec/ruby/core/array/to_h_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#to_h" do
it "converts empty array to empty hash" do
@@ -24,58 +25,67 @@ describe "Array#to_h" do
end
it "raises TypeError if an element is not an array" do
- -> { [:x].to_h }.should raise_error(TypeError)
+ -> { [:x].to_h }.should.raise(TypeError)
end
it "raises ArgumentError if an element is not a [key, value] pair" do
- -> { [[:x]].to_h }.should raise_error(ArgumentError)
+ -> { [[:x]].to_h }.should.raise(ArgumentError)
end
it "does not accept arguments" do
- -> { [].to_h(:a, :b) }.should raise_error(ArgumentError)
+ -> { [].to_h(:a, :b) }.should.raise(ArgumentError)
end
it "produces a hash that returns nil for a missing element" do
- [[:a, 1], [:b, 2]].to_h[:c].should be_nil
+ [[:a, 1], [:b, 2]].to_h[:c].should == nil
end
- ruby_version_is "2.6" do
- context "with block" do
- it "converts [key, value] pairs returned by the block to a Hash" do
- [:a, :b].to_h { |k| [k, k.to_s] }.should == { a: 'a', b: 'b' }
- end
-
- it "raises ArgumentError if block returns longer or shorter array" do
- -> do
- [:a, :b].to_h { |k| [k, k.to_s, 1] }
- end.should raise_error(ArgumentError, /wrong array length at 0/)
-
- -> do
- [:a, :b].to_h { |k| [k] }
- end.should raise_error(ArgumentError, /wrong array length at 0/)
- end
-
- it "raises TypeError if block returns something other than Array" do
- -> do
- [:a, :b].to_h { |k| "not-array" }
- end.should raise_error(TypeError, /wrong element type String at 0/)
- end
-
- it "coerces returned pair to Array with #to_ary" do
- x = mock('x')
- x.stub!(:to_ary).and_return([:b, 'b'])
-
- [:a].to_h { |k| x }.should == { :b => 'b' }
- end
-
- it "does not coerce returned pair to Array with #to_a" do
- x = mock('x')
- x.stub!(:to_a).and_return([:b, 'b'])
-
- -> do
- [:a].to_h { |k| x }
- end.should raise_error(TypeError, /wrong element type MockObject at 0/)
- end
+ context "with block" do
+ it "converts [key, value] pairs returned by the block to a Hash" do
+ [:a, :b].to_h { |k| [k, k.to_s] }.should == { a: 'a', b: 'b' }
+ end
+
+ it "passes to a block each element as a single argument" do
+ ScratchPad.record []
+ [[:a, 1], [:b, 2]].to_h { |*args| ScratchPad << args; [args[0], args[1]] }
+ ScratchPad.recorded.sort.should == [[[:a, 1]], [[:b, 2]]]
+ end
+
+ it "raises ArgumentError if block returns longer or shorter array" do
+ -> do
+ [:a, :b].to_h { |k| [k, k.to_s, 1] }
+ end.should.raise(ArgumentError, /wrong array length at 0/)
+
+ -> do
+ [:a, :b].to_h { |k| [k] }
+ end.should.raise(ArgumentError, /wrong array length at 0/)
+ end
+
+ it "raises TypeError if block returns something other than Array" do
+ -> do
+ [:a, :b].to_h { |k| "not-array" }
+ end.should.raise(TypeError, /wrong element type String at 0/)
+ end
+
+ it "coerces returned pair to Array with #to_ary" do
+ x = mock('x')
+ x.stub!(:to_ary).and_return([:b, 'b'])
+
+ [:a].to_h { |k| x }.should == { :b => 'b' }
+ end
+
+ it "does not coerce returned pair to Array with #to_a" do
+ x = mock('x')
+ x.stub!(:to_a).and_return([:b, 'b'])
+
+ -> do
+ [:a].to_h { |k| x }
+ end.should.raise(TypeError, /wrong element type MockObject at 0/)
end
end
end
+
+describe "Array#to_h" do
+ @value_to_return = -> e { [e, e.to_s] }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :to_h
+end
diff --git a/spec/ruby/core/array/to_s_spec.rb b/spec/ruby/core/array/to_s_spec.rb
index e8476702ec..483e14f902 100644
--- a/spec/ruby/core/array/to_s_spec.rb
+++ b/spec/ruby/core/array/to_s_spec.rb
@@ -1,8 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/join'
-require_relative 'shared/inspect'
describe "Array#to_s" do
- it_behaves_like :array_inspect, :to_s
+ it "is an alias of Array#inspect" do
+ Array.instance_method(:to_s).should == Array.instance_method(:inspect)
+ end
end
diff --git a/spec/ruby/core/array/transpose_spec.rb b/spec/ruby/core/array/transpose_spec.rb
index b39077f4c9..d45e9c351c 100644
--- a/spec/ruby/core/array/transpose_spec.rb
+++ b/spec/ruby/core/array/transpose_spec.rb
@@ -32,7 +32,7 @@ describe "Array#transpose" do
end
it "raises a TypeError if the passed Argument does not respond to #to_ary" do
- -> { [Object.new, [:a, :b]].transpose }.should raise_error(TypeError)
+ -> { [Object.new, [:a, :b]].transpose }.should.raise(TypeError)
end
it "does not call to_ary on array subclass elements" do
@@ -41,13 +41,13 @@ describe "Array#transpose" do
end
it "raises an IndexError if the arrays are not of the same length" do
- -> { [[1, 2], [:a]].transpose }.should raise_error(IndexError)
+ -> { [[1, 2], [:a]].transpose }.should.raise(IndexError)
end
it "does not return subclass instance on Array subclasses" do
result = ArraySpecs::MyArray[ArraySpecs::MyArray[1, 2, 3], ArraySpecs::MyArray[4, 5, 6]].transpose
- result.should be_an_instance_of(Array)
- result[0].should be_an_instance_of(Array)
- result[1].should be_an_instance_of(Array)
+ result.should.instance_of?(Array)
+ result[0].should.instance_of?(Array)
+ result[1].should.instance_of?(Array)
end
end
diff --git a/spec/ruby/core/array/try_convert_spec.rb b/spec/ruby/core/array/try_convert_spec.rb
index 47b4722d80..3eaa0f4b7c 100644
--- a/spec/ruby/core/array/try_convert_spec.rb
+++ b/spec/ruby/core/array/try_convert_spec.rb
@@ -4,47 +4,47 @@ require_relative 'fixtures/classes'
describe "Array.try_convert" do
it "returns the argument if it's an Array" do
x = Array.new
- Array.try_convert(x).should equal(x)
+ Array.try_convert(x).should.equal?(x)
end
it "returns the argument if it's a kind of Array" do
x = ArraySpecs::MyArray[]
- Array.try_convert(x).should equal(x)
+ Array.try_convert(x).should.equal?(x)
end
it "returns nil when the argument does not respond to #to_ary" do
- Array.try_convert(Object.new).should be_nil
+ Array.try_convert(Object.new).should == nil
end
it "sends #to_ary to the argument and returns the result if it's nil" do
obj = mock("to_ary")
obj.should_receive(:to_ary).and_return(nil)
- Array.try_convert(obj).should be_nil
+ Array.try_convert(obj).should == nil
end
it "sends #to_ary to the argument and returns the result if it's an Array" do
x = Array.new
obj = mock("to_ary")
obj.should_receive(:to_ary).and_return(x)
- Array.try_convert(obj).should equal(x)
+ Array.try_convert(obj).should.equal?(x)
end
it "sends #to_ary to the argument and returns the result if it's a kind of Array" do
x = ArraySpecs::MyArray[]
obj = mock("to_ary")
obj.should_receive(:to_ary).and_return(x)
- Array.try_convert(obj).should equal(x)
+ Array.try_convert(obj).should.equal?(x)
end
it "sends #to_ary to the argument and raises TypeError if it's not a kind of Array" do
obj = mock("to_ary")
obj.should_receive(:to_ary).and_return(Object.new)
- -> { Array.try_convert obj }.should raise_error(TypeError)
+ -> { Array.try_convert obj }.should raise_consistent_error(TypeError, "can't convert MockObject into Array (MockObject#to_ary gives Object)")
end
it "does not rescue exceptions raised by #to_ary" do
obj = mock("to_ary")
obj.should_receive(:to_ary).and_raise(RuntimeError)
- -> { Array.try_convert obj }.should raise_error(RuntimeError)
+ -> { Array.try_convert obj }.should.raise(RuntimeError)
end
end
diff --git a/spec/ruby/core/array/union_spec.rb b/spec/ruby/core/array/union_spec.rb
index 1dca47696d..110894e83d 100644
--- a/spec/ruby/core/array/union_spec.rb
+++ b/spec/ruby/core/array/union_spec.rb
@@ -6,22 +6,20 @@ describe "Array#|" do
it_behaves_like :array_binary_union, :|
end
-ruby_version_is "2.6" do
- describe "Array#union" do
- it_behaves_like :array_binary_union, :union
+describe "Array#union" do
+ it_behaves_like :array_binary_union, :union
- it "returns unique elements when given no argument" do
- x = [1, 2, 3, 2]
- x.union.should == [1, 2, 3]
- end
+ it "returns unique elements when given no argument" do
+ x = [1, 2, 3, 2]
+ x.union.should == [1, 2, 3]
+ end
- it "does not return subclass instances for Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].union.should be_an_instance_of(Array)
- end
+ it "does not return subclass instances for Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].union.should.instance_of?(Array)
+ end
- it "accepts multiple arguments" do
- x = [1, 2, 3]
- x.union(x, x, x, x, [3, 4], x).should == [1, 2, 3, 4]
- end
+ it "accepts multiple arguments" do
+ x = [1, 2, 3]
+ x.union(x, x, x, x, [3, 4], x).should == [1, 2, 3, 4]
end
end
diff --git a/spec/ruby/core/array/uniq_spec.rb b/spec/ruby/core/array/uniq_spec.rb
index fd604987c1..0289bee7c2 100644
--- a/spec/ruby/core/array/uniq_spec.rb
+++ b/spec/ruby/core/array/uniq_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative 'shared/iterable_and_tolerating_size_increasing'
describe "Array#uniq" do
it "returns an array with no duplicates" do
@@ -39,76 +40,32 @@ describe "Array#uniq" do
[x, y].uniq.should == [x, y]
end
- ruby_version_is '2.7' do
- it "compares elements with matching hash codes with #eql?" do
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
+ it "compares elements with matching hash codes with #eql?" do
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
- def obj.eql?(o)
- false
- end
-
- obj
+ def obj.eql?(o)
+ false
end
- a.uniq.should == a
-
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
-
- def obj.eql?(o)
- true
- end
-
- obj
- end
-
- a.uniq.size.should == 1
+ obj
end
- end
- ruby_version_is ''...'2.7' do
- it "compares elements with matching hash codes with #eql?" do
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
+ a.uniq.should == a
- def obj.eql?(o)
- # It's undefined whether the impl does a[0].eql?(a[1]) or
- # a[1].eql?(a[0]) so we taint both.
- taint
- o.taint
- false
- end
-
- obj
- end
-
- a.uniq.should == a
- a[0].should.tainted?
- a[1].should.tainted?
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
-
- def obj.eql?(o)
- # It's undefined whether the impl does a[0].eql?(a[1]) or
- # a[1].eql?(a[0]) so we taint both.
- taint
- o.taint
- true
- end
-
- obj
+ def obj.eql?(o)
+ true
end
- a.uniq.size.should == 1
- a[0].should.tainted?
- a[1].should.tainted?
+ obj
end
+
+ a.uniq.size.should == 1
end
it "compares elements based on the value returned from the block" do
@@ -128,8 +85,8 @@ describe "Array#uniq" do
[false, nil, 42].uniq { :bar }.should == [false]
end
- it "returns subclass instance on Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].uniq.should be_an_instance_of(ArraySpecs::MyArray)
+ it "returns Array instance on Array subclasses" do
+ ArraySpecs::MyArray[1, 2, 3].uniq.should.instance_of?(Array)
end
it "properly handles an identical item even when its #eql? isn't reflexive" do
@@ -167,6 +124,11 @@ describe "Array#uniq" do
end
end
+describe "Array#uniq" do
+ @value_to_return = -> e { e }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :uniq
+end
+
describe "Array#uniq!" do
it "modifies the array in place" do
a = [ "a", "a", "b", "b", "c" ]
@@ -176,7 +138,7 @@ describe "Array#uniq!" do
it "returns self" do
a = [ "a", "a", "b", "b", "c" ]
- a.should equal(a.uniq!)
+ a.should.equal?(a.uniq!)
end
it "properly handles recursive arrays" do
@@ -223,17 +185,17 @@ describe "Array#uniq!" do
it "raises a FrozenError on a frozen array when the array is modified" do
dup_ary = [1, 1, 2]
dup_ary.freeze
- -> { dup_ary.uniq! }.should raise_error(FrozenError)
+ -> { dup_ary.uniq! }.should.raise(FrozenError)
end
# see [ruby-core:23666]
it "raises a FrozenError on a frozen array when the array would not be modified" do
- -> { ArraySpecs.frozen_array.uniq!}.should raise_error(FrozenError)
- -> { ArraySpecs.empty_frozen_array.uniq!}.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.uniq!}.should.raise(FrozenError)
+ -> { ArraySpecs.empty_frozen_array.uniq!}.should.raise(FrozenError)
end
it "doesn't yield to the block on a frozen array" do
- -> { ArraySpecs.frozen_array.uniq!{ raise RangeError, "shouldn't yield"}}.should raise_error(FrozenError)
+ -> { ArraySpecs.frozen_array.uniq!{ raise RangeError, "shouldn't yield"}}.should.raise(FrozenError)
end
it "compares elements based on the value returned from the block" do
@@ -250,4 +212,32 @@ describe "Array#uniq!" do
a.uniq!
a.should == [x]
end
+
+ it "does not truncate the array is the block raises an exception" do
+ a = [1, 2, 3]
+ begin
+ a.send(@method) { raise StandardError, 'Oops' }
+ rescue
+ end
+
+ a.should == [1, 2, 3]
+ end
+
+ it "doesn't change array if error is raised" do
+ a = [1, 1, 2, 2, 3, 3, 4, 4]
+ begin
+ a.send(@method) do |e|
+ raise StandardError, 'Oops' if e == 3
+ e
+ end
+ rescue StandardError
+ end
+
+ a.should == [1, 1, 2, 2, 3, 3, 4, 4]
+ end
+end
+
+describe "Array#uniq!" do
+ @value_to_return = -> e { e }
+ it_behaves_like :array_iterable_and_tolerating_size_increasing, :uniq!
end
diff --git a/spec/ruby/core/array/unshift_spec.rb b/spec/ruby/core/array/unshift_spec.rb
index b8b675e5f8..c190db4d02 100644
--- a/spec/ruby/core/array/unshift_spec.rb
+++ b/spec/ruby/core/array/unshift_spec.rb
@@ -1,7 +1,67 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/unshift'
describe "Array#unshift" do
- it_behaves_like :array_unshift, :unshift
+ it "prepends object to the original array" do
+ a = [1, 2, 3]
+ a.unshift("a").should.equal?(a)
+ a.should == ['a', 1, 2, 3]
+ a.unshift.should.equal?(a)
+ a.should == ['a', 1, 2, 3]
+ a.unshift(5, 4, 3)
+ a.should == [5, 4, 3, 'a', 1, 2, 3]
+
+ # shift all but one element
+ a = [1, 2]
+ a.shift
+ a.unshift(3, 4)
+ a.should == [3, 4, 2]
+
+ # now shift all elements
+ a.shift
+ a.shift
+ a.shift
+ a.unshift(3, 4)
+ a.should == [3, 4]
+ end
+
+ it "returns self" do
+ a = [1, 2, 3]
+ a.unshift("a").should.equal?(a)
+ end
+
+ it "quietly ignores unshifting nothing" do
+ [].unshift.should == []
+ end
+
+ it "properly handles recursive arrays" do
+ empty = ArraySpecs.empty_recursive_array
+ empty.unshift(:new).should == [:new, empty]
+
+ array = ArraySpecs.recursive_array
+ array.unshift(:new)
+ array[0..5].should == [:new, 1, 'two', 3.0, array, array]
+ end
+
+ it "raises a FrozenError on a frozen array when the array is modified" do
+ -> { ArraySpecs.frozen_array.unshift(1) }.should.raise(FrozenError)
+ end
+
+ # see [ruby-core:23666]
+ it "raises a FrozenError on a frozen array when the array would not be modified" do
+ -> { ArraySpecs.frozen_array.unshift }.should.raise(FrozenError)
+ end
+
+ # https://github.com/truffleruby/truffleruby/issues/2772
+ it "doesn't rely on Array#[]= so it can be overridden" do
+ subclass = Class.new(Array) do
+ def []=(*)
+ raise "[]= is called"
+ end
+ end
+
+ array = subclass.new
+ array.unshift(1)
+ array.should == [1]
+ end
end
diff --git a/spec/ruby/core/array/values_at_spec.rb b/spec/ruby/core/array/values_at_spec.rb
index f6801335f8..e11e7e4451 100644
--- a/spec/ruby/core/array/values_at_spec.rb
+++ b/spec/ruby/core/array/values_at_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+# Should be synchronized with core/struct/values_at_spec.rb
describe "Array#values_at" do
it "returns an array of elements at the indexes when passed indexes" do
[1, 2, 3, 4, 5].values_at().should == []
@@ -58,13 +59,16 @@ describe "Array#values_at" do
end
it "does not return subclass instance on Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].values_at(0, 1..2, 1).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].values_at(0, 1..2, 1).should.instance_of?(Array)
end
- ruby_version_is "2.6" do
- it "works when given endless ranges" do
- [1, 2, 3, 4].values_at(eval("(1..)")).should == [2, 3, 4]
- [1, 2, 3, 4].values_at(eval("(3...)")).should == [4]
- end
+ it "works when given endless ranges" do
+ [1, 2, 3, 4].values_at(eval("(1..)")).should == [2, 3, 4]
+ [1, 2, 3, 4].values_at(eval("(3...)")).should == [4]
+ end
+
+ it "works when given beginless ranges" do
+ [1, 2, 3, 4].values_at((..2)).should == [1, 2, 3]
+ [1, 2, 3, 4].values_at((...2)).should == [1, 2]
end
end
diff --git a/spec/ruby/core/array/zip_spec.rb b/spec/ruby/core/array/zip_spec.rb
index af4013debe..3ccdf143d6 100644
--- a/spec/ruby/core/array/zip_spec.rb
+++ b/spec/ruby/core/array/zip_spec.rb
@@ -60,6 +60,12 @@ describe "Array#zip" do
end
it "does not return subclass instance on Array subclasses" do
- ArraySpecs::MyArray[1, 2, 3].zip(["a", "b"]).should be_an_instance_of(Array)
+ ArraySpecs::MyArray[1, 2, 3].zip(["a", "b"]).should.instance_of?(Array)
+ end
+
+ it "raises TypeError when some argument isn't Array and doesn't respond to #to_ary and #to_enum" do
+ -> { [1, 2, 3].zip(Object.new) }.should.raise(TypeError, "wrong argument type Object (must respond to :each)")
+ -> { [1, 2, 3].zip(1) }.should.raise(TypeError, "wrong argument type Integer (must respond to :each)")
+ -> { [1, 2, 3].zip(true) }.should.raise(TypeError, "wrong argument type TrueClass (must respond to :each)")
end
end
diff --git a/spec/ruby/core/basicobject/__send___spec.rb b/spec/ruby/core/basicobject/__send___spec.rb
index 005b1d0d90..2f814e448c 100644
--- a/spec/ruby/core/basicobject/__send___spec.rb
+++ b/spec/ruby/core/basicobject/__send___spec.rb
@@ -3,7 +3,7 @@ require_relative '../../shared/basicobject/send'
describe "BasicObject#__send__" do
it "is a public instance method" do
- BasicObject.should have_public_instance_method(:__send__)
+ BasicObject.public_instance_methods(false).should.include?(:__send__)
end
it_behaves_like :basicobject_send, :__send__
diff --git a/spec/ruby/core/basicobject/basicobject_spec.rb b/spec/ruby/core/basicobject/basicobject_spec.rb
index 27a322e72c..af28de0687 100644
--- a/spec/ruby/core/basicobject/basicobject_spec.rb
+++ b/spec/ruby/core/basicobject/basicobject_spec.rb
@@ -8,23 +8,23 @@ describe "BasicObject" do
end
it "raises NameError when referencing built-in constants" do
- -> { class BasicObjectSpecs::BOSubclass; Kernel; end }.should raise_error(NameError)
+ -> { class BasicObjectSpecs::BOSubclass; Kernel; end }.should.raise(NameError)
end
it "does not define built-in constants (according to const_defined?)" do
- BasicObject.const_defined?(:Kernel).should be_false
+ BasicObject.const_defined?(:Kernel).should == false
end
it "does not define built-in constants (according to defined?)" do
- BasicObjectSpecs::BOSubclass.kernel_defined?.should be_nil
+ BasicObjectSpecs::BOSubclass.kernel_defined?.should == nil
end
it "is included in Object's list of constants" do
- Object.constants(false).should include(:BasicObject)
+ Object.constants(false).should.include?(:BasicObject)
end
it "includes itself in its list of constants" do
- BasicObject.constants(false).should include(:BasicObject)
+ BasicObject.constants(false).should.include?(:BasicObject)
end
end
@@ -34,11 +34,11 @@ describe "BasicObject metaclass" do
end
it "is an instance of Class" do
- @meta.should be_an_instance_of(Class)
+ @meta.should.instance_of?(Class)
end
it "has Class as superclass" do
- @meta.superclass.should equal(Class)
+ @meta.superclass.should.equal?(Class)
end
it "contains methods for the BasicObject class" do
@@ -57,11 +57,11 @@ describe "BasicObject instance metaclass" do
end
it "is an instance of Class" do
- @meta.should be_an_instance_of(Class)
+ @meta.should.instance_of?(Class)
end
it "has BasicObject as superclass" do
- @meta.superclass.should equal(BasicObject)
+ @meta.superclass.should.equal?(BasicObject)
end
it "contains methods defined for the BasicObject instance" do
@@ -85,7 +85,7 @@ describe "BasicObject subclass" do
describe "BasicObject references" do
it "can refer to BasicObject from within itself" do
- -> { BasicObject::BasicObject }.should_not raise_error
+ -> { BasicObject::BasicObject }.should_not.raise
end
end
end
diff --git a/spec/ruby/core/basicobject/equal_spec.rb b/spec/ruby/core/basicobject/equal_spec.rb
index 3c1ad56d4a..c0f41dc0c0 100644
--- a/spec/ruby/core/basicobject/equal_spec.rb
+++ b/spec/ruby/core/basicobject/equal_spec.rb
@@ -3,7 +3,7 @@ require_relative '../../shared/kernel/equal'
describe "BasicObject#equal?" do
it "is a public instance method" do
- BasicObject.should have_public_instance_method(:equal?)
+ BasicObject.public_instance_methods(false).should.include?(:equal?)
end
it_behaves_like :object_equal, :equal?
@@ -11,9 +11,11 @@ describe "BasicObject#equal?" do
it "is unaffected by overriding __id__" do
o1 = mock("object")
o2 = mock("object")
- def o1.__id__; 10; end
- def o2.__id__; 10; end
- o1.equal?(o2).should be_false
+ suppress_warning {
+ def o1.__id__; 10; end
+ def o2.__id__; 10; end
+ }
+ o1.equal?(o2).should == false
end
it "is unaffected by overriding object_id" do
@@ -21,7 +23,7 @@ describe "BasicObject#equal?" do
o1.stub!(:object_id).and_return(10)
o2 = mock("object")
o2.stub!(:object_id).and_return(10)
- o1.equal?(o2).should be_false
+ o1.equal?(o2).should == false
end
it "is unaffected by overriding ==" do
@@ -29,12 +31,12 @@ describe "BasicObject#equal?" do
o1 = mock("object")
o1.stub!(:==).and_return(true)
o2 = mock("object")
- o1.equal?(o2).should be_false
+ o1.equal?(o2).should == false
# same objects, overriding == to return false
o3 = mock("object")
o3.stub!(:==).and_return(false)
- o3.equal?(o3).should be_true
+ o3.equal?(o3).should == true
end
it "is unaffected by overriding eql?" do
@@ -42,11 +44,11 @@ describe "BasicObject#equal?" do
o1 = mock("object")
o1.stub!(:eql?).and_return(true)
o2 = mock("object")
- o1.equal?(o2).should be_false
+ o1.equal?(o2).should == false
# same objects, overriding eql? to return false
o3 = mock("object")
o3.stub!(:eql?).and_return(false)
- o3.equal?(o3).should be_true
+ o3.equal?(o3).should == true
end
end
diff --git a/spec/ruby/core/basicobject/equal_value_spec.rb b/spec/ruby/core/basicobject/equal_value_spec.rb
index 6c825513c1..eb951a8305 100644
--- a/spec/ruby/core/basicobject/equal_value_spec.rb
+++ b/spec/ruby/core/basicobject/equal_value_spec.rb
@@ -3,7 +3,7 @@ require_relative '../../shared/kernel/equal'
describe "BasicObject#==" do
it "is a public instance method" do
- BasicObject.should have_public_instance_method(:==)
+ BasicObject.public_instance_methods(false).should.include?(:==)
end
it_behaves_like :object_equal, :==
diff --git a/spec/ruby/core/basicobject/fixtures/classes.rb b/spec/ruby/core/basicobject/fixtures/classes.rb
index d1785afe31..ed5a2dda17 100644
--- a/spec/ruby/core/basicobject/fixtures/classes.rb
+++ b/spec/ruby/core/basicobject/fixtures/classes.rb
@@ -15,8 +15,231 @@ module BasicObjectSpecs
include InstExec
end
- module InstEvalCVar
- instance_eval { @@count = 2 }
+ module InstEval
+ module CVar
+ module Get
+ class ReceiverScope
+ @@cvar = :value_defined_in_receiver_scope
+ end
+
+ class BlockDefinitionScope
+ @@cvar = :value_defined_in_block_definition_scope
+
+ def block
+ -> * { @@cvar }
+ end
+ end
+
+ class CallerScope
+ @@cvar = :value_defined_in_caller_scope
+
+ def get_class_variable_with_string(obj)
+ obj.instance_eval("@@cvar")
+ end
+
+ def get_class_variable_with_block(obj, block)
+ obj.instance_eval(&block)
+ end
+ end
+
+ class CallerWithoutCVarScope
+ def get_class_variable_with_string(obj)
+ obj.instance_eval("@@cvar")
+ end
+ end
+
+ ReceiverWithCVarDefinedInSingletonClass = Class.new.new.tap do |obj|
+ obj.singleton_class.class_variable_set(:@@cvar, :value_defined_in_receiver_singleton_class)
+ end
+ end
+
+ module Set
+ class ReceiverScope
+ end
+
+ class BlockDefinitionScope
+ def self.get_class_variable
+ @@cvar
+ end
+
+ def block_to_assign(value)
+ -> * { @@cvar = value }
+ end
+ end
+
+ class CallerScope
+ def self.get_class_variable
+ @@cvar
+ end
+
+ def set_class_variable_with_string(obj, value)
+ obj.instance_eval("@@cvar=#{value.inspect}")
+ end
+
+ def set_class_variable_with_block(obj, block)
+ obj.instance_eval(&block)
+ end
+ end
+ end
+ end
+ end
+
+ module InstEval
+ module Constants
+ module ConstantInReceiverSingletonClass
+ module ReceiverScope
+ FOO = :ReceiverScope
+
+ class ReceiverParent
+ FOO = :ReceiverParent
+ end
+
+ class Receiver < ReceiverParent
+ FOO = :Receiver
+
+ def initialize
+ self.singleton_class.const_set(:FOO, :singleton_class)
+ end
+ end
+ end
+
+ module CallerScope
+ FOO = :CallerScope
+
+ class CallerParent
+ FOO = :CallerParent
+ end
+
+ class Caller < CallerParent
+ FOO = :Caller
+
+ def get_constant_with_string(receiver)
+ receiver.instance_eval("FOO")
+ end
+ end
+ end
+ end
+
+ module ConstantInReceiverClass
+ module ReceiverScope
+ FOO = :ReceiverScope
+
+ class ReceiverParent
+ FOO = :ReceiverParent
+ end
+
+ class Receiver < ReceiverParent
+ FOO = :Receiver
+ end
+ end
+
+ module CallerScope
+ FOO = :CallerScope
+
+ class CallerParent
+ FOO = :CallerParent
+ end
+
+ class Caller < CallerParent
+ FOO = :Caller
+
+ def get_constant_with_string(receiver)
+ receiver.instance_eval("FOO")
+ end
+ end
+ end
+ end
+
+ module ConstantInCallerClass
+ module ReceiverScope
+ FOO = :ReceiverScope
+
+ class ReceiverParent
+ FOO = :ReceiverParent
+ end
+
+ class Receiver < ReceiverParent
+ # FOO is not declared in a receiver class
+ end
+ end
+
+ module CallerScope
+ FOO = :CallerScope
+
+ class CallerParent
+ FOO = :CallerParent
+ end
+
+ class Caller < CallerParent
+ FOO = :Caller
+
+ def get_constant_with_string(receiver)
+ receiver.instance_eval("FOO")
+ end
+ end
+ end
+ end
+
+ module ConstantInCallerOuterScopes
+ module ReceiverScope
+ FOO = :ReceiverScope
+
+ class ReceiverParent
+ FOO = :ReceiverParent
+ end
+
+ class Receiver < ReceiverParent
+ # FOO is not declared in a receiver class
+ end
+ end
+
+ module CallerScope
+ FOO = :CallerScope
+
+ class CallerParent
+ FOO = :CallerParent
+ end
+
+ class Caller < CallerParent
+ # FOO is not declared in a caller class
+
+ def get_constant_with_string(receiver)
+ receiver.instance_eval("FOO")
+ end
+ end
+ end
+ end
+
+ module ConstantInReceiverParentClass
+ module ReceiverScope
+ FOO = :ReceiverScope
+
+ class ReceiverParent
+ FOO = :ReceiverParent
+ end
+
+ class Receiver < ReceiverParent
+ # FOO is not declared in a receiver class
+ end
+ end
+
+ module CallerScope
+ # FOO is not declared in a caller outer scopes
+
+ class CallerParent
+ FOO = :CallerParent
+ end
+
+ class Caller < CallerParent
+ # FOO is not declared in a caller class
+
+ def get_constant_with_string(receiver)
+ receiver.instance_eval("FOO")
+ end
+ end
+ end
+ end
+ end
end
class InstEvalConst
@@ -26,7 +249,6 @@ module BasicObjectSpecs
module InstEvalOuter
module Inner
obj = InstEvalConst.new
- X_BY_STR = obj.instance_eval("INST_EVAL_CONST_X") rescue nil
X_BY_BLOCK = obj.instance_eval { INST_EVAL_CONST_X } rescue nil
end
end
diff --git a/spec/ruby/core/basicobject/initialize_spec.rb b/spec/ruby/core/basicobject/initialize_spec.rb
index b7ce73ffd5..09e86676b6 100644
--- a/spec/ruby/core/basicobject/initialize_spec.rb
+++ b/spec/ruby/core/basicobject/initialize_spec.rb
@@ -2,12 +2,12 @@ require_relative '../../spec_helper'
describe "BasicObject#initialize" do
it "is a private instance method" do
- BasicObject.should have_private_instance_method(:initialize)
+ BasicObject.private_instance_methods(false).should.include?(:initialize)
end
it "does not accept arguments" do
-> {
BasicObject.new("This", "makes it easier", "to call super", "from other constructors")
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/basicobject/instance_eval_spec.rb b/spec/ruby/core/basicobject/instance_eval_spec.rb
index d3dd05b745..124d179b5a 100644
--- a/spec/ruby/core/basicobject/instance_eval_spec.rb
+++ b/spec/ruby/core/basicobject/instance_eval_spec.rb
@@ -7,25 +7,31 @@ describe "BasicObject#instance_eval" do
end
it "is a public instance method" do
- BasicObject.should have_public_instance_method(:instance_eval)
+ BasicObject.public_instance_methods(false).should.include?(:instance_eval)
end
it "sets self to the receiver in the context of the passed block" do
a = BasicObject.new
- a.instance_eval { self }.equal?(a).should be_true
+ a.instance_eval { self }.equal?(a).should == true
end
it "evaluates strings" do
a = BasicObject.new
- a.instance_eval('self').equal?(a).should be_true
+ a.instance_eval('self').equal?(a).should == true
end
- it "expects a block with no arguments" do
- -> { "hola".instance_eval }.should raise_error(ArgumentError)
+ it "raises an ArgumentError when no arguments and no block are given" do
+ -> { "hola".instance_eval }.should.raise(ArgumentError, "wrong number of arguments (given 0, expected 1..3)")
end
- it "takes no arguments with a block" do
- -> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError)
+ it "raises an ArgumentError when a block and normal arguments are given" do
+ -> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should.raise(ArgumentError, "wrong number of arguments (given 2, expected 0)")
+ end
+
+ it "raises an ArgumentError when more than 3 arguments are given" do
+ -> {
+ "hola".instance_eval("1 + 1", "some file", 0, "bogus")
+ }.should.raise(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
it "yields the object to the block" do
@@ -45,7 +51,7 @@ describe "BasicObject#instance_eval" do
end
end
f.foo.should == 1
- -> { Object.new.foo }.should raise_error(NoMethodError)
+ -> { Object.new.foo }.should.raise(NoMethodError)
end
it "preserves self in the original block when passed a block argument" do
@@ -65,9 +71,9 @@ describe "BasicObject#instance_eval" do
it "binds self to the receiver" do
s = "hola"
- (s == s.instance_eval { self }).should be_true
+ (s == s.instance_eval { self }).should == true
o = mock('o')
- (o == o.instance_eval("self")).should be_true
+ (o == o.instance_eval("self")).should == true
end
it "executes in the context of the receiver" do
@@ -78,11 +84,28 @@ describe "BasicObject#instance_eval" do
end
+ it "uses the caller location as default location" do
+ f = Object.new
+ f.instance_eval("[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+
it "has access to receiver's instance variables" do
BasicObjectSpecs::IVars.new.instance_eval { @secret }.should == 99
BasicObjectSpecs::IVars.new.instance_eval("@secret").should == 99
end
+ it "raises TypeError for frozen objects when tries to set receiver's instance variables" do
+ -> { nil.instance_eval { @foo = 42 } }.should.raise(FrozenError, "can't modify frozen NilClass: nil")
+ -> { true.instance_eval { @foo = 42 } }.should.raise(FrozenError, "can't modify frozen TrueClass: true")
+ -> { false.instance_eval { @foo = 42 } }.should.raise(FrozenError, "can't modify frozen FalseClass: false")
+ -> { 1.instance_eval { @foo = 42 } }.should.raise(FrozenError, "can't modify frozen Integer: 1")
+ -> { :symbol.instance_eval { @foo = 42 } }.should.raise(FrozenError, "can't modify frozen Symbol: :symbol")
+
+ obj = Object.new
+ obj.freeze
+ -> { obj.instance_eval { @foo = 42 } }.should.raise(FrozenError)
+ end
+
it "treats block-local variables as local to the block" do
prc = instance_eval <<-CODE
proc do |x, prc|
@@ -99,56 +122,118 @@ describe "BasicObject#instance_eval" do
prc.call(false, prc).should == 1
end
- it "sets class variables in the receiver" do
- BasicObjectSpecs::InstEvalCVar.class_variables.should include(:@@count)
- BasicObjectSpecs::InstEvalCVar.send(:class_variable_get, :@@count).should == 2
- end
-
it "makes the receiver metaclass the scoped class when used with a string" do
obj = Object.new
obj.instance_eval %{
class B; end
B
}
- obj.singleton_class.const_get(:B).should be_an_instance_of(Class)
+ obj.singleton_class.const_get(:B).should.instance_of?(Class)
end
- it "gets constants in the receiver if a string given" do
- BasicObjectSpecs::InstEvalOuter::Inner::X_BY_STR.should == 2
+ describe "constants lookup when a String given" do
+ it "looks in the receiver singleton class first" do
+ receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverSingletonClass::ReceiverScope::Receiver.new
+ caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverSingletonClass::CallerScope::Caller.new
+
+ caller.get_constant_with_string(receiver).should == :singleton_class
+ end
+
+ it "looks in the receiver class next" do
+ receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::ReceiverScope::Receiver.new
+ caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::CallerScope::Caller.new
+
+ caller.get_constant_with_string(receiver).should == :Receiver
+ end
+
+ it "looks in the caller class next" do
+ receiver = BasicObjectSpecs::InstEval::Constants::ConstantInCallerClass::ReceiverScope::Receiver.new
+ caller = BasicObjectSpecs::InstEval::Constants::ConstantInCallerClass::CallerScope::Caller.new
+
+ caller.get_constant_with_string(receiver).should == :Caller
+ end
+
+ it "looks in the caller outer scopes next" do
+ receiver = BasicObjectSpecs::InstEval::Constants::ConstantInCallerOuterScopes::ReceiverScope::Receiver.new
+ caller = BasicObjectSpecs::InstEval::Constants::ConstantInCallerOuterScopes::CallerScope::Caller.new
+
+ caller.get_constant_with_string(receiver).should == :CallerScope
+ end
+
+ it "looks in the receiver class hierarchy next" do
+ receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverParentClass::ReceiverScope::Receiver.new
+ caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverParentClass::CallerScope::Caller.new
+
+ caller.get_constant_with_string(receiver).should == :ReceiverParent
+ end
end
it "doesn't get constants in the receiver if a block given" do
- BasicObjectSpecs::InstEvalOuter::Inner::X_BY_BLOCK.should be_nil
+ BasicObjectSpecs::InstEvalOuter::Inner::X_BY_BLOCK.should == nil
end
it "raises a TypeError when defining methods on an immediate" do
-> do
1.instance_eval { def foo; end }
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
-> do
:foo.instance_eval { def foo; end }
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
-quarantine! do # Not clean, leaves cvars lying around to break other specs
- it "scopes class var accesses in the caller when called on a Fixnum" do
- # Fixnum can take instance vars
- Fixnum.class_eval "@@__tmp_instance_eval_spec = 1"
- (defined? @@__tmp_instance_eval_spec).should be_nil
+ describe "class variables lookup" do
+ it "gets class variables in the caller class when called with a String" do
+ receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new
+ caller = BasicObjectSpecs::InstEval::CVar::Get::CallerScope.new
+
+ caller.get_class_variable_with_string(receiver).should == :value_defined_in_caller_scope
+ end
+
+ it "gets class variables in the block definition scope when called with a block" do
+ receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new
+ caller = BasicObjectSpecs::InstEval::CVar::Get::CallerScope.new
+ block = BasicObjectSpecs::InstEval::CVar::Get::BlockDefinitionScope.new.block
+
+ caller.get_class_variable_with_block(receiver, block).should == :value_defined_in_block_definition_scope
+ end
+
+ it "sets class variables in the caller class when called with a String" do
+ receiver = BasicObjectSpecs::InstEval::CVar::Set::ReceiverScope.new
+ caller = BasicObjectSpecs::InstEval::CVar::Set::CallerScope.new
- @@__tmp_instance_eval_spec = 2
- 1.instance_eval { @@__tmp_instance_eval_spec }.should == 2
- Fixnum.__send__(:remove_class_variable, :@@__tmp_instance_eval_spec)
+ caller.set_class_variable_with_string(receiver, 1)
+ BasicObjectSpecs::InstEval::CVar::Set::CallerScope.get_class_variable.should == 1
+ end
+
+ it "sets class variables in the block definition scope when called with a block" do
+ receiver = BasicObjectSpecs::InstEval::CVar::Set::ReceiverScope.new
+ caller = BasicObjectSpecs::InstEval::CVar::Set::CallerScope.new
+ block = BasicObjectSpecs::InstEval::CVar::Set::BlockDefinitionScope.new.block_to_assign(1)
+
+ caller.set_class_variable_with_block(receiver, block)
+ BasicObjectSpecs::InstEval::CVar::Set::BlockDefinitionScope.get_class_variable.should == 1
+ end
+
+ it "does not have access to class variables in the receiver class when called with a String" do
+ receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new
+ caller = BasicObjectSpecs::InstEval::CVar::Get::CallerWithoutCVarScope.new
+ -> { caller.get_class_variable_with_string(receiver) }.should.raise(NameError, /uninitialized class variable @@cvar/)
+ end
+
+ it "does not have access to class variables in the receiver's singleton class when called with a String" do
+ receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverWithCVarDefinedInSingletonClass
+ caller = BasicObjectSpecs::InstEval::CVar::Get::CallerWithoutCVarScope.new
+ -> { caller.get_class_variable_with_string(receiver) }.should.raise(NameError, /uninitialized class variable @@cvar/)
+ end
end
-end
it "raises a TypeError when defining methods on numerics" do
-> do
(1.0).instance_eval { def foo; end }
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
-> do
(1 << 64).instance_eval { def foo; end }
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it "evaluates procs originating from methods" do
@@ -185,4 +270,58 @@ end
x.should == :value
end
+
+ it "converts string argument with #to_str method" do
+ source_code = Object.new
+ def source_code.to_str() "1" end
+
+ a = BasicObject.new
+ a.instance_eval(source_code).should == 1
+ end
+
+ it "raises ArgumentError if returned value is not String" do
+ source_code = Object.new
+ def source_code.to_str() :symbol end
+
+ a = BasicObject.new
+ -> { a.instance_eval(source_code) }.should raise_consistent_error(TypeError, /can't convert Object into String/)
+ end
+
+ it "converts filename argument with #to_str method" do
+ filename = Object.new
+ def filename.to_str() "file.rb" end
+
+ err = begin
+ Object.new.instance_eval("raise", filename)
+ rescue => e
+ e
+ end
+ err.backtrace.first.split(":")[0].should == "file.rb"
+ end
+
+ it "raises ArgumentError if returned value is not String" do
+ filename = Object.new
+ def filename.to_str() :symbol end
+
+ -> { Object.new.instance_eval("raise", filename) }.should raise_consistent_error(TypeError, /can't convert Object into String/)
+ end
+
+ it "converts lineno argument with #to_int method" do
+ lineno = Object.new
+ def lineno.to_int() 15 end
+
+ err = begin
+ Object.new.instance_eval("raise", "file.rb", lineno)
+ rescue => e
+ e
+ end
+ err.backtrace.first.split(":")[1].should == "15"
+ end
+
+ it "raises ArgumentError if returned value is not Integer" do
+ lineno = Object.new
+ def lineno.to_int() :symbol end
+
+ -> { Object.new.instance_eval("raise", "file.rb", lineno) }.should raise_consistent_error(TypeError, /can't convert Object into Integer/)
+ end
end
diff --git a/spec/ruby/core/basicobject/instance_exec_spec.rb b/spec/ruby/core/basicobject/instance_exec_spec.rb
index e25482d58a..cfce9a65ad 100644
--- a/spec/ruby/core/basicobject/instance_exec_spec.rb
+++ b/spec/ruby/core/basicobject/instance_exec_spec.rb
@@ -3,21 +3,21 @@ require_relative 'fixtures/classes'
describe "BasicObject#instance_exec" do
it "is a public instance method" do
- BasicObject.should have_public_instance_method(:instance_exec)
+ BasicObject.public_instance_methods(false).should.include?(:instance_exec)
end
it "sets self to the receiver in the context of the passed block" do
a = BasicObject.new
- a.instance_exec { self }.equal?(a).should be_true
+ a.instance_exec { self }.equal?(a).should == true
end
it "passes arguments to the block" do
a = BasicObject.new
- a.instance_exec(1) { |b| b }.should equal(1)
+ a.instance_exec(1) { |b| b }.should.equal?(1)
end
it "raises a LocalJumpError unless given a block" do
- -> { "hola".instance_exec }.should raise_error(LocalJumpError)
+ -> { "hola".instance_exec }.should.raise(LocalJumpError)
end
it "has an arity of -1" do
@@ -25,17 +25,23 @@ describe "BasicObject#instance_exec" do
end
it "accepts arguments with a block" do
- -> { "hola".instance_exec(4, 5) { |a,b| a + b } }.should_not raise_error
+ -> { "hola".instance_exec(4, 5) { |a,b| a + b } }.should_not.raise
end
it "doesn't pass self to the block as an argument" do
- "hola".instance_exec { |o| o }.should be_nil
+ "hola".instance_exec { |o| o }.should == nil
end
it "passes any arguments to the block" do
Object.new.instance_exec(1,2) {|one, two| one + two}.should == 3
end
+ describe "with optional argument" do
+ it "does not destructure a single array argument" do
+ Object.new.instance_exec([1, 2, 3]) { |a = 99| a }.should == [1, 2, 3]
+ end
+ end
+
it "only binds the exec to the receiver" do
f = Object.new
f.instance_exec do
@@ -44,7 +50,7 @@ describe "BasicObject#instance_exec" do
end
end
f.foo.should == 1
- -> { Object.new.foo }.should raise_error(NoMethodError)
+ -> { Object.new.foo }.should.raise(NoMethodError)
end
# TODO: This should probably be replaced with a "should behave like" that uses
@@ -71,37 +77,37 @@ describe "BasicObject#instance_exec" do
end
it "sets class variables in the receiver" do
- BasicObjectSpecs::InstExec.class_variables.should include(:@@count)
+ BasicObjectSpecs::InstExec.class_variables.should.include?(:@@count)
BasicObjectSpecs::InstExec.send(:class_variable_get, :@@count).should == 2
end
it "raises a TypeError when defining methods on an immediate" do
-> do
1.instance_exec { def foo; end }
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
-> do
:foo.instance_exec { def foo; end }
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
-quarantine! do # Not clean, leaves cvars lying around to break other specs
- it "scopes class var accesses in the caller when called on a Fixnum" do
- # Fixnum can take instance vars
- Fixnum.class_eval "@@__tmp_instance_exec_spec = 1"
- (defined? @@__tmp_instance_exec_spec).should == nil
+ quarantine! do # Not clean, leaves cvars lying around to break other specs
+ it "scopes class var accesses in the caller when called on an Integer" do
+ # Integer can take instance vars
+ Integer.class_eval "@@__tmp_instance_exec_spec = 1"
+ (defined? @@__tmp_instance_exec_spec).should == nil
- @@__tmp_instance_exec_spec = 2
- 1.instance_exec { @@__tmp_instance_exec_spec }.should == 2
- Fixnum.__send__(:remove_class_variable, :@@__tmp_instance_exec_spec)
+ @@__tmp_instance_exec_spec = 2
+ 1.instance_exec { @@__tmp_instance_exec_spec }.should == 2
+ Integer.__send__(:remove_class_variable, :@@__tmp_instance_exec_spec)
+ end
end
-end
it "raises a TypeError when defining methods on numerics" do
-> do
(1.0).instance_exec { def foo; end }
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
-> do
(1 << 64).instance_exec { def foo; end }
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/basicobject/method_missing_spec.rb b/spec/ruby/core/basicobject/method_missing_spec.rb
index b048780ee8..8785b41b21 100644
--- a/spec/ruby/core/basicobject/method_missing_spec.rb
+++ b/spec/ruby/core/basicobject/method_missing_spec.rb
@@ -1,8 +1,9 @@
+require_relative "../../spec_helper"
require_relative '../../shared/basicobject/method_missing'
describe "BasicObject#method_missing" do
it "is a private method" do
- BasicObject.should have_private_instance_method(:method_missing)
+ BasicObject.private_instance_methods(false).should.include?(:method_missing)
end
end
diff --git a/spec/ruby/core/basicobject/not_equal_spec.rb b/spec/ruby/core/basicobject/not_equal_spec.rb
index 9329128c43..29b14b0fff 100644
--- a/spec/ruby/core/basicobject/not_equal_spec.rb
+++ b/spec/ruby/core/basicobject/not_equal_spec.rb
@@ -2,24 +2,24 @@ require_relative '../../spec_helper'
describe "BasicObject#!=" do
it "is a public instance method" do
- BasicObject.should have_public_instance_method(:'!=')
+ BasicObject.public_instance_methods(false).should.include?(:'!=')
end
it "returns true if other is not identical to self" do
a = BasicObject.new
b = BasicObject.new
- (a != b).should be_true
+ (a != b).should == true
end
it "returns true if other is an Object" do
a = BasicObject.new
b = Object.new
- (a != b).should be_true
+ (a != b).should == true
end
it "returns false if other is identical to self" do
a = BasicObject.new
- (a != a).should be_false
+ (a != a).should == false
end
it "dispatches to #==" do
@@ -27,19 +27,19 @@ describe "BasicObject#!=" do
b = BasicObject.new
a.should_receive(:==).and_return(true)
- (a != b).should be_false
+ (a != b).should == false
end
describe "when invoked using Kernel#send" do
it "returns true if other is not identical to self" do
a = Object.new
b = Object.new
- a.send(:!=, b).should be_true
+ a.send(:!=, b).should == true
end
it "returns false if other is identical to self" do
a = Object.new
- a.send(:!=, a).should be_false
+ a.send(:!=, a).should == false
end
it "dispatches to #==" do
@@ -47,7 +47,7 @@ describe "BasicObject#!=" do
b = Object.new
a.should_receive(:==).and_return(true)
- a.send(:!=, b).should be_false
+ a.send(:!=, b).should == false
end
end
end
diff --git a/spec/ruby/core/basicobject/not_spec.rb b/spec/ruby/core/basicobject/not_spec.rb
index ca4cb6f5ff..a6f58ae6f5 100644
--- a/spec/ruby/core/basicobject/not_spec.rb
+++ b/spec/ruby/core/basicobject/not_spec.rb
@@ -2,10 +2,10 @@ require_relative '../../spec_helper'
describe "BasicObject#!" do
it "is a public instance method" do
- BasicObject.should have_public_instance_method(:'!')
+ BasicObject.public_instance_methods(false).should.include?(:'!')
end
it "returns false" do
- (!BasicObject.new).should be_false
+ (!BasicObject.new).should == false
end
end
diff --git a/spec/ruby/core/basicobject/singleton_method_added_spec.rb b/spec/ruby/core/basicobject/singleton_method_added_spec.rb
index 8d256e22db..f39b91768c 100644
--- a/spec/ruby/core/basicobject/singleton_method_added_spec.rb
+++ b/spec/ruby/core/basicobject/singleton_method_added_spec.rb
@@ -7,7 +7,7 @@ describe "BasicObject#singleton_method_added" do
end
it "is a private method" do
- BasicObject.should have_private_instance_method(:singleton_method_added)
+ BasicObject.private_instance_methods(false).should.include?(:singleton_method_added)
end
it "is called when a singleton method is defined on an object" do
@@ -35,7 +35,7 @@ describe "BasicObject#singleton_method_added" do
end
end
- ScratchPad.recorded.should_not include(:new_instance_method)
+ ScratchPad.recorded.should_not.include?(:new_instance_method)
end
it "is called when a singleton method is defined on a module" do
@@ -83,4 +83,65 @@ describe "BasicObject#singleton_method_added" do
end
ScratchPad.recorded.should == [:singleton_method_added, :new_method_with_define_method]
end
+
+ describe "when singleton_method_added is undefined" do
+ it "raises NoMethodError for a metaclass" do
+ class BasicObjectSpecs::NoSingletonMethodAdded
+ class << self
+ undef_method :singleton_method_added
+ end
+
+ -> {
+ def self.foo
+ end
+ }.should.raise(NoMethodError, /undefined method [`']singleton_method_added' for/)
+ end
+ ensure
+ BasicObjectSpecs.send(:remove_const, :NoSingletonMethodAdded)
+ end
+
+ it "raises NoMethodError for a singleton instance" do
+ object = Object.new
+ class << object
+ undef_method :singleton_method_added
+
+ -> {
+ def foo
+ end
+ }.should.raise(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/)
+
+ -> {
+ define_method(:bar) {}
+ }.should.raise(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/)
+ end
+
+ -> {
+ object.define_singleton_method(:baz) {}
+ }.should.raise(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/)
+ end
+
+ it "calls #method_missing" do
+ ScratchPad.record []
+ object = Object.new
+ class << object
+ def method_missing(*args)
+ ScratchPad << args
+ end
+
+ undef_method :singleton_method_added
+
+ def foo
+ end
+
+ define_method(:bar) {}
+ end
+ object.define_singleton_method(:baz) {}
+
+ ScratchPad.recorded.should == [
+ [:singleton_method_added, :foo],
+ [:singleton_method_added, :bar],
+ [:singleton_method_added, :baz],
+ ]
+ end
+ end
end
diff --git a/spec/ruby/core/basicobject/singleton_method_removed_spec.rb b/spec/ruby/core/basicobject/singleton_method_removed_spec.rb
index 46f9a6894c..1831e3c070 100644
--- a/spec/ruby/core/basicobject/singleton_method_removed_spec.rb
+++ b/spec/ruby/core/basicobject/singleton_method_removed_spec.rb
@@ -6,7 +6,7 @@ describe "BasicObject#singleton_method_removed" do
end
it "is a private method" do
- BasicObject.should have_private_instance_method(:singleton_method_removed)
+ BasicObject.private_instance_methods(false).should.include?(:singleton_method_removed)
end
it "is called when a method is removed on self" do
diff --git a/spec/ruby/core/basicobject/singleton_method_undefined_spec.rb b/spec/ruby/core/basicobject/singleton_method_undefined_spec.rb
index 7d6c7207db..cc47341878 100644
--- a/spec/ruby/core/basicobject/singleton_method_undefined_spec.rb
+++ b/spec/ruby/core/basicobject/singleton_method_undefined_spec.rb
@@ -6,7 +6,7 @@ describe "BasicObject#singleton_method_undefined" do
end
it "is a private method" do
- BasicObject.should have_private_instance_method(:singleton_method_undefined)
+ BasicObject.private_instance_methods(false).should.include?(:singleton_method_undefined)
end
it "is called when a method is removed on self" do
diff --git a/spec/ruby/core/binding/clone_spec.rb b/spec/ruby/core/binding/clone_spec.rb
index ebd40f5377..f1769ac6de 100644
--- a/spec/ruby/core/binding/clone_spec.rb
+++ b/spec/ruby/core/binding/clone_spec.rb
@@ -4,4 +4,10 @@ require_relative 'shared/clone'
describe "Binding#clone" do
it_behaves_like :binding_clone, :clone
+
+ it "preserves frozen status" do
+ bind = binding.freeze
+ bind.frozen?.should == true
+ bind.clone.frozen?.should == true
+ end
end
diff --git a/spec/ruby/core/binding/dup_spec.rb b/spec/ruby/core/binding/dup_spec.rb
index 43968213c8..f5f0c72d5d 100644
--- a/spec/ruby/core/binding/dup_spec.rb
+++ b/spec/ruby/core/binding/dup_spec.rb
@@ -4,4 +4,27 @@ require_relative 'shared/clone'
describe "Binding#dup" do
it_behaves_like :binding_clone, :dup
+
+ it "resets frozen status" do
+ bind = binding.freeze
+ bind.frozen?.should == true
+ bind.dup.frozen?.should == false
+ end
+
+ it "retains original binding variables but the list is distinct" do
+ bind1 = binding
+ eval "a = 1", bind1
+
+ bind2 = bind1.dup
+ eval("a = 2", bind2)
+ eval("a", bind1).should == 2
+ eval("a", bind2).should == 2
+
+ eval("b = 2", bind2)
+ -> { eval("b", bind1) }.should.raise(NameError)
+ eval("b", bind2).should == 2
+
+ bind1.local_variables.sort.should == [:a, :bind1, :bind2]
+ bind2.local_variables.sort.should == [:a, :b, :bind1, :bind2]
+ end
end
diff --git a/spec/ruby/core/binding/eval_spec.rb b/spec/ruby/core/binding/eval_spec.rb
index 224bce4c33..f1d8591320 100644
--- a/spec/ruby/core/binding/eval_spec.rb
+++ b/spec/ruby/core/binding/eval_spec.rb
@@ -7,7 +7,7 @@ describe "Binding#eval" do
bind = obj.get_binding
bind.eval("@secret += square(3)").should == 10
- bind.eval("a").should be_true
+ bind.eval("a").should == true
bind.eval("class Inside; end")
bind.eval("Inside.name").should == "BindingSpecs::Demo::Inside"
@@ -23,58 +23,29 @@ describe "Binding#eval" do
bind2.local_variables.should == []
end
- ruby_version_is ""..."2.8" do
- it "inherits __LINE__ from the enclosing scope" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- suppress_warning {bind.eval("__LINE__")}.should == obj.get_line_of_binding
- end
-
- it "preserves __LINE__ across multiple calls to eval" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- suppress_warning {bind.eval("__LINE__")}.should == obj.get_line_of_binding
- suppress_warning {bind.eval("__LINE__")}.should == obj.get_line_of_binding
- end
-
- it "increments __LINE__ on each line of a multiline eval" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- suppress_warning {bind.eval("#foo\n__LINE__")}.should == obj.get_line_of_binding + 1
- end
-
- it "inherits __LINE__ from the enclosing scope even if the Binding is created with #send" do
- obj = BindingSpecs::Demo.new(1)
- bind, line = obj.get_binding_with_send_and_line
- suppress_warning {bind.eval("__LINE__")}.should == line
- end
+ it "starts with line 1 if single argument is given" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__LINE__").should == 1
end
- ruby_version_is "2.8" do
- it "starts with line 1 if single argument is given" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("__LINE__").should == 1
- end
-
- it "preserves __LINE__ across multiple calls to eval" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("__LINE__").should == 1
- bind.eval("__LINE__").should == 1
- end
+ it "preserves __LINE__ across multiple calls to eval" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__LINE__").should == 1
+ bind.eval("__LINE__").should == 1
+ end
- it "increments __LINE__ on each line of a multiline eval" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("#foo\n__LINE__").should == 2
- end
+ it "increments __LINE__ on each line of a multiline eval" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("#foo\n__LINE__").should == 2
+ end
- it "starts with line 1 if the Binding is created with #send" do
- obj = BindingSpecs::Demo.new(1)
- bind, line = obj.get_binding_with_send_and_line
- bind.eval("__LINE__").should == 1
- end
+ it "starts with line 1 if the Binding is created with #send" do
+ obj = BindingSpecs::Demo.new(1)
+ bind, line = obj.get_binding_with_send_and_line
+ bind.eval("__LINE__").should == 1
end
it "starts with a __LINE__ of 1 if a filename is passed" do
@@ -89,20 +60,10 @@ describe "Binding#eval" do
bind.eval("#foo\n__LINE__", "(test)", 88).should == 89
end
- ruby_version_is ""..."2.8" do
- it "inherits __FILE__ from the enclosing scope" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- suppress_warning {bind.eval("__FILE__")}.should == obj.get_file_of_binding
- end
- end
-
- ruby_version_is "2.8" do
- it "Uses (eval) as __FILE__ if single argument given" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("__FILE__").should == '(eval)'
- end
+ it "uses 1 as __LINE__" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ suppress_warning { bind.eval("__LINE__") }.should == 1
end
it "uses the __FILE__ that is passed in" do
@@ -137,4 +98,8 @@ describe "Binding#eval" do
bind.eval("'bar'.foo").should == "foo"
end
+
+ it "uses the caller location as default filename" do
+ binding.eval("[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
end
diff --git a/spec/ruby/core/binding/fixtures/irb.rb b/spec/ruby/core/binding/fixtures/irb.rb
deleted file mode 100644
index 5f305f2d5d..0000000000
--- a/spec/ruby/core/binding/fixtures/irb.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-a = 10
-
-binding.irb
diff --git a/spec/ruby/core/binding/fixtures/irbrc b/spec/ruby/core/binding/fixtures/irbrc
deleted file mode 100644
index 2bc12af2f7..0000000000
--- a/spec/ruby/core/binding/fixtures/irbrc
+++ /dev/null
@@ -1 +0,0 @@
-# empty configuration
diff --git a/spec/ruby/core/binding/irb_spec.rb b/spec/ruby/core/binding/irb_spec.rb
deleted file mode 100644
index b3bc274f78..0000000000
--- a/spec/ruby/core/binding/irb_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require_relative '../../spec_helper'
-
-describe "Binding#irb" do
- it "creates an IRB session with the binding in scope" do
- irb_fixture = fixture __FILE__, "irb.rb"
- irbrc_fixture = fixture __FILE__, "irbrc"
-
- out = IO.popen([{"IRBRC"=>irbrc_fixture}, *ruby_exe, irb_fixture], "r+") do |pipe|
- pipe.puts "a ** 2"
- pipe.puts "exit"
- pipe.readlines.map(&:chomp)
- end
-
- out[-3..-1].should == ["a ** 2", "100", "exit"]
- end
-end
diff --git a/spec/ruby/core/binding/local_variable_get_spec.rb b/spec/ruby/core/binding/local_variable_get_spec.rb
index 005670becc..d97100deda 100644
--- a/spec/ruby/core/binding/local_variable_get_spec.rb
+++ b/spec/ruby/core/binding/local_variable_get_spec.rb
@@ -13,7 +13,7 @@ describe "Binding#local_variable_get" do
-> {
bind.local_variable_get(:no_such_variable)
- }.should raise_error(NameError)
+ }.should.raise(NameError)
end
it "reads variables added later to the binding" do
@@ -21,7 +21,7 @@ describe "Binding#local_variable_get" do
-> {
bind.local_variable_get(:a)
- }.should raise_error(NameError)
+ }.should.raise(NameError)
bind.local_variable_set(:a, 42)
@@ -45,12 +45,12 @@ describe "Binding#local_variable_get" do
it "raises a NameError on global access" do
bind = binding
- -> { bind.local_variable_get(:$0) }.should raise_error(NameError)
+ -> { bind.local_variable_get(:$0) }.should.raise(NameError)
end
it "raises a NameError on special variable access" do
bind = binding
- -> { bind.local_variable_get(:$~) }.should raise_error(NameError)
- -> { bind.local_variable_get(:$_) }.should raise_error(NameError)
+ -> { bind.local_variable_get(:$~) }.should.raise(NameError)
+ -> { bind.local_variable_get(:$_) }.should.raise(NameError)
end
end
diff --git a/spec/ruby/core/binding/local_variable_set_spec.rb b/spec/ruby/core/binding/local_variable_set_spec.rb
index 1456c6dda1..3e4f407fc3 100644
--- a/spec/ruby/core/binding/local_variable_set_spec.rb
+++ b/spec/ruby/core/binding/local_variable_set_spec.rb
@@ -38,7 +38,7 @@ describe "Binding#local_variable_set" do
bind = binding
bind.local_variable_set(:number, 10)
- -> { number }.should raise_error(NameError)
+ -> { number }.should.raise(NameError)
end
it 'overwrites an existing local variable defined before a Binding' do
@@ -59,13 +59,13 @@ describe "Binding#local_variable_set" do
it "raises a NameError on global access" do
bind = binding
- -> { bind.local_variable_set(:$0, "") }.should raise_error(NameError)
+ -> { bind.local_variable_set(:$0, "") }.should.raise(NameError)
end
it "raises a NameError on special variable access" do
bind = binding
- -> { bind.local_variable_set(:$~, "") }.should raise_error(NameError)
- -> { bind.local_variable_set(:$_, "") }.should raise_error(NameError)
+ -> { bind.local_variable_set(:$~, "") }.should.raise(NameError)
+ -> { bind.local_variable_set(:$_, "") }.should.raise(NameError)
end
end
diff --git a/spec/ruby/core/binding/local_variables_spec.rb b/spec/ruby/core/binding/local_variables_spec.rb
index 92c817b9a8..0f59681342 100644
--- a/spec/ruby/core/binding/local_variables_spec.rb
+++ b/spec/ruby/core/binding/local_variables_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "Binding#local_variables" do
it "returns an Array" do
- binding.local_variables.should be_kind_of(Array)
+ binding.local_variables.should.is_a?(Array)
end
it "includes local variables in the current scope" do
diff --git a/spec/ruby/core/binding/shared/clone.rb b/spec/ruby/core/binding/shared/clone.rb
index 0e934ac1b5..2d854fce96 100644
--- a/spec/ruby/core/binding/shared/clone.rb
+++ b/spec/ruby/core/binding/shared/clone.rb
@@ -31,4 +31,26 @@ describe :binding_clone, shared: true do
b2.local_variable_defined?(:x).should == false
end
end
+
+ ruby_version_is "3.4" do
+ it "copies instance variables" do
+ @b1.instance_variable_set(:@ivar, 1)
+ cl = @b1.send(@method)
+ cl.instance_variables.should == [:@ivar]
+ end
+
+ it "copies the finalizer" do
+ code = <<-'RUBY'
+ obj = binding
+
+ ObjectSpace.define_finalizer(obj, Proc.new { STDOUT.write "finalized\n" })
+
+ obj.clone
+
+ exit 0
+ RUBY
+
+ ruby_exe(code).lines.sort.should == ["finalized\n", "finalized\n"]
+ end
+ end
end
diff --git a/spec/ruby/core/binding/source_location_spec.rb b/spec/ruby/core/binding/source_location_spec.rb
index e562bc65c8..d1c8191ea8 100644
--- a/spec/ruby/core/binding/source_location_spec.rb
+++ b/spec/ruby/core/binding/source_location_spec.rb
@@ -1,11 +1,14 @@
require_relative '../../spec_helper'
require_relative 'fixtures/location'
-ruby_version_is "2.6" do
- describe "Binding#source_location" do
- it "returns an [file, line] pair" do
- b = BindingSpecs::LocationMethod::TEST_BINDING
- b.source_location.should == [BindingSpecs::LocationMethod::FILE_PATH, 4]
- end
+describe "Binding#source_location" do
+ it "returns an [file, line] pair" do
+ b = BindingSpecs::LocationMethod::TEST_BINDING
+ b.source_location.should == [BindingSpecs::LocationMethod::FILE_PATH, 4]
+ end
+
+ it "works for eval with a given line" do
+ b = eval('binding', nil, "foo", 100)
+ b.source_location.should == ["foo", 100]
end
end
diff --git a/spec/ruby/core/builtin_constants/builtin_constants_spec.rb b/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
index 21102bee9f..67b3339aa6 100644
--- a/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
+++ b/spec/ruby/core/builtin_constants/builtin_constants_spec.rb
@@ -2,62 +2,148 @@ require_relative '../../spec_helper'
describe "RUBY_VERSION" do
it "is a String" do
- RUBY_VERSION.should be_kind_of(String)
+ RUBY_VERSION.should.is_a?(String)
+ end
+
+ it "is frozen" do
+ RUBY_VERSION.should.frozen?
end
end
describe "RUBY_PATCHLEVEL" do
- it "is a Fixnum" do
- RUBY_PATCHLEVEL.should be_kind_of(Fixnum)
+ it "is an Integer" do
+ RUBY_PATCHLEVEL.should.is_a?(Integer)
end
end
describe "RUBY_COPYRIGHT" do
it "is a String" do
- RUBY_COPYRIGHT.should be_kind_of(String)
+ RUBY_COPYRIGHT.should.is_a?(String)
+ end
+
+ it "is frozen" do
+ RUBY_COPYRIGHT.should.frozen?
end
end
describe "RUBY_DESCRIPTION" do
it "is a String" do
- RUBY_DESCRIPTION.should be_kind_of(String)
+ RUBY_DESCRIPTION.should.is_a?(String)
+ end
+
+ it "is frozen" do
+ RUBY_DESCRIPTION.should.frozen?
end
end
describe "RUBY_ENGINE" do
it "is a String" do
- RUBY_ENGINE.should be_kind_of(String)
+ RUBY_ENGINE.should.is_a?(String)
+ end
+
+ it "is frozen" do
+ RUBY_ENGINE.should.frozen?
+ end
+end
+
+describe "RUBY_ENGINE_VERSION" do
+ it "is a String" do
+ RUBY_ENGINE_VERSION.should.is_a?(String)
+ end
+
+ it "is frozen" do
+ RUBY_ENGINE_VERSION.should.frozen?
end
end
describe "RUBY_PLATFORM" do
it "is a String" do
- RUBY_PLATFORM.should be_kind_of(String)
+ RUBY_PLATFORM.should.is_a?(String)
end
- platform_is :darwin do
- it 'ends with the build time kernel major version on darwin' do
- RUBY_PLATFORM.should =~ /-darwin\d+$/
- end
+ it "is frozen" do
+ RUBY_PLATFORM.should.frozen?
end
end
describe "RUBY_RELEASE_DATE" do
it "is a String" do
- RUBY_RELEASE_DATE.should be_kind_of(String)
+ RUBY_RELEASE_DATE.should.is_a?(String)
+ end
+
+ it "is frozen" do
+ RUBY_RELEASE_DATE.should.frozen?
end
end
describe "RUBY_REVISION" do
- ruby_version_is ""..."2.7" do
- it "is an Integer" do
- RUBY_REVISION.should be_kind_of(Fixnum)
+ it "is a String" do
+ RUBY_REVISION.should.is_a?(String)
+ end
+
+ it "is frozen" do
+ RUBY_REVISION.should.frozen?
+ end
+end
+
+ruby_version_is "4.0" do
+ describe "Ruby" do
+ it "is a Module" do
+ Ruby.should.instance_of?(Module)
+ end
+ end
+
+ describe "Ruby::VERSION" do
+ it "is equal to RUBY_VERSION" do
+ Ruby::VERSION.should.equal?(RUBY_VERSION)
+ end
+ end
+
+ describe "RUBY::PATCHLEVEL" do
+ it "is equal to RUBY_PATCHLEVEL" do
+ Ruby::PATCHLEVEL.should.equal?(RUBY_PATCHLEVEL)
+ end
+ end
+
+ describe "Ruby::COPYRIGHT" do
+ it "is equal to RUBY_COPYRIGHT" do
+ Ruby::COPYRIGHT.should.equal?(RUBY_COPYRIGHT)
+ end
+ end
+
+ describe "Ruby::DESCRIPTION" do
+ it "is equal to RUBY_DESCRIPTION" do
+ Ruby::DESCRIPTION.should.equal?(RUBY_DESCRIPTION)
+ end
+ end
+
+ describe "Ruby::ENGINE" do
+ it "is equal to RUBY_ENGINE" do
+ Ruby::ENGINE.should.equal?(RUBY_ENGINE)
+ end
+ end
+
+ describe "Ruby::ENGINE_VERSION" do
+ it "is equal to RUBY_ENGINE_VERSION" do
+ Ruby::ENGINE_VERSION.should.equal?(RUBY_ENGINE_VERSION)
+ end
+ end
+
+ describe "Ruby::PLATFORM" do
+ it "is equal to RUBY_PLATFORM" do
+ Ruby::PLATFORM.should.equal?(RUBY_PLATFORM)
+ end
+ end
+
+ describe "Ruby::RELEASE_DATE" do
+ it "is equal to RUBY_RELEASE_DATE" do
+ Ruby::RELEASE_DATE.should.equal?(RUBY_RELEASE_DATE)
end
end
- ruby_version_is "2.7" do
- it "is a String" do
- RUBY_REVISION.should be_kind_of(String)
+ describe "Ruby::REVISION" do
+ it "is equal to RUBY_REVISION" do
+ Ruby::REVISION.should.equal?(RUBY_REVISION)
end
end
end
diff --git a/spec/ruby/core/class/allocate_spec.rb b/spec/ruby/core/class/allocate_spec.rb
index b39622e06a..b8950a678e 100644
--- a/spec/ruby/core/class/allocate_spec.rb
+++ b/spec/ruby/core/class/allocate_spec.rb
@@ -3,7 +3,7 @@ require_relative '../../spec_helper'
describe "Class#allocate" do
it "returns an instance of self" do
klass = Class.new
- klass.allocate.should be_an_instance_of(klass)
+ klass.allocate.should.instance_of?(klass)
end
it "returns a fully-formed instance of Module" do
@@ -16,7 +16,7 @@ describe "Class#allocate" do
klass = Class.allocate
-> do
klass.new
- end.should raise_error(Exception)
+ end.should.raise(Exception)
end
it "does not call initialize on the new instance" do
@@ -36,6 +36,6 @@ describe "Class#allocate" do
it "raises TypeError for #superclass" do
-> do
Class.allocate.superclass
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/class/attached_object_spec.rb b/spec/ruby/core/class/attached_object_spec.rb
new file mode 100644
index 0000000000..b75e61ca07
--- /dev/null
+++ b/spec/ruby/core/class/attached_object_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+
+describe "Class#attached_object" do
+ it "returns the object that is attached to a singleton class" do
+ a = Class.new
+
+ a_obj = a.new
+ a_obj.singleton_class.attached_object.should == a_obj
+ end
+
+ it "returns the class object that is attached to a class's singleton class" do
+ a = Class.new
+ singleton_class = (class << a; self; end)
+
+ singleton_class.attached_object.should == a
+ end
+
+ it "raises TypeError if the class is not a singleton class" do
+ a = Class.new
+
+ -> { a.attached_object }.should.raise(TypeError, /is not a singleton class/)
+ end
+
+ it "raises TypeError for special singleton classes" do
+ -> { nil.singleton_class.attached_object }.should.raise(TypeError, /[`']NilClass' is not a singleton class/)
+ -> { true.singleton_class.attached_object }.should.raise(TypeError, /[`']TrueClass' is not a singleton class/)
+ -> { false.singleton_class.attached_object }.should.raise(TypeError, /[`']FalseClass' is not a singleton class/)
+ end
+end
diff --git a/spec/ruby/core/class/dup_spec.rb b/spec/ruby/core/class/dup_spec.rb
index 701fd72e19..17c0171ceb 100644
--- a/spec/ruby/core/class/dup_spec.rb
+++ b/spec/ruby/core/class/dup_spec.rb
@@ -53,12 +53,17 @@ describe "Class#dup" do
it "sets the name from the class to nil if not assigned to a constant" do
copy = CoreClassSpecs::Record.dup
- copy.name.should be_nil
+ copy.name.should == nil
end
it "stores the new name if assigned to a constant" do
CoreClassSpecs::RecordCopy = CoreClassSpecs::Record.dup
CoreClassSpecs::RecordCopy.name.should == "CoreClassSpecs::RecordCopy"
+ ensure
+ CoreClassSpecs.send(:remove_const, :RecordCopy)
end
+ it "raises TypeError if called on BasicObject" do
+ -> { BasicObject.dup }.should.raise(TypeError, "can't copy the root class")
+ end
end
diff --git a/spec/ruby/core/class/inherited_spec.rb b/spec/ruby/core/class/inherited_spec.rb
index 8ef8bb8c35..c9acc740a1 100644
--- a/spec/ruby/core/class/inherited_spec.rb
+++ b/spec/ruby/core/class/inherited_spec.rb
@@ -92,10 +92,27 @@ describe "Class.inherited" do
end
class << top; private :inherited; end
- -> { Class.new(top) }.should_not raise_error
+ -> { Class.new(top) }.should_not.raise
class << top; protected :inherited; end
- -> { Class.new(top) }.should_not raise_error
+ -> { Class.new(top) }.should_not.raise
end
+ it "if the subclass is assigned to a constant, it is all set" do
+ ScratchPad.record []
+
+ parent = Class.new do
+ def self.inherited(subclass)
+ ScratchPad << defined?(self::C)
+ ScratchPad << const_defined?(:C)
+ ScratchPad << constants
+ ScratchPad << const_get(:C)
+ ScratchPad << subclass.name.match?(/\A#<Class:0x\w+>::C\z/)
+ end
+ end
+
+ class parent::C < parent; end
+
+ ScratchPad.recorded.should == ["constant", true, [:C], parent::C, true]
+ end
end
diff --git a/spec/ruby/core/class/initialize_spec.rb b/spec/ruby/core/class/initialize_spec.rb
index 9678d7b373..ab8f0a157e 100644
--- a/spec/ruby/core/class/initialize_spec.rb
+++ b/spec/ruby/core/class/initialize_spec.rb
@@ -2,24 +2,24 @@ require_relative '../../spec_helper'
describe "Class#initialize" do
it "is private" do
- Class.should have_private_method(:initialize)
+ Class.private_methods(false).should.include?(:initialize)
end
it "raises a TypeError when called on already initialized classes" do
->{
- Fixnum.send :initialize
- }.should raise_error(TypeError)
+ Integer.send :initialize
+ }.should.raise(TypeError)
->{
Object.send :initialize
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
# See [redmine:2601]
it "raises a TypeError when called on BasicObject" do
->{
BasicObject.send :initialize
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
describe "when given the Class" do
@@ -28,7 +28,7 @@ describe "Class#initialize" do
end
it "raises a TypeError" do
- ->{@uninitialized.send(:initialize, Class)}.should raise_error(TypeError)
+ ->{@uninitialized.send(:initialize, Class)}.should.raise(TypeError)
end
end
end
diff --git a/spec/ruby/core/class/new_spec.rb b/spec/ruby/core/class/new_spec.rb
index 989d674558..111e31252e 100644
--- a/spec/ruby/core/class/new_spec.rb
+++ b/spec/ruby/core/class/new_spec.rb
@@ -7,7 +7,7 @@ describe "Class.new with a block given" do
klass = Class.new do
self_in_block = self
end
- self_in_block.should equal klass
+ self_in_block.should.equal? klass
end
it "uses the given block as the class' body" do
@@ -70,11 +70,11 @@ describe "Class.new" do
it "raises a TypeError if passed a metaclass" do
obj = mock("Class.new metaclass")
meta = obj.singleton_class
- -> { Class.new meta }.should raise_error(TypeError)
+ -> { Class.new meta }.should.raise(TypeError)
end
it "creates a class without a name" do
- Class.new.name.should be_nil
+ Class.new.name.should == nil
end
it "creates a class that can be given a name by assigning it to a constant" do
@@ -83,6 +83,8 @@ describe "Class.new" do
a = Class.new
MyClass::NestedClass = a
MyClass::NestedClass.name.should == "MyClass::NestedClass"
+ ensure
+ Object.send(:remove_const, :MyClass)
end
it "sets the new class' superclass to the given class" do
@@ -95,12 +97,13 @@ describe "Class.new" do
end
it "raises a TypeError when given a non-Class" do
- error_msg = /superclass must be a Class/
- -> { Class.new("") }.should raise_error(TypeError, error_msg)
- -> { Class.new(1) }.should raise_error(TypeError, error_msg)
- -> { Class.new(:symbol) }.should raise_error(TypeError, error_msg)
- -> { Class.new(mock('o')) }.should raise_error(TypeError, error_msg)
- -> { Class.new(Module.new) }.should raise_error(TypeError, error_msg)
+ error_msg = /superclass must be a.*Class/
+ -> { Class.new("") }.should.raise(TypeError, error_msg)
+ -> { Class.new(1) }.should.raise(TypeError, error_msg)
+ -> { Class.new(:symbol) }.should.raise(TypeError, error_msg)
+ -> { Class.new(mock('o')) }.should.raise(TypeError, error_msg)
+ -> { Class.new(Module.new) }.should.raise(TypeError, error_msg)
+ -> { Class.new(BasicObject.new) }.should.raise(TypeError, error_msg)
end
end
@@ -138,8 +141,8 @@ describe "Class#new" do
end
instance = klass.new
- instance.should be_kind_of klass
- instance.class.should equal klass
+ instance.should.is_a? klass
+ instance.class.should.equal? klass
end
it "passes the block to #initialize" do
diff --git a/spec/ruby/core/class/subclasses_spec.rb b/spec/ruby/core/class/subclasses_spec.rb
new file mode 100644
index 0000000000..c3d7b07e12
--- /dev/null
+++ b/spec/ruby/core/class/subclasses_spec.rb
@@ -0,0 +1,85 @@
+require_relative '../../spec_helper'
+require_relative '../module/fixtures/classes'
+
+describe "Class#subclasses" do
+ it "returns a list of classes directly inheriting from self" do
+ assert_subclasses(ModuleSpecs::Parent, [ModuleSpecs::Child, ModuleSpecs::Child2])
+ end
+
+ it "does not return included modules from the parent" do
+ parent = Class.new
+ child = Class.new(parent)
+ mod = Module.new
+ parent.include(mod)
+
+ assert_subclasses(parent, [child])
+ end
+
+ it "does not return included modules from the child" do
+ parent = Class.new
+ child = Class.new(parent)
+ mod = Module.new
+ parent.include(mod)
+
+ assert_subclasses(parent, [child])
+ end
+
+ it "does not return prepended modules from the parent" do
+ parent = Class.new
+ child = Class.new(parent)
+ mod = Module.new
+ parent.prepend(mod)
+
+ assert_subclasses(parent, [child])
+ end
+
+ it "does not return prepended modules from the child" do
+ parent = Class.new
+ child = Class.new(parent)
+ mod = Module.new
+ child.prepend(mod)
+
+ assert_subclasses(parent, [child])
+ end
+
+ it "does not return singleton classes" do
+ a = Class.new
+
+ a_obj = a.new
+ def a_obj.force_singleton_class
+ 42
+ end
+
+ a.subclasses.should_not.include?(a_obj.singleton_class)
+ end
+
+ it "has 1 entry per module or class" do
+ ModuleSpecs::Parent.subclasses.should == ModuleSpecs::Parent.subclasses.uniq
+ end
+
+ it "works when creating subclasses concurrently" do
+ t = 16
+ n = 1000
+ go = false
+ superclass = Class.new
+
+ threads = t.times.map do
+ Thread.new do
+ Thread.pass until go
+ n.times.map do
+ Class.new(superclass)
+ end
+ end
+ end
+
+ go = true
+ classes = threads.map(&:value)
+
+ superclass.subclasses.size.should == t * n
+ superclass.subclasses.each { |c| c.should.is_a?(Class) }
+ end
+
+ def assert_subclasses(mod,subclasses)
+ mod.subclasses.sort_by(&:inspect).should == subclasses.sort_by(&:inspect)
+ end
+end
diff --git a/spec/ruby/core/class/superclass_spec.rb b/spec/ruby/core/class/superclass_spec.rb
index deb5a45336..87d9b20490 100644
--- a/spec/ruby/core/class/superclass_spec.rb
+++ b/spec/ruby/core/class/superclass_spec.rb
@@ -3,12 +3,12 @@ require_relative 'fixtures/classes'
describe "Class#superclass" do
it "returns the superclass of self" do
- BasicObject.superclass.should be_nil
+ BasicObject.superclass.should == nil
Object.superclass.should == BasicObject
Class.superclass.should == Module
Class.new.superclass.should == Object
Class.new(String).superclass.should == String
- Class.new(Fixnum).superclass.should == Fixnum
+ Class.new(Integer).superclass.should == Integer
end
# redmine:567
diff --git a/spec/ruby/core/comparable/clamp_spec.rb b/spec/ruby/core/comparable/clamp_spec.rb
index 393496fc76..eb1dc1ff98 100644
--- a/spec/ruby/core/comparable/clamp_spec.rb
+++ b/spec/ruby/core/comparable/clamp_spec.rb
@@ -2,22 +2,14 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'Comparable#clamp' do
- ruby_version_is ""..."2.7" do
- it 'raises an Argument error unless given 2 parameters' do
- c = ComparableSpecs::Weird.new(0)
- -> { c.clamp(c) }.should raise_error(ArgumentError)
- -> { c.clamp(c, c, c) }.should raise_error(ArgumentError)
- end
- end
-
it 'raises an Argument error unless the 2 parameters are correctly ordered' do
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
two = ComparableSpecs::WithOnlyCompareDefined.new(2)
c = ComparableSpecs::Weird.new(3)
- -> { c.clamp(two, one) }.should raise_error(ArgumentError)
+ -> { c.clamp(two, one) }.should.raise(ArgumentError)
one.should_receive(:<=>).any_number_of_times.and_return(nil)
- -> { c.clamp(one, two) }.should raise_error(ArgumentError)
+ -> { c.clamp(one, two) }.should.raise(ArgumentError)
end
it 'returns self if within the given parameters' do
@@ -26,18 +18,18 @@ describe 'Comparable#clamp' do
three = ComparableSpecs::WithOnlyCompareDefined.new(3)
c = ComparableSpecs::Weird.new(2)
- c.clamp(one, two).should equal(c)
- c.clamp(two, two).should equal(c)
- c.clamp(one, three).should equal(c)
- c.clamp(two, three).should equal(c)
+ c.clamp(one, two).should.equal?(c)
+ c.clamp(two, two).should.equal?(c)
+ c.clamp(one, three).should.equal?(c)
+ c.clamp(two, three).should.equal?(c)
end
- it 'returns the min parameter if smaller than it' do
+ it 'returns the min parameter if less than it' do
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
two = ComparableSpecs::WithOnlyCompareDefined.new(2)
c = ComparableSpecs::Weird.new(0)
- c.clamp(one, two).should equal(one)
+ c.clamp(one, two).should.equal?(one)
end
it 'returns the max parameter if greater than it' do
@@ -45,44 +37,187 @@ describe 'Comparable#clamp' do
two = ComparableSpecs::WithOnlyCompareDefined.new(2)
c = ComparableSpecs::Weird.new(3)
- c.clamp(one, two).should equal(two)
+ c.clamp(one, two).should.equal?(two)
end
- ruby_version_is "2.7" do
- it 'returns self if within the given range parameters' do
+ context 'max is nil' do
+ it 'returns min if less than it' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ c = ComparableSpecs::Weird.new(0)
+ c.clamp(one, nil).should.equal?(one)
+ end
+
+ it 'always returns self if greater than min' do
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
- two = ComparableSpecs::WithOnlyCompareDefined.new(2)
- three = ComparableSpecs::WithOnlyCompareDefined.new(3)
c = ComparableSpecs::Weird.new(2)
+ c.clamp(one, nil).should.equal?(c)
+ end
+ end
+
+ context 'min is nil' do
+ it 'returns max if greater than it' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ c = ComparableSpecs::Weird.new(2)
+ c.clamp(nil, one).should.equal?(one)
+ end
+
+ it 'always returns self if less than max' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ c = ComparableSpecs::Weird.new(0)
+ c.clamp(nil, one).should.equal?(c)
+ end
+ end
+
+ it 'always returns self when min is nil and max is nil' do
+ c = ComparableSpecs::Weird.new(1)
+ c.clamp(nil, nil).should.equal?(c)
+ end
+
+ it 'returns self if within the given range parameters' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+ three = ComparableSpecs::WithOnlyCompareDefined.new(3)
+ c = ComparableSpecs::Weird.new(2)
+
+ c.clamp(one..two).should.equal?(c)
+ c.clamp(two..two).should.equal?(c)
+ c.clamp(one..three).should.equal?(c)
+ c.clamp(two..three).should.equal?(c)
+ end
+
+ it 'returns the minimum value of the range parameters if less than it' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+ c = ComparableSpecs::Weird.new(0)
+
+ c.clamp(one..two).should.equal?(one)
+ end
+
+ it 'returns the maximum value of the range parameters if greater than it' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+ c = ComparableSpecs::Weird.new(3)
+
+ c.clamp(one..two).should.equal?(two)
+ end
+
+ it 'raises an Argument error if the range parameter is exclusive' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+ c = ComparableSpecs::Weird.new(3)
+
+ -> { c.clamp(one...two) }.should.raise(ArgumentError)
+ end
+
+ context 'with nil as the max argument' do
+ it 'returns min argument if less than it' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ zero = ComparableSpecs::WithOnlyCompareDefined.new(0)
+ c = ComparableSpecs::Weird.new(0)
- c.clamp(one..two).should equal(c)
- c.clamp(two..two).should equal(c)
- c.clamp(one..three).should equal(c)
- c.clamp(two..three).should equal(c)
+ c.clamp(one, nil).should.equal?(one)
+ c.clamp(zero, nil).should.equal?(c)
end
- it 'returns the minimum value of the range parameters if smaller than it' do
+ it 'always returns self if greater than min argument' do
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
two = ComparableSpecs::WithOnlyCompareDefined.new(2)
+ c = ComparableSpecs::Weird.new(2)
+
+ c.clamp(one, nil).should.equal?(c)
+ c.clamp(two, nil).should.equal?(c)
+ end
+ end
+
+ context 'with endless range' do
+ it 'returns minimum value of the range parameters if less than it' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ zero = ComparableSpecs::WithOnlyCompareDefined.new(0)
c = ComparableSpecs::Weird.new(0)
- c.clamp(one..two).should equal(one)
+ c.clamp(one..).should.equal?(one)
+ c.clamp(zero..).should.equal?(c)
end
- it 'returns the maximum value of the range parameters if greater than it' do
+ it 'always returns self if greater than minimum value of the range parameters' do
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
two = ComparableSpecs::WithOnlyCompareDefined.new(2)
- c = ComparableSpecs::Weird.new(3)
+ c = ComparableSpecs::Weird.new(2)
+
+ c.clamp(one..).should.equal?(c)
+ c.clamp(two..).should.equal?(c)
+ end
+
+ it 'works with exclusive range' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ c = ComparableSpecs::Weird.new(2)
- c.clamp(one..two).should equal(two)
+ c.clamp(one...).should.equal?(c)
+ end
+ end
+
+ context 'with nil as the min argument' do
+ it 'returns max argument if greater than it' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ c = ComparableSpecs::Weird.new(2)
+
+ c.clamp(nil, one).should.equal?(one)
+ end
+
+ it 'always returns self if less than max argument' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ zero = ComparableSpecs::WithOnlyCompareDefined.new(0)
+ c = ComparableSpecs::Weird.new(0)
+
+ c.clamp(nil, one).should.equal?(c)
+ c.clamp(nil, zero).should.equal?(c)
+ end
+ end
+
+ context 'with beginless range' do
+ it 'returns maximum value of the range parameters if greater than it' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ c = ComparableSpecs::Weird.new(2)
+
+ c.clamp(..one).should.equal?(one)
+ end
+
+ it 'always returns self if less than maximum value of the range parameters' do
+ one = ComparableSpecs::WithOnlyCompareDefined.new(1)
+ zero = ComparableSpecs::WithOnlyCompareDefined.new(0)
+ c = ComparableSpecs::Weird.new(0)
+
+ c.clamp(..one).should.equal?(c)
+ c.clamp(..zero).should.equal?(c)
end
it 'raises an Argument error if the range parameter is exclusive' do
one = ComparableSpecs::WithOnlyCompareDefined.new(1)
- two = ComparableSpecs::WithOnlyCompareDefined.new(2)
- c = ComparableSpecs::Weird.new(3)
+ c = ComparableSpecs::Weird.new(0)
+
+ -> { c.clamp(...one) }.should.raise(ArgumentError)
+ end
+ end
+
+ context 'with nil as the min and the max argument' do
+ it 'always returns self' do
+ c = ComparableSpecs::Weird.new(1)
+
+ c.clamp(nil, nil).should.equal?(c)
+ end
+ end
+
+ context 'with beginless-and-endless range' do
+ it 'always returns self' do
+ c = ComparableSpecs::Weird.new(1)
+
+ c.clamp(nil..nil).should.equal?(c)
+ end
+
+ it 'works with exclusive range' do
+ c = ComparableSpecs::Weird.new(2)
- -> { c.clamp(one...two) }.should raise_error(ArgumentError)
+ c.clamp(nil...nil).should.equal?(c)
end
end
end
diff --git a/spec/ruby/core/comparable/equal_value_spec.rb b/spec/ruby/core/comparable/equal_value_spec.rb
index ddcc03cb41..3af40d1c7e 100644
--- a/spec/ruby/core/comparable/equal_value_spec.rb
+++ b/spec/ruby/core/comparable/equal_value_spec.rb
@@ -39,7 +39,7 @@ describe "Comparable#==" do
end
it "returns false" do
- (a == b).should be_false
+ (a == b).should == false
end
end
@@ -49,7 +49,7 @@ describe "Comparable#==" do
end
it "raises an ArgumentError" do
- -> { (a == b) }.should raise_error(ArgumentError)
+ -> { (a == b) }.should.raise(ArgumentError)
end
end
@@ -60,7 +60,7 @@ describe "Comparable#==" do
end
it "lets it go through" do
- -> { (a == b) }.should raise_error(StandardError)
+ -> { (a == b) }.should.raise(StandardError)
end
end
@@ -71,13 +71,13 @@ describe "Comparable#==" do
end
it "lets it go through" do
- -> { (a == b) }.should raise_error(TypeError)
+ -> { (a == b) }.should.raise(TypeError)
end
end
it "lets it go through if it is not a StandardError" do
a.should_receive(:<=>).once.and_raise(Exception)
- -> { (a == b) }.should raise_error(Exception)
+ -> { (a == b) }.should.raise(Exception)
end
end
diff --git a/spec/ruby/core/comparable/fixtures/classes.rb b/spec/ruby/core/comparable/fixtures/classes.rb
index 4239a47d2f..2bdabbf014 100644
--- a/spec/ruby/core/comparable/fixtures/classes.rb
+++ b/spec/ruby/core/comparable/fixtures/classes.rb
@@ -7,6 +7,7 @@ module ComparableSpecs
end
def <=>(other)
+ return nil if other.nil?
self.value <=> other.value
end
end
diff --git a/spec/ruby/core/comparable/gt_spec.rb b/spec/ruby/core/comparable/gt_spec.rb
index 150e653dc7..cebb5464ad 100644
--- a/spec/ruby/core/comparable/gt_spec.rb
+++ b/spec/ruby/core/comparable/gt_spec.rb
@@ -38,6 +38,6 @@ describe "Comparable#>" do
b = ComparableSpecs::Weird.new(20)
a.should_receive(:<=>).any_number_of_times.and_return(nil)
- -> { (a > b) }.should raise_error(ArgumentError)
+ -> { (a > b) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/comparable/gte_spec.rb b/spec/ruby/core/comparable/gte_spec.rb
index 328f58c66c..16da81b3ea 100644
--- a/spec/ruby/core/comparable/gte_spec.rb
+++ b/spec/ruby/core/comparable/gte_spec.rb
@@ -42,6 +42,6 @@ describe "Comparable#>=" do
b = ComparableSpecs::Weird.new(20)
a.should_receive(:<=>).any_number_of_times.and_return(nil)
- -> { (a >= b) }.should raise_error(ArgumentError)
+ -> { (a >= b) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/comparable/lt_spec.rb b/spec/ruby/core/comparable/lt_spec.rb
index bca95f8d25..175646d0d7 100644
--- a/spec/ruby/core/comparable/lt_spec.rb
+++ b/spec/ruby/core/comparable/lt_spec.rb
@@ -38,12 +38,12 @@ describe "Comparable#<" do
b = ComparableSpecs::Weird.new(20)
a.should_receive(:<=>).any_number_of_times.and_return(nil)
- -> { (a < b) }.should raise_error(ArgumentError)
+ -> { (a < b) }.should.raise(ArgumentError)
end
it "raises an argument error with a message containing the value" do
- -> { ("foo" < 7) }.should raise_error(ArgumentError) { |e|
- e.message.should == "comparison of String with 7 failed"
+ -> { ("foo" < 7) }.should.raise(ArgumentError) { |e|
+ e.message.should.include? "String with 7 failed"
}
end
end
diff --git a/spec/ruby/core/comparable/lte_spec.rb b/spec/ruby/core/comparable/lte_spec.rb
index b5cb9cc4e7..8cbbd5ebb4 100644
--- a/spec/ruby/core/comparable/lte_spec.rb
+++ b/spec/ruby/core/comparable/lte_spec.rb
@@ -41,6 +41,6 @@ describe "Comparable#<=" do
b = ComparableSpecs::Weird.new(20)
a.should_receive(:<=>).any_number_of_times.and_return(nil)
- -> { (a <= b) }.should raise_error(ArgumentError)
+ -> { (a <= b) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/complex/abs_spec.rb b/spec/ruby/core/complex/abs_spec.rb
index 43912c517f..ed5aebb11d 100644
--- a/spec/ruby/core/complex/abs_spec.rb
+++ b/spec/ruby/core/complex/abs_spec.rb
@@ -1,6 +1,12 @@
require_relative '../../spec_helper'
-require_relative 'shared/abs'
describe "Complex#abs" do
- it_behaves_like :complex_abs, :abs
+ it "returns the modulus: |a + bi| = sqrt((a ^ 2) + (b ^ 2))" do
+ Complex(0, 0).abs.should == 0
+ Complex(3, 4).abs.should == 5 # well-known integer case
+ Complex(-3, 4).abs.should == 5
+ Complex(1, -1).abs.should be_close(Math.sqrt(2), TOLERANCE)
+ Complex(6.5, 0).abs.should be_close(6.5, TOLERANCE)
+ Complex(0, -7.2).abs.should be_close(7.2, TOLERANCE)
+ end
end
diff --git a/spec/ruby/core/complex/angle_spec.rb b/spec/ruby/core/complex/angle_spec.rb
index 4aa176956f..7551214d2b 100644
--- a/spec/ruby/core/complex/angle_spec.rb
+++ b/spec/ruby/core/complex/angle_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/arg'
describe "Complex#angle" do
- it_behaves_like :complex_arg, :angle
+ it "is an alias of Complex#arg" do
+ Complex.instance_method(:angle).should == Complex.instance_method(:arg)
+ end
end
diff --git a/spec/ruby/core/complex/arg_spec.rb b/spec/ruby/core/complex/arg_spec.rb
index 009f19429f..dd64102d77 100644
--- a/spec/ruby/core/complex/arg_spec.rb
+++ b/spec/ruby/core/complex/arg_spec.rb
@@ -1,6 +1,11 @@
require_relative '../../spec_helper'
-require_relative 'shared/arg'
describe "Complex#arg" do
- it_behaves_like :complex_arg, :arg
+ it "returns the argument -- i.e., the angle from (1, 0) in the complex plane" do
+ two_pi = 2 * Math::PI
+ (Complex(1, 0).arg % two_pi).should be_close(0, TOLERANCE)
+ (Complex(0, 2).arg % two_pi).should be_close(Math::PI * 0.5, TOLERANCE)
+ (Complex(-100, 0).arg % two_pi).should be_close(Math::PI, TOLERANCE)
+ (Complex(0, -75.3).arg % two_pi).should be_close(Math::PI * 1.5, TOLERANCE)
+ end
end
diff --git a/spec/ruby/core/complex/coerce_spec.rb b/spec/ruby/core/complex/coerce_spec.rb
index a30a6c1d5f..d4ea85a713 100644
--- a/spec/ruby/core/complex/coerce_spec.rb
+++ b/spec/ruby/core/complex/coerce_spec.rb
@@ -8,37 +8,37 @@ describe "Complex#coerce" do
it "returns an array containing other and self as Complex when other is an Integer" do
result = @one.coerce(2)
result.should == [2, 1]
- result.first.should be_kind_of(Complex)
- result.last.should be_kind_of(Complex)
+ result.first.should.is_a?(Complex)
+ result.last.should.is_a?(Complex)
end
it "returns an array containing other and self as Complex when other is a Float" do
result = @one.coerce(20.5)
result.should == [20.5, 1]
- result.first.should be_kind_of(Complex)
- result.last.should be_kind_of(Complex)
+ result.first.should.is_a?(Complex)
+ result.last.should.is_a?(Complex)
end
it "returns an array containing other and self as Complex when other is a Bignum" do
result = @one.coerce(4294967296)
result.should == [4294967296, 1]
- result.first.should be_kind_of(Complex)
- result.last.should be_kind_of(Complex)
+ result.first.should.is_a?(Complex)
+ result.last.should.is_a?(Complex)
end
it "returns an array containing other and self as Complex when other is a Rational" do
result = @one.coerce(Rational(5,6))
result.should == [Rational(5,6), 1]
- result.first.should be_kind_of(Complex)
- result.last.should be_kind_of(Complex)
+ result.first.should.is_a?(Complex)
+ result.last.should.is_a?(Complex)
end
it "returns an array containing other and self when other is a Complex" do
other = Complex(2)
result = @one.coerce(other)
result.should == [other, @one]
- result.first.should equal(other)
- result.last.should equal(@one)
+ result.first.should.equal?(other)
+ result.last.should.equal?(@one)
end
it "returns an array containing other as Complex and self when other is a Numeric which responds to #real? with true" do
@@ -46,25 +46,25 @@ describe "Complex#coerce" do
other.should_receive(:real?).any_number_of_times.and_return(true)
result = @one.coerce(other)
result.should == [other, @one]
- result.first.should eql(Complex(other))
- result.last.should equal(@one)
+ result.first.should.eql?(Complex(other))
+ result.last.should.equal?(@one)
end
it "raises TypeError when other is a Numeric which responds to #real? with false" do
other = mock_numeric('other')
other.should_receive(:real?).any_number_of_times.and_return(false)
- -> { @one.coerce(other) }.should raise_error(TypeError)
+ -> { @one.coerce(other) }.should.raise(TypeError)
end
it "raises a TypeError when other is a String" do
- -> { @one.coerce("20") }.should raise_error(TypeError)
+ -> { @one.coerce("20") }.should.raise(TypeError)
end
it "raises a TypeError when other is nil" do
- -> { @one.coerce(nil) }.should raise_error(TypeError)
+ -> { @one.coerce(nil) }.should.raise(TypeError)
end
it "raises a TypeError when other is false" do
- -> { @one.coerce(false) }.should raise_error(TypeError)
+ -> { @one.coerce(false) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/complex/comparision_spec.rb b/spec/ruby/core/complex/comparision_spec.rb
deleted file mode 100644
index 2a437afb71..0000000000
--- a/spec/ruby/core/complex/comparision_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative '../../spec_helper'
-
-describe "Complex#<=>" do
- ruby_version_is "2.7" do
- it "returns nil if either self or argument has imaginary part" do
- (Complex(5, 1) <=> Complex(2)).should be_nil
- (Complex(1) <=> Complex(2, 1)).should be_nil
- (5 <=> Complex(2, 1)).should be_nil
- end
-
- it "returns nil if argument is not numeric" do
- (Complex(5, 1) <=> "cmp").should be_nil
- (Complex(1) <=> "cmp").should be_nil
- (Complex(1) <=> Object.new).should be_nil
- end
-
- it "returns 0, 1, or -1 if self and argument do not have imaginary part" do
- (Complex(5) <=> Complex(2)).should == 1
- (Complex(2) <=> Complex(3)).should == -1
- (Complex(2) <=> Complex(2)).should == 0
-
- (Complex(5) <=> 2).should == 1
- (Complex(2) <=> 3).should == -1
- (Complex(2) <=> 2).should == 0
- end
- end
-end
diff --git a/spec/ruby/core/complex/comparison_spec.rb b/spec/ruby/core/complex/comparison_spec.rb
new file mode 100644
index 0000000000..3115f4e205
--- /dev/null
+++ b/spec/ruby/core/complex/comparison_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+
+describe "Complex#<=>" do
+ it "returns nil if either self or argument has imaginary part" do
+ (Complex(5, 1) <=> Complex(2)).should == nil
+ (Complex(1) <=> Complex(2, 1)).should == nil
+ (5 <=> Complex(2, 1)).should == nil
+ end
+
+ it "returns nil if argument is not numeric" do
+ (Complex(5, 1) <=> "cmp").should == nil
+ (Complex(1) <=> "cmp").should == nil
+ (Complex(1) <=> Object.new).should == nil
+ end
+
+ it "returns 0, 1, or -1 if self and argument do not have imaginary part" do
+ (Complex(5) <=> Complex(2)).should == 1
+ (Complex(2) <=> Complex(3)).should == -1
+ (Complex(2) <=> Complex(2)).should == 0
+
+ (Complex(5) <=> 2).should == 1
+ (Complex(2) <=> 3).should == -1
+ (Complex(2) <=> 2).should == 0
+ end
+end
diff --git a/spec/ruby/core/complex/conj_spec.rb b/spec/ruby/core/complex/conj_spec.rb
index 5e3bc1acb8..063c85faec 100644
--- a/spec/ruby/core/complex/conj_spec.rb
+++ b/spec/ruby/core/complex/conj_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/conjugate'
describe "Complex#conj" do
- it_behaves_like :complex_conjugate, :conj
+ it "is an alias of Complex#conjugate" do
+ Complex.instance_method(:conj).should == Complex.instance_method(:conjugate)
+ end
end
diff --git a/spec/ruby/core/complex/conjugate_spec.rb b/spec/ruby/core/complex/conjugate_spec.rb
index f658bab4da..256fe4b3be 100644
--- a/spec/ruby/core/complex/conjugate_spec.rb
+++ b/spec/ruby/core/complex/conjugate_spec.rb
@@ -1,6 +1,10 @@
require_relative '../../spec_helper'
-require_relative 'shared/conjugate'
describe "Complex#conjugate" do
- it_behaves_like :complex_conjugate, :conjugate
+ it "returns the complex conjugate: conj a + bi = a - bi" do
+ Complex(3, 5).conjugate.should == Complex(3, -5)
+ Complex(3, -5).conjugate.should == Complex(3, 5)
+ Complex(-3.0, 5.2).conjugate.should be_close(Complex(-3.0, -5.2), TOLERANCE)
+ Complex(3.0, -5.2).conjugate.should be_close(Complex(3.0, 5.2), TOLERANCE)
+ end
end
diff --git a/spec/ruby/core/complex/constants_spec.rb b/spec/ruby/core/complex/constants_spec.rb
index 50303de16c..200e97731a 100644
--- a/spec/ruby/core/complex/constants_spec.rb
+++ b/spec/ruby/core/complex/constants_spec.rb
@@ -2,6 +2,6 @@ require_relative '../../spec_helper'
describe "Complex::I" do
it "is Complex(0, 1)" do
- Complex::I.should eql(Complex(0, 1))
+ Complex::I.should.eql?(Complex(0, 1))
end
end
diff --git a/spec/ruby/core/complex/divide_spec.rb b/spec/ruby/core/complex/divide_spec.rb
index bebf862312..d5b4aa3885 100644
--- a/spec/ruby/core/complex/divide_spec.rb
+++ b/spec/ruby/core/complex/divide_spec.rb
@@ -1,6 +1,84 @@
require_relative '../../spec_helper'
-require_relative 'shared/divide'
describe "Complex#/" do
- it_behaves_like :complex_divide, :/
+ describe "with Complex" do
+ it "divides according to the usual rule for complex numbers" do
+ a = Complex((1 * 10) - (2 * 20), (1 * 20) + (2 * 10))
+ b = Complex(1, 2)
+ (a / b).should == Complex(10, 20)
+
+ c = Complex((1.5 * 100.2) - (2.1 * -30.3), (1.5 * -30.3) + (2.1 * 100.2))
+ d = Complex(1.5, 2.1)
+ # remember the floating-point arithmetic
+ (c / d).should be_close(Complex(100.2, -30.3), TOLERANCE)
+ end
+ end
+
+ describe "with Fixnum" do
+ it "divides both parts of the Complex number" do
+ (Complex(20, 40) / 2).should == Complex(10, 20)
+ (Complex(30, 30) / 10).should == Complex(3, 3)
+ end
+
+ it "raises a ZeroDivisionError when given zero" do
+ -> { Complex(20, 40) / 0 }.should.raise(ZeroDivisionError)
+ end
+
+ it "produces Rational parts" do
+ (Complex(5, 9) / 2).should.eql?(Complex(Rational(5,2), Rational(9,2)))
+ end
+ end
+
+ describe "with Bignum" do
+ it "divides both parts of the Complex number" do
+ (Complex(20, 40) / 2).should == Complex(10, 20)
+ (Complex(15, 16) / 2.0).should be_close(Complex(7.5, 8), TOLERANCE)
+ end
+ end
+
+ describe "with Float" do
+ it "divides both parts of the Complex number" do
+ (Complex(3, 9) / 1.5).should == Complex(2, 6)
+ (Complex(15, 16) / 2.0).should be_close(Complex(7.5, 8), TOLERANCE)
+ end
+
+ it "returns Complex(Infinity, Infinity) when given zero" do
+ (Complex(20, 40) / 0.0).real.infinite?.should == 1
+ (Complex(20, 40) / 0.0).imag.infinite?.should == 1
+ (Complex(-20, 40) / 0.0).real.infinite?.should == -1
+ (Complex(-20, 40) / 0.0).imag.infinite?.should == 1
+ end
+ end
+
+ describe "with Object" do
+ it "tries to coerce self into other" do
+ value = Complex(3, 9)
+
+ obj = mock("Object")
+ obj.should_receive(:coerce).with(value).and_return([4, 2])
+ (value / obj).should == 2
+ end
+ end
+
+ describe "with a Numeric which responds to #real? with true" do
+ it "returns Complex(real.quo(other), imag.quo(other))" do
+ other = mock_numeric('other')
+ real = mock_numeric('real')
+ imag = mock_numeric('imag')
+ other.should_receive(:real?).and_return(true)
+ real.should_receive(:quo).with(other).and_return(1)
+ imag.should_receive(:quo).with(other).and_return(2)
+ (Complex(real, imag) / other).should == Complex(1, 2)
+ end
+ end
+
+ describe "with a Numeric which responds to #real? with false" do
+ it "coerces the passed argument to Complex and divides the resulting elements" do
+ complex = Complex(3, 0)
+ other = mock_numeric('other')
+ other.should_receive(:real?).any_number_of_times.and_return(false)
+ other.should_receive(:coerce).with(complex).and_return([5, 2])
+ (complex / other).should.eql?(Rational(5, 2))
+ end
+ end
end
diff --git a/spec/ruby/core/complex/eql_spec.rb b/spec/ruby/core/complex/eql_spec.rb
index 9194efc074..2082a22feb 100644
--- a/spec/ruby/core/complex/eql_spec.rb
+++ b/spec/ruby/core/complex/eql_spec.rb
@@ -2,23 +2,23 @@ require_relative '../../spec_helper'
describe "Complex#eql?" do
it "returns false if other is not Complex" do
- Complex(1).eql?(1).should be_false
+ Complex(1).eql?(1).should == false
end
it "returns true when the respective parts are of the same classes and self == other" do
- Complex(1, 2).eql?(Complex(1, 2)).should be_true
+ Complex(1, 2).eql?(Complex(1, 2)).should == true
end
it "returns false when the real parts are of different classes" do
- Complex(1).eql?(Complex(1.0)).should be_false
+ Complex(1).eql?(Complex(1.0)).should == false
end
it "returns false when the imaginary parts are of different classes" do
- Complex(1, 2).eql?(Complex(1, 2.0)).should be_false
+ Complex(1, 2).eql?(Complex(1, 2.0)).should == false
end
it "returns false when self == other is false" do
- Complex(1, 2).eql?(Complex(2, 3)).should be_false
+ Complex(1, 2).eql?(Complex(2, 3)).should == false
end
it "does NOT send #eql? to real or imaginary parts" do
@@ -26,6 +26,6 @@ describe "Complex#eql?" do
imag = mock_numeric('imag')
real.should_not_receive(:eql?)
imag.should_not_receive(:eql?)
- Complex(real, imag).eql?(Complex(real, imag)).should be_true
+ Complex(real, imag).eql?(Complex(real, imag)).should == true
end
end
diff --git a/spec/ruby/core/complex/equal_value_spec.rb b/spec/ruby/core/complex/equal_value_spec.rb
index b1e4f9cfcd..b3562ff3fb 100644
--- a/spec/ruby/core/complex/equal_value_spec.rb
+++ b/spec/ruby/core/complex/equal_value_spec.rb
@@ -53,14 +53,14 @@ describe "Complex#==" do
end
describe "with Object" do
- # Fixnum#==, Float#== and Bignum#== only return booleans - Bug?
+ # Integer#== and Float#== only return booleans - Bug?
it "calls other#== with self" do
value = Complex(3, 0)
obj = mock("Object")
obj.should_receive(:==).with(value).and_return(:expected)
- (value == obj).should_not be_false
+ (value == obj).should_not == false
end
end
@@ -73,11 +73,11 @@ describe "Complex#==" do
it "returns real == other when the imaginary part is zero" do
real = mock_numeric('real')
real.should_receive(:==).with(@other).and_return(true)
- (Complex(real, 0) == @other).should be_true
+ (Complex(real, 0) == @other).should == true
end
- it "returns false when when the imaginary part is not zero" do
- (Complex(3, 1) == @other).should be_false
+ it "returns false when the imaginary part is not zero" do
+ (Complex(3, 1) == @other).should == false
end
end
@@ -87,7 +87,7 @@ describe "Complex#==" do
other = mock_numeric('other')
other.should_receive(:real?).any_number_of_times.and_return(false)
other.should_receive(:==).with(complex).and_return(true)
- (complex == other).should be_true
+ (complex == other).should == true
end
end
end
diff --git a/spec/ruby/core/complex/exponent_spec.rb b/spec/ruby/core/complex/exponent_spec.rb
index bf07c90038..d0db0a40c2 100644
--- a/spec/ruby/core/complex/exponent_spec.rb
+++ b/spec/ruby/core/complex/exponent_spec.rb
@@ -1,15 +1,15 @@
require_relative '../../spec_helper'
describe "Complex#**" do
- describe "with Fixnum 0" do
+ describe "with Integer 0" do
it "returns Complex(1)" do
- (Complex(3, 4) ** 0).should eql(Complex(1))
+ (Complex(3, 4) ** 0).should.eql?(Complex(1))
end
end
describe "with Float 0.0" do
it "returns Complex(1.0, 0.0)" do
- (Complex(3, 4) ** 0.0).should eql(Complex(1.0, 0.0))
+ (Complex(3, 4) ** 0.0).should.eql?(Complex(1.0, 0.0))
end
end
diff --git a/spec/ruby/core/complex/fdiv_spec.rb b/spec/ruby/core/complex/fdiv_spec.rb
index 68f7d1b309..fdcbc6a95d 100644
--- a/spec/ruby/core/complex/fdiv_spec.rb
+++ b/spec/ruby/core/complex/fdiv_spec.rb
@@ -2,44 +2,44 @@ require_relative '../../spec_helper'
describe "Complex#fdiv" do
it "accepts a numeric argument" do
- -> { Complex(20).fdiv(2) }.should_not raise_error(TypeError)
- -> { Complex(20).fdiv(2.0) }.should_not raise_error(TypeError)
- -> { Complex(20).fdiv(bignum_value) }.should_not raise_error(TypeError)
+ -> { Complex(20).fdiv(2) }.should_not.raise(TypeError)
+ -> { Complex(20).fdiv(2.0) }.should_not.raise(TypeError)
+ -> { Complex(20).fdiv(bignum_value) }.should_not.raise(TypeError)
end
it "accepts a negative numeric argument" do
- -> { Complex(20).fdiv(-2) }.should_not raise_error(TypeError)
- -> { Complex(20).fdiv(-2.0) }.should_not raise_error(TypeError)
- -> { Complex(20).fdiv(-bignum_value) }.should_not raise_error(TypeError)
+ -> { Complex(20).fdiv(-2) }.should_not.raise(TypeError)
+ -> { Complex(20).fdiv(-2.0) }.should_not.raise(TypeError)
+ -> { Complex(20).fdiv(-bignum_value) }.should_not.raise(TypeError)
end
it "raises a TypeError if passed a non-numeric argument" do
- -> { Complex(20).fdiv([]) }.should raise_error(TypeError)
- -> { Complex(20).fdiv(:sym) }.should raise_error(TypeError)
- -> { Complex(20).fdiv('s') }.should raise_error(TypeError)
+ -> { Complex(20).fdiv([]) }.should.raise(TypeError)
+ -> { Complex(20).fdiv(:sym) }.should.raise(TypeError)
+ -> { Complex(20).fdiv('s') }.should.raise(TypeError)
end
it "sets the real part to NaN if self's real part is NaN" do
- Complex(nan_value).fdiv(2).real.nan?.should be_true
+ Complex(nan_value).fdiv(2).real.nan?.should == true
end
it "sets the imaginary part to NaN if self's imaginary part is NaN" do
- Complex(2, nan_value).fdiv(2).imag.nan?.should be_true
+ Complex(2, nan_value).fdiv(2).imag.nan?.should == true
end
it "sets the real and imaginary part to NaN if self's real and imaginary parts are NaN" do
- Complex(nan_value, nan_value).fdiv(2).imag.nan?.should be_true
- Complex(nan_value, nan_value).fdiv(2).real.nan?.should be_true
+ Complex(nan_value, nan_value).fdiv(2).imag.nan?.should == true
+ Complex(nan_value, nan_value).fdiv(2).real.nan?.should == true
end
it "sets the real and imaginary part to NaN if self's real part and the argument are both NaN" do
- Complex(nan_value, 2).fdiv(nan_value).imag.nan?.should be_true
- Complex(nan_value, 2).fdiv(nan_value).real.nan?.should be_true
+ Complex(nan_value, 2).fdiv(nan_value).imag.nan?.should == true
+ Complex(nan_value, 2).fdiv(nan_value).real.nan?.should == true
end
it "sets the real and imaginary part to NaN if self's real part, self's imaginary part, and the argument are NaN" do
- Complex(nan_value, nan_value).fdiv(nan_value).imag.nan?.should be_true
- Complex(nan_value, nan_value).fdiv(nan_value).real.nan?.should be_true
+ Complex(nan_value, nan_value).fdiv(nan_value).imag.nan?.should == true
+ Complex(nan_value, nan_value).fdiv(nan_value).real.nan?.should == true
end
it "sets the real part to Infinity if self's real part is Infinity" do
@@ -58,8 +58,8 @@ describe "Complex#fdiv" do
end
it "sets the real part to NaN and the imaginary part to NaN if self's imaginary part, self's real part, and the argument are Infinity" do
- Complex(infinity_value, infinity_value).fdiv(infinity_value).real.nan?.should be_true
- Complex(infinity_value, infinity_value).fdiv(infinity_value).imag.nan?.should be_true
+ Complex(infinity_value, infinity_value).fdiv(infinity_value).real.nan?.should == true
+ Complex(infinity_value, infinity_value).fdiv(infinity_value).imag.nan?.should == true
end
end
@@ -71,7 +71,7 @@ describe "Complex#fdiv with no imaginary part" do
it "returns a Complex number" do
@numbers.each do |real|
@numbers.each do |other|
- Complex(real).fdiv(other).should be_an_instance_of(Complex)
+ Complex(real).fdiv(other).should.instance_of?(Complex)
end
end
end
@@ -103,7 +103,7 @@ describe "Complex#fdiv with an imaginary part" do
@numbers.each_with_index do |other,idx|
Complex(
real,@numbers[idx == 0 ? -1 : idx-1]
- ).fdiv(other).should be_an_instance_of(Complex)
+ ).fdiv(other).should.instance_of?(Complex)
end
end
end
diff --git a/spec/ruby/core/complex/imag_spec.rb b/spec/ruby/core/complex/imag_spec.rb
index 2bafd1ab54..225f168e78 100644
--- a/spec/ruby/core/complex/imag_spec.rb
+++ b/spec/ruby/core/complex/imag_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/image'
describe "Complex#imag" do
- it_behaves_like :complex_image, :imag
+ it "is an alias of Complex#imaginary" do
+ Complex.instance_method(:imag).should == Complex.instance_method(:imaginary)
+ end
end
diff --git a/spec/ruby/core/complex/imaginary_spec.rb b/spec/ruby/core/complex/imaginary_spec.rb
index a8a1bfea90..ac9284e934 100644
--- a/spec/ruby/core/complex/imaginary_spec.rb
+++ b/spec/ruby/core/complex/imaginary_spec.rb
@@ -1,6 +1,10 @@
require_relative '../../spec_helper'
-require_relative 'shared/image'
describe "Complex#imaginary" do
- it_behaves_like :complex_image, :imaginary
+ it "returns the imaginary part of self" do
+ Complex(1, 0).imaginary.should == 0
+ Complex(2, 1).imaginary.should == 1
+ Complex(6.7, 8.9).imaginary.should == 8.9
+ Complex(1, bignum_value).imaginary.should == bignum_value
+ end
end
diff --git a/spec/ruby/core/complex/inspect_spec.rb b/spec/ruby/core/complex/inspect_spec.rb
index 71aabde5be..045be94b22 100644
--- a/spec/ruby/core/complex/inspect_spec.rb
+++ b/spec/ruby/core/complex/inspect_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative '../numeric/fixtures/classes'
describe "Complex#inspect" do
it "returns (${real}+${image}i) for positive imaginary parts" do
@@ -13,4 +14,24 @@ describe "Complex#inspect" do
Complex(-1, -4).inspect.should == "(-1-4i)"
Complex(-7, -6.7).inspect.should == "(-7-6.7i)"
end
+
+ it "calls #inspect on real and imaginary" do
+ real = NumericSpecs::Subclass.new
+ # + because of https://bugs.ruby-lang.org/issues/20337
+ real.should_receive(:inspect).and_return(+"1")
+ imaginary = NumericSpecs::Subclass.new
+ imaginary.should_receive(:inspect).and_return("2")
+ imaginary.should_receive(:<).any_number_of_times.and_return(false)
+ Complex(real, imaginary).inspect.should == "(1+2i)"
+ end
+
+ it "adds an `*' before the `i' if the last character of the imaginary part is not numeric" do
+ real = NumericSpecs::Subclass.new
+ # + because of https://bugs.ruby-lang.org/issues/20337
+ real.should_receive(:inspect).and_return(+"(1)")
+ imaginary = NumericSpecs::Subclass.new
+ imaginary.should_receive(:inspect).and_return("(2)")
+ imaginary.should_receive(:<).any_number_of_times.and_return(false)
+ Complex(real, imaginary).inspect.should == "((1)+(2)*i)"
+ end
end
diff --git a/spec/ruby/core/complex/integer_spec.rb b/spec/ruby/core/complex/integer_spec.rb
index 0957accb70..559bfbccfd 100644
--- a/spec/ruby/core/complex/integer_spec.rb
+++ b/spec/ruby/core/complex/integer_spec.rb
@@ -2,10 +2,10 @@ require_relative '../../spec_helper'
describe "Complex#integer?" do
it "returns false for a Complex with no imaginary part" do
- Complex(20).integer?.should be_false
+ Complex(20).integer?.should == false
end
it "returns false for a Complex with an imaginary part" do
- Complex(20,3).integer?.should be_false
+ Complex(20,3).integer?.should == false
end
end
diff --git a/spec/ruby/core/complex/magnitude_spec.rb b/spec/ruby/core/complex/magnitude_spec.rb
index 86f3b29868..6341b4eec8 100644
--- a/spec/ruby/core/complex/magnitude_spec.rb
+++ b/spec/ruby/core/complex/magnitude_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/abs'
describe "Complex#magnitude" do
- it_behaves_like :complex_abs, :magnitude
+ it "is an alias of Complex#abs" do
+ Complex.instance_method(:magnitude).should == Complex.instance_method(:abs)
+ end
end
diff --git a/spec/ruby/core/complex/marshal_dump_spec.rb b/spec/ruby/core/complex/marshal_dump_spec.rb
index 116899b0ad..201d55e9e5 100644
--- a/spec/ruby/core/complex/marshal_dump_spec.rb
+++ b/spec/ruby/core/complex/marshal_dump_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "Complex#marshal_dump" do
it "is a private method" do
- Complex.should have_private_instance_method(:marshal_dump, false)
+ Complex.private_instance_methods(false).should.include?(:marshal_dump)
end
it "dumps real and imaginary parts" do
diff --git a/spec/ruby/core/complex/negative_spec.rb b/spec/ruby/core/complex/negative_spec.rb
index 62ab89c04a..566975b8e1 100644
--- a/spec/ruby/core/complex/negative_spec.rb
+++ b/spec/ruby/core/complex/negative_spec.rb
@@ -4,10 +4,10 @@ describe "Complex#negative?" do
it "is undefined" do
c = Complex(1)
- c.methods.should_not include(:negative?)
+ c.methods.should_not.include?(:negative?)
-> {
c.negative?
- }.should raise_error(NoMethodError)
+ }.should.raise(NoMethodError)
end
end
diff --git a/spec/ruby/core/complex/phase_spec.rb b/spec/ruby/core/complex/phase_spec.rb
index 89574bf533..2ab90989e1 100644
--- a/spec/ruby/core/complex/phase_spec.rb
+++ b/spec/ruby/core/complex/phase_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/arg'
describe "Complex#phase" do
- it_behaves_like :complex_arg, :phase
+ it "is an alias of Complex#arg" do
+ Complex.instance_method(:phase).should == Complex.instance_method(:arg)
+ end
end
diff --git a/spec/ruby/core/complex/polar_spec.rb b/spec/ruby/core/complex/polar_spec.rb
index 2a5d8ebd69..824211fcd0 100644
--- a/spec/ruby/core/complex/polar_spec.rb
+++ b/spec/ruby/core/complex/polar_spec.rb
@@ -7,8 +7,22 @@ describe "Complex.polar" do
end
it "raises a TypeError when given non real arguments" do
- ->{ Complex.polar(nil) }.should raise_error(TypeError)
- ->{ Complex.polar(nil, nil) }.should raise_error(TypeError)
+ ->{ Complex.polar(nil) }.should.raise(TypeError)
+ ->{ Complex.polar(nil, nil) }.should.raise(TypeError)
+ end
+
+ it "computes the real values of the real & imaginary parts from the polar form" do
+ a = Complex.polar(1.0+0.0i, Math::PI/2+0.0i)
+ a.real.should be_close(0.0, TOLERANCE)
+ a.imag.should be_close(1.0, TOLERANCE)
+ a.real.real?.should == true
+ a.imag.real?.should == true
+
+ b = Complex.polar(1+0.0i)
+ b.real.should be_close(1.0, TOLERANCE)
+ b.imag.should be_close(0.0, TOLERANCE)
+ b.real.real?.should == true
+ b.imag.real?.should == true
end
end
diff --git a/spec/ruby/core/complex/positive_spec.rb b/spec/ruby/core/complex/positive_spec.rb
index f1bad8608c..d2fb256538 100644
--- a/spec/ruby/core/complex/positive_spec.rb
+++ b/spec/ruby/core/complex/positive_spec.rb
@@ -4,10 +4,10 @@ describe "Complex#positive?" do
it "is undefined" do
c = Complex(1)
- c.methods.should_not include(:positive?)
+ c.methods.should_not.include?(:positive?)
-> {
c.positive?
- }.should raise_error(NoMethodError)
+ }.should.raise(NoMethodError)
end
end
diff --git a/spec/ruby/core/complex/quo_spec.rb b/spec/ruby/core/complex/quo_spec.rb
index ee6fd65c79..be0a44d532 100644
--- a/spec/ruby/core/complex/quo_spec.rb
+++ b/spec/ruby/core/complex/quo_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'shared/divide'
describe "Complex#quo" do
- it_behaves_like :complex_divide, :quo
+ it "is an alias of Complex#/" do
+ Complex.instance_method(:quo).should == Complex.instance_method(:/)
+ end
end
diff --git a/spec/ruby/core/complex/rationalize_spec.rb b/spec/ruby/core/complex/rationalize_spec.rb
index 043b8ddf2a..d49bb52def 100644
--- a/spec/ruby/core/complex/rationalize_spec.rb
+++ b/spec/ruby/core/complex/rationalize_spec.rb
@@ -2,11 +2,11 @@ require_relative '../../spec_helper'
describe "Complex#rationalize" do
it "raises RangeError if self has non-zero imaginary part" do
- -> { Complex(1,5).rationalize }.should raise_error(RangeError)
+ -> { Complex(1,5).rationalize }.should.raise(RangeError)
end
it "raises RangeError if self has 0.0 imaginary part" do
- -> { Complex(1,0.0).rationalize }.should raise_error(RangeError)
+ -> { Complex(1,0.0).rationalize }.should.raise(RangeError)
end
it "returns a Rational if self has zero imaginary part" do
@@ -25,7 +25,7 @@ describe "Complex#rationalize" do
end
it "raises ArgumentError when passed more than one argument" do
- -> { Complex(1,0).rationalize(0.1, 0.1) }.should raise_error(ArgumentError)
- -> { Complex(1,0).rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError)
+ -> { Complex(1,0).rationalize(0.1, 0.1) }.should.raise(ArgumentError)
+ -> { Complex(1,0).rationalize(0.1, 0.1, 2) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/complex/real_spec.rb b/spec/ruby/core/complex/real_spec.rb
index 2ea791c005..41734b23f0 100644
--- a/spec/ruby/core/complex/real_spec.rb
+++ b/spec/ruby/core/complex/real_spec.rb
@@ -11,18 +11,18 @@ end
describe "Complex#real?" do
it "returns false if there is an imaginary part" do
- Complex(2,3).real?.should be_false
+ Complex(2,3).real?.should == false
end
it "returns false if there is not an imaginary part" do
- Complex(2).real?.should be_false
+ Complex(2).real?.should == false
end
it "returns false if the real part is Infinity" do
- Complex(infinity_value).real?.should be_false
+ Complex(infinity_value).real?.should == false
end
it "returns false if the real part is NaN" do
- Complex(nan_value).real?.should be_false
+ Complex(nan_value).real?.should == false
end
end
diff --git a/spec/ruby/core/complex/rect_spec.rb b/spec/ruby/core/complex/rect_spec.rb
index 9e95f3efc2..72f2bf9930 100644
--- a/spec/ruby/core/complex/rect_spec.rb
+++ b/spec/ruby/core/complex/rect_spec.rb
@@ -1,10 +1,13 @@
require_relative '../../spec_helper'
-require_relative 'shared/rect'
describe "Complex#rect" do
- it_behaves_like :complex_rect, :rect
+ it "is an alias of Complex#rectangular" do
+ Complex.instance_method(:rect).should == Complex.instance_method(:rectangular)
+ end
end
describe "Complex.rect" do
- it_behaves_like :complex_rect_class, :rect
+ it "is an alias of Complex#rectangular" do
+ Complex.method(:rect).should == Complex.method(:rectangular)
+ end
end
diff --git a/spec/ruby/core/complex/rectangular_spec.rb b/spec/ruby/core/complex/rectangular_spec.rb
index d4b8ad9782..7789bf925e 100644
--- a/spec/ruby/core/complex/rectangular_spec.rb
+++ b/spec/ruby/core/complex/rectangular_spec.rb
@@ -1,10 +1,114 @@
require_relative '../../spec_helper'
-require_relative 'shared/rect'
describe "Complex#rectangular" do
- it_behaves_like :complex_rect, :rectangular
+ before :each do
+ @numbers = [
+ Complex(1),
+ Complex(0, 20),
+ Complex(0, 0),
+ Complex(0.0),
+ Complex(9999999**99),
+ Complex(-20),
+ Complex.polar(76, 10)
+ ]
+ end
+
+ it "returns an Array" do
+ @numbers.each do |number|
+ number.rectangular.should.instance_of?(Array)
+ end
+ end
+
+ it "returns a two-element Array" do
+ @numbers.each do |number|
+ number.rectangular.size.should == 2
+ end
+ end
+
+ it "returns the real part of self as the first element" do
+ @numbers.each do |number|
+ number.rectangular.first.should == number.real
+ end
+ end
+
+ it "returns the imaginary part of self as the last element" do
+ @numbers.each do |number|
+ number.rectangular.last.should == number.imaginary
+ end
+ end
+
+ it "raises an ArgumentError if given any arguments" do
+ @numbers.each do |number|
+ -> { number.rectangular(number) }.should.raise(ArgumentError)
+ end
+ end
end
describe "Complex.rectangular" do
- it_behaves_like :complex_rect_class, :rectangular
+ describe "passed a Numeric n which responds to #real? with true" do
+ it "returns a Complex with real part n and imaginary part 0" do
+ n = mock_numeric('n')
+ n.should_receive(:real?).any_number_of_times.and_return(true)
+ result = Complex.rectangular(n)
+ result.real.should == n
+ result.imag.should == 0
+ end
+ end
+
+ describe "passed a Numeric which responds to #real? with false" do
+ it "raises TypeError" do
+ n = mock_numeric('n')
+ n.should_receive(:real?).any_number_of_times.and_return(false)
+ -> { Complex.rectangular(n) }.should.raise(TypeError)
+ end
+ end
+
+ describe "passed Numerics n1 and n2 and at least one responds to #real? with false" do
+ [[false, false], [false, true], [true, false]].each do |r1, r2|
+ it "raises TypeError" do
+ n1 = mock_numeric('n1')
+ n2 = mock_numeric('n2')
+ n1.should_receive(:real?).any_number_of_times.and_return(r1)
+ n2.should_receive(:real?).any_number_of_times.and_return(r2)
+ -> { Complex.rectangular(n1, n2) }.should.raise(TypeError)
+ end
+ end
+ end
+
+ describe "passed Numerics n1 and n2 and both respond to #real? with true" do
+ it "returns a Complex with real part n1 and imaginary part n2" do
+ n1 = mock_numeric('n1')
+ n2 = mock_numeric('n2')
+ n1.should_receive(:real?).any_number_of_times.and_return(true)
+ n2.should_receive(:real?).any_number_of_times.and_return(true)
+ result = Complex.rectangular(n1, n2)
+ result.real.should == n1
+ result.imag.should == n2
+ end
+ end
+
+ describe "when passed a Complex" do
+ it "raises a TypeError when the imaginary part is not zero" do
+ -> {
+ Complex.rectangular(1.0+1i, 2)
+ }.should.raise(TypeError)
+
+ -> {
+ Complex.rectangular(1.0, 2i)
+ }.should.raise(TypeError)
+ end
+
+ it "ignores the imaginary part if it is zero" do
+ result = Complex.rectangular(1.0+0i, 2+0.0i)
+ result.real.should == 1.0
+ result.imag.should == 2
+ end
+ end
+
+ describe "passed a non-Numeric" do
+ it "raises TypeError" do
+ -> { Complex.rectangular(:sym) }.should.raise(TypeError)
+ -> { Complex.rectangular(0, :sym) }.should.raise(TypeError)
+ end
+ end
end
diff --git a/spec/ruby/core/complex/shared/abs.rb b/spec/ruby/core/complex/shared/abs.rb
deleted file mode 100644
index 2299479341..0000000000
--- a/spec/ruby/core/complex/shared/abs.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-describe :complex_abs, shared: true do
- it "returns the modulus: |a + bi| = sqrt((a ^ 2) + (b ^ 2))" do
- Complex(0, 0).send(@method).should == 0
- Complex(3, 4).send(@method).should == 5 # well-known integer case
- Complex(-3, 4).send(@method).should == 5
- Complex(1, -1).send(@method).should be_close(Math.sqrt(2), TOLERANCE)
- Complex(6.5, 0).send(@method).should be_close(6.5, TOLERANCE)
- Complex(0, -7.2).send(@method).should be_close(7.2, TOLERANCE)
- end
-end
diff --git a/spec/ruby/core/complex/shared/arg.rb b/spec/ruby/core/complex/shared/arg.rb
deleted file mode 100644
index c81f197433..0000000000
--- a/spec/ruby/core/complex/shared/arg.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-describe :complex_arg, shared: true do
- it "returns the argument -- i.e., the angle from (1, 0) in the complex plane" do
- two_pi = 2 * Math::PI
- (Complex(1, 0).send(@method) % two_pi).should be_close(0, TOLERANCE)
- (Complex(0, 2).send(@method) % two_pi).should be_close(Math::PI * 0.5, TOLERANCE)
- (Complex(-100, 0).send(@method) % two_pi).should be_close(Math::PI, TOLERANCE)
- (Complex(0, -75.3).send(@method) % two_pi).should be_close(Math::PI * 1.5, TOLERANCE)
- end
-end
diff --git a/spec/ruby/core/complex/shared/conjugate.rb b/spec/ruby/core/complex/shared/conjugate.rb
deleted file mode 100644
index d1ae47bcb6..0000000000
--- a/spec/ruby/core/complex/shared/conjugate.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-describe :complex_conjugate, shared: true do
- it "returns the complex conjugate: conj a + bi = a - bi" do
- Complex(3, 5).send(@method).should == Complex(3, -5)
- Complex(3, -5).send(@method).should == Complex(3, 5)
- Complex(-3.0, 5.2).send(@method).should be_close(Complex(-3.0, -5.2), TOLERANCE)
- Complex(3.0, -5.2).send(@method).should be_close(Complex(3.0, 5.2), TOLERANCE)
- end
-end
diff --git a/spec/ruby/core/complex/shared/divide.rb b/spec/ruby/core/complex/shared/divide.rb
deleted file mode 100644
index a60802c74c..0000000000
--- a/spec/ruby/core/complex/shared/divide.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-describe :complex_divide, shared: true do
- describe "with Complex" do
- it "divides according to the usual rule for complex numbers" do
- a = Complex((1 * 10) - (2 * 20), (1 * 20) + (2 * 10))
- b = Complex(1, 2)
- a.send(@method, b).should == Complex(10, 20)
-
- c = Complex((1.5 * 100.2) - (2.1 * -30.3), (1.5 * -30.3) + (2.1 * 100.2))
- d = Complex(1.5, 2.1)
- # remember the floating-point arithmetic
- c.send(@method, d).should be_close(Complex(100.2, -30.3), TOLERANCE)
- end
- end
-
- describe "with Fixnum" do
- it "divides both parts of the Complex number" do
- Complex(20, 40).send(@method, 2).should == Complex(10, 20)
- Complex(30, 30).send(@method, 10).should == Complex(3, 3)
- end
-
- it "raises a ZeroDivisionError when given zero" do
- -> { Complex(20, 40).send(@method, 0) }.should raise_error(ZeroDivisionError)
- end
-
- it "produces Rational parts" do
- Complex(5, 9).send(@method, 2).should eql(Complex(Rational(5,2), Rational(9,2)))
- end
- end
-
- describe "with Bignum" do
- it "divides both parts of the Complex number" do
- Complex(20, 40).send(@method, 2).should == Complex(10, 20)
- Complex(15, 16).send(@method, 2.0).should be_close(Complex(7.5, 8), TOLERANCE)
- end
- end
-
- describe "with Float" do
- it "divides both parts of the Complex number" do
- Complex(3, 9).send(@method, 1.5).should == Complex(2, 6)
- Complex(15, 16).send(@method, 2.0).should be_close(Complex(7.5, 8), TOLERANCE)
- end
-
- it "returns Complex(Infinity, Infinity) when given zero" do
- Complex(20, 40).send(@method, 0.0).real.infinite?.should == 1
- Complex(20, 40).send(@method, 0.0).imag.infinite?.should == 1
- Complex(-20, 40).send(@method, 0.0).real.infinite?.should == -1
- Complex(-20, 40).send(@method, 0.0).imag.infinite?.should == 1
- end
- end
-
- describe "with Object" do
- it "tries to coerce self into other" do
- value = Complex(3, 9)
-
- obj = mock("Object")
- obj.should_receive(:coerce).with(value).and_return([4, 2])
- value.send(@method, obj).should == 2
- end
- end
-
- describe "with a Numeric which responds to #real? with true" do
- it "returns Complex(real.quo(other), imag.quo(other))" do
- other = mock_numeric('other')
- real = mock_numeric('real')
- imag = mock_numeric('imag')
- other.should_receive(:real?).and_return(true)
- real.should_receive(:quo).with(other).and_return(1)
- imag.should_receive(:quo).with(other).and_return(2)
- Complex(real, imag).send(@method, other).should == Complex(1, 2)
- end
- end
-
- describe "with a Numeric which responds to #real? with false" do
- it "coerces the passed argument to Complex and divides the resulting elements" do
- complex = Complex(3, 0)
- other = mock_numeric('other')
- other.should_receive(:real?).any_number_of_times.and_return(false)
- other.should_receive(:coerce).with(complex).and_return([5, 2])
- complex.send(@method, other).should eql(Rational(5, 2))
- end
- end
-end
diff --git a/spec/ruby/core/complex/shared/image.rb b/spec/ruby/core/complex/shared/image.rb
deleted file mode 100644
index f839dbcaf9..0000000000
--- a/spec/ruby/core/complex/shared/image.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-describe :complex_image, shared: true do
- it "returns the imaginary part of self" do
- Complex(1, 0).send(@method).should == 0
- Complex(2, 1).send(@method).should == 1
- Complex(6.7, 8.9).send(@method).should == 8.9
- Complex(1, bignum_value).send(@method).should == bignum_value
- end
-end
diff --git a/spec/ruby/core/complex/shared/rect.rb b/spec/ruby/core/complex/shared/rect.rb
deleted file mode 100644
index 9f5de1ffeb..0000000000
--- a/spec/ruby/core/complex/shared/rect.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-describe :complex_rect, shared: true do
- before :each do
- @numbers = [
- Complex(1),
- Complex(0, 20),
- Complex(0, 0),
- Complex(0.0),
- Complex(9999999**99),
- Complex(-20),
- Complex.polar(76, 10)
- ]
- end
-
- it "returns an Array" do
- @numbers.each do |number|
- number.send(@method).should be_an_instance_of(Array)
- end
- end
-
- it "returns a two-element Array" do
- @numbers.each do |number|
- number.send(@method).size.should == 2
- end
- end
-
- it "returns the real part of self as the first element" do
- @numbers.each do |number|
- number.send(@method).first.should == number.real
- end
- end
-
- it "returns the imaginary part of self as the last element" do
- @numbers.each do |number|
- number.send(@method).last.should == number.imaginary
- end
- end
-
- it "raises an ArgumentError if given any arguments" do
- @numbers.each do |number|
- -> { number.send(@method, number) }.should raise_error(ArgumentError)
- end
- end
-end
-
-describe :complex_rect_class, shared: true do
- describe "passed a Numeric n which responds to #real? with true" do
- it "returns a Complex with real part n and imaginary part 0" do
- n = mock_numeric('n')
- n.should_receive(:real?).any_number_of_times.and_return(true)
- result = Complex.send(@method, n)
- result.real.should == n
- result.imag.should == 0
- end
- end
-
- describe "passed a Numeric which responds to #real? with false" do
- it "raises TypeError" do
- n = mock_numeric('n')
- n.should_receive(:real?).any_number_of_times.and_return(false)
- -> { Complex.send(@method, n) }.should raise_error(TypeError)
- end
- end
-
- describe "passed Numerics n1 and n2 and at least one responds to #real? with false" do
- [[false, false], [false, true], [true, false]].each do |r1, r2|
- it "raises TypeError" do
- n1 = mock_numeric('n1')
- n2 = mock_numeric('n2')
- n1.should_receive(:real?).any_number_of_times.and_return(r1)
- n2.should_receive(:real?).any_number_of_times.and_return(r2)
- -> { Complex.send(@method, n1, n2) }.should raise_error(TypeError)
- end
- end
- end
-
- describe "passed Numerics n1 and n2 and both respond to #real? with true" do
- it "returns a Complex with real part n1 and imaginary part n2" do
- n1 = mock_numeric('n1')
- n2 = mock_numeric('n2')
- n1.should_receive(:real?).any_number_of_times.and_return(true)
- n2.should_receive(:real?).any_number_of_times.and_return(true)
- result = Complex.send(@method, n1, n2)
- result.real.should == n1
- result.imag.should == n2
- end
- end
-
- describe "passed a non-Numeric" do
- it "raises TypeError" do
- -> { Complex.send(@method, :sym) }.should raise_error(TypeError)
- -> { Complex.send(@method, 0, :sym) }.should raise_error(TypeError)
- end
- end
-end
diff --git a/spec/ruby/core/complex/to_c_spec.rb b/spec/ruby/core/complex/to_c_spec.rb
new file mode 100644
index 0000000000..cd7195556c
--- /dev/null
+++ b/spec/ruby/core/complex/to_c_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+
+describe "Complex#to_c" do
+ it "returns self" do
+ value = Complex(1, 5)
+ value.to_c.should.equal?(value)
+ end
+
+ it 'returns the same value' do
+ Complex(1, 5).to_c.should == Complex(1, 5)
+ end
+end
diff --git a/spec/ruby/core/complex/to_f_spec.rb b/spec/ruby/core/complex/to_f_spec.rb
index 78e6526491..9f3265cdb9 100644
--- a/spec/ruby/core/complex/to_f_spec.rb
+++ b/spec/ruby/core/complex/to_f_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "Complex#to_f" do
- describe "when the imaginary part is Fixnum 0" do
+ describe "when the imaginary part is Integer 0" do
it "returns the result of sending #to_f to the real part" do
real = mock_numeric('real')
real.should_receive(:to_f).and_return(:f)
@@ -29,13 +29,13 @@ describe "Complex#to_f" do
describe "when the imaginary part is non-zero" do
it "raises RangeError" do
- -> { Complex(0, 1).to_f }.should raise_error(RangeError)
+ -> { Complex(0, 1).to_f }.should.raise(RangeError)
end
end
describe "when the imaginary part is Float 0.0" do
it "raises RangeError" do
- -> { Complex(0, 0.0).to_f }.should raise_error(RangeError)
+ -> { Complex(0, 0.0).to_f }.should.raise(RangeError)
end
end
end
diff --git a/spec/ruby/core/complex/to_i_spec.rb b/spec/ruby/core/complex/to_i_spec.rb
index 23134705ba..9149ffbbaa 100644
--- a/spec/ruby/core/complex/to_i_spec.rb
+++ b/spec/ruby/core/complex/to_i_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "Complex#to_i" do
- describe "when the imaginary part is Fixnum 0" do
+ describe "when the imaginary part is Integer 0" do
it "returns the result of sending #to_i to the real part" do
real = mock_numeric('real')
real.should_receive(:to_i).and_return(:i)
@@ -29,13 +29,13 @@ describe "Complex#to_i" do
describe "when the imaginary part is non-zero" do
it "raises RangeError" do
- -> { Complex(0, 1).to_i }.should raise_error(RangeError)
+ -> { Complex(0, 1).to_i }.should.raise(RangeError)
end
end
describe "when the imaginary part is Float 0.0" do
it "raises RangeError" do
- -> { Complex(0, 0.0).to_i }.should raise_error(RangeError)
+ -> { Complex(0, 0.0).to_i }.should.raise(RangeError)
end
end
end
diff --git a/spec/ruby/core/complex/to_r_spec.rb b/spec/ruby/core/complex/to_r_spec.rb
index 76a69a0b93..6587ae9e2e 100644
--- a/spec/ruby/core/complex/to_r_spec.rb
+++ b/spec/ruby/core/complex/to_r_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "Complex#to_r" do
- describe "when the imaginary part is Fixnum 0" do
+ describe "when the imaginary part is Integer 0" do
it "returns the result of sending #to_r to the real part" do
real = mock_numeric('real')
real.should_receive(:to_r).and_return(:r)
@@ -29,13 +29,21 @@ describe "Complex#to_r" do
describe "when the imaginary part is non-zero" do
it "raises RangeError" do
- -> { Complex(0, 1).to_r }.should raise_error(RangeError)
+ -> { Complex(0, 1).to_r }.should.raise(RangeError)
end
end
describe "when the imaginary part is Float 0.0" do
- it "raises RangeError" do
- -> { Complex(0, 0.0).to_r }.should raise_error(RangeError)
+ ruby_version_is ''...'3.4' do
+ it "raises RangeError" do
+ -> { Complex(0, 0.0).to_r }.should.raise(RangeError)
+ end
+ end
+
+ ruby_version_is '3.4' do
+ it "returns a Rational" do
+ Complex(0, 0.0).to_r.should == 0r
+ end
end
end
end
diff --git a/spec/ruby/core/complex/to_s_spec.rb b/spec/ruby/core/complex/to_s_spec.rb
index 989a7ae0b7..ceccffe470 100644
--- a/spec/ruby/core/complex/to_s_spec.rb
+++ b/spec/ruby/core/complex/to_s_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative '../numeric/fixtures/classes'
describe "Complex#to_s" do
describe "when self's real component is 0" do
@@ -41,4 +42,14 @@ describe "Complex#to_s" do
it "returns 1+NaN*i for Complex(1, NaN)" do
Complex(1, nan_value).to_s.should == "1+NaN*i"
end
+
+ it "treats real and imaginary parts as strings" do
+ real = NumericSpecs::Subclass.new
+ # + because of https://bugs.ruby-lang.org/issues/20337
+ real.should_receive(:to_s).and_return(+"1")
+ imaginary = NumericSpecs::Subclass.new
+ imaginary.should_receive(:to_s).and_return("2")
+ imaginary.should_receive(:<).any_number_of_times.and_return(false)
+ Complex(real, imaginary).to_s.should == "1+2i"
+ end
end
diff --git a/spec/ruby/core/conditionvariable/broadcast_spec.rb b/spec/ruby/core/conditionvariable/broadcast_spec.rb
new file mode 100644
index 0000000000..16c7b4cc8d
--- /dev/null
+++ b/spec/ruby/core/conditionvariable/broadcast_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+
+describe "ConditionVariable#broadcast" do
+ it "releases all threads waiting in line for this resource" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ threads = []
+ r1 = []
+ r2 = []
+
+ # large number to attempt to cause race conditions
+ 100.times do |i|
+ threads << Thread.new(i) do |tid|
+ m.synchronize do
+ r1 << tid
+ cv.wait(m)
+ r2 << tid
+ end
+ end
+ end
+
+ # wait for all threads to acquire the mutex the first time
+ Thread.pass until m.synchronize { r1.size == threads.size }
+ # wait until all threads are sleeping (ie waiting)
+ Thread.pass until threads.all?(&:stop?)
+
+ r2.should.empty?
+ m.synchronize do
+ cv.broadcast
+ end
+
+ threads.each {|t| t.join }
+
+ # ensure that all threads that enter cv.wait are released
+ r2.sort.should == r1.sort
+ # note that order is not specified as broadcast results in a race
+ # condition on regaining the lock m
+ end
+end
diff --git a/spec/ruby/core/conditionvariable/marshal_dump_spec.rb b/spec/ruby/core/conditionvariable/marshal_dump_spec.rb
new file mode 100644
index 0000000000..594c178e88
--- /dev/null
+++ b/spec/ruby/core/conditionvariable/marshal_dump_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+
+describe "ConditionVariable#marshal_dump" do
+ it "raises a TypeError" do
+ cv = ConditionVariable.new
+ -> { cv.marshal_dump }.should.raise(TypeError, /can't dump/)
+ end
+end
diff --git a/spec/ruby/core/conditionvariable/signal_spec.rb b/spec/ruby/core/conditionvariable/signal_spec.rb
new file mode 100644
index 0000000000..3b266cf8c6
--- /dev/null
+++ b/spec/ruby/core/conditionvariable/signal_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../spec_helper'
+
+describe "ConditionVariable#signal" do
+ it "releases the first thread waiting in line for this resource" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ threads = []
+ r1 = []
+ r2 = []
+
+ # large number to attempt to cause race conditions
+ 100.times do |i|
+ threads << Thread.new(i) do |tid|
+ m.synchronize do
+ r1 << tid
+ cv.wait(m)
+ r2 << tid
+ end
+ end
+ end
+
+ # wait for all threads to acquire the mutex the first time
+ Thread.pass until m.synchronize { r1.size == threads.size }
+ # wait until all threads are sleeping (ie waiting)
+ Thread.pass until threads.all?(&:stop?)
+
+ r2.should.empty?
+ 100.times do |i|
+ m.synchronize do
+ cv.signal
+ end
+ Thread.pass until r2.size == i+1
+ end
+
+ threads.each {|t| t.join }
+
+ # ensure that all the threads that went into the cv.wait are
+ # released in the same order
+ r2.should == r1
+ end
+
+ it "allows control to be passed between a pair of threads" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ repeats = 100
+ in_synchronize = false
+
+ t1 = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ repeats.times do
+ cv.wait(m)
+ cv.signal
+ end
+ end
+ end
+
+ # Make sure t1 is waiting for a signal before launching t2.
+ Thread.pass until in_synchronize
+ Thread.pass until t1.stop?
+
+ t2 = Thread.new do
+ m.synchronize do
+ repeats.times do
+ cv.signal
+ cv.wait(m)
+ end
+ end
+ end
+
+ # Check that both threads terminated without exception
+ t1.join
+ t2.join
+ m.should_not.locked?
+ end
+end
diff --git a/spec/ruby/core/conditionvariable/wait_spec.rb b/spec/ruby/core/conditionvariable/wait_spec.rb
new file mode 100644
index 0000000000..1af53a15a2
--- /dev/null
+++ b/spec/ruby/core/conditionvariable/wait_spec.rb
@@ -0,0 +1,174 @@
+require_relative '../../spec_helper'
+
+describe "ConditionVariable#wait" do
+ it "calls #sleep on the given object" do
+ o = Object.new
+ o.should_receive(:sleep).with(1234)
+
+ cv = ConditionVariable.new
+
+ cv.wait(o, 1234)
+ end
+
+ it "can be woken up by ConditionVariable#signal" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ in_synchronize = false
+
+ th = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ cv.wait(m)
+ end
+ :success
+ end
+
+ # wait for m to acquire the mutex
+ Thread.pass until in_synchronize
+ # wait until th is sleeping (ie waiting)
+ Thread.pass until th.stop?
+
+ m.synchronize { cv.signal }
+ th.value.should == :success
+ end
+
+ it "can be interrupted by Thread#run" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ in_synchronize = false
+
+ th = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ cv.wait(m)
+ end
+ :success
+ end
+
+ # wait for m to acquire the mutex
+ Thread.pass until in_synchronize
+ # wait until th is sleeping (ie waiting)
+ Thread.pass until th.stop?
+
+ th.run
+ th.value.should == :success
+ end
+
+ it "can be interrupted by Thread#wakeup" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ in_synchronize = false
+
+ th = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ cv.wait(m)
+ end
+ :success
+ end
+
+ # wait for m to acquire the mutex
+ Thread.pass until in_synchronize
+ # wait until th is sleeping (ie waiting)
+ Thread.pass until th.stop?
+
+ th.wakeup
+ th.value.should == :success
+ end
+
+ it "reacquires the lock even if the thread is killed" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ in_synchronize = false
+ owned = nil
+
+ th = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ begin
+ cv.wait(m)
+ ensure
+ owned = m.owned?
+ $stderr.puts "\nThe Thread doesn't own the Mutex!" unless owned
+ end
+ end
+ end
+
+ # wait for m to acquire the mutex
+ Thread.pass until in_synchronize
+ # wait until th is sleeping (ie waiting)
+ Thread.pass until th.stop?
+
+ th.kill
+ th.join
+
+ owned.should == true
+ end
+
+ it "reacquires the lock even if the thread is killed after being signaled" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ in_synchronize = false
+ owned = nil
+
+ th = Thread.new do
+ m.synchronize do
+ in_synchronize = true
+ begin
+ cv.wait(m)
+ ensure
+ owned = m.owned?
+ $stderr.puts "\nThe Thread doesn't own the Mutex!" unless owned
+ end
+ end
+ end
+
+ # wait for m to acquire the mutex
+ Thread.pass until in_synchronize
+ # wait until th is sleeping (ie waiting)
+ Thread.pass until th.stop?
+
+ m.synchronize {
+ cv.signal
+ # Wait that the thread is blocked on acquiring the Mutex
+ sleep 0.001
+ # Kill the thread, yet the thread should first acquire the Mutex before going on
+ th.kill
+ }
+
+ th.join
+ owned.should == true
+ end
+
+ it "supports multiple Threads waiting on the same ConditionVariable and Mutex" do
+ m = Mutex.new
+ cv = ConditionVariable.new
+ n_threads = 4
+ events = []
+
+ threads = n_threads.times.map {
+ Thread.new {
+ m.synchronize {
+ events << :t_in_synchronize
+ cv.wait(m)
+ }
+ }
+ }
+
+ Thread.pass until m.synchronize { events.size } == n_threads
+ Thread.pass until threads.any?(&:stop?)
+ m.synchronize do
+ threads.each { |t|
+ # Cause interactions with the waiting threads.
+ # On TruffleRuby, this causes a safepoint which has interesting
+ # interactions with the ConditionVariable.
+ bt = t.backtrace
+ bt.should.is_a?(Array)
+ bt.size.should >= 2
+ }
+ end
+
+ cv.broadcast
+ threads.each(&:join)
+ end
+end
diff --git a/spec/ruby/core/data/constants_spec.rb b/spec/ruby/core/data/constants_spec.rb
index 0e47a82e26..ad0b1ddea7 100644
--- a/spec/ruby/core/data/constants_spec.rb
+++ b/spec/ruby/core/data/constants_spec.rb
@@ -1,13 +1,11 @@
require_relative '../../spec_helper'
describe "Data" do
- it "is a subclass of Object" do
- suppress_warning do
- Data.superclass.should == Object
- end
+ it "is a new constant" do
+ Data.superclass.should == Object
end
- it "is deprecated" do
- -> { Data }.should complain(/constant ::Data is deprecated/)
+ it "is not deprecated" do
+ -> { Data }.should_not complain
end
end
diff --git a/spec/ruby/core/data/deconstruct_keys_spec.rb b/spec/ruby/core/data/deconstruct_keys_spec.rb
new file mode 100644
index 0000000000..7e81f966ea
--- /dev/null
+++ b/spec/ruby/core/data/deconstruct_keys_spec.rb
@@ -0,0 +1,110 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#deconstruct_keys" do
+ it "returns a hash of attributes" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ d.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
+ end
+
+ it "requires one argument" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ -> {
+ d.deconstruct_keys
+ }.should.raise(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
+ end
+
+ it "returns only specified keys" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ d.deconstruct_keys([:x, :y]).should == {x: 1, y: 2}
+ d.deconstruct_keys([:x] ).should == {x: 1}
+ d.deconstruct_keys([] ).should == {}
+ end
+
+ it "accepts string attribute names" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ d.deconstruct_keys(['x', 'y']).should == {'x' => 1, 'y' => 2}
+ end
+
+ it "returns an empty hash when there are more keys than attributes" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ d.deconstruct_keys([:x, :y, :x]).should == {}
+ end
+
+ it "returns at first not existing attribute name" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ d.deconstruct_keys([:a, :x]).should == {}
+ d.deconstruct_keys([:x, :a]).should == {x: 1}
+ end
+
+ it "accepts nil argument and return all the attributes" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ d.deconstruct_keys(nil).should == {x: 1, y: 2}
+ end
+
+ ruby_version_is "4.0" do # https://bugs.ruby-lang.org/issues/21844
+ it "tries to convert a key with #to_str if index is not a String nor a Symbol, but responds to #to_str" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ key = mock("to_str")
+ key.should_receive(:to_str).and_return("y")
+
+ d.deconstruct_keys([key]).should == { "y" => 2 }
+ end
+
+ it "raise an error on argument position number" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ -> {
+ d.deconstruct_keys([0, 1])
+ }.should.raise(TypeError, "0 is not a symbol nor a string")
+ end
+
+ it "raises a TypeError if the conversion with #to_str does not return a String" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ key = mock("to_str")
+ key.should_receive(:to_str).and_return(0)
+
+ -> {
+ d.deconstruct_keys([key])
+ }.should raise_consistent_error(TypeError, /can't convert MockObject into String/)
+ end
+
+ it "raises TypeError if index is not a Symbol and not convertible to String" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ -> {
+ d.deconstruct_keys([0, []])
+ }.should.raise(TypeError, "0 is not a symbol nor a string")
+ end
+ end
+
+ it "raise TypeError if passed anything except nil or array" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ -> { d.deconstruct_keys('x') }.should.raise(TypeError, /expected Array or nil/)
+ -> { d.deconstruct_keys(1) }.should.raise(TypeError, /expected Array or nil/)
+ -> { d.deconstruct_keys(:x) }.should.raise(TypeError, /expected Array or nil/)
+ -> { d.deconstruct_keys({}) }.should.raise(TypeError, /expected Array or nil/)
+ end
+end
diff --git a/spec/ruby/core/data/deconstruct_spec.rb b/spec/ruby/core/data/deconstruct_spec.rb
new file mode 100644
index 0000000000..4ca0b87039
--- /dev/null
+++ b/spec/ruby/core/data/deconstruct_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#deconstruct" do
+ it "returns an array of attribute values" do
+ DataSpecs::Measure.new(42, "km").deconstruct.should == [42, "km"]
+ end
+end
diff --git a/spec/ruby/core/data/define_spec.rb b/spec/ruby/core/data/define_spec.rb
new file mode 100644
index 0000000000..c0b4671e39
--- /dev/null
+++ b/spec/ruby/core/data/define_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data.define" do
+ it "accepts no arguments" do
+ empty_data = Data.define
+ empty_data.members.should == []
+ end
+
+ it "accepts symbols" do
+ movie = Data.define(:title, :year)
+ movie.members.should == [:title, :year]
+ end
+
+ it "accepts strings" do
+ movie = Data.define("title", "year")
+ movie.members.should == [:title, :year]
+ end
+
+ it "accepts a mix of strings and symbols" do
+ movie = Data.define("title", :year, "genre")
+ movie.members.should == [:title, :year, :genre]
+ end
+
+ it "accepts a block" do
+ movie = Data.define(:title, :year) do
+ def title_with_year
+ "#{title} (#{year})"
+ end
+ end
+ movie.members.should == [:title, :year]
+ movie.new("Matrix", 1999).title_with_year.should == "Matrix (1999)"
+ end
+end
diff --git a/spec/ruby/core/data/eql_spec.rb b/spec/ruby/core/data/eql_spec.rb
new file mode 100644
index 0000000000..6958d5de4a
--- /dev/null
+++ b/spec/ruby/core/data/eql_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#eql?" do
+ it "returns true if the other is the same object" do
+ a = DataSpecs::Measure.new(42, "km")
+ a.should.eql?(a)
+ end
+
+ it "returns true if the other has all the same fields" do
+ a = DataSpecs::Measure.new(42, "km")
+ b = DataSpecs::Measure.new(42, "km")
+ a.should.eql?(b)
+ end
+
+ it "returns false if the other is a different object or has different fields" do
+ a = DataSpecs::Measure.new(42, "km")
+ b = DataSpecs::Measure.new(42, "mi")
+ a.should_not.eql?(b)
+ end
+
+ it "returns false if other is of a different class" do
+ a = DataSpecs::Measure.new(42, "km")
+ klass = Data.define(*DataSpecs::Measure.members)
+ b = klass.new(42, "km")
+ a.should_not.eql?(b)
+ end
+
+ it "returns false if any corresponding elements are not equal with #eql?" do
+ a = DataSpecs::Measure.new(42, "km")
+ b = DataSpecs::Measure.new(42.0, "mi")
+ a.should_not.eql?(b)
+ end
+
+ context "recursive structure" do
+ it "returns true the other is the same object" do
+ a = DataSpecs::Measure.allocate
+ a.send(:initialize, amount: 42, unit: a)
+
+ a.should.eql?(a)
+ end
+
+ it "returns true if the other has all the same fields" do
+ a = DataSpecs::Measure.allocate
+ a.send(:initialize, amount: 42, unit: a)
+
+ b = DataSpecs::Measure.allocate
+ b.send(:initialize, amount: 42, unit: b)
+
+ a.should.eql?(b)
+ end
+
+ it "returns false if any corresponding elements are not equal with #eql?" do
+ a = DataSpecs::Measure.allocate
+ a.send(:initialize, amount: a, unit: "km")
+
+ b = DataSpecs::Measure.allocate
+ b.send(:initialize, amount: b, unit: "mi")
+
+ a.should_not.eql?(b)
+ end
+ end
+end
diff --git a/spec/ruby/core/data/equal_value_spec.rb b/spec/ruby/core/data/equal_value_spec.rb
new file mode 100644
index 0000000000..d9a0dcff3e
--- /dev/null
+++ b/spec/ruby/core/data/equal_value_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#==" do
+ it "returns true if the other is the same object" do
+ a = DataSpecs::Measure.new(42, "km")
+ a.should == a
+ end
+
+ it "returns true if the other has all the same fields" do
+ a = DataSpecs::Measure.new(42, "km")
+ b = DataSpecs::Measure.new(42, "km")
+ a.should == b
+ end
+
+ it "returns false if the other is a different object or has different fields" do
+ a = DataSpecs::Measure.new(42, "km")
+ b = DataSpecs::Measure.new(42, "mi")
+ a.should_not == b
+ end
+
+ it "returns false if other is of a different class" do
+ a = DataSpecs::Measure.new(42, "km")
+ klass = Data.define(*DataSpecs::Measure.members)
+ b = klass.new(42, "km")
+ a.should_not == b
+ end
+
+ it "returns false if any corresponding elements are not equal with #==" do
+ a = DataSpecs::Measure.new(42, "km")
+ b = DataSpecs::Measure.new(42.0, "mi")
+ a.should_not == b
+ end
+
+ context "recursive structure" do
+ it "returns true the other is the same object" do
+ a = DataSpecs::Measure.allocate
+ a.send(:initialize, amount: 42, unit: a)
+
+ a.should == a
+ end
+
+ it "returns true if the other has all the same fields" do
+ a = DataSpecs::Measure.allocate
+ a.send(:initialize, amount: 42, unit: a)
+
+ b = DataSpecs::Measure.allocate
+ b.send(:initialize, amount: 42, unit: b)
+
+ a.should == b
+ end
+
+ it "returns false if any corresponding elements are not equal with #==" do
+ a = DataSpecs::Measure.allocate
+ a.send(:initialize, amount: a, unit: "km")
+
+ b = DataSpecs::Measure.allocate
+ b.send(:initialize, amount: b, unit: "mi")
+
+ a.should_not == b
+ end
+ end
+end
diff --git a/spec/ruby/core/data/fixtures/classes.rb b/spec/ruby/core/data/fixtures/classes.rb
new file mode 100644
index 0000000000..147293ee45
--- /dev/null
+++ b/spec/ruby/core/data/fixtures/classes.rb
@@ -0,0 +1,41 @@
+module DataSpecs
+ if Data.respond_to?(:define)
+ Measure = Data.define(:amount, :unit)
+ Single = Data.define(:value)
+
+ class MeasureWithOverriddenName < Measure
+ def self.name
+ "A"
+ end
+ end
+
+ class SingleWithOverriddenName < Single
+ def self.name
+ "A"
+ end
+ end
+
+ class DataSubclass < Data; end
+
+ MeasureSubclass = Class.new(Measure) do
+ def initialize(amount:, unit:)
+ super
+ end
+ end
+
+ Empty = Data.define()
+
+ DataWithOverriddenInitialize = Data.define(:amount, :unit) do
+ def initialize(*rest, **kw)
+ super
+ ScratchPad.record [:initialize, rest, kw]
+ end
+ end
+
+ Area = Data.define(:width, :height, :area) do
+ def initialize(width:, height:)
+ super(width: width, height: height, area: width * height)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/data/hash_spec.rb b/spec/ruby/core/data/hash_spec.rb
new file mode 100644
index 0000000000..bab146c92e
--- /dev/null
+++ b/spec/ruby/core/data/hash_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#hash" do
+ it "returns the same integer for objects with the same content" do
+ a = DataSpecs::Measure.new(42, "km")
+ b = DataSpecs::Measure.new(42, "km")
+ a.hash.should == b.hash
+ a.hash.should.instance_of?(Integer)
+ end
+
+ it "returns different hashes for objects with different values" do
+ a = DataSpecs::Measure.new(42, "km")
+ b = DataSpecs::Measure.new(42, "ml")
+ a.hash.should_not == b.hash
+
+ a = DataSpecs::Measure.new(42, "km")
+ b = DataSpecs::Measure.new(13, "km")
+ a.hash.should_not == b.hash
+ end
+
+ it "returns different hashes for different classes" do
+ Data.define(:x).new(1).hash.should != Data.define(:x).new(1).hash
+ end
+end
diff --git a/spec/ruby/core/data/initialize_spec.rb b/spec/ruby/core/data/initialize_spec.rb
new file mode 100644
index 0000000000..0320ca880c
--- /dev/null
+++ b/spec/ruby/core/data/initialize_spec.rb
@@ -0,0 +1,204 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#initialize" do
+ context "with no members" do
+ ruby_bug "#21819", ""..."4.0.1" do
+ it "is frozen" do
+ data = Data.define
+
+ data.new.should.frozen?
+ end
+ end
+ end
+
+ it "accepts positional arguments" do
+ data = DataSpecs::Measure.new(42, "km")
+
+ data.amount.should == 42
+ data.unit.should == "km"
+ end
+
+ it "accepts alternative positional arguments" do
+ data = DataSpecs::Measure[42, "km"]
+
+ data.amount.should == 42
+ data.unit.should == "km"
+ end
+
+ it "accepts keyword arguments" do
+ data = DataSpecs::Measure.new(amount: 42, unit: "km")
+
+ data.amount.should == 42
+ data.unit.should == "km"
+ end
+
+ it "accepts alternative keyword arguments" do
+ data = DataSpecs::Measure[amount: 42, unit: "km"]
+
+ data.amount.should == 42
+ data.unit.should == "km"
+ end
+
+ it "accepts String keyword arguments" do
+ data = DataSpecs::Measure.new("amount" => 42, "unit" => "km")
+
+ data.amount.should == 42
+ data.unit.should == "km"
+ end
+
+ it "accepts the last entry when a keyword is given as both String and Symbol" do
+ data = DataSpecs::Single.new("value" => -1, value: 42)
+
+ data.value.should == 42
+ end
+
+ it "accepts positional arguments with empty keyword arguments" do
+ data = DataSpecs::Single.new(42, **{})
+
+ data.value.should == 42
+
+ data = DataSpecs::Measure.new(42, "km", **{})
+
+ data.amount.should == 42
+ data.unit.should == "km"
+ end
+
+ it "raises ArgumentError if no arguments are given" do
+ -> {
+ DataSpecs::Measure.new
+ }.should.raise(ArgumentError) { |e|
+ e.message.should.include?("missing keywords: :amount, :unit")
+ }
+ end
+
+ it "raises ArgumentError if at least one argument is missing" do
+ -> {
+ DataSpecs::Measure.new(unit: "km")
+ }.should.raise(ArgumentError) { |e|
+ e.message.should.include?("missing keyword: :amount")
+ }
+ end
+
+ ruby_version_is "4.0" do # https://bugs.ruby-lang.org/issues/21844
+ it "raises ArgumentError if at least one argument is missing and other is provided as both String and Symbol" do
+ -> {
+ DataSpecs::Measure.new(unit: "km", "unit" => "km")
+ }.should.raise(ArgumentError) { |e|
+ e.message.should.include?("missing keyword: :amount")
+ }
+ end
+ end
+
+ it "raises ArgumentError if unknown keyword is given" do
+ -> {
+ DataSpecs::Measure.new(amount: 42, unit: "km", system: "metric")
+ }.should.raise(ArgumentError) { |e|
+ e.message.should.include?("unknown keyword: :system")
+ }
+ end
+
+ ruby_version_is "4.0" do # https://bugs.ruby-lang.org/issues/21844
+ it "raises ArgumentError if unknown keyword is given which is convertable to String" do
+ key = mock("to_str")
+ key.should_receive(:to_str).and_return("system")
+
+ -> {
+ DataSpecs::Measure.new(amount: 42, unit: "km", key => "metric")
+ }.should.raise(ArgumentError) { |e|
+ e.message.should.include?('unknown keyword: "system"')
+ }
+ end
+
+ it "raises TypeError when the keyword is not convertable to String" do
+ -> {
+ DataSpecs::Measure.new(1 => 2)
+ }.should.raise(TypeError) { |e|
+ e.message.should == "1 is not a symbol nor a string"
+ }
+ end
+
+ it "raises TypeError if the conversion with #to_str does not return a String" do
+ klass = Data.define(:x, :y)
+
+ key = mock("to_str")
+ key.should_receive(:to_str).and_return(0)
+
+ -> {
+ klass.new(key => 2)
+ }.should raise_consistent_error(TypeError, /can't convert MockObject into String/)
+ end
+ end
+
+ it "supports super from a subclass" do
+ ms = DataSpecs::MeasureSubclass.new(amount: 1, unit: "km")
+
+ ms.amount.should == 1
+ ms.unit.should == "km"
+ end
+
+ it "supports Data with no fields" do
+ -> { DataSpecs::Empty.new }.should_not.raise
+ end
+
+ it "can be overridden" do
+ ScratchPad.record []
+
+ measure_class = Data.define(:amount, :unit) do
+ def initialize(*, **)
+ super
+ ScratchPad << :initialize
+ end
+ end
+
+ measure_class.new(42, "m")
+ ScratchPad.recorded.should == [:initialize]
+ end
+
+ context "when it is overridden" do
+ it "is called with keyword arguments when given positional arguments" do
+ ScratchPad.clear
+ DataSpecs::DataWithOverriddenInitialize.new(42, "m")
+ ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}]
+ end
+
+ it "is called with keyword arguments when given keyword arguments" do
+ ScratchPad.clear
+ DataSpecs::DataWithOverriddenInitialize.new(amount: 42, unit: "m")
+ ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}]
+ end
+
+ it "is called with keyword arguments when given alternative positional arguments" do
+ ScratchPad.clear
+ DataSpecs::DataWithOverriddenInitialize[42, "m"]
+ ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}]
+ end
+
+ it "is called with keyword arguments when given alternative keyword arguments" do
+ ScratchPad.clear
+ DataSpecs::DataWithOverriddenInitialize[amount: 42, unit: "m"]
+ ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}]
+ end
+
+ it "accepts positional arguments with empty keyword arguments" do
+ data = DataSpecs::SingleWithOverriddenName.new(42, **{})
+
+ data.value.should == 42
+
+ data = DataSpecs::MeasureWithOverriddenName.new(42, "km", **{})
+
+ data.amount.should == 42
+ data.unit.should == "km"
+ end
+
+ # See https://github.com/ruby/psych/pull/765
+ it "can be deserialized by calling Data.instance_method(:initialize)" do
+ d1 = DataSpecs::Area.new(width: 2, height: 3)
+ d1.area.should == 6
+
+ d2 = DataSpecs::Area.allocate
+ Data.instance_method(:initialize).bind_call(d2, **d1.to_h)
+ d2.should == d1
+ end
+ end
+end
diff --git a/spec/ruby/core/data/inspect_spec.rb b/spec/ruby/core/data/inspect_spec.rb
new file mode 100644
index 0000000000..6c97a719e3
--- /dev/null
+++ b/spec/ruby/core/data/inspect_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#inspect" do
+ it "returns a string representation showing members and values" do
+ a = DataSpecs::Measure.new(42, "km")
+ a.inspect.should == '#<data DataSpecs::Measure amount=42, unit="km">'
+ end
+
+ it "returns a string representation without the class name for anonymous structs" do
+ Data.define(:a).new("").inspect.should == '#<data a="">'
+ end
+
+ it "returns a string representation without the class name for structs nested in anonymous classes" do
+ c = Class.new
+ c.class_eval <<~DOC
+ Foo = Data.define(:a)
+ DOC
+
+ c::Foo.new("").inspect.should == '#<data a="">'
+ end
+
+ it "returns a string representation without the class name for structs nested in anonymous modules" do
+ m = Module.new
+ m.class_eval <<~DOC
+ Foo = Data.define(:a)
+ DOC
+
+ m::Foo.new("").inspect.should == '#<data a="">'
+ end
+
+ it "does not call #name method" do
+ struct = DataSpecs::MeasureWithOverriddenName.new(42, "km")
+ struct.inspect.should == '#<data DataSpecs::MeasureWithOverriddenName amount=42, unit="km">'
+ end
+
+ it "does not call #name method when struct is anonymous" do
+ klass = Class.new(DataSpecs::Measure) do
+ def self.name
+ "A"
+ end
+ end
+ struct = klass.new(42, "km")
+ struct.inspect.should == '#<data amount=42, unit="km">'
+ end
+
+ context "recursive structure" do
+ it "returns string representation with recursive attribute replaced with ..." do
+ a = DataSpecs::Measure.allocate
+ a.send(:initialize, amount: 42, unit: a)
+
+ a.inspect.should == "#<data DataSpecs::Measure amount=42, unit=#<data DataSpecs::Measure:...>>"
+ end
+
+ it "returns string representation with recursive attribute replaced with ... when an anonymous class" do
+ klass = Class.new(DataSpecs::Measure)
+ a = klass.allocate
+ a.send(:initialize, amount: 42, unit: a)
+
+ a.inspect.should =~ /#<data amount=42, unit=#<data #<Class:0x.+?>:\.\.\.>>/
+ end
+ end
+end
diff --git a/spec/ruby/core/data/members_spec.rb b/spec/ruby/core/data/members_spec.rb
new file mode 100644
index 0000000000..457a90a0d6
--- /dev/null
+++ b/spec/ruby/core/data/members_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#members" do
+ it "returns an array of attribute names" do
+ measure = DataSpecs::Measure.new(amount: 42, unit: 'km')
+ measure.members.should == [:amount, :unit]
+ end
+end
+
+describe "DataClass#members" do
+ it "returns an array of attribute names" do
+ DataSpecs::Measure.members.should == [:amount, :unit]
+ end
+
+ context "class inheriting Data" do
+ it "isn't available in a subclass" do
+ DataSpecs::DataSubclass.should_not.respond_to?(:members)
+ end
+ end
+end
diff --git a/spec/ruby/core/data/to_h_spec.rb b/spec/ruby/core/data/to_h_spec.rb
new file mode 100644
index 0000000000..41925cf3b2
--- /dev/null
+++ b/spec/ruby/core/data/to_h_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#to_h" do
+ it "transforms the data object into a hash" do
+ data = DataSpecs::Measure.new(amount: 42, unit: 'km')
+ data.to_h.should == { amount: 42, unit: 'km' }
+ end
+
+ context "with block" do
+ it "transforms [key, value] pairs returned by the block into a hash" do
+ data = DataSpecs::Measure.new(amount: 42, unit: 'km')
+ data.to_h { |key, value| [value, key] }.should == { 42 => :amount, 'km' => :unit }
+ end
+
+ it "passes to a block each pair's key and value as separate arguments" do
+ ScratchPad.record []
+ data = DataSpecs::Measure.new(amount: 42, unit: 'km')
+ data.to_h { |k, v| ScratchPad << [k, v]; [k, v] }
+ ScratchPad.recorded.sort.should == [[:amount, 42], [:unit, 'km']]
+
+ ScratchPad.record []
+ data.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
+ ScratchPad.recorded.sort.should == [[:amount, 42], [:unit, 'km']]
+ end
+
+ it "raises ArgumentError if block returns longer or shorter array" do
+ data = DataSpecs::Measure.new(amount: 42, unit: 'km')
+ -> do
+ data.to_h { |k, v| [k.to_s, v*v, 1] }
+ end.should.raise(ArgumentError, /element has wrong array length/)
+
+ -> do
+ data.to_h { |k, v| [k] }
+ end.should.raise(ArgumentError, /element has wrong array length/)
+ end
+
+ it "raises TypeError if block returns something other than Array" do
+ data = DataSpecs::Measure.new(amount: 42, unit: 'km')
+ -> do
+ data.to_h { |k, v| "not-array" }
+ end.should.raise(TypeError, /wrong element type String/)
+ end
+
+ it "coerces returned pair to Array with #to_ary" do
+ x = mock('x')
+ x.stub!(:to_ary).and_return([:b, 'b'])
+ data = DataSpecs::Measure.new(amount: 42, unit: 'km')
+
+ data.to_h { |k| x }.should == { :b => 'b' }
+ end
+
+ it "does not coerce returned pair to Array with #to_a" do
+ x = mock('x')
+ x.stub!(:to_a).and_return([:b, 'b'])
+ data = DataSpecs::Measure.new(amount: 42, unit: 'km')
+
+ -> do
+ data.to_h { |k| x }
+ end.should.raise(TypeError, /wrong element type MockObject/)
+ end
+ end
+end
diff --git a/spec/ruby/core/data/to_s_spec.rb b/spec/ruby/core/data/to_s_spec.rb
new file mode 100644
index 0000000000..a552e4659b
--- /dev/null
+++ b/spec/ruby/core/data/to_s_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#to_s" do
+ it "is an alias of Data#inspect" do
+ a = DataSpecs::Measure.new(42, "km")
+ a.method(:to_s).should == a.method(:inspect)
+ end
+end
diff --git a/spec/ruby/core/data/with_spec.rb b/spec/ruby/core/data/with_spec.rb
new file mode 100644
index 0000000000..b74df185c7
--- /dev/null
+++ b/spec/ruby/core/data/with_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Data#with" do
+ it "returns self if given no arguments" do
+ data = DataSpecs::Measure.new(amount: 42, unit: "km")
+ data = data.with.should.equal?(data)
+ end
+
+ it "accepts keyword arguments" do
+ data = DataSpecs::Measure.new(amount: 42, unit: "km")
+ data = data.with(amount: 4, unit: "m")
+
+ data.amount.should == 4
+ data.unit.should == "m"
+ end
+
+ it "accepts String keyword arguments" do
+ data = DataSpecs::Measure.new(amount: 42, unit: "km")
+ data = data.with("amount" => 4, "unit" => "m")
+
+ data.amount.should == 4
+ data.unit.should == "m"
+ end
+
+ it "raises ArgumentError if no keyword arguments are given" do
+ data = DataSpecs::Measure.new(amount: 42, unit: "km")
+
+ -> {
+ data.with(4, "m")
+ }.should.raise(ArgumentError, "wrong number of arguments (given 2, expected 0)")
+ end
+
+ it "does not depend on the Data.new method" do
+ subclass = Class.new(DataSpecs::Measure)
+ data = subclass.new(amount: 42, unit: "km")
+
+ def subclass.new(*)
+ raise "Data.new is called"
+ end
+
+ data_copy = data.with(unit: "m")
+ data_copy.amount.should == 42
+ data_copy.unit.should == "m"
+ end
+
+ it "calls #initialize" do
+ data = DataSpecs::DataWithOverriddenInitialize.new(42, "m")
+ ScratchPad.clear
+
+ data.with(amount: 0)
+
+ ScratchPad.recorded.should == [:initialize, [], {amount: 0, unit: "m"}]
+ end
+end
diff --git a/spec/ruby/core/dir/chdir_spec.rb b/spec/ruby/core/dir/chdir_spec.rb
index 729ac403e3..2dc598e2a9 100644
--- a/spec/ruby/core/dir/chdir_spec.rb
+++ b/spec/ruby/core/dir/chdir_spec.rb
@@ -19,14 +19,14 @@ describe "Dir.chdir" do
end
it "defaults to $HOME with no arguments" do
- if ENV['HOME']
- Dir.chdir
- current_dir = Dir.pwd
+ skip "$HOME not valid directory" unless ENV['HOME'] && File.directory?(ENV['HOME'])
- Dir.chdir(ENV['HOME'])
- home = Dir.pwd
- current_dir.should == home
- end
+ Dir.chdir
+ current_dir = Dir.pwd
+
+ Dir.chdir(ENV['HOME'])
+ home = Dir.pwd
+ current_dir.should == home
end
it "changes to the specified directory" do
@@ -70,6 +70,8 @@ describe "Dir.chdir" do
end
it "defaults to the home directory when given a block but no argument" do
+ skip "$HOME not valid directory" unless ENV['HOME'] && File.directory?(ENV['HOME'])
+
# Windows will return a path with forward slashes for ENV["HOME"] so we have
# to compare the route representations returned by Dir.chdir.
current_dir = ""
@@ -88,15 +90,15 @@ describe "Dir.chdir" do
end
it "raises an Errno::ENOENT if the directory does not exist" do
- -> { Dir.chdir DirSpecs.nonexistent }.should raise_error(Errno::ENOENT)
- -> { Dir.chdir(DirSpecs.nonexistent) { } }.should raise_error(Errno::ENOENT)
+ -> { Dir.chdir DirSpecs.nonexistent }.should.raise(Errno::ENOENT)
+ -> { Dir.chdir(DirSpecs.nonexistent) { } }.should.raise(Errno::ENOENT)
end
it "raises an Errno::ENOENT if the original directory no longer exists" do
- dir1 = tmp('/testdir1')
- dir2 = tmp('/testdir2')
- File.should_not.exist?(dir1)
- File.should_not.exist?(dir2)
+ dir1 = tmp('testdir1')
+ dir2 = tmp('testdir2')
+ Dir.should_not.exist?(dir1)
+ Dir.should_not.exist?(dir2)
Dir.mkdir dir1
Dir.mkdir dir2
begin
@@ -104,10 +106,10 @@ describe "Dir.chdir" do
Dir.chdir dir1 do
Dir.chdir(dir2) { Dir.unlink dir1 }
end
- }.should raise_error(Errno::ENOENT)
+ }.should.raise(Errno::ENOENT)
ensure
- Dir.unlink dir1 if File.exist?(dir1)
- Dir.unlink dir2 if File.exist?(dir2)
+ Dir.unlink dir1 if Dir.exist?(dir1)
+ Dir.unlink dir2 if Dir.exist?(dir2)
end
end
@@ -122,3 +124,95 @@ describe "Dir.chdir" do
Dir.pwd.should == @original
end
end
+
+describe "Dir#chdir" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ before :each do
+ @original = Dir.pwd
+ end
+
+ after :each do
+ Dir.chdir(@original)
+ end
+
+ it "changes the current working directory to self" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ dir.chdir
+ Dir.pwd.should == DirSpecs.mock_dir
+ ensure
+ dir.close
+ end
+
+ it "changes the current working directory to self for duration of the block when a block is given" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ pwd_in_block = nil
+
+ dir.chdir { pwd_in_block = Dir.pwd }
+
+ pwd_in_block.should == DirSpecs.mock_dir
+ Dir.pwd.should == @original
+ ensure
+ dir.close
+ end
+
+ it "returns 0 when successfully changing directory" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ dir.chdir.should == 0
+ ensure
+ dir.close
+ end
+
+ it "returns the value of the block when a block is given" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ dir.chdir { :block_value }.should == :block_value
+ ensure
+ dir.close
+ end
+
+ platform_is_not :windows do
+ it "does not raise an Errno::ENOENT if the original directory no longer exists" do
+ dir_name1 = tmp('testdir1')
+ dir_name2 = tmp('testdir2')
+ Dir.should_not.exist?(dir_name1)
+ Dir.should_not.exist?(dir_name2)
+ Dir.mkdir dir_name1
+ Dir.mkdir dir_name2
+
+ dir2 = Dir.new(dir_name2)
+
+ begin
+ Dir.chdir(dir_name1) do
+ dir2.chdir { Dir.unlink dir_name1 }
+ end
+ Dir.pwd.should == @original
+ ensure
+ Dir.unlink dir_name1 if Dir.exist?(dir_name1)
+ Dir.unlink dir_name2 if Dir.exist?(dir_name2)
+ end
+ ensure
+ dir2.close
+ end
+ end
+
+ it "always returns to the original directory when given a block" do
+ dir = Dir.new(DirSpecs.mock_dir)
+
+ begin
+ dir.chdir do
+ raise StandardError, "something bad happened"
+ end
+ rescue StandardError
+ end
+
+ Dir.pwd.should == @original
+ ensure
+ dir.close
+ end
+end
diff --git a/spec/ruby/core/dir/children_spec.rb b/spec/ruby/core/dir/children_spec.rb
index e0325a24b8..6e6da1dd44 100644
--- a/spec/ruby/core/dir/children_spec.rb
+++ b/spec/ruby/core/dir/children_spec.rb
@@ -47,90 +47,101 @@ describe "Dir.children" do
encoding = Encoding.find("filesystem")
encoding = Encoding::BINARY if encoding == Encoding::US_ASCII
platform_is_not :windows do
- children.should include("こんにちは.txt".force_encoding(encoding))
+ children.should.include?("こんにちは.txt".dup.force_encoding(encoding))
end
- children.first.encoding.should equal(Encoding.find("filesystem"))
+ children.first.encoding.should.equal?(Encoding.find("filesystem"))
end
it "returns children encoded with the specified encoding" do
dir = File.join(DirSpecs.mock_dir, 'special')
children = Dir.children(dir, encoding: "euc-jp").sort
- children.first.encoding.should equal(Encoding::EUC_JP)
+ children.first.encoding.should.equal?(Encoding::EUC_JP)
end
it "returns children transcoded to the default internal encoding" do
Encoding.default_internal = Encoding::EUC_KR
children = Dir.children(File.join(DirSpecs.mock_dir, 'special')).sort
- children.first.encoding.should equal(Encoding::EUC_KR)
+ children.first.encoding.should.equal?(Encoding::EUC_KR)
end
it "raises a SystemCallError if called with a nonexistent directory" do
- -> { Dir.children DirSpecs.nonexistent }.should raise_error(SystemCallError)
+ -> { Dir.children DirSpecs.nonexistent }.should.raise(SystemCallError)
end
end
-ruby_version_is "2.6" do
- describe "Dir#children" do
- before :all do
- DirSpecs.create_mock_dirs
- end
+describe "Dir#children" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
- before :each do
- @internal = Encoding.default_internal
- end
+ before :each do
+ @internal = Encoding.default_internal
+ end
- after :all do
- DirSpecs.delete_mock_dirs
- end
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
- after :each do
- Encoding.default_internal = @internal
- @dir.close if @dir
- end
+ after :each do
+ Encoding.default_internal = @internal
+ @dir.close if @dir
+ end
- it "returns an Array of filenames in an existing directory including dotfiles" do
- @dir = Dir.new(DirSpecs.mock_dir)
- a = @dir.children.sort
- @dir.close
+ it "returns an Array of filenames in an existing directory including dotfiles" do
+ @dir = Dir.new(DirSpecs.mock_dir)
+ a = @dir.children.sort
+ @dir.close
- a.should == DirSpecs.expected_paths - %w[. ..]
+ a.should == DirSpecs.expected_paths - %w[. ..]
- @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested")
- a = @dir.children.sort
- a.should == %w|.dotfile.ext directory|
- end
+ @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested")
+ a = @dir.children.sort
+ a.should == %w|.dotfile.ext directory|
+ end
- it "accepts an options Hash" do
- @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8")
- a = @dir.children.sort
- a.should == %w|.dotfile.ext directory|
- end
+ it "accepts an encoding keyword for the encoding of the entries" do
+ @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8")
+ dirs = @dir.to_a.sort
+ dirs.each { |d| d.encoding.should == Encoding::UTF_8 }
+ end
- it "returns children encoded with the filesystem encoding by default" do
- # This spec depends on the locale not being US-ASCII because if it is, the
- # children that are not ascii_only? will be BINARY encoded.
- @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special'))
- children = @dir.children.sort
- encoding = Encoding.find("filesystem")
- encoding = Encoding::BINARY if encoding == Encoding::US_ASCII
- platform_is_not :windows do
- children.should include("こんにちは.txt".force_encoding(encoding))
- end
- children.first.encoding.should equal(Encoding.find("filesystem"))
+ it "returns children encoded with the filesystem encoding by default" do
+ # This spec depends on the locale not being US-ASCII because if it is, the
+ # children that are not ascii_only? will be BINARY encoded.
+ @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special'))
+ children = @dir.children.sort
+ encoding = Encoding.find("filesystem")
+ encoding = Encoding::BINARY if encoding == Encoding::US_ASCII
+ platform_is_not :windows do
+ children.should.include?("こんにちは.txt".dup.force_encoding(encoding))
end
+ children.first.encoding.should.equal?(Encoding.find("filesystem"))
+ end
- it "returns children encoded with the specified encoding" do
- path = File.join(DirSpecs.mock_dir, 'special')
- @dir = Dir.new(path, encoding: "euc-jp")
- children = @dir.children.sort
- children.first.encoding.should equal(Encoding::EUC_JP)
- end
+ it "returns children encoded with the specified encoding" do
+ path = File.join(DirSpecs.mock_dir, 'special')
+ @dir = Dir.new(path, encoding: "euc-jp")
+ children = @dir.children.sort
+ children.first.encoding.should.equal?(Encoding::EUC_JP)
+ end
- it "returns children transcoded to the default internal encoding" do
- Encoding.default_internal = Encoding::EUC_KR
- @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special'))
- children = @dir.children.sort
- children.first.encoding.should equal(Encoding::EUC_KR)
- end
+ it "returns children transcoded to the default internal encoding" do
+ Encoding.default_internal = Encoding::EUC_KR
+ @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special'))
+ children = @dir.children.sort
+ children.first.encoding.should.equal?(Encoding::EUC_KR)
+ end
+
+ it "returns the same result when called repeatedly" do
+ @dir = Dir.open DirSpecs.mock_dir
+
+ a = []
+ @dir.each {|dir| a << dir}
+
+ b = []
+ @dir.each {|dir| b << dir}
+
+ a.sort.should == b.sort
+ a.sort.should == DirSpecs.expected_paths
end
end
diff --git a/spec/ruby/core/dir/chroot_spec.rb b/spec/ruby/core/dir/chroot_spec.rb
index a5ca8943fc..79ad9759b0 100644
--- a/spec/ruby/core/dir/chroot_spec.rb
+++ b/spec/ruby/core/dir/chroot_spec.rb
@@ -21,17 +21,17 @@ platform_is_not :windows do
end
it "raises an Errno::EPERM exception if the directory exists" do
- -> { Dir.chroot('.') }.should raise_error(Errno::EPERM)
+ -> { Dir.chroot('.') }.should.raise(Errno::EPERM)
end
it "raises a SystemCallError if the directory doesn't exist" do
- -> { Dir.chroot('xgwhwhsjai2222jg') }.should raise_error(SystemCallError)
+ -> { Dir.chroot('xgwhwhsjai2222jg') }.should.raise(SystemCallError)
end
it "calls #to_path on non-String argument" do
p = mock('path')
p.should_receive(:to_path).and_return('.')
- -> { Dir.chroot(p) }.should raise_error(Errno::EPERM)
+ -> { Dir.chroot(p) }.should.raise(Errno::EPERM)
end
end
end
diff --git a/spec/ruby/core/dir/close_spec.rb b/spec/ruby/core/dir/close_spec.rb
index 5fad5eecfb..9902d98934 100644
--- a/spec/ruby/core/dir/close_spec.rb
+++ b/spec/ruby/core/dir/close_spec.rb
@@ -11,9 +11,43 @@ describe "Dir#close" do
it "does not raise an IOError even if the Dir instance is closed" do
dir = Dir.open DirSpecs.mock_dir
- dir.close
- -> {
- dir.close
- }.should_not raise_error(IOError)
+ dir.close.should == nil
+ dir.close.should == nil
+
+ platform_is_not :windows do
+ -> { dir.fileno }.should.raise(IOError, /closed directory/)
+ end
+ end
+
+ it "returns nil" do
+ dir = Dir.open DirSpecs.mock_dir
+ dir.close.should == nil
+ end
+
+ ruby_version_is ''...'3.4' do
+ platform_is_not :windows do
+ it "does not raise an error even if the file descriptor is closed with another Dir instance" do
+ dir = Dir.open DirSpecs.mock_dir
+ dir_new = Dir.for_fd(dir.fileno)
+
+ dir.close
+ dir_new.close
+
+ -> { dir.fileno }.should.raise(IOError, /closed directory/)
+ -> { dir_new.fileno }.should.raise(IOError, /closed directory/)
+ end
+ end
+ end
+
+ ruby_version_is '3.4' do
+ platform_is_not :windows do
+ it "raises an error if the file descriptor is closed with another Dir instance" do
+ dir = Dir.open DirSpecs.mock_dir
+ dir_new = Dir.for_fd(dir.fileno)
+ dir.close
+
+ -> { dir_new.close }.should.raise(Errno::EBADF, 'Bad file descriptor - closedir')
+ end
+ end
end
end
diff --git a/spec/ruby/core/dir/each_child_spec.rb b/spec/ruby/core/dir/each_child_spec.rb
index 93b4a1aec1..4d6575df39 100644
--- a/spec/ruby/core/dir/each_child_spec.rb
+++ b/spec/ruby/core/dir/each_child_spec.rb
@@ -10,6 +10,11 @@ describe "Dir.each_child" do
DirSpecs.delete_mock_dirs
end
+ it "accepts an encoding keyword for the encoding of the entries" do
+ dirs = Dir.each_child("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort
+ dirs.each {|dir| dir.encoding.should == Encoding::UTF_8}
+ end
+
it "yields all names in an existing directory to the provided block" do
a, b = [], []
@@ -31,12 +36,12 @@ describe "Dir.each_child" do
end
it "raises a SystemCallError if passed a nonexistent directory" do
- -> { Dir.each_child(DirSpecs.nonexistent) {} }.should raise_error(SystemCallError)
+ -> { Dir.each_child(DirSpecs.nonexistent) {} }.should.raise(SystemCallError)
end
describe "when no block is given" do
it "returns an Enumerator" do
- Dir.each_child(DirSpecs.mock_dir).should be_an_instance_of(Enumerator)
+ Dir.each_child(DirSpecs.mock_dir).should.instance_of?(Enumerator)
Dir.each_child(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths - %w[. ..]
end
@@ -50,52 +55,63 @@ describe "Dir.each_child" do
end
end
-ruby_version_is "2.6" do
- describe "Dir#each_child" do
- before :all do
- DirSpecs.create_mock_dirs
- end
+describe "Dir#each_child" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
- after :all do
- DirSpecs.delete_mock_dirs
- end
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
- after :each do
- @dir.close if @dir
- end
+ after :each do
+ @dir.close if @dir
+ end
- it "yields all names in an existing directory to the provided block" do
- a, b = [], []
- @dir = Dir.new(DirSpecs.mock_dir)
- @dir2 = Dir.new("#{DirSpecs.mock_dir}/deeply/nested")
+ it "yields all names in an existing directory to the provided block" do
+ a, b = [], []
+ @dir = Dir.new(DirSpecs.mock_dir)
+ @dir2 = Dir.new("#{DirSpecs.mock_dir}/deeply/nested")
- @dir.each_child { |f| a << f }
- @dir2.each_child { |f| b << f }
- @dir2.close
+ @dir.each_child { |f| a << f }
+ @dir2.each_child { |f| b << f }
+ @dir2.close
- a.sort.should == DirSpecs.expected_paths - %w|. ..|
- b.sort.should == %w|.dotfile.ext directory|
- end
+ a.sort.should == DirSpecs.expected_paths - %w|. ..|
+ b.sort.should == %w|.dotfile.ext directory|
+ end
- it "returns self when successful" do
- @dir = Dir.new(DirSpecs.mock_dir)
- @dir.each_child { |f| f }.should == @dir
- end
+ it "returns self when successful" do
+ @dir = Dir.new(DirSpecs.mock_dir)
+ @dir.each_child { |f| f }.should == @dir
+ end
- describe "when no block is given" do
- it "returns an Enumerator" do
- @dir = Dir.new(DirSpecs.mock_dir)
+ it "returns the same result when called repeatedly" do
+ @dir = Dir.open DirSpecs.mock_dir
- @dir.each_child.should be_an_instance_of(Enumerator)
- @dir.each_child.to_a.sort.should == DirSpecs.expected_paths - %w|. ..|
- end
+ a = []
+ @dir.each {|dir| a << dir}
- describe "returned Enumerator" do
- describe "size" do
- it "should return nil" do
- @dir = Dir.new(DirSpecs.mock_dir)
- @dir.each_child.size.should == nil
- end
+ b = []
+ @dir.each {|dir| b << dir}
+
+ a.sort.should == b.sort
+ a.sort.should == DirSpecs.expected_paths
+ end
+
+ describe "when no block is given" do
+ it "returns an Enumerator" do
+ @dir = Dir.new(DirSpecs.mock_dir)
+
+ @dir.each_child.should.instance_of?(Enumerator)
+ @dir.each_child.to_a.sort.should == DirSpecs.expected_paths - %w|. ..|
+ end
+
+ describe "returned Enumerator" do
+ describe "size" do
+ it "should return nil" do
+ @dir = Dir.new(DirSpecs.mock_dir)
+ @dir.each_child.size.should == nil
end
end
end
diff --git a/spec/ruby/core/dir/each_spec.rb b/spec/ruby/core/dir/each_spec.rb
index 8c69a7212b..e997e340b1 100644
--- a/spec/ruby/core/dir/each_spec.rb
+++ b/spec/ruby/core/dir/each_spec.rb
@@ -32,12 +32,23 @@ describe "Dir#each" do
@dir.each {}.should == @dir
@dir.read.should == nil
@dir.rewind
- ls.should include(@dir.read)
+ ls.should.include?(@dir.read)
+ end
+
+ it "returns the same result when called repeatedly" do
+ a = []
+ @dir.each {|dir| a << dir}
+
+ b = []
+ @dir.each {|dir| b << dir}
+
+ a.sort.should == b.sort
+ a.sort.should == DirSpecs.expected_paths
end
describe "when no block is given" do
it "returns an Enumerator" do
- @dir.each.should be_an_instance_of(Enumerator)
+ @dir.each.should.instance_of?(Enumerator)
@dir.each.to_a.sort.should == DirSpecs.expected_paths
end
diff --git a/spec/ruby/core/dir/empty_spec.rb b/spec/ruby/core/dir/empty_spec.rb
index 8cc8757798..3b6b2bac85 100644
--- a/spec/ruby/core/dir/empty_spec.rb
+++ b/spec/ruby/core/dir/empty_spec.rb
@@ -12,20 +12,20 @@ describe "Dir.empty?" do
it "returns true for empty directories" do
result = Dir.empty? @empty_dir
- result.should be_true
+ result.should == true
end
it "returns false for non-empty directories" do
result = Dir.empty? __dir__
- result.should be_false
+ result.should == false
end
it "returns false for a non-directory" do
result = Dir.empty? __FILE__
- result.should be_false
+ result.should == false
end
it "raises ENOENT for nonexistent directories" do
- -> { Dir.empty? tmp("nonexistent") }.should raise_error(Errno::ENOENT)
+ -> { Dir.empty? tmp("nonexistent") }.should.raise(Errno::ENOENT)
end
end
diff --git a/spec/ruby/core/dir/entries_spec.rb b/spec/ruby/core/dir/entries_spec.rb
index 33568b6fc4..f3ca49b26d 100644
--- a/spec/ruby/core/dir/entries_spec.rb
+++ b/spec/ruby/core/dir/entries_spec.rb
@@ -35,9 +35,9 @@ describe "Dir.entries" do
Dir.entries(p)
end
- it "accepts an options Hash" do
- a = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").sort
- a.should == %w|. .. .dotfile.ext directory|
+ it "accepts an encoding keyword for the encoding of the entries" do
+ dirs = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort
+ dirs.each {|dir| dir.encoding.should == Encoding::UTF_8}
end
it "returns entries encoded with the filesystem encoding by default" do
@@ -47,24 +47,24 @@ describe "Dir.entries" do
encoding = Encoding.find("filesystem")
encoding = Encoding::BINARY if encoding == Encoding::US_ASCII
platform_is_not :windows do
- entries.should include("こんにちは.txt".force_encoding(encoding))
+ entries.should.include?("こんにちは.txt".dup.force_encoding(encoding))
end
- entries.first.encoding.should equal(Encoding.find("filesystem"))
+ entries.first.encoding.should.equal?(Encoding.find("filesystem"))
end
it "returns entries encoded with the specified encoding" do
dir = File.join(DirSpecs.mock_dir, 'special')
entries = Dir.entries(dir, encoding: "euc-jp").sort
- entries.first.encoding.should equal(Encoding::EUC_JP)
+ entries.first.encoding.should.equal?(Encoding::EUC_JP)
end
it "returns entries transcoded to the default internal encoding" do
Encoding.default_internal = Encoding::EUC_KR
entries = Dir.entries(File.join(DirSpecs.mock_dir, 'special')).sort
- entries.first.encoding.should equal(Encoding::EUC_KR)
+ entries.first.encoding.should.equal?(Encoding::EUC_KR)
end
it "raises a SystemCallError if called with a nonexistent directory" do
- -> { Dir.entries DirSpecs.nonexistent }.should raise_error(SystemCallError)
+ -> { Dir.entries DirSpecs.nonexistent }.should.raise(SystemCallError)
end
end
diff --git a/spec/ruby/core/dir/exist_spec.rb b/spec/ruby/core/dir/exist_spec.rb
index 43987b0f32..05ad67dd03 100644
--- a/spec/ruby/core/dir/exist_spec.rb
+++ b/spec/ruby/core/dir/exist_spec.rb
@@ -1,6 +1,5 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'
-require_relative 'shared/exist'
describe "Dir.exist?" do
before :all do
@@ -11,5 +10,65 @@ describe "Dir.exist?" do
DirSpecs.delete_mock_dirs
end
- it_behaves_like :dir_exist, :exist?
+ it "returns true if the given directory exists" do
+ Dir.exist?(__dir__).should == true
+ end
+
+ it "returns true for '.'" do
+ Dir.exist?('.').should == true
+ end
+
+ it "returns true for '..'" do
+ Dir.exist?('..').should == true
+ end
+
+ it "understands non-ASCII paths" do
+ subdir = File.join(tmp("\u{9876}\u{665}"))
+ Dir.exist?(subdir).should == false
+ Dir.mkdir(subdir)
+ Dir.exist?(subdir).should == true
+ Dir.rmdir(subdir)
+ end
+
+ it "understands relative paths" do
+ Dir.exist?(__dir__ + '/../').should == true
+ end
+
+ it "returns false if the given directory doesn't exist" do
+ Dir.exist?('y26dg27n2nwjs8a/').should == false
+ end
+
+ it "doesn't require the name to have a trailing slash" do
+ dir = __dir__
+ dir.sub!(/\/$/,'')
+ Dir.exist?(dir).should == true
+ end
+
+ it "doesn't expand paths" do
+ skip "$HOME not valid directory" unless ENV['HOME'] && File.directory?(ENV['HOME'])
+ Dir.exist?(File.expand_path('~')).should == true
+ Dir.exist?('~').should == false
+ end
+
+ it "returns false if the argument exists but is a file" do
+ File.should.exist?(__FILE__)
+ Dir.exist?(__FILE__).should == false
+ end
+
+ it "doesn't set $! when file doesn't exist" do
+ Dir.exist?("/path/to/non/existent/dir")
+ $!.should == nil
+ end
+
+ it "calls #to_path on non String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(__dir__)
+ Dir.exist?(p)
+ end
+end
+
+describe "Dir.exists?" do
+ it "has been removed" do
+ Dir.should_not.respond_to?(:exists?)
+ end
end
diff --git a/spec/ruby/core/dir/fchdir_spec.rb b/spec/ruby/core/dir/fchdir_spec.rb
new file mode 100644
index 0000000000..bd1a92b05e
--- /dev/null
+++ b/spec/ruby/core/dir/fchdir_spec.rb
@@ -0,0 +1,71 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+platform_is_not :windows do
+ describe "Dir.fchdir" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ before :each do
+ @original = Dir.pwd
+ end
+
+ after :each do
+ Dir.chdir(@original)
+ end
+
+ it "changes the current working directory to the directory specified by the integer file descriptor" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ Dir.fchdir dir.fileno
+ Dir.pwd.should == DirSpecs.mock_dir
+ ensure
+ dir.close
+ end
+
+ it "returns 0 when successfully changing directory" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ Dir.fchdir(dir.fileno).should == 0
+ ensure
+ dir.close
+ end
+
+ it "returns the value of the block when a block is given" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ Dir.fchdir(dir.fileno) { :block_value }.should == :block_value
+ ensure
+ dir.close
+ end
+
+ it "changes to the specified directory for the duration of the block" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ Dir.fchdir(dir.fileno) { Dir.pwd }.should == DirSpecs.mock_dir
+ Dir.pwd.should == @original
+ ensure
+ dir.close
+ end
+
+ it "raises a SystemCallError if the file descriptor given is not valid" do
+ -> { Dir.fchdir(-1) }.should.raise(SystemCallError, "Bad file descriptor - fchdir")
+ -> { Dir.fchdir(-1) { } }.should.raise(SystemCallError, "Bad file descriptor - fchdir")
+ end
+
+ it "raises a SystemCallError if the file descriptor given is not for a directory" do
+ -> { Dir.fchdir $stdout.fileno }.should.raise(SystemCallError, /(Not a directory|Invalid argument) - fchdir/)
+ -> { Dir.fchdir($stdout.fileno) { } }.should.raise(SystemCallError, /(Not a directory|Invalid argument) - fchdir/)
+ end
+ end
+end
+
+platform_is :windows do
+ describe "Dir.fchdir" do
+ it "raises NotImplementedError" do
+ -> { Dir.fchdir 1 }.should.raise(NotImplementedError)
+ -> { Dir.fchdir(1) { } }.should.raise(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/fileno_spec.rb b/spec/ruby/core/dir/fileno_spec.rb
index 504e36d44b..3b563eb18f 100644
--- a/spec/ruby/core/dir/fileno_spec.rb
+++ b/spec/ruby/core/dir/fileno_spec.rb
@@ -27,11 +27,11 @@ describe "Dir#fileno" do
if has_dir_fileno
it "returns the file descriptor of the dir" do
- @dir.fileno.should be_kind_of(Fixnum)
+ @dir.fileno.should.is_a?(Integer)
end
else
it "raises an error when not implemented on the platform" do
- -> { @dir.fileno }.should raise_error(NotImplementedError)
+ -> { @dir.fileno }.should.raise(NotImplementedError)
end
end
end
diff --git a/spec/ruby/core/dir/fixtures/common.rb b/spec/ruby/core/dir/fixtures/common.rb
index 96dba56c93..cfec91f68f 100644
--- a/spec/ruby/core/dir/fixtures/common.rb
+++ b/spec/ruby/core/dir/fixtures/common.rb
@@ -36,6 +36,8 @@ module DirSpecs
.dotfile
.dotsubdir/.dotfile
.dotsubdir/nondotfile
+ nested/.dotsubir/.dotfile
+ nested/.dotsubir/nondotfile
deeply/.dotfile
deeply/nested/.dotfile.ext
@@ -79,6 +81,8 @@ module DirSpecs
special/}
special/test{1}/file[1]
+ special/{}/special
+ special/test\ +()[]{}/hello_world.erb
]
platform_is_not :windows do
@@ -89,19 +93,40 @@ module DirSpecs
special/|
special/こんにちは.txt
+ special/\a
]
+ @mock_dir_files << "special/_\u{1f60e}.erb"
end
end
@mock_dir_files
end
+ def self.mock_dir_links
+ unless @mock_dir_links
+ @mock_dir_links = []
+ platform_is_not :windows do
+ @mock_dir_links += [
+ ['special/ln', 'subdir_one']
+ ]
+ end
+ end
+ @mock_dir_links
+ end
+
def self.create_mock_dirs
+ delete_mock_dirs
mock_dir_files.each do |name|
file = File.join mock_dir, name
mkdir_p File.dirname(file)
touch file
end
+ mock_dir_links.each do |link, target|
+ full_link = File.join mock_dir, link
+ full_target = File.join mock_dir, target
+
+ File.symlink full_target, full_link
+ end
end
def self.delete_mock_dirs
@@ -148,22 +173,31 @@ module DirSpecs
end
end
+ def self.expected_paths_with_type
+ [
+ [".", :directory],
+ ["..", :directory],
+ [".dotfile", :file],
+ [".dotsubdir", :directory],
+ ["brace", :directory],
+ ["deeply", :directory],
+ ["dir", :directory],
+ ["dir_filename_ordering", :file],
+ ["file_one.ext", :file],
+ ["file_two.ext", :file],
+ ["nested", :directory],
+ ["nondotfile", :file],
+ ["special", :directory],
+ ["subdir_one", :directory],
+ ["subdir_two", :directory],
+ ]
+ end
+
def self.expected_paths
- %w[
- .
- ..
- .dotfile
- .dotsubdir
- brace
- deeply
- dir
- dir_filename_ordering
- file_one.ext
- file_two.ext
- nondotfile
- special
- subdir_one
- subdir_two
- ]
+ expected_paths_with_type.map(&:first)
+ end
+
+ def self.expected_glob_paths
+ expected_paths - ['..']
end
end
diff --git a/spec/ruby/core/dir/for_fd_spec.rb b/spec/ruby/core/dir/for_fd_spec.rb
new file mode 100644
index 0000000000..bbc75e0f8f
--- /dev/null
+++ b/spec/ruby/core/dir/for_fd_spec.rb
@@ -0,0 +1,77 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+quarantine! do # leads to "Errno::EBADF: Bad file descriptor - closedir" in DirSpecs.delete_mock_dirs
+platform_is_not :windows do
+ describe "Dir.for_fd" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ before :each do
+ @original = Dir.pwd
+ end
+
+ after :each do
+ Dir.chdir(@original)
+ end
+
+ it "returns a new Dir object representing the directory specified by the given integer directory file descriptor" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ dir_new = Dir.for_fd(dir.fileno)
+
+ dir_new.should.instance_of?(Dir)
+ dir_new.children.should == dir.children
+ dir_new.fileno.should == dir.fileno
+ ensure
+ dir.close
+ end
+
+ it "returns a new Dir object without associated path" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ dir_new = Dir.for_fd(dir.fileno)
+
+ dir_new.path.should == nil
+ ensure
+ dir.close
+ end
+
+ it "calls #to_int to convert a value to an Integer" do
+ dir = Dir.new(DirSpecs.mock_dir)
+ obj = mock("fd")
+ obj.should_receive(:to_int).and_return(dir.fileno)
+
+ dir_new = Dir.for_fd(obj)
+ dir_new.fileno.should == dir.fileno
+ ensure
+ dir.close
+ end
+
+ it "raises TypeError when value cannot be converted to Integer" do
+ -> {
+ Dir.for_fd(nil)
+ }.should raise_consistent_error(TypeError, "no implicit conversion of nil into Integer")
+ end
+
+ it "raises a SystemCallError if the file descriptor given is not valid" do
+ -> { Dir.for_fd(-1) }.should.raise(SystemCallError, "Bad file descriptor - fdopendir")
+ end
+
+ it "raises a SystemCallError if the file descriptor given is not for a directory" do
+ -> { Dir.for_fd $stdout.fileno }.should.raise(SystemCallError, "Not a directory - fdopendir")
+ end
+ end
+end
+
+platform_is :windows do
+ describe "Dir.for_fd" do
+ it "raises NotImplementedError" do
+ -> { Dir.for_fd 1 }.should.raise(NotImplementedError)
+ end
+ end
+end
+end
diff --git a/spec/ruby/core/dir/foreach_spec.rb b/spec/ruby/core/dir/foreach_spec.rb
index 1560b85f8a..2a2265a029 100644
--- a/spec/ruby/core/dir/foreach_spec.rb
+++ b/spec/ruby/core/dir/foreach_spec.rb
@@ -31,17 +31,29 @@ describe "Dir.foreach" do
end
it "raises a SystemCallError if passed a nonexistent directory" do
- -> { Dir.foreach(DirSpecs.nonexistent) {} }.should raise_error(SystemCallError)
+ -> { Dir.foreach(DirSpecs.nonexistent) {} }.should.raise(SystemCallError)
end
it "returns an Enumerator if no block given" do
- Dir.foreach(DirSpecs.mock_dir).should be_an_instance_of(Enumerator)
+ Dir.foreach(DirSpecs.mock_dir).should.instance_of?(Enumerator)
Dir.foreach(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths
end
+ it "accepts an encoding keyword for the encoding of the entries" do
+ dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort
+ dirs.each { |dir| dir.encoding.should == Encoding::UTF_8 }
+
+ dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::ISO_8859_1).to_a.sort
+ dirs.each { |dir| dir.encoding.should == Encoding::ISO_8859_1 }
+
+ Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::ISO_8859_1) do |f|
+ f.encoding.should == Encoding::ISO_8859_1
+ end
+ end
+
describe "when no block is given" do
it "returns an Enumerator" do
- Dir.foreach(DirSpecs.mock_dir).should be_an_instance_of(Enumerator)
+ Dir.foreach(DirSpecs.mock_dir).should.instance_of?(Enumerator)
Dir.foreach(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths
end
diff --git a/spec/ruby/core/dir/getwd_spec.rb b/spec/ruby/core/dir/getwd_spec.rb
index 132634347c..138481821f 100644
--- a/spec/ruby/core/dir/getwd_spec.rb
+++ b/spec/ruby/core/dir/getwd_spec.rb
@@ -1,15 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/common'
-require_relative 'shared/pwd'
describe "Dir.getwd" do
- before :all do
- DirSpecs.create_mock_dirs
+ it "is an alias of Dir.pwd" do
+ Dir.method(:getwd).should == Dir.method(:pwd)
end
-
- after :all do
- DirSpecs.delete_mock_dirs
- end
-
- it_behaves_like :dir_pwd, :getwd
end
diff --git a/spec/ruby/core/dir/glob_spec.rb b/spec/ruby/core/dir/glob_spec.rb
index d4888ee147..9e81feb15f 100644
--- a/spec/ruby/core/dir/glob_spec.rb
+++ b/spec/ruby/core/dir/glob_spec.rb
@@ -29,6 +29,23 @@ describe "Dir.glob" do
%w!file_one.ext file_two.ext!
end
+ it 'returns matching file paths when supplied :base keyword argument' do
+ dir = tmp('dir_glob_base')
+ file_1 = "#{dir}/lib/bloop.rb"
+ file_2 = "#{dir}/lib/soup.rb"
+ file_3 = "#{dir}/lib/mismatched_file_type.txt"
+ file_4 = "#{dir}/mismatched_directory.rb"
+
+ touch file_1
+ touch file_2
+ touch file_3
+ touch file_4
+
+ Dir.glob('**/*.rb', base: "#{dir}/lib").sort.should == ["bloop.rb", "soup.rb"].sort
+ ensure
+ rm_r dir
+ end
+
it "calls #to_path to convert multiple patterns" do
pat1 = mock('file_one.ext')
pat1.should_receive(:to_path).and_return('file_one.ext')
@@ -39,7 +56,7 @@ describe "Dir.glob" do
end
it "matches both dot and non-dotfiles with '*' and option File::FNM_DOTMATCH" do
- Dir.glob('*', File::FNM_DOTMATCH).sort.should == DirSpecs.expected_paths
+ Dir.glob('*', File::FNM_DOTMATCH).sort.should == DirSpecs.expected_glob_paths
end
it "matches files with any beginning with '*<non-special characters>' and option File::FNM_DOTMATCH" do
@@ -47,7 +64,7 @@ describe "Dir.glob" do
end
it "matches any files in the current directory with '**' and option File::FNM_DOTMATCH" do
- Dir.glob('**', File::FNM_DOTMATCH).sort.should == DirSpecs.expected_paths
+ Dir.glob('**', File::FNM_DOTMATCH).sort.should == DirSpecs.expected_glob_paths
end
it "recursively matches any subdirectories except './' or '../' with '**/' from the current directory and option File::FNM_DOTMATCH" do
@@ -59,8 +76,12 @@ describe "Dir.glob" do
deeply/nested/directory/
deeply/nested/directory/structure/
dir/
+ nested/
+ nested/.dotsubir/
special/
+ special/test\ +()[]{}/
special/test{1}/
+ special/{}/
subdir_one/
subdir_two/
]
@@ -68,6 +89,17 @@ describe "Dir.glob" do
Dir.glob('**/', File::FNM_DOTMATCH).sort.should == expected
end
+ it "recursively matches files and directories in nested dot subdirectory except . with 'nested/**/*' from the current directory and option File::FNM_DOTMATCH" do
+ expected = %w[
+ nested/.
+ nested/.dotsubir
+ nested/.dotsubir/.dotfile
+ nested/.dotsubir/nondotfile
+ ]
+
+ Dir.glob('nested/**/*', File::FNM_DOTMATCH).sort.should == expected.sort
+ end
+
# This is a separate case to check **/ coming after a constant
# directory as well.
it "recursively matches any subdirectories except './' or '../' with '**/' and option File::FNM_DOTMATCH" do
@@ -80,8 +112,12 @@ describe "Dir.glob" do
./deeply/nested/directory/
./deeply/nested/directory/structure/
./dir/
+ ./nested/
+ ./nested/.dotsubir/
./special/
+ ./special/test\ +()[]{}/
./special/test{1}/
+ ./special/{}/
./subdir_one/
./subdir_two/
]
@@ -102,10 +138,18 @@ describe "Dir.glob" do
Dir.glob('{deeply/**/,subdir_two/*}').sort.should == expected
end
+ it "preserves multiple /s before a **" do
+ expected = %w[
+ deeply//nested/directory/structure
+ ]
+
+ Dir.glob('{deeply//**/structure}').sort.should == expected
+ end
+
it "accepts a block and yields it with each elements" do
ary = []
ret = Dir.glob(["file_o*", "file_t*"]) { |t| ary << t }
- ret.should be_nil
+ ret.should == nil
ary.should == %w!file_one.ext file_two.ext!
end
@@ -122,6 +166,131 @@ describe "Dir.glob" do
Dir.glob('**/**/**').should_not.empty?
end
+ it "handles **/** with base keyword argument" do
+ Dir.glob('**/**', base: "dir").should == ["filename_ordering"]
+
+ expected = %w[
+ nested
+ nested/directory
+ nested/directory/structure
+ nested/directory/structure/bar
+ nested/directory/structure/baz
+ nested/directory/structure/file_one
+ nested/directory/structure/file_one.ext
+ nested/directory/structure/foo
+ nondotfile
+ ].sort
+
+ Dir.glob('**/**', base: "deeply").sort.should == expected
+ end
+
+ it "handles **/ with base keyword argument" do
+ expected = %w[
+ /
+ directory/
+ directory/structure/
+ ]
+ Dir.glob('**/', base: "deeply/nested").sort.should == expected
+ end
+
+ it "handles **/nondotfile with base keyword argument" do
+ expected = %w[
+ deeply/nondotfile
+ nondotfile
+ subdir_one/nondotfile
+ subdir_two/nondotfile
+ ]
+ Dir.glob('**/nondotfile', base: ".").sort.should == expected
+ end
+
+ it "handles **/nondotfile with base keyword argument and FNM_DOTMATCH" do
+ expected = %w[
+ .dotsubdir/nondotfile
+ deeply/nondotfile
+ nested/.dotsubir/nondotfile
+ nondotfile
+ subdir_one/nondotfile
+ subdir_two/nondotfile
+ ]
+ Dir.glob('**/nondotfile', File::FNM_DOTMATCH, base: ".").sort.should == expected
+ end
+
+ it "handles **/.dotfile with base keyword argument" do
+ expected = %w[
+ .dotfile
+ deeply/.dotfile
+ subdir_one/.dotfile
+ ]
+ Dir.glob('**/.dotfile', base: ".").sort.should == expected
+ end
+
+ it "handles **/.dotfile with base keyword argument and FNM_DOTMATCH" do
+ expected = %w[
+ .dotfile
+ .dotsubdir/.dotfile
+ deeply/.dotfile
+ nested/.dotsubir/.dotfile
+ subdir_one/.dotfile
+ ]
+ Dir.glob('**/.dotfile', File::FNM_DOTMATCH, base: ".").sort.should == expected
+ end
+
+ it "handles **/.* with base keyword argument" do
+ expected = %w[
+ .dotfile.ext
+ directory/structure/.ext
+ ].sort
+
+ Dir.glob('**/.*', base: "deeply/nested").sort.should == expected
+ end
+
+ it "handles **/.* with base keyword argument and FNM_DOTMATCH" do
+ expected = %w[
+ .
+ .dotfile.ext
+ directory/structure/.ext
+ ].sort
+
+ Dir.glob('**/.*', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected
+ end
+
+ it "handles **/** with base keyword argument and FNM_DOTMATCH" do
+ expected = %w[
+ .
+ .dotfile.ext
+ directory
+ directory/structure
+ directory/structure/.ext
+ directory/structure/bar
+ directory/structure/baz
+ directory/structure/file_one
+ directory/structure/file_one.ext
+ directory/structure/foo
+ ].sort
+
+ Dir.glob('**/**', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected
+ end
+
+ it "handles **/*pattern* with base keyword argument and FNM_DOTMATCH" do
+ expected = %w[
+ .dotfile.ext
+ directory/structure/file_one
+ directory/structure/file_one.ext
+ ]
+
+ Dir.glob('**/*file*', File::FNM_DOTMATCH, base: "deeply/nested").sort.should == expected
+ end
+
+ it "handles **/glob with base keyword argument and FNM_EXTGLOB" do
+ expected = %w[
+ directory/structure/bar
+ directory/structure/file_one
+ directory/structure/file_one.ext
+ ]
+
+ Dir.glob('**/*{file,bar}*', File::FNM_EXTGLOB, base: "deeply/nested").sort.should == expected
+ end
+
it "handles simple filename patterns" do
Dir.glob('.dotfile').should == ['.dotfile']
end
@@ -164,5 +333,30 @@ describe "Dir.glob" do
Dir.rmdir('no_permission')
end
end
+
+ it "will follow symlinks when processing a `*/` pattern." do
+ expected = ['special/ln/nondotfile']
+ Dir.glob('special/*/nondotfile').should == expected
+ end
+
+ it "will not follow symlinks when recursively traversing directories" do
+ expected = %w[
+ deeply/nondotfile
+ nondotfile
+ subdir_one/nondotfile
+ subdir_two/nondotfile
+ ]
+ Dir.glob('**/nondotfile').sort.should == expected
+ end
+
+ it "will follow symlinks when testing directory after recursive directory in pattern" do
+ expected = %w[
+ deeply/nondotfile
+ special/ln/nondotfile
+ subdir_one/nondotfile
+ subdir_two/nondotfile
+ ]
+ Dir.glob('**/*/nondotfile').sort.should == expected
+ end
end
end
diff --git a/spec/ruby/core/dir/home_spec.rb b/spec/ruby/core/dir/home_spec.rb
index 713ba9db9a..f0b20e0687 100644
--- a/spec/ruby/core/dir/home_spec.rb
+++ b/spec/ruby/core/dir/home_spec.rb
@@ -19,16 +19,46 @@ describe "Dir.home" do
it "returns a non-frozen string" do
Dir.home.should_not.frozen?
end
- end
- describe "when called with the current user name" do
- platform_is :solaris do
- it "returns the named user's home directory from the user database" do
- Dir.home(ENV['USER']).should == `getent passwd #{ENV['USER']}|cut -d: -f6`.chomp
+ it "returns a string with the filesystem encoding" do
+ Dir.home.encoding.should == Encoding.find("filesystem")
+ end
+
+ platform_is_not :windows do
+ it "works even if HOME is unset" do
+ ENV.delete('HOME')
+ Dir.home.should.start_with?('/')
+ Dir.home.encoding.should == Encoding.find("filesystem")
+ end
+ end
+
+ platform_is :windows do
+ it "returns the home directory with forward slashs and as UTF-8" do
+ ENV['HOME'] = "C:\\rubyspäc\\home"
+ home = Dir.home
+ home.should == "C:/rubyspäc/home"
+ home.encoding.should == Encoding::UTF_8
+ end
+
+ it "retrieves the directory from HOME, USERPROFILE, HOMEDRIVE/HOMEPATH and the WinAPI in that order" do
+ old_dirs = [ENV.delete('HOME'), ENV.delete('USERPROFILE'), ENV.delete('HOMEDRIVE'), ENV.delete('HOMEPATH')]
+
+ Dir.home.should == old_dirs[1].gsub("\\", "/")
+ ENV['HOMEDRIVE'] = "C:"
+ ENV['HOMEPATH'] = "\\rubyspec\\home1"
+ Dir.home.should == "C:/rubyspec/home1"
+ ENV['USERPROFILE'] = "C:\\rubyspec\\home2"
+ Dir.home.should == "C:/rubyspec/home2"
+ ENV['HOME'] = "C:\\rubyspec\\home3"
+ Dir.home.should == "C:/rubyspec/home3"
+ ensure
+ ENV['HOME'], ENV['USERPROFILE'], ENV['HOMEDRIVE'], ENV['HOMEPATH'] = *old_dirs
end
end
+ end
- platform_is_not :windows, :solaris, :android do
+ describe "when called with the current user name" do
+ platform_is_not :windows, :android, :wasi do
it "returns the named user's home directory, from the user database" do
Dir.home(ENV['USER']).should == `echo ~#{ENV['USER']}`.chomp
end
@@ -37,9 +67,19 @@ describe "Dir.home" do
it "returns a non-frozen string" do
Dir.home(ENV['USER']).should_not.frozen?
end
+
+ it "returns a string with the filesystem encoding" do
+ Dir.home(ENV['USER']).encoding.should == Encoding.find("filesystem")
+ end
end
it "raises an ArgumentError if the named user doesn't exist" do
- -> { Dir.home('geuw2n288dh2k') }.should raise_error(ArgumentError)
+ -> { Dir.home('geuw2n288dh2k') }.should.raise(ArgumentError)
+ end
+
+ describe "when called with a nil user name" do
+ it "returns the current user's home directory, reading $HOME first" do
+ Dir.home(nil).should == "/rubyspec_home"
+ end
end
end
diff --git a/spec/ruby/core/dir/inspect_spec.rb b/spec/ruby/core/dir/inspect_spec.rb
index 37338a97d4..eabaa54ce0 100644
--- a/spec/ruby/core/dir/inspect_spec.rb
+++ b/spec/ruby/core/dir/inspect_spec.rb
@@ -11,7 +11,7 @@ describe "Dir#inspect" do
end
it "returns a String" do
- @dir.inspect.should be_an_instance_of(String)
+ @dir.inspect.should.instance_of?(String)
end
it "includes the class name" do
@@ -19,6 +19,6 @@ describe "Dir#inspect" do
end
it "includes the directory name" do
- @dir.inspect.should include(Dir.getwd)
+ @dir.inspect.should.include?(Dir.getwd)
end
end
diff --git a/spec/ruby/core/dir/mkdir_spec.rb b/spec/ruby/core/dir/mkdir_spec.rb
index 0ed28f5a99..37513e417a 100644
--- a/spec/ruby/core/dir/mkdir_spec.rb
+++ b/spec/ruby/core/dir/mkdir_spec.rb
@@ -46,7 +46,7 @@ describe "Dir.mkdir" do
end
end
- it "calls #to_path on non-String arguments" do
+ it "calls #to_path on non-String path arguments" do
DirSpecs.clear_dirs
p = mock('path')
p.should_receive(:to_path).and_return(DirSpecs.mock_dir('nonexisting'))
@@ -54,16 +54,32 @@ describe "Dir.mkdir" do
DirSpecs.clear_dirs
end
+ it "calls #to_int on non-Integer permissions argument" do
+ DirSpecs.clear_dirs
+ path = DirSpecs.mock_dir('nonexisting')
+ permissions = mock('permissions')
+ permissions.should_receive(:to_int).and_return(0666)
+ Dir.mkdir(path, permissions)
+ DirSpecs.clear_dirs
+ end
+
+ it "raises TypeError if non-Integer permissions argument does not have #to_int method" do
+ path = DirSpecs.mock_dir('nonexisting')
+ permissions = Object.new
+
+ -> { Dir.mkdir(path, permissions) }.should.raise(TypeError, 'no implicit conversion of Object into Integer')
+ end
+
it "raises a SystemCallError if any of the directories in the path before the last does not exist" do
- -> { Dir.mkdir "#{DirSpecs.nonexistent}/subdir" }.should raise_error(SystemCallError)
+ -> { Dir.mkdir "#{DirSpecs.nonexistent}/subdir" }.should.raise(SystemCallError)
end
it "raises Errno::EEXIST if the specified directory already exists" do
- -> { Dir.mkdir("#{DirSpecs.mock_dir}/dir") }.should raise_error(Errno::EEXIST)
+ -> { Dir.mkdir("#{DirSpecs.mock_dir}/dir") }.should.raise(Errno::EEXIST)
end
it "raises Errno::EEXIST if the argument points to the existing file" do
- -> { Dir.mkdir("#{DirSpecs.mock_dir}/file_one.ext") }.should raise_error(Errno::EEXIST)
+ -> { Dir.mkdir("#{DirSpecs.mock_dir}/file_one.ext") }.should.raise(Errno::EEXIST)
end
end
@@ -84,7 +100,7 @@ platform_is_not :windows do
it "raises a SystemCallError when lacking adequate permissions in the parent dir" do
Dir.mkdir @dir, 0000
- -> { Dir.mkdir "#{@dir}/subdir" }.should raise_error(SystemCallError)
+ -> { Dir.mkdir "#{@dir}/subdir" }.should.raise(SystemCallError)
end
end
end
diff --git a/spec/ruby/core/dir/open_spec.rb b/spec/ruby/core/dir/open_spec.rb
index 27f362320b..be01638fbc 100644
--- a/spec/ruby/core/dir/open_spec.rb
+++ b/spec/ruby/core/dir/open_spec.rb
@@ -1,6 +1,5 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'
-require_relative 'shared/open'
describe "Dir.open" do
before :all do
@@ -11,5 +10,75 @@ describe "Dir.open" do
DirSpecs.delete_mock_dirs
end
- it_behaves_like :dir_open, :open
+ it "returns a Dir instance representing the specified directory" do
+ dir = Dir.open(DirSpecs.mock_dir)
+ dir.should.is_a?(Dir)
+ dir.close
+ end
+
+ it "raises a SystemCallError if the directory does not exist" do
+ -> do
+ Dir.open(DirSpecs.nonexistent)
+ end.should.raise(SystemCallError)
+ end
+
+ it "may take a block which is yielded to with the Dir instance" do
+ Dir.open(DirSpecs.mock_dir) {|dir| dir.should.is_a?(Dir)}
+ end
+
+ it "returns the value of the block if a block is given" do
+ Dir.open(DirSpecs.mock_dir) {|dir| :value }.should == :value
+ end
+
+ it "closes the Dir instance when the block exits if given a block" do
+ closed_dir = Dir.open(DirSpecs.mock_dir) { |dir| dir }
+ -> { closed_dir.read }.should.raise(IOError)
+ end
+
+ it "closes the Dir instance when the block exits the block even due to an exception" do
+ closed_dir = nil
+
+ -> do
+ Dir.open(DirSpecs.mock_dir) do |dir|
+ closed_dir = dir
+ raise "dir specs"
+ end
+ end.should.raise(RuntimeError, "dir specs")
+
+ -> { closed_dir.read }.should.raise(IOError)
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(DirSpecs.mock_dir)
+ Dir.open(p) { true }
+ end
+
+ it "accepts an options Hash" do
+ dir = Dir.open(DirSpecs.mock_dir, encoding: "utf-8") {|d| d }
+ dir.should.is_a?(Dir)
+ end
+
+ it "calls #to_hash to convert the options object" do
+ options = mock("dir_open")
+ options.should_receive(:to_hash).and_return({ encoding: Encoding::UTF_8 })
+
+ dir = Dir.open(DirSpecs.mock_dir, **options) {|d| d }
+ dir.should.is_a?(Dir)
+ end
+
+ it "ignores the :encoding option if it is nil" do
+ dir = Dir.open(DirSpecs.mock_dir, encoding: nil) {|d| d }
+ dir.should.is_a?(Dir)
+ end
+
+ platform_is_not :windows do
+ it 'sets the close-on-exec flag for the directory file descriptor' do
+ Dir.open(DirSpecs.mock_dir) do |dir|
+ io = IO.for_fd(dir.fileno)
+ io.autoclose = false
+ io.should.close_on_exec?
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/dir/path_spec.rb b/spec/ruby/core/dir/path_spec.rb
index b1c24c406b..02ddd2cd42 100644
--- a/spec/ruby/core/dir/path_spec.rb
+++ b/spec/ruby/core/dir/path_spec.rb
@@ -1,6 +1,5 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'
-require_relative 'shared/path'
describe "Dir#path" do
before :all do
@@ -11,5 +10,28 @@ describe "Dir#path" do
DirSpecs.delete_mock_dirs
end
- it_behaves_like :dir_path, :path
+ it "returns the path that was supplied to .new or .open" do
+ dir = Dir.open DirSpecs.mock_dir
+ begin
+ dir.path.should == DirSpecs.mock_dir
+ ensure
+ dir.close rescue nil
+ end
+ end
+
+ it "returns the path even when called on a closed Dir instance" do
+ dir = Dir.open DirSpecs.mock_dir
+ dir.close
+ dir.path.should == DirSpecs.mock_dir
+ end
+
+ it "returns a String with the same encoding as the argument to .open" do
+ path = DirSpecs.mock_dir.force_encoding Encoding::IBM866
+ dir = Dir.open path
+ begin
+ dir.path.encoding.should.equal?(Encoding::IBM866)
+ ensure
+ dir.close
+ end
+ end
end
diff --git a/spec/ruby/core/dir/pos_spec.rb b/spec/ruby/core/dir/pos_spec.rb
index b382bff81f..f8ca06d778 100644
--- a/spec/ruby/core/dir/pos_spec.rb
+++ b/spec/ruby/core/dir/pos_spec.rb
@@ -1,30 +1,11 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'
-require_relative 'shared/closed'
require_relative 'shared/pos'
describe "Dir#pos" do
- before :all do
- DirSpecs.create_mock_dirs
- end
-
- after :all do
- DirSpecs.delete_mock_dirs
- end
-
- it_behaves_like :dir_pos, :pos
-end
-
-describe "Dir#pos" do
- before :all do
- DirSpecs.create_mock_dirs
+ it "is an alias of Dir#tell" do
+ Dir.instance_method(:pos).should == Dir.instance_method(:tell)
end
-
- after :all do
- DirSpecs.delete_mock_dirs
- end
-
- it_behaves_like :dir_closed, :pos
end
describe "Dir#pos=" do
diff --git a/spec/ruby/core/dir/pwd_spec.rb b/spec/ruby/core/dir/pwd_spec.rb
index ad01286c90..70208b03d6 100644
--- a/spec/ruby/core/dir/pwd_spec.rb
+++ b/spec/ruby/core/dir/pwd_spec.rb
@@ -1,7 +1,6 @@
# -*- encoding: utf-8 -*-
require_relative '../../spec_helper'
require_relative 'fixtures/common'
-require_relative 'shared/pwd'
describe "Dir.pwd" do
before :all do
@@ -12,7 +11,49 @@ describe "Dir.pwd" do
DirSpecs.delete_mock_dirs
end
- it_behaves_like :dir_pwd, :pwd
+ before :each do
+ @fs_encoding = Encoding.find('filesystem')
+ end
+
+ it "returns the current working directory" do
+ pwd = Dir.pwd
+
+ File.directory?(pwd).should == true
+
+ # On ubuntu gutsy, for example, /bin/pwd does not
+ # understand -P. With just `pwd -P`, /bin/pwd is run.
+
+ # The following uses inode rather than file names to account for
+ # case insensitive file systems like default OS/X file systems
+ platform_is_not :windows do
+ File.stat(pwd).ino.should == File.stat(`/bin/sh -c "pwd -P"`.chomp).ino
+ end
+ platform_is :windows do
+ File.stat(pwd).ino.should == File.stat(File.expand_path(`cd`.chomp)).ino
+ end
+ end
+
+ it "returns an absolute path" do
+ pwd = Dir.pwd
+ pwd.should == File.expand_path(pwd)
+ end
+
+ it "returns an absolute path even when chdir to a relative path" do
+ Dir.chdir(".") do
+ pwd = Dir.pwd
+ File.directory?(pwd).should == true
+ pwd.should == File.expand_path(pwd)
+ end
+ end
+
+ it "returns a String with the filesystem encoding" do
+ enc = Dir.pwd.encoding
+ if @fs_encoding == Encoding::US_ASCII
+ [Encoding::US_ASCII, Encoding::BINARY].should.include?(enc)
+ else
+ enc.should.equal?(@fs_encoding)
+ end
+ end
end
describe "Dir.pwd" do
diff --git a/spec/ruby/core/dir/read_spec.rb b/spec/ruby/core/dir/read_spec.rb
index 59de2e81cf..3f842457f4 100644
--- a/spec/ruby/core/dir/read_spec.rb
+++ b/spec/ruby/core/dir/read_spec.rb
@@ -15,7 +15,7 @@ describe "Dir#read" do
# an FS does not necessarily impose order
ls = Dir.entries DirSpecs.mock_dir
dir = Dir.open DirSpecs.mock_dir
- ls.should include(dir.read)
+ ls.should.include?(dir.read)
dir.close
end
@@ -39,5 +39,38 @@ describe "Dir#read" do
entries.sort.should == DirSpecs.expected_paths
end
+ platform_is_not :windows do
+ it "returns all directory entries even when encoding conversion will fail" do
+ dir = Dir.open(File.join(DirSpecs.mock_dir, 'special'))
+ utf8_entries = []
+ begin
+ while entry = dir.read
+ utf8_entries << entry
+ end
+ ensure
+ dir.close
+ end
+ old_internal_encoding = Encoding::default_internal
+ old_external_encoding = Encoding::default_external
+ Encoding.default_internal = Encoding::UTF_8
+ Encoding.default_external = Encoding::SHIFT_JIS
+ shift_jis_entries = []
+ begin
+ Dir.open(File.join(DirSpecs.mock_dir, 'special')) do |d|
+ -> {
+ while entry = d.read
+ shift_jis_entries << entry
+ end
+ }.should_not.raise
+ end
+ ensure
+ Encoding.default_internal = old_internal_encoding
+ Encoding.default_external = old_external_encoding
+ end
+ shift_jis_entries.size.should == utf8_entries.size
+ shift_jis_entries.filter { |f| f.encoding == Encoding::SHIFT_JIS }.size.should == 1
+ end
+ end
+
it_behaves_like :dir_closed, :read
end
diff --git a/spec/ruby/core/dir/scan_spec.rb b/spec/ruby/core/dir/scan_spec.rb
new file mode 100644
index 0000000000..3aa071337b
--- /dev/null
+++ b/spec/ruby/core/dir/scan_spec.rb
@@ -0,0 +1,224 @@
+# encoding: utf-8
+
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+require_relative '../file/fixtures/file_types'
+
+ruby_version_is "4.1" do
+ describe "Dir.scan" do
+ before :all do
+ FileSpecs.configure_types
+ end
+
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ before :each do
+ @internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @internal
+ end
+
+ it "returns an Array of filename and type pairs in an existing directory including dotfiles" do
+ a = Dir.scan(DirSpecs.mock_dir).sort
+
+ a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]]
+
+ a = Dir.scan("#{DirSpecs.mock_dir}/deeply/nested").sort
+ a.should == [[".dotfile.ext", :file], ["directory", :directory]]
+ end
+
+ it "yields filename and type in an existing directory including dotfiles" do
+ a = []
+ Dir.scan(DirSpecs.mock_dir) do |n, t|
+ a << [n, t]
+ end
+ a.sort!
+ a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]]
+
+ a = []
+ Dir.scan("#{DirSpecs.mock_dir}/deeply/nested") do |n, t|
+ a << [n, t]
+ end
+ a.sort!
+ a.should == [[".dotfile.ext", :file], ["directory", :directory]]
+ end
+
+ it "calls #to_path on non-String arguments" do
+ p = mock('path')
+ p.should_receive(:to_path).and_return(DirSpecs.mock_dir)
+ Dir.scan(p)
+ end
+
+ it "accepts an options Hash" do
+ a = Dir.scan("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").sort
+ a.should == [[".dotfile.ext", :file], ["directory", :directory]]
+ end
+
+ it "returns children names encoded with the filesystem encoding by default" do
+ # This spec depends on the locale not being US-ASCII because if it is, the
+ # children that are not ascii_only? will be BINARY encoded.
+ children = Dir.scan(File.join(DirSpecs.mock_dir, 'special')).sort
+ encoding = Encoding.find("filesystem")
+ encoding = Encoding::BINARY if encoding == Encoding::US_ASCII
+ platform_is_not :windows do
+ children.should.include?(["こんにちは.txt".dup.force_encoding(encoding), :file])
+ end
+ children.first.first.encoding.should.equal?(Encoding.find("filesystem"))
+ end
+
+ it "returns children names encoded with the specified encoding" do
+ dir = File.join(DirSpecs.mock_dir, 'special')
+ children = Dir.scan(dir, encoding: "euc-jp").sort
+ children.first.first.encoding.should.equal?(Encoding::EUC_JP)
+ end
+
+ it "returns children names transcoded to the default internal encoding" do
+ Encoding.default_internal = Encoding::EUC_KR
+ children = Dir.scan(File.join(DirSpecs.mock_dir, 'special')).sort
+ children.first.first.encoding.should.equal?(Encoding::EUC_KR)
+ end
+
+ it "raises a SystemCallError if called with a nonexistent directory" do
+ -> { Dir.scan DirSpecs.nonexistent }.should.raise(SystemCallError)
+ end
+
+ it "handles symlink" do
+ FileSpecs.symlink do |path|
+ Dir.scan(File.dirname(path)).map(&:last).should.include?(:link)
+ end
+ end
+
+ platform_is_not :windows do
+ it "handles socket" do
+ FileSpecs.socket do |path|
+ Dir.scan(File.dirname(path)).map(&:last).should.include?(:socket)
+ end
+ end
+
+ it "handles FIFO" do
+ FileSpecs.fifo do |path|
+ Dir.scan(File.dirname(path)).map(&:last).should.include?(:fifo)
+ end
+ end
+
+ it "handles character devices" do
+ FileSpecs.character_device do |path|
+ Dir.scan(File.dirname(path)).map(&:last).should.include?(:characterSpecial)
+ end
+ end
+ end
+
+ platform_is_not :freebsd, :windows do
+ with_block_device do
+ it "handles block devices" do
+ FileSpecs.block_device do |path|
+ Dir.scan(File.dirname(path)).map(&:last).should.include?(:blockSpecial)
+ end
+ end
+ end
+ end
+ end
+
+ describe "Dir#scan" do
+ before :all do
+ DirSpecs.create_mock_dirs
+ end
+
+ after :all do
+ DirSpecs.delete_mock_dirs
+ end
+
+ before :each do
+ @internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @internal
+ @dir.close if @dir
+ end
+
+ it "returns an Array of filenames in an existing directory including dotfiles" do
+ @dir = Dir.new(DirSpecs.mock_dir)
+ a = @dir.scan.sort
+ @dir.close
+
+ a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]]
+
+ @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested")
+ a = @dir.scan.sort
+ a.should == [[".dotfile.ext", :file], ["directory", :directory]]
+ end
+
+ it "yields filename and type in an existing directory including dotfiles" do
+ @dir = Dir.new(DirSpecs.mock_dir)
+ a = []
+ @dir.scan do |n, t|
+ a << [n, t]
+ end
+ a.sort!
+ a.should == DirSpecs.expected_paths_with_type - [[".", :directory], ["..", :directory]]
+
+ @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested")
+ a = []
+ @dir.scan do |n, t|
+ a << [n, t]
+ end
+ a.sort!
+ a.should == [[".dotfile.ext", :file], ["directory", :directory]]
+ end
+
+ it "accepts an encoding keyword for the encoding of the entries" do
+ @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8")
+ dirs = @dir.to_a.sort
+ dirs.each { |d| d.encoding.should == Encoding::UTF_8 }
+ end
+
+ it "returns children names encoded with the filesystem encoding by default" do
+ # This spec depends on the locale not being US-ASCII because if it is, the
+ # children that are not ascii_only? will be BINARY encoded.
+ @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special'))
+ children = @dir.scan.sort
+ encoding = Encoding.find("filesystem")
+ encoding = Encoding::BINARY if encoding == Encoding::US_ASCII
+ platform_is_not :windows do
+ children.should.include?(["こんにちは.txt".dup.force_encoding(encoding), :file])
+ end
+ children.first.first.encoding.should.equal?(Encoding.find("filesystem"))
+ end
+
+ it "returns children names encoded with the specified encoding" do
+ path = File.join(DirSpecs.mock_dir, 'special')
+ @dir = Dir.new(path, encoding: "euc-jp")
+ children = @dir.children.sort
+ children.first.encoding.should.equal?(Encoding::EUC_JP)
+ end
+
+ it "returns children names transcoded to the default internal encoding" do
+ Encoding.default_internal = Encoding::EUC_KR
+ @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special'))
+ children = @dir.scan.sort
+ children.first.first.encoding.should.equal?(Encoding::EUC_KR)
+ end
+
+ it "returns the same result when called repeatedly" do
+ @dir = Dir.open DirSpecs.mock_dir
+
+ a = []
+ @dir.each {|dir| a << dir}
+
+ b = []
+ @dir.each {|dir| b << dir}
+
+ a.sort.should == b.sort
+ a.sort.should == DirSpecs.expected_paths
+ end
+ end
+end
diff --git a/spec/ruby/core/dir/shared/chroot.rb b/spec/ruby/core/dir/shared/chroot.rb
index b14a433670..e4e6103799 100644
--- a/spec/ruby/core/dir/shared/chroot.rb
+++ b/spec/ruby/core/dir/shared/chroot.rb
@@ -2,8 +2,8 @@ describe :dir_chroot_as_root, shared: true do
before :all do
DirSpecs.create_mock_dirs
- @real_root = "../" * (File.dirname(__FILE__).count('/') - 1)
- @ref_dir = File.join("/", Dir.new('/').entries.first)
+ @real_root = "../" * (__dir__.count('/') - 1)
+ @ref_dir = File.join("/", File.basename(Dir["/*"].first))
end
after :all do
@@ -14,24 +14,27 @@ describe :dir_chroot_as_root, shared: true do
DirSpecs.delete_mock_dirs
end
+ # Pending until https://github.com/ruby/ruby/runs/8075149420 is fixed
+ compilations_ci = ENV["GITHUB_WORKFLOW"] == "Compilations"
+
it "can be used to change the process' root directory" do
- -> { Dir.send(@method, File.dirname(__FILE__)) }.should_not raise_error
+ -> { Dir.send(@method, __dir__) }.should_not.raise
File.should.exist?("/#{File.basename(__FILE__)}")
- end
+ end unless compilations_ci
it "returns 0 if successful" do
Dir.send(@method, '/').should == 0
end
it "raises an Errno::ENOENT exception if the directory doesn't exist" do
- -> { Dir.send(@method, 'xgwhwhsjai2222jg') }.should raise_error(Errno::ENOENT)
+ -> { Dir.send(@method, 'xgwhwhsjai2222jg') }.should.raise(Errno::ENOENT)
end
it "can be escaped from with ../" do
Dir.send(@method, @real_root)
File.should.exist?(@ref_dir)
File.should_not.exist?("/#{File.basename(__FILE__)}")
- end
+ end unless compilations_ci
it "calls #to_path on non-String argument" do
p = mock('path')
diff --git a/spec/ruby/core/dir/shared/closed.rb b/spec/ruby/core/dir/shared/closed.rb
index 17d8332c2a..c868fd6e6d 100644
--- a/spec/ruby/core/dir/shared/closed.rb
+++ b/spec/ruby/core/dir/shared/closed.rb
@@ -4,6 +4,6 @@ describe :dir_closed, shared: true do
dir = Dir.open DirSpecs.mock_dir
dir.close
dir.send(@method) {}
- }.should raise_error(IOError)
+ }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/dir/shared/delete.rb b/spec/ruby/core/dir/shared/delete.rb
index 49e88360e8..ba013e8615 100644
--- a/spec/ruby/core/dir/shared/delete.rb
+++ b/spec/ruby/core/dir/shared/delete.rb
@@ -17,26 +17,16 @@ describe :dir_delete, shared: true do
Dir.send(@method, p)
end
- platform_is_not :solaris do
- it "raises an Errno::ENOTEMPTY when trying to remove a nonempty directory" do
- -> do
- Dir.send @method, DirSpecs.mock_rmdir("nonempty")
- end.should raise_error(Errno::ENOTEMPTY)
- end
- end
-
- platform_is :solaris do
- it "raises an Errno::EEXIST when trying to remove a nonempty directory" do
- -> do
- Dir.send @method, DirSpecs.mock_rmdir("nonempty")
- end.should raise_error(Errno::EEXIST)
- end
+ it "raises an Errno::ENOTEMPTY when trying to remove a nonempty directory" do
+ -> do
+ Dir.send @method, DirSpecs.mock_rmdir("nonempty")
+ end.should.raise(Errno::ENOTEMPTY)
end
it "raises an Errno::ENOENT when trying to remove a non-existing directory" do
-> do
Dir.send @method, DirSpecs.nonexistent
- end.should raise_error(Errno::ENOENT)
+ end.should.raise(Errno::ENOENT)
end
it "raises an Errno::ENOTDIR when trying to remove a non-directory" do
@@ -44,7 +34,7 @@ describe :dir_delete, shared: true do
touch(file)
-> do
Dir.send @method, file
- end.should raise_error(Errno::ENOTDIR)
+ end.should.raise(Errno::ENOTDIR)
end
# this won't work on Windows, since chmod(0000) does not remove all permissions
@@ -56,7 +46,7 @@ describe :dir_delete, shared: true do
File.chmod(0000, parent)
-> do
Dir.send @method, child
- end.should raise_error(Errno::EACCES)
+ end.should.raise(Errno::EACCES)
end
end
end
diff --git a/spec/ruby/core/dir/shared/exist.rb b/spec/ruby/core/dir/shared/exist.rb
deleted file mode 100644
index 765d1b656c..0000000000
--- a/spec/ruby/core/dir/shared/exist.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-describe :dir_exist, shared: true do
- it "returns true if the given directory exists" do
- Dir.send(@method, File.dirname(__FILE__)).should be_true
- end
-
- it "returns true for '.'" do
- Dir.send(@method, '.').should be_true
- end
-
- it "returns true for '..'" do
- Dir.send(@method, '..').should be_true
- end
-
- it "understands non-ASCII paths" do
- subdir = File.join(tmp("\u{9876}\u{665}"))
- Dir.send(@method, subdir).should be_false
- Dir.mkdir(subdir)
- Dir.send(@method, subdir).should be_true
- Dir.rmdir(subdir)
- end
-
- it "understands relative paths" do
- Dir.send(@method, File.dirname(__FILE__) + '/../').should be_true
- end
-
- it "returns false if the given directory doesn't exist" do
- Dir.send(@method, 'y26dg27n2nwjs8a/').should be_false
- end
-
- it "doesn't require the name to have a trailing slash" do
- dir = File.dirname(__FILE__)
- dir.sub!(/\/$/,'')
- Dir.send(@method, dir).should be_true
- end
-
- it "doesn't expand paths" do
- Dir.send(@method, File.expand_path('~')).should be_true
- Dir.send(@method, '~').should be_false
- end
-
- it "returns false if the argument exists but is a file" do
- File.should.exist?(__FILE__)
- Dir.send(@method, __FILE__).should be_false
- end
-
- it "doesn't set $! when file doesn't exist" do
- Dir.send(@method, "/path/to/non/existent/dir")
- $!.should be_nil
- end
-
- it "calls #to_path on non String arguments" do
- p = mock('path')
- p.should_receive(:to_path).and_return(File.dirname(__FILE__))
- Dir.send(@method, p)
- end
-end
diff --git a/spec/ruby/core/dir/shared/glob.rb b/spec/ruby/core/dir/shared/glob.rb
index fcaa0d8a43..86aa105259 100644
--- a/spec/ruby/core/dir/shared/glob.rb
+++ b/spec/ruby/core/dir/shared/glob.rb
@@ -12,8 +12,8 @@ describe :dir_glob, shared: true do
end
it "raises an Encoding::CompatibilityError if the argument encoding is not compatible with US-ASCII" do
- pattern = "file*".force_encoding Encoding::UTF_16BE
- -> { Dir.send(@method, pattern) }.should raise_error(Encoding::CompatibilityError)
+ pattern = "files*".dup.force_encoding Encoding::UTF_16BE
+ -> { Dir.send(@method, pattern) }.should.raise(Encoding::CompatibilityError)
end
it "calls #to_path to convert a pattern" do
@@ -23,26 +23,29 @@ describe :dir_glob, shared: true do
Dir.send(@method, obj).should == %w[file_one.ext]
end
- ruby_version_is ""..."2.6" do
- it "splits the string on \\0 if there is only one string given" do
- Dir.send(@method, "file_o*\0file_t*").should ==
- %w!file_one.ext file_two.ext!
- end
+ it "raises an ArgumentError if the string contains \\0" do
+ -> {Dir.send(@method, "file_o*\0file_t*")}.should.raise ArgumentError, /nul-separated/
end
- ruby_version_is "2.6"..."2.7" do
- it "splits the string on \\0 if there is only one string given and warns" do
- -> {
- Dir.send(@method, "file_o*\0file_t*").should ==
- %w!file_one.ext file_two.ext!
- }.should complain(/warning: use glob patterns list instead of nul-separated patterns/)
- end
+ it "result is sorted by default" do
+ result = Dir.send(@method, '*')
+ result.should == result.sort
end
- ruby_version_is "2.7" do
- it "raises an ArgumentError if the string contains \\0" do
- -> {Dir.send(@method, "file_o*\0file_t*")}.should raise_error ArgumentError, /nul-separated/
- end
+ it "result is sorted with sort: true" do
+ result = Dir.send(@method, '*', sort: true)
+ result.should == result.sort
+ end
+
+ it "sort: false returns same files" do
+ result = Dir.send(@method,'*', sort: false)
+ result.sort.should == Dir.send(@method, '*').sort
+ end
+
+ it "raises an ArgumentError if sort: is not true or false" do
+ -> { Dir.send(@method, '*', sort: 0) }.should.raise ArgumentError, /expected true or false/
+ -> { Dir.send(@method, '*', sort: nil) }.should.raise ArgumentError, /expected true or false/
+ -> { Dir.send(@method, '*', sort: 'false') }.should.raise ArgumentError, /expected true or false/
end
it "matches non-dotfiles with '*'" do
@@ -53,6 +56,7 @@ describe :dir_glob, shared: true do
dir_filename_ordering
file_one.ext
file_two.ext
+ nested
nondotfile
special
subdir_one
@@ -70,6 +74,10 @@ describe :dir_glob, shared: true do
Dir.send(@method, 'special/+').should == ['special/+']
end
+ it "matches directories with special characters when escaped" do
+ Dir.send(@method, 'special/\{}/special').should == ["special/{}/special"]
+ end
+
platform_is_not :windows do
it "matches regexp special *" do
Dir.send(@method, 'special/\*').should == ['special/*']
@@ -82,6 +90,14 @@ describe :dir_glob, shared: true do
it "matches regexp special |" do
Dir.send(@method, 'special/|').should == ['special/|']
end
+
+ it "matches files with backslashes in their name" do
+ Dir.glob('special/\\\\{a,b}').should == ['special/\a']
+ end
+
+ it "matches directory with special characters in their name in complex patterns" do
+ Dir.glob("special/test +()\\[\\]\\{\\}/hello_world{.{en},}{.{html},}{+{phone},}{.{erb},}").should == ['special/test +()[]{}/hello_world.erb']
+ end
end
it "matches regexp special ^" do
@@ -120,8 +136,8 @@ describe :dir_glob, shared: true do
Dir.send(@method, 'special/test\{1\}/*').should == ['special/test{1}/file[1]']
end
- it "matches dotfiles with '.*'" do
- Dir.send(@method, '.*').sort.should == %w|. .. .dotfile .dotsubdir|.sort
+ it "matches dotfiles except .. with '.*'" do
+ Dir.send(@method, '.*').sort.should == %w|. .dotfile .dotsubdir|.sort
end
it "matches non-dotfiles with '*<non-special characters>'" do
@@ -156,6 +172,7 @@ describe :dir_glob, shared: true do
dir_filename_ordering
file_one.ext
file_two.ext
+ nested
nondotfile
special
subdir_one
@@ -165,8 +182,8 @@ describe :dir_glob, shared: true do
Dir.send(@method, '**').sort.should == expected
end
- it "matches dotfiles in the current directory with '.**'" do
- Dir.send(@method, '.**').sort.should == %w|. .. .dotsubdir .dotfile|.sort
+ it "matches dotfiles in the current directory except .. with '.**'" do
+ Dir.send(@method, '.**').sort.should == %w|. .dotsubdir .dotfile|.sort
end
it "recursively matches any nondot subdirectories with '**/'" do
@@ -177,8 +194,11 @@ describe :dir_glob, shared: true do
deeply/nested/directory/
deeply/nested/directory/structure/
dir/
+ nested/
special/
+ special/test\ +()[]{}/
special/test{1}/
+ special/{}/
subdir_one/
subdir_two/
]
@@ -186,9 +206,17 @@ describe :dir_glob, shared: true do
Dir.send(@method, '**/').sort.should == expected
end
- it "recursively matches any subdirectories including ./ and ../ with '.**/'" do
+ it "recursively matches any subdirectories except './' or '../' with '**/' from the base directory if that is specified" do
+ expected = %w[
+ nested/directory
+ ]
+
+ Dir.send(@method, '**/*ory', base: 'deeply').sort.should == expected
+ end
+
+ it "recursively matches any subdirectories including ./ with '.**/'" do
Dir.chdir("#{DirSpecs.mock_dir}/subdir_one") do
- Dir.send(@method, '.**/').sort.should == %w|./ ../|.sort
+ Dir.send(@method, '.**/').should == ['./']
end
end
@@ -231,7 +259,7 @@ describe :dir_glob, shared: true do
end
it "matches dot or non-dotfiles with '{,.}*'" do
- Dir.send(@method, '{,.}*').sort.should == DirSpecs.expected_paths
+ Dir.send(@method, '{,.}*').sort.should == DirSpecs.expected_glob_paths
end
it "respects the order of {} expressions, expanding left most first" do
@@ -345,7 +373,7 @@ describe :dir_glob, shared: true do
it "raises TypeError when cannot convert value to string" do
-> {
Dir.send(@method, "*", base: [])
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
it "handles '' as current directory path" do
diff --git a/spec/ruby/core/dir/shared/open.rb b/spec/ruby/core/dir/shared/open.rb
deleted file mode 100644
index 920845cba1..0000000000
--- a/spec/ruby/core/dir/shared/open.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-describe :dir_open, shared: true do
- it "returns a Dir instance representing the specified directory" do
- dir = Dir.send(@method, DirSpecs.mock_dir)
- dir.should be_kind_of(Dir)
- dir.close
- end
-
- it "raises a SystemCallError if the directory does not exist" do
- -> do
- Dir.send @method, DirSpecs.nonexistent
- end.should raise_error(SystemCallError)
- end
-
- it "may take a block which is yielded to with the Dir instance" do
- Dir.send(@method, DirSpecs.mock_dir) {|dir| dir.should be_kind_of(Dir)}
- end
-
- it "returns the value of the block if a block is given" do
- Dir.send(@method, DirSpecs.mock_dir) {|dir| :value }.should == :value
- end
-
- it "closes the Dir instance when the block exits if given a block" do
- closed_dir = Dir.send(@method, DirSpecs.mock_dir) { |dir| dir }
- -> { closed_dir.read }.should raise_error(IOError)
- end
-
- it "closes the Dir instance when the block exits the block even due to an exception" do
- closed_dir = nil
-
- -> do
- Dir.send(@method, DirSpecs.mock_dir) do |dir|
- closed_dir = dir
- raise "dir specs"
- end
- end.should raise_error(RuntimeError, "dir specs")
-
- -> { closed_dir.read }.should raise_error(IOError)
- end
-
- it "calls #to_path on non-String arguments" do
- p = mock('path')
- p.should_receive(:to_path).and_return(DirSpecs.mock_dir)
- Dir.send(@method, p) { true }
- end
-
- it "accepts an options Hash" do
- dir = Dir.send(@method, DirSpecs.mock_dir, encoding: "utf-8") {|d| d }
- dir.should be_kind_of(Dir)
- end
-
- it "calls #to_hash to convert the options object" do
- options = mock("dir_open")
- options.should_receive(:to_hash).and_return({ encoding: Encoding::UTF_8 })
-
- dir = Dir.send(@method, DirSpecs.mock_dir, **options) {|d| d }
- dir.should be_kind_of(Dir)
- end
-
- it "ignores the :encoding option if it is nil" do
- dir = Dir.send(@method, DirSpecs.mock_dir, encoding: nil) {|d| d }
- dir.should be_kind_of(Dir)
- end
-
- platform_is_not :windows do
- it 'sets the close-on-exec flag for the directory file descriptor' do
- Dir.send(@method, DirSpecs.mock_dir) do |dir|
- io = IO.for_fd(dir.fileno)
- io.autoclose = false
- io.should.close_on_exec?
- end
- end
- end
-end
diff --git a/spec/ruby/core/dir/shared/path.rb b/spec/ruby/core/dir/shared/path.rb
deleted file mode 100644
index 494dcca775..0000000000
--- a/spec/ruby/core/dir/shared/path.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative '../../../spec_helper'
-require_relative '../fixtures/common'
-require_relative 'closed'
-
-describe :dir_path, shared: true do
- it "returns the path that was supplied to .new or .open" do
- dir = Dir.open DirSpecs.mock_dir
- begin
- dir.send(@method).should == DirSpecs.mock_dir
- ensure
- dir.close rescue nil
- end
- end
-
- it "returns the path even when called on a closed Dir instance" do
- dir = Dir.open DirSpecs.mock_dir
- dir.close
- dir.send(@method).should == DirSpecs.mock_dir
- end
-
- it "returns a String with the same encoding as the argument to .open" do
- path = DirSpecs.mock_dir.force_encoding Encoding::IBM866
- dir = Dir.open path
- begin
- dir.send(@method).encoding.should equal(Encoding::IBM866)
- ensure
- dir.close
- end
- end
-end
diff --git a/spec/ruby/core/dir/shared/pos.rb b/spec/ruby/core/dir/shared/pos.rb
index 2165932d99..741de8918c 100644
--- a/spec/ruby/core/dir/shared/pos.rb
+++ b/spec/ruby/core/dir/shared/pos.rb
@@ -1,30 +1,3 @@
-describe :dir_pos, shared: true do
- before :each do
- @dir = Dir.open DirSpecs.mock_dir
- end
-
- after :each do
- @dir.close rescue nil
- end
-
- it "returns an Integer representing the current position in the directory" do
- @dir.send(@method).should be_kind_of(Integer)
- @dir.send(@method).should be_kind_of(Integer)
- @dir.send(@method).should be_kind_of(Integer)
- end
-
- it "returns a different Integer if moved from previous position" do
- a = @dir.send(@method)
- @dir.read
- b = @dir.send(@method)
-
- a.should be_kind_of(Integer)
- b.should be_kind_of(Integer)
-
- a.should_not == b
- end
-end
-
describe :dir_pos_set, shared: true do
before :each do
@dir = Dir.open DirSpecs.mock_dir
diff --git a/spec/ruby/core/dir/shared/pwd.rb b/spec/ruby/core/dir/shared/pwd.rb
deleted file mode 100644
index 2a8d7fe790..0000000000
--- a/spec/ruby/core/dir/shared/pwd.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-describe :dir_pwd, shared: true do
- before :each do
- @fs_encoding = Encoding.find('filesystem')
- end
-
- it "returns the current working directory" do
- pwd = Dir.send(@method)
-
- File.directory?(pwd).should == true
-
- # On ubuntu gutsy, for example, /bin/pwd does not
- # understand -P. With just `pwd -P`, /bin/pwd is run.
-
- # The following uses inode rather than file names to account for
- # case insensitive file systems like default OS/X file systems
- platform_is_not :windows do
- File.stat(pwd).ino.should == File.stat(`/bin/sh -c "pwd -P"`.chomp).ino
- end
- platform_is :windows do
- File.stat(pwd).ino.should == File.stat(File.expand_path(`cd`.chomp)).ino
- end
- end
-
- it "returns an absolute path" do
- pwd = Dir.send(@method)
- pwd.should == File.expand_path(pwd)
- end
-
- it "returns an absolute path even when chdir to a relative path" do
- Dir.chdir(".") do
- pwd = Dir.send(@method)
- File.directory?(pwd).should == true
- pwd.should == File.expand_path(pwd)
- end
- end
-
- it "returns a String with the filesystem encoding" do
- enc = Dir.send(@method).encoding
- if @fs_encoding == Encoding::US_ASCII
- [Encoding::US_ASCII, Encoding::BINARY].should include(enc)
- else
- enc.should equal(@fs_encoding)
- end
- end
-end
diff --git a/spec/ruby/core/dir/tell_spec.rb b/spec/ruby/core/dir/tell_spec.rb
index af86dc1598..dcbb40438f 100644
--- a/spec/ruby/core/dir/tell_spec.rb
+++ b/spec/ruby/core/dir/tell_spec.rb
@@ -12,7 +12,30 @@ describe "Dir#tell" do
DirSpecs.delete_mock_dirs
end
- it_behaves_like :dir_pos, :tell
-
it_behaves_like :dir_closed, :tell
+
+ before :each do
+ @dir = Dir.open DirSpecs.mock_dir
+ end
+
+ after :each do
+ @dir.close rescue nil
+ end
+
+ it "returns an Integer representing the current position in the directory" do
+ @dir.tell.should.is_a?(Integer)
+ @dir.tell.should.is_a?(Integer)
+ @dir.tell.should.is_a?(Integer)
+ end
+
+ it "returns a different Integer if moved from previous position" do
+ a = @dir.tell
+ @dir.read
+ b = @dir.tell
+
+ a.should.is_a?(Integer)
+ b.should.is_a?(Integer)
+
+ a.should_not == b
+ end
end
diff --git a/spec/ruby/core/dir/to_path_spec.rb b/spec/ruby/core/dir/to_path_spec.rb
index 77404a3dc8..2ed533e757 100644
--- a/spec/ruby/core/dir/to_path_spec.rb
+++ b/spec/ruby/core/dir/to_path_spec.rb
@@ -1,15 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/common'
-require_relative 'shared/path'
describe "Dir#to_path" do
- before :all do
- DirSpecs.create_mock_dirs
+ it "is an alias of Dir#path" do
+ Dir.instance_method(:to_path).should == Dir.instance_method(:path)
end
-
- after :all do
- DirSpecs.delete_mock_dirs
- end
-
- it_behaves_like :dir_path, :to_path
end
diff --git a/spec/ruby/core/encoding/aliases_spec.rb b/spec/ruby/core/encoding/aliases_spec.rb
index 786157981a..12c6c6cf85 100644
--- a/spec/ruby/core/encoding/aliases_spec.rb
+++ b/spec/ruby/core/encoding/aliases_spec.rb
@@ -2,24 +2,24 @@ require_relative '../../spec_helper'
describe "Encoding.aliases" do
it "returns a Hash" do
- Encoding.aliases.should be_an_instance_of(Hash)
+ Encoding.aliases.should.instance_of?(Hash)
end
it "has Strings as keys" do
Encoding.aliases.keys.each do |key|
- key.should be_an_instance_of(String)
+ key.should.instance_of?(String)
end
end
it "has Strings as values" do
Encoding.aliases.values.each do |value|
- value.should be_an_instance_of(String)
+ value.should.instance_of?(String)
end
end
it "has alias names as its keys" do
- Encoding.aliases.key?('BINARY').should be_true
- Encoding.aliases.key?('ASCII').should be_true
+ Encoding.aliases.key?('BINARY').should == true
+ Encoding.aliases.key?('ASCII').should == true
end
it "has the names of the aliased encoding as its values" do
diff --git a/spec/ruby/core/encoding/ascii_compatible_spec.rb b/spec/ruby/core/encoding/ascii_compatible_spec.rb
index 4804300e85..04fc159bb8 100644
--- a/spec/ruby/core/encoding/ascii_compatible_spec.rb
+++ b/spec/ruby/core/encoding/ascii_compatible_spec.rb
@@ -2,10 +2,21 @@ require_relative '../../spec_helper'
describe "Encoding#ascii_compatible?" do
it "returns true if self represents an ASCII-compatible encoding" do
- Encoding::UTF_8.ascii_compatible?.should be_true
+ Encoding::UTF_8.ascii_compatible?.should == true
end
it "returns false if self does not represent an ASCII-compatible encoding" do
- Encoding::UTF_16LE.ascii_compatible?.should be_false
+ Encoding::UTF_16LE.ascii_compatible?.should == false
+ end
+
+ it "returns false for UTF_16 and UTF_32" do
+ Encoding::UTF_16.should_not.ascii_compatible?
+ Encoding::UTF_32.should_not.ascii_compatible?
+ end
+
+ it "is always false for dummy encodings" do
+ Encoding.list.select(&:dummy?).each do |encoding|
+ encoding.should_not.ascii_compatible?
+ end
end
end
diff --git a/spec/ruby/core/encoding/compatible_spec.rb b/spec/ruby/core/encoding/compatible_spec.rb
index dc47a6553a..0d620e5bf3 100644
--- a/spec/ruby/core/encoding/compatible_spec.rb
+++ b/spec/ruby/core/encoding/compatible_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../spec_helper'
@@ -7,7 +7,7 @@ require_relative '../../spec_helper'
describe "Encoding.compatible? String, String" do
describe "when the first's Encoding is valid US-ASCII" do
before :each do
- @str = "abc".force_encoding Encoding::US_ASCII
+ @str = "abc".dup.force_encoding Encoding::US_ASCII
end
it "returns US-ASCII when the second's is US-ASCII" do
@@ -33,29 +33,29 @@ describe "Encoding.compatible? String, String" do
describe "when the first's Encoding is ASCII compatible and ASCII only" do
it "returns the first's Encoding if the second is ASCII compatible and ASCII only" do
- [ [Encoding, "abc".force_encoding("UTF-8"), "123".force_encoding("Shift_JIS"), Encoding::UTF_8],
- [Encoding, "123".force_encoding("Shift_JIS"), "abc".force_encoding("UTF-8"), Encoding::Shift_JIS]
+ [ [Encoding, "abc".dup.force_encoding("UTF-8"), "123".dup.force_encoding("Shift_JIS"), Encoding::UTF_8],
+ [Encoding, "123".dup.force_encoding("Shift_JIS"), "abc".dup.force_encoding("UTF-8"), Encoding::Shift_JIS]
].should be_computed_by(:compatible?)
end
it "returns the first's Encoding if the second is ASCII compatible and ASCII only" do
- [ [Encoding, "abc".force_encoding("BINARY"), "123".force_encoding("US-ASCII"), Encoding::BINARY],
- [Encoding, "123".force_encoding("US-ASCII"), "abc".force_encoding("BINARY"), Encoding::US_ASCII]
+ [ [Encoding, "abc".dup.force_encoding("BINARY"), "123".dup.force_encoding("US-ASCII"), Encoding::BINARY],
+ [Encoding, "123".dup.force_encoding("US-ASCII"), "abc".dup.force_encoding("BINARY"), Encoding::US_ASCII]
].should be_computed_by(:compatible?)
end
it "returns the second's Encoding if the second is ASCII compatible but not ASCII only" do
- [ [Encoding, "abc".force_encoding("UTF-8"), "\xff".force_encoding("Shift_JIS"), Encoding::Shift_JIS],
- [Encoding, "123".force_encoding("Shift_JIS"), "\xff".force_encoding("UTF-8"), Encoding::UTF_8],
- [Encoding, "abc".force_encoding("BINARY"), "\xff".force_encoding("US-ASCII"), Encoding::US_ASCII],
- [Encoding, "123".force_encoding("US-ASCII"), "\xff".force_encoding("BINARY"), Encoding::BINARY],
+ [ [Encoding, "abc".dup.force_encoding("UTF-8"), "\xff".dup.force_encoding("Shift_JIS"), Encoding::Shift_JIS],
+ [Encoding, "123".dup.force_encoding("Shift_JIS"), "\xff".dup.force_encoding("UTF-8"), Encoding::UTF_8],
+ [Encoding, "abc".dup.force_encoding("BINARY"), "\xff".dup.force_encoding("US-ASCII"), Encoding::US_ASCII],
+ [Encoding, "123".dup.force_encoding("US-ASCII"), "\xff".dup.force_encoding("BINARY"), Encoding::BINARY],
].should be_computed_by(:compatible?)
end
it "returns nil if the second's Encoding is not ASCII compatible" do
- a = "abc".force_encoding("UTF-8")
- b = "123".force_encoding("UTF-16LE")
- Encoding.compatible?(a, b).should be_nil
+ a = "abc".dup.force_encoding("UTF-8")
+ b = "1234".dup.force_encoding("UTF-16LE")
+ Encoding.compatible?(a, b).should == nil
end
end
@@ -69,36 +69,36 @@ describe "Encoding.compatible? String, String" do
end
it "returns nil if the second encoding is ASCII compatible but neither String's encoding is ASCII only" do
- Encoding.compatible?("\xff", "\u3042".encode("utf-8")).should be_nil
+ Encoding.compatible?("\xff", "\u3042".encode("utf-8")).should == nil
end
end
describe "when the first's Encoding is not ASCII compatible" do
before :each do
- @str = "abc".force_encoding Encoding::UTF_7
+ @str = "abc".dup.force_encoding Encoding::UTF_7
end
it "returns nil when the second String is US-ASCII" do
- Encoding.compatible?(@str, "def".encode("us-ascii")).should be_nil
+ Encoding.compatible?(@str, "def".encode("us-ascii")).should == nil
end
it "returns nil when the second String is BINARY and ASCII only" do
- Encoding.compatible?(@str, "\x7f").should be_nil
+ Encoding.compatible?(@str, "\x7f").should == nil
end
it "returns nil when the second String is BINARY but not ASCII only" do
- Encoding.compatible?(@str, "\xff").should be_nil
+ Encoding.compatible?(@str, "\xff").should == nil
end
it "returns the Encoding when the second's Encoding is not ASCII compatible but the same as the first's Encoding" do
- encoding = Encoding.compatible?(@str, "def".force_encoding("utf-7"))
+ encoding = Encoding.compatible?(@str, "def".dup.force_encoding("utf-7"))
encoding.should == Encoding::UTF_7
end
end
describe "when the first's Encoding is invalid" do
before :each do
- @str = "\xff".force_encoding Encoding::UTF_8
+ @str = "\xff".dup.force_encoding Encoding::UTF_8
end
it "returns the first's Encoding when the second's Encoding is US-ASCII" do
@@ -110,15 +110,15 @@ describe "Encoding.compatible? String, String" do
end
it "returns nil when the second's Encoding is BINARY but not ASCII only" do
- Encoding.compatible?(@str, "\xff").should be_nil
+ Encoding.compatible?(@str, "\xff").should == nil
end
it "returns nil when the second's Encoding is invalid and ASCII only" do
- Encoding.compatible?(@str, "\x7f".force_encoding("utf-16be")).should be_nil
+ Encoding.compatible?(@str, "\x7f\x7f".dup.force_encoding("utf-16be")).should == nil
end
it "returns nil when the second's Encoding is invalid and not ASCII only" do
- Encoding.compatible?(@str, "\xff".force_encoding("utf-16be")).should be_nil
+ Encoding.compatible?(@str, "\xff\xff".dup.force_encoding("utf-16be")).should == nil
end
it "returns the Encoding when the second's Encoding is invalid but the same as the first" do
@@ -129,7 +129,7 @@ describe "Encoding.compatible? String, String" do
describe "when the first String is empty and the second is not" do
describe "and the first's Encoding is ASCII compatible" do
before :each do
- @str = "".force_encoding("utf-8")
+ @str = "".dup.force_encoding("utf-8")
end
it "returns the first's encoding when the second String is ASCII only" do
@@ -143,7 +143,7 @@ describe "Encoding.compatible? String, String" do
describe "when the first's Encoding is not ASCII compatible" do
before :each do
- @str = "".force_encoding Encoding::UTF_7
+ @str = "".dup.force_encoding Encoding::UTF_7
end
it "returns the second string's encoding" do
@@ -154,18 +154,391 @@ describe "Encoding.compatible? String, String" do
describe "when the second String is empty" do
before :each do
- @str = "abc".force_encoding("utf-7")
+ @str = "abc".dup.force_encoding("utf-7")
end
it "returns the first Encoding" do
Encoding.compatible?(@str, "").should == Encoding::UTF_7
end
end
+
+ # Encoding negotiation depends on whether encodings are ASCII-compatible, empty
+ # and contain only ASCII characters (that take 7 bits). Check US-ASCII, UTF-8 and
+ # BINARY encodings (as most common) as well as an ASCII-compatible, a non-ASCII-compatible and a dummy
+ # encodings in all possible combinations.
+ describe "compatibility matrix" do
+
+# Use the following script to regenerate the matrix:
+#
+# ```
+# # encoding: binary
+#
+# ENCODINGS = [
+# "US-ASCII",
+# "UTF-8",
+# "ASCII-8BIT",
+# "ISO-8859-1", # ASCII-compatible
+# "UTF-16BE", # non-ASCII-compatible
+# "ISO-2022-JP" # dummy
+# ]
+#
+# TYPES = [:empty, :"7bits", :non7bits]
+#
+# VALUES = {
+# empty: "",
+# :"7bits" => "\x01\x01",
+# non7bits: "\x01\x81"
+# }
+#
+# ENCODINGS.product(TYPES, ENCODINGS, TYPES).each do |encoding1, type1, encoding2, type2|
+# value1 = VALUES[type1].dup.force_encoding(encoding1)
+# value2 = VALUES[type2].dup.force_encoding(encoding2)
+#
+# result_encoding = Encoding.compatible?(value1, value2)
+#
+# puts "[#{encoding1.inspect}, #{value1.inspect}, #{encoding2.inspect}, #{value2.inspect}, #{result_encoding&.name.inspect}],"
+# end
+# ```
+
+ matrix = [
+ ["US-ASCII", "", "US-ASCII", "", "US-ASCII"],
+ ["US-ASCII", "", "US-ASCII", "\x01\x01", "US-ASCII"],
+ ["US-ASCII", "", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["US-ASCII", "", "UTF-8", "", "US-ASCII"],
+ ["US-ASCII", "", "UTF-8", "\u0001\u0001", "US-ASCII"],
+ ["US-ASCII", "", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["US-ASCII", "", "ASCII-8BIT", "", "US-ASCII"],
+ ["US-ASCII", "", "ASCII-8BIT", "\x01\x01", "US-ASCII"],
+ ["US-ASCII", "", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["US-ASCII", "", "ISO-8859-1", "", "US-ASCII"],
+ ["US-ASCII", "", "ISO-8859-1", "\x01\x01", "US-ASCII"],
+ ["US-ASCII", "", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["US-ASCII", "", "UTF-16BE", "", "US-ASCII"],
+ ["US-ASCII", "", "UTF-16BE", "\u0101", "UTF-16BE"],
+ ["US-ASCII", "", "UTF-16BE", "\u0181", "UTF-16BE"],
+ ["US-ASCII", "", "ISO-2022-JP", "", "US-ASCII"],
+ ["US-ASCII", "", "ISO-2022-JP", "\x01\x01", "ISO-2022-JP"],
+ ["US-ASCII", "", "ISO-2022-JP", "\x01\x81", "ISO-2022-JP"],
+ ["US-ASCII", "\x01\x01", "US-ASCII", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "US-ASCII", "\x01\x01", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "UTF-8", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "UTF-8", "\u0001\u0001", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["US-ASCII", "\x01\x01", "ASCII-8BIT", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "ASCII-8BIT", "\x01\x01", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["US-ASCII", "\x01\x01", "ISO-8859-1", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "ISO-8859-1", "\x01\x01", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["US-ASCII", "\x01\x01", "UTF-16BE", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "UTF-16BE", "\u0101", nil],
+ ["US-ASCII", "\x01\x01", "UTF-16BE", "\u0181", nil],
+ ["US-ASCII", "\x01\x01", "ISO-2022-JP", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x01", "ISO-2022-JP", "\x01\x01", nil],
+ ["US-ASCII", "\x01\x01", "ISO-2022-JP", "\x01\x81", nil],
+ ["US-ASCII", "\x01\x81", "US-ASCII", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "US-ASCII", "\x01\x01", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "UTF-8", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "UTF-8", "\u0001\u0001", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "UTF-8", "\u0001\x81", nil],
+ ["US-ASCII", "\x01\x81", "ASCII-8BIT", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "ASCII-8BIT", "\x01\x01", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "ASCII-8BIT", "\x01\x81", nil],
+ ["US-ASCII", "\x01\x81", "ISO-8859-1", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "ISO-8859-1", "\x01\x01", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "ISO-8859-1", "\x01\x81", nil],
+ ["US-ASCII", "\x01\x81", "UTF-16BE", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "UTF-16BE", "\u0101", nil],
+ ["US-ASCII", "\x01\x81", "UTF-16BE", "\u0181", nil],
+ ["US-ASCII", "\x01\x81", "ISO-2022-JP", "", "US-ASCII"],
+ ["US-ASCII", "\x01\x81", "ISO-2022-JP", "\x01\x01", nil],
+ ["US-ASCII", "\x01\x81", "ISO-2022-JP", "\x01\x81", nil],
+ ["UTF-8", "", "US-ASCII", "", "UTF-8"],
+ ["UTF-8", "", "US-ASCII", "\x01\x01", "UTF-8"],
+ ["UTF-8", "", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["UTF-8", "", "UTF-8", "", "UTF-8"],
+ ["UTF-8", "", "UTF-8", "\u0001\u0001", "UTF-8"],
+ ["UTF-8", "", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["UTF-8", "", "ASCII-8BIT", "", "UTF-8"],
+ ["UTF-8", "", "ASCII-8BIT", "\x01\x01", "UTF-8"],
+ ["UTF-8", "", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["UTF-8", "", "ISO-8859-1", "", "UTF-8"],
+ ["UTF-8", "", "ISO-8859-1", "\x01\x01", "UTF-8"],
+ ["UTF-8", "", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["UTF-8", "", "UTF-16BE", "", "UTF-8"],
+ ["UTF-8", "", "UTF-16BE", "\u0101", "UTF-16BE"],
+ ["UTF-8", "", "UTF-16BE", "\u0181", "UTF-16BE"],
+ ["UTF-8", "", "ISO-2022-JP", "", "UTF-8"],
+ ["UTF-8", "", "ISO-2022-JP", "\x01\x01", "ISO-2022-JP"],
+ ["UTF-8", "", "ISO-2022-JP", "\x01\x81", "ISO-2022-JP"],
+ ["UTF-8", "\u0001\u0001", "US-ASCII", "", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "US-ASCII", "\x01\x01", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["UTF-8", "\u0001\u0001", "UTF-8", "", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "UTF-8", "\u0001\u0001", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "ASCII-8BIT", "", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "ASCII-8BIT", "\x01\x01", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["UTF-8", "\u0001\u0001", "ISO-8859-1", "", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "ISO-8859-1", "\x01\x01", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["UTF-8", "\u0001\u0001", "UTF-16BE", "", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "UTF-16BE", "\u0101", nil],
+ ["UTF-8", "\u0001\u0001", "UTF-16BE", "\u0181", nil],
+ ["UTF-8", "\u0001\u0001", "ISO-2022-JP", "", "UTF-8"],
+ ["UTF-8", "\u0001\u0001", "ISO-2022-JP", "\x01\x01", nil],
+ ["UTF-8", "\u0001\u0001", "ISO-2022-JP", "\x01\x81", nil],
+ ["UTF-8", "\u0001\x81", "US-ASCII", "", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "US-ASCII", "\x01\x01", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "US-ASCII", "\x01\x81", nil],
+ ["UTF-8", "\u0001\x81", "UTF-8", "", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "UTF-8", "\u0001\u0001", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "ASCII-8BIT", "", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "ASCII-8BIT", "\x01\x01", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "ASCII-8BIT", "\x01\x81", nil],
+ ["UTF-8", "\u0001\x81", "ISO-8859-1", "", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "ISO-8859-1", "\x01\x01", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "ISO-8859-1", "\x01\x81", nil],
+ ["UTF-8", "\u0001\x81", "UTF-16BE", "", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "UTF-16BE", "\u0101", nil],
+ ["UTF-8", "\u0001\x81", "UTF-16BE", "\u0181", nil],
+ ["UTF-8", "\u0001\x81", "ISO-2022-JP", "", "UTF-8"],
+ ["UTF-8", "\u0001\x81", "ISO-2022-JP", "\x01\x01", nil],
+ ["UTF-8", "\u0001\x81", "ISO-2022-JP", "\x01\x81", nil],
+ ["ASCII-8BIT", "", "US-ASCII", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "US-ASCII", "\x01\x01", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["ASCII-8BIT", "", "UTF-8", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "UTF-8", "\u0001\u0001", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["ASCII-8BIT", "", "ASCII-8BIT", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "ASCII-8BIT", "\x01\x01", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "ISO-8859-1", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "ISO-8859-1", "\x01\x01", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["ASCII-8BIT", "", "UTF-16BE", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "UTF-16BE", "\u0101", "UTF-16BE"],
+ ["ASCII-8BIT", "", "UTF-16BE", "\u0181", "UTF-16BE"],
+ ["ASCII-8BIT", "", "ISO-2022-JP", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "", "ISO-2022-JP", "\x01\x01", "ISO-2022-JP"],
+ ["ASCII-8BIT", "", "ISO-2022-JP", "\x01\x81", "ISO-2022-JP"],
+ ["ASCII-8BIT", "\x01\x01", "US-ASCII", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "US-ASCII", "\x01\x01", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["ASCII-8BIT", "\x01\x01", "UTF-8", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "UTF-8", "\u0001\u0001", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["ASCII-8BIT", "\x01\x01", "ASCII-8BIT", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "ASCII-8BIT", "\x01\x01", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "ISO-8859-1", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "ISO-8859-1", "\x01\x01", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["ASCII-8BIT", "\x01\x01", "UTF-16BE", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "UTF-16BE", "\u0101", nil],
+ ["ASCII-8BIT", "\x01\x01", "UTF-16BE", "\u0181", nil],
+ ["ASCII-8BIT", "\x01\x01", "ISO-2022-JP", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x01", "ISO-2022-JP", "\x01\x01", nil],
+ ["ASCII-8BIT", "\x01\x01", "ISO-2022-JP", "\x01\x81", nil],
+ ["ASCII-8BIT", "\x01\x81", "US-ASCII", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "US-ASCII", "\x01\x01", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "US-ASCII", "\x01\x81", nil],
+ ["ASCII-8BIT", "\x01\x81", "UTF-8", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "UTF-8", "\u0001\u0001", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "UTF-8", "\u0001\x81", nil],
+ ["ASCII-8BIT", "\x01\x81", "ASCII-8BIT", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "ASCII-8BIT", "\x01\x01", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "ISO-8859-1", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "ISO-8859-1", "\x01\x01", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "ISO-8859-1", "\x01\x81", nil],
+ ["ASCII-8BIT", "\x01\x81", "UTF-16BE", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "UTF-16BE", "\u0101", nil],
+ ["ASCII-8BIT", "\x01\x81", "UTF-16BE", "\u0181", nil],
+ ["ASCII-8BIT", "\x01\x81", "ISO-2022-JP", "", "ASCII-8BIT"],
+ ["ASCII-8BIT", "\x01\x81", "ISO-2022-JP", "\x01\x01", nil],
+ ["ASCII-8BIT", "\x01\x81", "ISO-2022-JP", "\x01\x81", nil],
+ ["ISO-8859-1", "", "US-ASCII", "", "ISO-8859-1"],
+ ["ISO-8859-1", "", "US-ASCII", "\x01\x01", "ISO-8859-1"],
+ ["ISO-8859-1", "", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["ISO-8859-1", "", "UTF-8", "", "ISO-8859-1"],
+ ["ISO-8859-1", "", "UTF-8", "\u0001\u0001", "ISO-8859-1"],
+ ["ISO-8859-1", "", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["ISO-8859-1", "", "ASCII-8BIT", "", "ISO-8859-1"],
+ ["ISO-8859-1", "", "ASCII-8BIT", "\x01\x01", "ISO-8859-1"],
+ ["ISO-8859-1", "", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["ISO-8859-1", "", "ISO-8859-1", "", "ISO-8859-1"],
+ ["ISO-8859-1", "", "ISO-8859-1", "\x01\x01", "ISO-8859-1"],
+ ["ISO-8859-1", "", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["ISO-8859-1", "", "UTF-16BE", "", "ISO-8859-1"],
+ ["ISO-8859-1", "", "UTF-16BE", "\u0101", "UTF-16BE"],
+ ["ISO-8859-1", "", "UTF-16BE", "\u0181", "UTF-16BE"],
+ ["ISO-8859-1", "", "ISO-2022-JP", "", "ISO-8859-1"],
+ ["ISO-8859-1", "", "ISO-2022-JP", "\x01\x01", "ISO-2022-JP"],
+ ["ISO-8859-1", "", "ISO-2022-JP", "\x01\x81", "ISO-2022-JP"],
+ ["ISO-8859-1", "\x01\x01", "US-ASCII", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "US-ASCII", "\x01\x01", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["ISO-8859-1", "\x01\x01", "UTF-8", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "UTF-8", "\u0001\u0001", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["ISO-8859-1", "\x01\x01", "ASCII-8BIT", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "ASCII-8BIT", "\x01\x01", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["ISO-8859-1", "\x01\x01", "ISO-8859-1", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "ISO-8859-1", "\x01\x01", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "UTF-16BE", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "UTF-16BE", "\u0101", nil],
+ ["ISO-8859-1", "\x01\x01", "UTF-16BE", "\u0181", nil],
+ ["ISO-8859-1", "\x01\x01", "ISO-2022-JP", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x01", "ISO-2022-JP", "\x01\x01", nil],
+ ["ISO-8859-1", "\x01\x01", "ISO-2022-JP", "\x01\x81", nil],
+ ["ISO-8859-1", "\x01\x81", "US-ASCII", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "US-ASCII", "\x01\x01", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "US-ASCII", "\x01\x81", nil],
+ ["ISO-8859-1", "\x01\x81", "UTF-8", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "UTF-8", "\u0001\u0001", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "UTF-8", "\u0001\x81", nil],
+ ["ISO-8859-1", "\x01\x81", "ASCII-8BIT", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "ASCII-8BIT", "\x01\x01", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "ASCII-8BIT", "\x01\x81", nil],
+ ["ISO-8859-1", "\x01\x81", "ISO-8859-1", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "ISO-8859-1", "\x01\x01", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "UTF-16BE", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "UTF-16BE", "\u0101", nil],
+ ["ISO-8859-1", "\x01\x81", "UTF-16BE", "\u0181", nil],
+ ["ISO-8859-1", "\x01\x81", "ISO-2022-JP", "", "ISO-8859-1"],
+ ["ISO-8859-1", "\x01\x81", "ISO-2022-JP", "\x01\x01", nil],
+ ["ISO-8859-1", "\x01\x81", "ISO-2022-JP", "\x01\x81", nil],
+ ["UTF-16BE", "", "US-ASCII", "", "UTF-16BE"],
+ ["UTF-16BE", "", "US-ASCII", "\x01\x01", "US-ASCII"],
+ ["UTF-16BE", "", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["UTF-16BE", "", "UTF-8", "", "UTF-16BE"],
+ ["UTF-16BE", "", "UTF-8", "\u0001\u0001", "UTF-8"],
+ ["UTF-16BE", "", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["UTF-16BE", "", "ASCII-8BIT", "", "UTF-16BE"],
+ ["UTF-16BE", "", "ASCII-8BIT", "\x01\x01", "ASCII-8BIT"],
+ ["UTF-16BE", "", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["UTF-16BE", "", "ISO-8859-1", "", "UTF-16BE"],
+ ["UTF-16BE", "", "ISO-8859-1", "\x01\x01", "ISO-8859-1"],
+ ["UTF-16BE", "", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["UTF-16BE", "", "UTF-16BE", "", "UTF-16BE"],
+ ["UTF-16BE", "", "UTF-16BE", "\u0101", "UTF-16BE"],
+ ["UTF-16BE", "", "UTF-16BE", "\u0181", "UTF-16BE"],
+ ["UTF-16BE", "", "ISO-2022-JP", "", "UTF-16BE"],
+ ["UTF-16BE", "", "ISO-2022-JP", "\x01\x01", "ISO-2022-JP"],
+ ["UTF-16BE", "", "ISO-2022-JP", "\x01\x81", "ISO-2022-JP"],
+ ["UTF-16BE", "\u0101", "US-ASCII", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0101", "US-ASCII", "\x01\x01", nil],
+ ["UTF-16BE", "\u0101", "US-ASCII", "\x01\x81", nil],
+ ["UTF-16BE", "\u0101", "UTF-8", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0101", "UTF-8", "\u0001\u0001", nil],
+ ["UTF-16BE", "\u0101", "UTF-8", "\u0001\x81", nil],
+ ["UTF-16BE", "\u0101", "ASCII-8BIT", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0101", "ASCII-8BIT", "\x01\x01", nil],
+ ["UTF-16BE", "\u0101", "ASCII-8BIT", "\x01\x81", nil],
+ ["UTF-16BE", "\u0101", "ISO-8859-1", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0101", "ISO-8859-1", "\x01\x01", nil],
+ ["UTF-16BE", "\u0101", "ISO-8859-1", "\x01\x81", nil],
+ ["UTF-16BE", "\u0101", "UTF-16BE", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0101", "UTF-16BE", "\u0101", "UTF-16BE"],
+ ["UTF-16BE", "\u0101", "UTF-16BE", "\u0181", "UTF-16BE"],
+ ["UTF-16BE", "\u0101", "ISO-2022-JP", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0101", "ISO-2022-JP", "\x01\x01", nil],
+ ["UTF-16BE", "\u0101", "ISO-2022-JP", "\x01\x81", nil],
+ ["UTF-16BE", "\u0181", "US-ASCII", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0181", "US-ASCII", "\x01\x01", nil],
+ ["UTF-16BE", "\u0181", "US-ASCII", "\x01\x81", nil],
+ ["UTF-16BE", "\u0181", "UTF-8", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0181", "UTF-8", "\u0001\u0001", nil],
+ ["UTF-16BE", "\u0181", "UTF-8", "\u0001\x81", nil],
+ ["UTF-16BE", "\u0181", "ASCII-8BIT", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0181", "ASCII-8BIT", "\x01\x01", nil],
+ ["UTF-16BE", "\u0181", "ASCII-8BIT", "\x01\x81", nil],
+ ["UTF-16BE", "\u0181", "ISO-8859-1", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0181", "ISO-8859-1", "\x01\x01", nil],
+ ["UTF-16BE", "\u0181", "ISO-8859-1", "\x01\x81", nil],
+ ["UTF-16BE", "\u0181", "UTF-16BE", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0181", "UTF-16BE", "\u0101", "UTF-16BE"],
+ ["UTF-16BE", "\u0181", "UTF-16BE", "\u0181", "UTF-16BE"],
+ ["UTF-16BE", "\u0181", "ISO-2022-JP", "", "UTF-16BE"],
+ ["UTF-16BE", "\u0181", "ISO-2022-JP", "\x01\x01", nil],
+ ["UTF-16BE", "\u0181", "ISO-2022-JP", "\x01\x81", nil],
+ ["ISO-2022-JP", "", "US-ASCII", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "", "US-ASCII", "\x01\x01", "US-ASCII"],
+ ["ISO-2022-JP", "", "US-ASCII", "\x01\x81", "US-ASCII"],
+ ["ISO-2022-JP", "", "UTF-8", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "", "UTF-8", "\u0001\u0001", "UTF-8"],
+ ["ISO-2022-JP", "", "UTF-8", "\u0001\x81", "UTF-8"],
+ ["ISO-2022-JP", "", "ASCII-8BIT", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "", "ASCII-8BIT", "\x01\x01", "ASCII-8BIT"],
+ ["ISO-2022-JP", "", "ASCII-8BIT", "\x01\x81", "ASCII-8BIT"],
+ ["ISO-2022-JP", "", "ISO-8859-1", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "", "ISO-8859-1", "\x01\x01", "ISO-8859-1"],
+ ["ISO-2022-JP", "", "ISO-8859-1", "\x01\x81", "ISO-8859-1"],
+ ["ISO-2022-JP", "", "UTF-16BE", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "", "UTF-16BE", "\u0101", "UTF-16BE"],
+ ["ISO-2022-JP", "", "UTF-16BE", "\u0181", "UTF-16BE"],
+ ["ISO-2022-JP", "", "ISO-2022-JP", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "", "ISO-2022-JP", "\x01\x01", "ISO-2022-JP"],
+ ["ISO-2022-JP", "", "ISO-2022-JP", "\x01\x81", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x01", "US-ASCII", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x01", "US-ASCII", "\x01\x01", nil],
+ ["ISO-2022-JP", "\x01\x01", "US-ASCII", "\x01\x81", nil],
+ ["ISO-2022-JP", "\x01\x01", "UTF-8", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x01", "UTF-8", "\u0001\u0001", nil],
+ ["ISO-2022-JP", "\x01\x01", "UTF-8", "\u0001\x81", nil],
+ ["ISO-2022-JP", "\x01\x01", "ASCII-8BIT", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x01", "ASCII-8BIT", "\x01\x01", nil],
+ ["ISO-2022-JP", "\x01\x01", "ASCII-8BIT", "\x01\x81", nil],
+ ["ISO-2022-JP", "\x01\x01", "ISO-8859-1", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x01", "ISO-8859-1", "\x01\x01", nil],
+ ["ISO-2022-JP", "\x01\x01", "ISO-8859-1", "\x01\x81", nil],
+ ["ISO-2022-JP", "\x01\x01", "UTF-16BE", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x01", "UTF-16BE", "\u0101", nil],
+ ["ISO-2022-JP", "\x01\x01", "UTF-16BE", "\u0181", nil],
+ ["ISO-2022-JP", "\x01\x01", "ISO-2022-JP", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x01", "ISO-2022-JP", "\x01\x01", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x01", "ISO-2022-JP", "\x01\x81", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x81", "US-ASCII", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x81", "US-ASCII", "\x01\x01", nil],
+ ["ISO-2022-JP", "\x01\x81", "US-ASCII", "\x01\x81", nil],
+ ["ISO-2022-JP", "\x01\x81", "UTF-8", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x81", "UTF-8", "\u0001\u0001", nil],
+ ["ISO-2022-JP", "\x01\x81", "UTF-8", "\u0001\x81", nil],
+ ["ISO-2022-JP", "\x01\x81", "ASCII-8BIT", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x81", "ASCII-8BIT", "\x01\x01", nil],
+ ["ISO-2022-JP", "\x01\x81", "ASCII-8BIT", "\x01\x81", nil],
+ ["ISO-2022-JP", "\x01\x81", "ISO-8859-1", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x81", "ISO-8859-1", "\x01\x01", nil],
+ ["ISO-2022-JP", "\x01\x81", "ISO-8859-1", "\x01\x81", nil],
+ ["ISO-2022-JP", "\x01\x81", "UTF-16BE", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x81", "UTF-16BE", "\u0101", nil],
+ ["ISO-2022-JP", "\x01\x81", "UTF-16BE", "\u0181", nil],
+ ["ISO-2022-JP", "\x01\x81", "ISO-2022-JP", "", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x81", "ISO-2022-JP", "\x01\x01", "ISO-2022-JP"],
+ ["ISO-2022-JP", "\x01\x81", "ISO-2022-JP", "\x01\x81", "ISO-2022-JP"],
+ ]
+
+ matrix.each do |encoding1, value1, encoding2, value2, compatible_encoding|
+ it "returns #{compatible_encoding} for #{value1.inspect} in #{encoding1} and #{value2.inspect} in #{encoding2}" do
+ actual_encoding = Encoding.compatible?(value1.dup.force_encoding(encoding1), value2.dup.force_encoding(encoding2))
+ actual_encoding&.name.should == compatible_encoding
+ end
+ end
+ end
end
describe "Encoding.compatible? String, Regexp" do
it "returns US-ASCII if both are US-ASCII" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
Encoding.compatible?(str, /abc/).should == Encoding::US_ASCII
end
@@ -180,15 +553,20 @@ describe "Encoding.compatible? String, Regexp" do
it "returns the String's Encoding if the String is not ASCII only" do
[ [Encoding, "\xff", Encoding::BINARY],
[Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8],
- [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP],
- [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS],
+ [Encoding, "\xa4\xa2".dup.force_encoding("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".dup.force_encoding("shift_jis"), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, /abc/)
end
+
+ it "returns the Regexp's Encoding if the String is ASCII only and the Regexp is not" do
+ r = Regexp.new("\xa4\xa2".dup.force_encoding("euc-jp"))
+ Encoding.compatible?("hello".dup.force_encoding("utf-8"), r).should == Encoding::EUC_JP
+ end
end
describe "Encoding.compatible? String, Symbol" do
it "returns US-ASCII if both are ASCII only" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
Encoding.compatible?(str, :abc).should == Encoding::US_ASCII
end
@@ -203,26 +581,26 @@ describe "Encoding.compatible? String, Symbol" do
it "returns the String's Encoding if the String is not ASCII only" do
[ [Encoding, "\xff", Encoding::BINARY],
[Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8],
- [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP],
- [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS],
+ [Encoding, "\xa4\xa2".dup.force_encoding("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".dup.force_encoding("shift_jis"), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, :abc)
end
end
describe "Encoding.compatible? String, Encoding" do
it "returns nil if the String's encoding is not ASCII compatible" do
- Encoding.compatible?("abc".encode("utf-32le"), Encoding::US_ASCII).should be_nil
+ Encoding.compatible?("abc".encode("utf-32le"), Encoding::US_ASCII).should == nil
end
it "returns nil if the Encoding is not ASCII compatible" do
- Encoding.compatible?("abc".encode("us-ascii"), Encoding::UTF_32LE).should be_nil
+ Encoding.compatible?("abc".encode("us-ascii"), Encoding::UTF_32LE).should == nil
end
it "returns the String's encoding if the Encoding is US-ASCII" do
[ [Encoding, "\xff", Encoding::BINARY],
[Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8],
- [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP],
- [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS],
+ [Encoding, "\xa4\xa2".dup.force_encoding("euc-jp"), Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".dup.force_encoding("shift_jis"), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, Encoding::US_ASCII)
end
@@ -236,16 +614,25 @@ describe "Encoding.compatible? String, Encoding" do
end
it "returns nil if the String's encoding is ASCII compatible but the string is not ASCII only" do
- Encoding.compatible?("\u3042".encode("utf-8"), Encoding::BINARY).should be_nil
+ Encoding.compatible?("\u3042".encode("utf-8"), Encoding::BINARY).should == nil
end
end
describe "Encoding.compatible? Regexp, String" do
it "returns US-ASCII if both are US-ASCII" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
Encoding.compatible?(/abc/, str).should == Encoding::US_ASCII
end
+ it "returns the String's Encoding when the String is ASCII only with a different encoding" do
+ r = Regexp.new("\xa4\xa2".dup.force_encoding("euc-jp"))
+ Encoding.compatible?(r, "hello".dup.force_encoding("utf-8")).should == Encoding::UTF_8
+ end
+
+ it "returns the Regexp's Encoding if the String has the same non-ASCII encoding" do
+ r = Regexp.new("\xa4\xa2".dup.force_encoding("euc-jp"))
+ Encoding.compatible?(r, "hello".dup.force_encoding("euc-jp")).should == Encoding::EUC_JP
+ end
end
describe "Encoding.compatible? Regexp, Regexp" do
@@ -256,8 +643,8 @@ describe "Encoding.compatible? Regexp, Regexp" do
it "returns the first's Encoding if it is not US-ASCII and not ASCII only" do
[ [Encoding, Regexp.new("\xff"), Encoding::BINARY],
[Encoding, Regexp.new("\u3042".encode("utf-8")), Encoding::UTF_8],
- [Encoding, Regexp.new("\xa4\xa2".force_encoding("euc-jp")), Encoding::EUC_JP],
- [Encoding, Regexp.new("\x82\xa0".force_encoding("shift_jis")), Encoding::Shift_JIS],
+ [Encoding, Regexp.new("\xa4\xa2".dup.force_encoding("euc-jp")), Encoding::EUC_JP],
+ [Encoding, Regexp.new("\x82\xa0".dup.force_encoding("shift_jis")), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, /abc/)
end
end
@@ -270,15 +657,15 @@ describe "Encoding.compatible? Regexp, Symbol" do
it "returns the first's Encoding if it is not US-ASCII and not ASCII only" do
[ [Encoding, Regexp.new("\xff"), Encoding::BINARY],
[Encoding, Regexp.new("\u3042".encode("utf-8")), Encoding::UTF_8],
- [Encoding, Regexp.new("\xa4\xa2".force_encoding("euc-jp")), Encoding::EUC_JP],
- [Encoding, Regexp.new("\x82\xa0".force_encoding("shift_jis")), Encoding::Shift_JIS],
+ [Encoding, Regexp.new("\xa4\xa2".dup.force_encoding("euc-jp")), Encoding::EUC_JP],
+ [Encoding, Regexp.new("\x82\xa0".dup.force_encoding("shift_jis")), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, /abc/)
end
end
describe "Encoding.compatible? Symbol, String" do
it "returns US-ASCII if both are ASCII only" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
Encoding.compatible?(str, :abc).should == Encoding::US_ASCII
end
end
@@ -291,8 +678,8 @@ describe "Encoding.compatible? Symbol, Regexp" do
it "returns the Regexp's Encoding if it is not US-ASCII and not ASCII only" do
a = Regexp.new("\xff")
b = Regexp.new("\u3042".encode("utf-8"))
- c = Regexp.new("\xa4\xa2".force_encoding("euc-jp"))
- d = Regexp.new("\x82\xa0".force_encoding("shift_jis"))
+ c = Regexp.new("\xa4\xa2".dup.force_encoding("euc-jp"))
+ d = Regexp.new("\x82\xa0".dup.force_encoding("shift_jis"))
[ [Encoding, :abc, a, Encoding::BINARY],
[Encoding, :abc, b, Encoding::UTF_8],
@@ -310,8 +697,8 @@ describe "Encoding.compatible? Symbol, Symbol" do
it "returns the first's Encoding if it is not ASCII only" do
[ [Encoding, "\xff".to_sym, Encoding::BINARY],
[Encoding, "\u3042".encode("utf-8").to_sym, Encoding::UTF_8],
- [Encoding, "\xa4\xa2".force_encoding("euc-jp").to_sym, Encoding::EUC_JP],
- [Encoding, "\x82\xa0".force_encoding("shift_jis").to_sym, Encoding::Shift_JIS],
+ [Encoding, "\xa4\xa2".dup.force_encoding("euc-jp").to_sym, Encoding::EUC_JP],
+ [Encoding, "\x82\xa0".dup.force_encoding("shift_jis").to_sym, Encoding::Shift_JIS],
].should be_computed_by(:compatible?, :abc)
end
end
@@ -354,26 +741,32 @@ end
describe "Encoding.compatible? Object, Object" do
it "returns nil for Object, String" do
- Encoding.compatible?(Object.new, "abc").should be_nil
+ Encoding.compatible?(Object.new, "abc").should == nil
end
it "returns nil for Object, Regexp" do
- Encoding.compatible?(Object.new, /./).should be_nil
+ Encoding.compatible?(Object.new, /./).should == nil
end
it "returns nil for Object, Symbol" do
- Encoding.compatible?(Object.new, :sym).should be_nil
+ Encoding.compatible?(Object.new, :sym).should == nil
end
it "returns nil for String, Object" do
- Encoding.compatible?("abc", Object.new).should be_nil
+ Encoding.compatible?("abc", Object.new).should == nil
end
it "returns nil for Regexp, Object" do
- Encoding.compatible?(/./, Object.new).should be_nil
+ Encoding.compatible?(/./, Object.new).should == nil
end
it "returns nil for Symbol, Object" do
- Encoding.compatible?(:sym, Object.new).should be_nil
+ Encoding.compatible?(:sym, Object.new).should == nil
+ end
+end
+
+describe "Encoding.compatible? nil, nil" do
+ it "returns nil" do
+ Encoding.compatible?(nil, nil).should == nil
end
end
diff --git a/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb b/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb
index 1beb40af3f..07c7a88356 100644
--- a/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb
+++ b/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb
@@ -24,14 +24,14 @@ describe "Encoding::Converter.asciicompat_encoding" do
end
it "returns nil when the given encoding is ASCII compatible" do
- Encoding::Converter.asciicompat_encoding('ASCII').should be_nil
- Encoding::Converter.asciicompat_encoding('UTF-8').should be_nil
+ Encoding::Converter.asciicompat_encoding('ASCII').should == nil
+ Encoding::Converter.asciicompat_encoding('UTF-8').should == nil
end
it "handles encoding names who resolve to nil encodings" do
internal = Encoding.default_internal
Encoding.default_internal = nil
- Encoding::Converter.asciicompat_encoding('internal').should be_nil
+ Encoding::Converter.asciicompat_encoding('internal').should == nil
Encoding.default_internal = internal
end
end
diff --git a/spec/ruby/core/encoding/converter/constants_spec.rb b/spec/ruby/core/encoding/converter/constants_spec.rb
index 57b6a4d4e7..e2d21b5429 100644
--- a/spec/ruby/core/encoding/converter/constants_spec.rb
+++ b/spec/ruby/core/encoding/converter/constants_spec.rb
@@ -2,130 +2,130 @@ require_relative '../../../spec_helper'
describe "Encoding::Converter::INVALID_MASK" do
it "exists" do
- Encoding::Converter.should have_constant(:INVALID_MASK)
+ Encoding::Converter.should.const_defined?(:INVALID_MASK, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::INVALID_MASK.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::INVALID_MASK.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::INVALID_REPLACE" do
it "exists" do
- Encoding::Converter.should have_constant(:INVALID_REPLACE)
+ Encoding::Converter.should.const_defined?(:INVALID_REPLACE, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::INVALID_REPLACE.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::INVALID_REPLACE.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::UNDEF_MASK" do
it "exists" do
- Encoding::Converter.should have_constant(:UNDEF_MASK)
+ Encoding::Converter.should.const_defined?(:UNDEF_MASK, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::UNDEF_MASK.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::UNDEF_MASK.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::UNDEF_REPLACE" do
it "exists" do
- Encoding::Converter.should have_constant(:UNDEF_REPLACE)
+ Encoding::Converter.should.const_defined?(:UNDEF_REPLACE, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::UNDEF_REPLACE.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::UNDEF_REPLACE.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::UNDEF_HEX_CHARREF" do
it "exists" do
- Encoding::Converter.should have_constant(:UNDEF_HEX_CHARREF)
+ Encoding::Converter.should.const_defined?(:UNDEF_HEX_CHARREF, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::UNDEF_HEX_CHARREF.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::UNDEF_HEX_CHARREF.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::PARTIAL_INPUT" do
it "exists" do
- Encoding::Converter.should have_constant(:PARTIAL_INPUT)
+ Encoding::Converter.should.const_defined?(:PARTIAL_INPUT, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::PARTIAL_INPUT.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::PARTIAL_INPUT.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::AFTER_OUTPUT" do
it "exists" do
- Encoding::Converter.should have_constant(:AFTER_OUTPUT)
+ Encoding::Converter.should.const_defined?(:AFTER_OUTPUT, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::AFTER_OUTPUT.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::AFTER_OUTPUT.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR" do
it "exists" do
- Encoding::Converter.should have_constant(:UNIVERSAL_NEWLINE_DECORATOR)
+ Encoding::Converter.should.const_defined?(:UNIVERSAL_NEWLINE_DECORATOR, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::CRLF_NEWLINE_DECORATOR" do
it "exists" do
- Encoding::Converter.should have_constant(:CRLF_NEWLINE_DECORATOR)
+ Encoding::Converter.should.const_defined?(:CRLF_NEWLINE_DECORATOR, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::CRLF_NEWLINE_DECORATOR.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::CRLF_NEWLINE_DECORATOR.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::CR_NEWLINE_DECORATOR" do
it "exists" do
- Encoding::Converter.should have_constant(:CR_NEWLINE_DECORATOR)
+ Encoding::Converter.should.const_defined?(:CR_NEWLINE_DECORATOR, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::CR_NEWLINE_DECORATOR.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::CR_NEWLINE_DECORATOR.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::XML_TEXT_DECORATOR" do
it "exists" do
- Encoding::Converter.should have_constant(:XML_TEXT_DECORATOR)
+ Encoding::Converter.should.const_defined?(:XML_TEXT_DECORATOR, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::XML_TEXT_DECORATOR.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::XML_TEXT_DECORATOR.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::XML_ATTR_CONTENT_DECORATOR" do
it "exists" do
- Encoding::Converter.should have_constant(:XML_ATTR_CONTENT_DECORATOR)
+ Encoding::Converter.should.const_defined?(:XML_ATTR_CONTENT_DECORATOR, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::XML_ATTR_CONTENT_DECORATOR.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::XML_ATTR_CONTENT_DECORATOR.should.instance_of?(Integer)
end
end
describe "Encoding::Converter::XML_ATTR_QUOTE_DECORATOR" do
it "exists" do
- Encoding::Converter.should have_constant(:XML_ATTR_QUOTE_DECORATOR)
+ Encoding::Converter.should.const_defined?(:XML_ATTR_QUOTE_DECORATOR, false)
end
- it "has a Fixnum value" do
- Encoding::Converter::XML_ATTR_QUOTE_DECORATOR.should be_an_instance_of(Fixnum)
+ it "has an Integer value" do
+ Encoding::Converter::XML_ATTR_QUOTE_DECORATOR.should.instance_of?(Integer)
end
end
diff --git a/spec/ruby/core/encoding/converter/convert_spec.rb b/spec/ruby/core/encoding/converter/convert_spec.rb
index 95a9e0b758..c95e88a491 100644
--- a/spec/ruby/core/encoding/converter/convert_spec.rb
+++ b/spec/ruby/core/encoding/converter/convert_spec.rb
@@ -1,45 +1,45 @@
-# -*- encoding: binary -*-
+# encoding: binary
+# frozen_string_literal: true
require_relative '../../../spec_helper'
describe "Encoding::Converter#convert" do
it "returns a String" do
ec = Encoding::Converter.new('ascii', 'utf-8')
- ec.convert('glark').should be_an_instance_of(String)
+ ec.convert('glark').should.instance_of?(String)
end
it "sets the encoding of the result to the target encoding" do
ec = Encoding::Converter.new('ascii', 'utf-8')
- str = 'glark'.force_encoding('ascii')
+ str = 'glark'.dup.force_encoding('ascii')
ec.convert(str).encoding.should == Encoding::UTF_8
end
it "transcodes the given String to the target encoding" do
ec = Encoding::Converter.new("utf-8", "euc-jp")
- ec.convert("\u3042".force_encoding('UTF-8')).should == \
- "\xA4\xA2".force_encoding('EUC-JP')
+ ec.convert("\u3042".dup.force_encoding('UTF-8')).should == \
+ "\xA4\xA2".dup.force_encoding('EUC-JP')
end
it "allows Strings of different encodings to the source encoding" do
ec = Encoding::Converter.new('ascii', 'utf-8')
- str = 'glark'.force_encoding('SJIS')
+ str = 'glark'.dup.force_encoding('SJIS')
ec.convert(str).encoding.should == Encoding::UTF_8
end
it "reuses the given encoding pair if called multiple times" do
ec = Encoding::Converter.new('ascii', 'SJIS')
- ec.convert('a'.force_encoding('ASCII')).should == 'a'.force_encoding('SJIS')
- ec.convert('b'.force_encoding('ASCII')).should == 'b'.force_encoding('SJIS')
+ ec.convert('a'.dup.force_encoding('ASCII')).should == 'a'.dup.force_encoding('SJIS')
+ ec.convert('b'.dup.force_encoding('ASCII')).should == 'b'.dup.force_encoding('SJIS')
end
it "raises UndefinedConversionError if the String contains characters invalid for the target encoding" do
ec = Encoding::Converter.new('UTF-8', Encoding.find('macCyrillic'))
- -> { ec.convert("\u{6543}".force_encoding('UTF-8')) }.should \
- raise_error(Encoding::UndefinedConversionError)
+ -> { ec.convert("\u{6543}".dup.force_encoding('UTF-8')) }.should.raise(Encoding::UndefinedConversionError)
end
it "raises an ArgumentError if called on a finished stream" do
ec = Encoding::Converter.new('UTF-8', Encoding.find('macCyrillic'))
ec.finish
- -> { ec.convert("\u{65}") }.should raise_error(ArgumentError)
+ -> { ec.convert("\u{65}") }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/encoding/converter/finish_spec.rb b/spec/ruby/core/encoding/converter/finish_spec.rb
index 11ca7e8510..e13b7415f8 100644
--- a/spec/ruby/core/encoding/converter/finish_spec.rb
+++ b/spec/ruby/core/encoding/converter/finish_spec.rb
@@ -7,7 +7,7 @@ describe "Encoding::Converter#finish" do
it "returns a String" do
@ec.convert('foo')
- @ec.finish.should be_an_instance_of(String)
+ @ec.finish.should.instance_of?(String)
end
it "returns an empty String if there is nothing more to convert" do
@@ -16,8 +16,8 @@ describe "Encoding::Converter#finish" do
end
it "returns the last part of the converted String if it hasn't already" do
- @ec.convert("\u{9999}").should == "\e$B9a".force_encoding('iso-2022-jp')
- @ec.finish.should == "\e(B".force_encoding('iso-2022-jp')
+ @ec.convert("\u{9999}").should == "\e$B9a".dup.force_encoding('iso-2022-jp')
+ @ec.finish.should == "\e(B".dup.force_encoding('iso-2022-jp')
end
it "returns a String in the destination encoding" do
diff --git a/spec/ruby/core/encoding/converter/last_error_spec.rb b/spec/ruby/core/encoding/converter/last_error_spec.rb
index 68567737b7..3984a628f5 100644
--- a/spec/ruby/core/encoding/converter/last_error_spec.rb
+++ b/spec/ruby/core/encoding/converter/last_error_spec.rb
@@ -1,54 +1,54 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
describe "Encoding::Converter#last_error" do
it "returns nil when the no conversion has been attempted" do
ec = Encoding::Converter.new('ascii','utf-8')
- ec.last_error.should be_nil
+ ec.last_error.should == nil
end
it "returns nil when the last conversion did not produce an error" do
ec = Encoding::Converter.new('ascii','utf-8')
- ec.convert('a'.force_encoding('ascii'))
- ec.last_error.should be_nil
+ ec.convert('a'.dup.force_encoding('ascii'))
+ ec.last_error.should == nil
end
it "returns nil when #primitive_convert last returned :destination_buffer_full" do
ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
- ec.primitive_convert("\u{9999}", "", 0, 0, partial_input: false) \
+ ec.primitive_convert(+"\u{9999}", +"", 0, 0, partial_input: false) \
.should == :destination_buffer_full
- ec.last_error.should be_nil
+ ec.last_error.should == nil
end
it "returns nil when #primitive_convert last returned :finished" do
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
- ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished
- ec.last_error.should be_nil
+ ec.primitive_convert("glark".dup.force_encoding('utf-8'), +"").should == :finished
+ ec.last_error.should == nil
end
it "returns nil if the last conversion succeeded but the penultimate failed" do
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
- ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence
- ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished
- ec.last_error.should be_nil
+ ec.primitive_convert(+"\xf1abcd", +"").should == :invalid_byte_sequence
+ ec.primitive_convert("glark".dup.force_encoding('utf-8'), +"").should == :finished
+ ec.last_error.should == nil
end
it "returns an Encoding::InvalidByteSequenceError when #primitive_convert last returned :invalid_byte_sequence" do
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
- ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence
- ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError)
+ ec.primitive_convert(+"\xf1abcd", +"").should == :invalid_byte_sequence
+ ec.last_error.should.instance_of?(Encoding::InvalidByteSequenceError)
end
it "returns an Encoding::UndefinedConversionError when #primitive_convert last returned :undefined_conversion" do
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
- ec.primitive_convert("\u{9876}","").should == :undefined_conversion
- ec.last_error.should be_an_instance_of(Encoding::UndefinedConversionError)
+ ec.primitive_convert(+"\u{9876}", +"").should == :undefined_conversion
+ ec.last_error.should.instance_of?(Encoding::UndefinedConversionError)
end
it "returns an Encoding::InvalidByteSequenceError when #primitive_convert last returned :incomplete_input" do
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- ec.primitive_convert("\xa4", "", nil, 10).should == :incomplete_input
- ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError)
+ ec.primitive_convert(+"\xa4", +"", nil, 10).should == :incomplete_input
+ ec.last_error.should.instance_of?(Encoding::InvalidByteSequenceError)
end
it "returns an Encoding::InvalidByteSequenceError when the last call to #convert produced one" do
@@ -56,10 +56,10 @@ describe "Encoding::Converter#last_error" do
exception = nil
-> {
ec.convert("\xf1abcd")
- }.should raise_error(Encoding::InvalidByteSequenceError) { |e|
+ }.should.raise(Encoding::InvalidByteSequenceError) { |e|
exception = e
}
- ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError)
+ ec.last_error.should.instance_of?(Encoding::InvalidByteSequenceError)
ec.last_error.message.should == exception.message
end
@@ -68,12 +68,12 @@ describe "Encoding::Converter#last_error" do
exception = nil
-> {
ec.convert("\u{9899}")
- }.should raise_error(Encoding::UndefinedConversionError) { |e|
+ }.should.raise(Encoding::UndefinedConversionError) { |e|
exception = e
}
- ec.last_error.should be_an_instance_of(Encoding::UndefinedConversionError)
+ ec.last_error.should.instance_of?(Encoding::UndefinedConversionError)
ec.last_error.message.should == exception.message
- ec.last_error.message.should include "from UTF-8 to ISO-8859-1"
+ ec.last_error.message.should.include? "from UTF-8 to ISO-8859-1"
end
it "returns the last error of #convert with a message showing the transcoding path" do
@@ -81,11 +81,11 @@ describe "Encoding::Converter#last_error" do
exception = nil
-> {
ec.convert("\xE9") # é in ISO-8859-1
- }.should raise_error(Encoding::UndefinedConversionError) { |e|
+ }.should.raise(Encoding::UndefinedConversionError) { |e|
exception = e
}
- ec.last_error.should be_an_instance_of(Encoding::UndefinedConversionError)
+ ec.last_error.should.instance_of?(Encoding::UndefinedConversionError)
ec.last_error.message.should == exception.message
- ec.last_error.message.should include "from ISO-8859-1 to UTF-8 to Big5"
+ ec.last_error.message.should.include? "from ISO-8859-1 to UTF-8 to Big5"
end
end
diff --git a/spec/ruby/core/encoding/converter/new_spec.rb b/spec/ruby/core/encoding/converter/new_spec.rb
index 9fc97263fe..bbdfb48bce 100644
--- a/spec/ruby/core/encoding/converter/new_spec.rb
+++ b/spec/ruby/core/encoding/converter/new_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
describe "Encoding::Converter.new" do
@@ -25,7 +25,7 @@ describe "Encoding::Converter.new" do
it "raises an Encoding::ConverterNotFoundError if both encodings are the same" do
-> do
Encoding::Converter.new "utf-8", "utf-8"
- end.should raise_error(Encoding::ConverterNotFoundError)
+ end.should.raise(Encoding::ConverterNotFoundError)
end
it "calls #to_str to convert the source encoding argument to an encoding name" do
@@ -47,7 +47,7 @@ describe "Encoding::Converter.new" do
conv.replacement.should == "fubar"
end
- it "calls #to_hash to convert the options argument to a Hash if not a Fixnum" do
+ it "calls #to_hash to convert the options argument to a Hash if not an Integer" do
opts = mock("encoding converter options")
opts.should_receive(:to_hash).and_return({ replace: "fubar" })
conv = Encoding::Converter.new("us-ascii", "utf-8", **opts)
@@ -67,25 +67,25 @@ describe "Encoding::Converter.new" do
-> do
Encoding::Converter.new("us-ascii", "utf-8", replace: obj)
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it "raises a TypeError if passed true for the replacement object" do
-> do
Encoding::Converter.new("us-ascii", "utf-8", replace: true)
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it "raises a TypeError if passed false for the replacement object" do
-> do
Encoding::Converter.new("us-ascii", "utf-8", replace: false)
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
- it "raises a TypeError if passed a Fixnum for the replacement object" do
+ it "raises a TypeError if passed an Integer for the replacement object" do
-> do
Encoding::Converter.new("us-ascii", "utf-8", replace: 1)
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it "accepts an empty String for the replacement object" do
@@ -107,7 +107,7 @@ describe "Encoding::Converter.new" do
it "sets the replacement String to '\\uFFFD'" do
conv = Encoding::Converter.new("us-ascii", "utf-8", replace: nil)
- conv.replacement.should == "\u{fffd}".force_encoding("utf-8")
+ conv.replacement.should == "\u{fffd}".dup.force_encoding("utf-8")
end
it "sets the replacement String encoding to UTF-8" do
diff --git a/spec/ruby/core/encoding/converter/primitive_convert_spec.rb b/spec/ruby/core/encoding/converter/primitive_convert_spec.rb
index 802d8e7cb1..ab9ce6a992 100644
--- a/spec/ruby/core/encoding/converter/primitive_convert_spec.rb
+++ b/spec/ruby/core/encoding/converter/primitive_convert_spec.rb
@@ -1,4 +1,5 @@
-# -*- encoding: binary -*-
+# encoding: binary
+# frozen_string_literal: false
require_relative '../../../spec_helper'
describe "Encoding::Converter#primitive_convert" do
@@ -7,19 +8,23 @@ describe "Encoding::Converter#primitive_convert" do
end
it "accepts a nil source buffer" do
- -> { @ec.primitive_convert(nil,"") }.should_not raise_error
+ -> { @ec.primitive_convert(nil,"") }.should_not.raise
end
it "accepts a String as the source buffer" do
- -> { @ec.primitive_convert("","") }.should_not raise_error
+ -> { @ec.primitive_convert("","") }.should_not.raise
+ end
+
+ it "raises FrozenError when the destination buffer is a frozen String" do
+ -> { @ec.primitive_convert("", "".freeze) }.should.raise(FrozenError)
end
it "accepts nil for the destination byte offset" do
- -> { @ec.primitive_convert("","", nil) }.should_not raise_error
+ -> { @ec.primitive_convert("","", nil) }.should_not.raise
end
it "accepts an integer for the destination byte offset" do
- -> { @ec.primitive_convert("","a", 1) }.should_not raise_error
+ -> { @ec.primitive_convert("","a", 1) }.should_not.raise
end
it "calls #to_int to convert the destination byte offset" do
@@ -30,10 +35,10 @@ describe "Encoding::Converter#primitive_convert" do
end
it "raises an ArgumentError if the destination byte offset is greater than the bytesize of the destination buffer" do
- -> { @ec.primitive_convert("","am", 0) }.should_not raise_error
- -> { @ec.primitive_convert("","am", 1) }.should_not raise_error
- -> { @ec.primitive_convert("","am", 2) }.should_not raise_error
- -> { @ec.primitive_convert("","am", 3) }.should raise_error(ArgumentError)
+ -> { @ec.primitive_convert("","am", 0) }.should_not.raise
+ -> { @ec.primitive_convert("","am", 1) }.should_not.raise
+ -> { @ec.primitive_convert("","am", 2) }.should_not.raise
+ -> { @ec.primitive_convert("","am", 3) }.should.raise(ArgumentError)
end
it "uses the destination byte offset to determine where to write the result in the destination buffer" do
@@ -49,19 +54,19 @@ describe "Encoding::Converter#primitive_convert" do
end
it "accepts nil for the destination bytesize" do
- -> { @ec.primitive_convert("","", nil, nil) }.should_not raise_error
+ -> { @ec.primitive_convert("","", nil, nil) }.should_not.raise
end
it "accepts an integer for the destination bytesize" do
- -> { @ec.primitive_convert("","", nil, 0) }.should_not raise_error
+ -> { @ec.primitive_convert("","", nil, 0) }.should_not.raise
end
it "allows a destination bytesize value greater than the bytesize of the source buffer" do
- -> { @ec.primitive_convert("am","", nil, 3) }.should_not raise_error
+ -> { @ec.primitive_convert("am","", nil, 3) }.should_not.raise
end
it "allows a destination bytesize value less than the bytesize of the source buffer" do
- -> { @ec.primitive_convert("am","", nil, 1) }.should_not raise_error
+ -> { @ec.primitive_convert("am","", nil, 1) }.should_not.raise
end
it "calls #to_int to convert the destination byte size" do
diff --git a/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb b/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb
index 1f836b259f..580e2e37e1 100644
--- a/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb
+++ b/spec/ruby/core/encoding/converter/primitive_errinfo_spec.rb
@@ -1,4 +1,5 @@
-# -*- encoding: binary -*-
+# encoding: binary
+# frozen_string_literal: false
require_relative '../../../spec_helper'
describe "Encoding::Converter#primitive_errinfo" do
@@ -54,7 +55,7 @@ describe "Encoding::Converter#primitive_errinfo" do
it "returns the state, source encoding, target encoding, erroneous bytes, and the read-again bytes when #convert last raised InvalidByteSequenceError" do
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
- -> { ec.convert("\xf1abcd") }.should raise_error(Encoding::InvalidByteSequenceError)
+ -> { ec.convert("\xf1abcd") }.should.raise(Encoding::InvalidByteSequenceError)
ec.primitive_errinfo.should ==
[:invalid_byte_sequence, "UTF-8", "ISO-8859-1", "\xF1", "a"]
end
@@ -62,7 +63,7 @@ describe "Encoding::Converter#primitive_errinfo" do
it "returns the state, source encoding, target encoding, erroneous bytes, and the read-again bytes when #finish last raised InvalidByteSequenceError" do
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
ec.convert("\xa4")
- -> { ec.finish }.should raise_error(Encoding::InvalidByteSequenceError)
+ -> { ec.finish }.should.raise(Encoding::InvalidByteSequenceError)
ec.primitive_errinfo.should == [:incomplete_input, "EUC-JP", "UTF-8", "\xA4", ""]
end
end
diff --git a/spec/ruby/core/encoding/converter/putback_spec.rb b/spec/ruby/core/encoding/converter/putback_spec.rb
index 87495eaf3f..a85cec5145 100644
--- a/spec/ruby/core/encoding/converter/putback_spec.rb
+++ b/spec/ruby/core/encoding/converter/putback_spec.rb
@@ -1,14 +1,14 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
describe "Encoding::Converter#putback" do
before :each do
@ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- @ret = @ec.primitive_convert(@src="abc\xa1def", @dst="", nil, 10)
+ @ret = @ec.primitive_convert(@src=+"abc\xa1def", @dst=+"", nil, 10)
end
it "returns a String" do
- @ec.putback.should be_an_instance_of(String)
+ @ec.putback.should.instance_of?(String)
end
it "returns a String in the source encoding" do
@@ -34,14 +34,23 @@ describe "Encoding::Converter#putback" do
@ec.putback.should == ""
end
+ it "returns the problematic bytes for UTF-16LE" do
+ ec = Encoding::Converter.new("utf-16le", "iso-8859-1")
+ src = +"\x00\xd8\x61\x00"
+ dst = +""
+ ec.primitive_convert(src, dst).should == :invalid_byte_sequence
+ ec.primitive_errinfo.should == [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
+ ec.putback.should == "a\x00".dup.force_encoding("utf-16le")
+ ec.putback.should == ""
+ end
+
it "accepts an integer argument corresponding to the number of bytes to be put back" do
ec = Encoding::Converter.new("utf-16le", "iso-8859-1")
- src = "\x00\xd8\x61\x00"
- dst = ""
+ src = +"\x00\xd8\x61\x00"
+ dst = +""
ec.primitive_convert(src, dst).should == :invalid_byte_sequence
ec.primitive_errinfo.should == [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
- ec.putback(1).should == "\x00".force_encoding("utf-16le")
- ec.putback.should == "a".force_encoding("utf-16le")
+ ec.putback(2).should == "a\x00".dup.force_encoding("utf-16le")
ec.putback.should == ""
end
end
diff --git a/spec/ruby/core/encoding/converter/replacement_spec.rb b/spec/ruby/core/encoding/converter/replacement_spec.rb
index 5ca42e7e5a..c25ec36517 100644
--- a/spec/ruby/core/encoding/converter/replacement_spec.rb
+++ b/spec/ruby/core/encoding/converter/replacement_spec.rb
@@ -13,7 +13,7 @@ describe "Encoding::Converter#replacement" do
it "returns \\uFFFD when the destination encoding is UTF-8" do
ec = Encoding::Converter.new("us-ascii", "utf-8")
- ec.replacement.should == "\u{fffd}".force_encoding('utf-8')
+ ec.replacement.should == "\u{fffd}".dup.force_encoding('utf-8')
ec.replacement.encoding.should == Encoding::UTF_8
end
end
@@ -33,38 +33,36 @@ describe "Encoding::Converter#replacement=" do
it "raises a TypeError if assigned a non-String argument" do
ec = Encoding::Converter.new("utf-8", "us-ascii")
- -> { ec.replacement = nil }.should raise_error(TypeError)
+ -> { ec.replacement = nil }.should.raise(TypeError)
end
it "sets #replacement" do
ec = Encoding::Converter.new("us-ascii", "utf-8")
- ec.replacement.should == "\u{fffd}".force_encoding('utf-8')
+ ec.replacement.should == "\u{fffd}".dup.force_encoding('utf-8')
ec.replacement = '?'.encode('utf-8')
- ec.replacement.should == '?'.force_encoding('utf-8')
+ ec.replacement.should == '?'.dup.force_encoding('utf-8')
end
it "raises an UndefinedConversionError is the argument cannot be converted into the destination encoding" do
ec = Encoding::Converter.new("sjis", "ascii")
- utf8_q = "\u{986}".force_encoding('utf-8')
- ec.primitive_convert(utf8_q.dup, "").should == :undefined_conversion
- -> { ec.replacement = utf8_q }.should \
- raise_error(Encoding::UndefinedConversionError)
+ utf8_q = "\u{986}".dup.force_encoding('utf-8')
+ ec.primitive_convert(utf8_q.dup, +"").should == :undefined_conversion
+ -> { ec.replacement = utf8_q }.should.raise(Encoding::UndefinedConversionError)
end
it "does not change the replacement character if the argument cannot be converted into the destination encoding" do
ec = Encoding::Converter.new("sjis", "ascii")
- utf8_q = "\u{986}".force_encoding('utf-8')
- ec.primitive_convert(utf8_q.dup, "").should == :undefined_conversion
- -> { ec.replacement = utf8_q }.should \
- raise_error(Encoding::UndefinedConversionError)
- ec.replacement.should == "?".force_encoding('us-ascii')
+ utf8_q = "\u{986}".dup.force_encoding('utf-8')
+ ec.primitive_convert(utf8_q.dup, +"").should == :undefined_conversion
+ -> { ec.replacement = utf8_q }.should.raise(Encoding::UndefinedConversionError)
+ ec.replacement.should == "?".dup.force_encoding('us-ascii')
end
it "uses the replacement character" do
ec = Encoding::Converter.new("utf-8", "us-ascii", :invalid => :replace, :undef => :replace)
ec.replacement = "!"
- dest = ""
- status = ec.primitive_convert "中文123", dest
+ dest = +""
+ status = ec.primitive_convert(+"中文123", dest)
status.should == :finished
dest.should == "!!123"
diff --git a/spec/ruby/core/encoding/converter/search_convpath_spec.rb b/spec/ruby/core/encoding/converter/search_convpath_spec.rb
index 0882af5539..cac44765f8 100644
--- a/spec/ruby/core/encoding/converter/search_convpath_spec.rb
+++ b/spec/ruby/core/encoding/converter/search_convpath_spec.rb
@@ -23,8 +23,8 @@ describe "Encoding::Converter.search_convpath" do
end
it "raises an Encoding::ConverterNotFoundError if no conversion path exists" do
- -> do
- Encoding::Converter.search_convpath(Encoding::BINARY, Encoding::Emacs_Mule)
- end.should raise_error(Encoding::ConverterNotFoundError)
+ -> do
+ Encoding::Converter.search_convpath(Encoding::BINARY, Encoding::Emacs_Mule)
+ end.should.raise(Encoding::ConverterNotFoundError)
end
end
diff --git a/spec/ruby/core/encoding/default_external_spec.rb b/spec/ruby/core/encoding/default_external_spec.rb
index e2cb9b02f4..2a2bd7f6ae 100644
--- a/spec/ruby/core/encoding/default_external_spec.rb
+++ b/spec/ruby/core/encoding/default_external_spec.rb
@@ -10,13 +10,19 @@ describe "Encoding.default_external" do
end
it "returns an Encoding object" do
- Encoding.default_external.should be_an_instance_of(Encoding)
+ Encoding.default_external.should.instance_of?(Encoding)
end
it "returns the default external encoding" do
Encoding.default_external = Encoding::SHIFT_JIS
Encoding.default_external.should == Encoding::SHIFT_JIS
end
+
+ platform_is :windows do
+ it 'is UTF-8 by default on Windows' do
+ Encoding.default_external.should == Encoding::UTF_8
+ end
+ end
end
describe "Encoding.default_external=" do
@@ -54,10 +60,10 @@ describe "Encoding.default_external=" do
end
it "raises a TypeError unless the argument is an Encoding or convertible to a String" do
- -> { Encoding.default_external = [] }.should raise_error(TypeError)
+ -> { Encoding.default_external = [] }.should.raise(TypeError)
end
it "raises an ArgumentError if the argument is nil" do
- -> { Encoding.default_external = nil }.should raise_error(ArgumentError)
+ -> { Encoding.default_external = nil }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/encoding/default_internal_spec.rb b/spec/ruby/core/encoding/default_internal_spec.rb
index 855f4e9f32..38aef9dce9 100644
--- a/spec/ruby/core/encoding/default_internal_spec.rb
+++ b/spec/ruby/core/encoding/default_internal_spec.rb
@@ -10,17 +10,17 @@ describe "Encoding.default_internal" do
end
it "is nil by default" do
- Encoding.default_internal.should be_nil
+ Encoding.default_internal.should == nil
end
it "returns an Encoding object if a default internal encoding is set" do
Encoding.default_internal = Encoding::ASCII
- Encoding.default_internal.should be_an_instance_of(Encoding)
+ Encoding.default_internal.should.instance_of?(Encoding)
end
it "returns nil if no default internal encoding is set" do
Encoding.default_internal = nil
- Encoding.default_internal.should be_nil
+ Encoding.default_internal.should == nil
end
it "returns the default internal encoding" do
@@ -60,15 +60,15 @@ describe "Encoding.default_internal=" do
obj = mock('string')
obj.should_receive(:to_str).at_least(1).times.and_return(1)
- -> { Encoding.default_internal = obj }.should raise_error(TypeError)
+ -> { Encoding.default_internal = obj }.should.raise(TypeError)
end
it "raises a TypeError when passed an object not providing #to_str" do
- -> { Encoding.default_internal = mock("encoding") }.should raise_error(TypeError)
+ -> { Encoding.default_internal = mock("encoding") }.should.raise(TypeError)
end
it "accepts an argument of nil to unset the default internal encoding" do
Encoding.default_internal = nil
- Encoding.default_internal.should be_nil
+ Encoding.default_internal.should == nil
end
end
diff --git a/spec/ruby/core/encoding/dummy_spec.rb b/spec/ruby/core/encoding/dummy_spec.rb
index 75ffcd5a4e..05530a8186 100644
--- a/spec/ruby/core/encoding/dummy_spec.rb
+++ b/spec/ruby/core/encoding/dummy_spec.rb
@@ -2,13 +2,24 @@ require_relative '../../spec_helper'
describe "Encoding#dummy?" do
it "returns false for proper encodings" do
- Encoding::UTF_8.dummy?.should be_false
- Encoding::ASCII.dummy?.should be_false
+ Encoding::UTF_8.dummy?.should == false
+ Encoding::ASCII.dummy?.should == false
end
it "returns true for dummy encodings" do
- Encoding::ISO_2022_JP.dummy?.should be_true
- Encoding::CP50221.dummy?.should be_true
- Encoding::UTF_7.dummy?.should be_true
+ Encoding::ISO_2022_JP.dummy?.should == true
+ Encoding::CP50221.dummy?.should == true
+ Encoding::UTF_7.dummy?.should == true
+ end
+
+ it "returns true for UTF_16 and UTF_32" do
+ Encoding::UTF_16.should.dummy?
+ Encoding::UTF_32.should.dummy?
+ end
+
+ it "implies not #ascii_compatible?" do
+ Encoding.list.select(&:dummy?).each do |encoding|
+ encoding.should_not.ascii_compatible?
+ end
end
end
diff --git a/spec/ruby/core/encoding/find_spec.rb b/spec/ruby/core/encoding/find_spec.rb
index 8a0873070f..c5356560eb 100644
--- a/spec/ruby/core/encoding/find_spec.rb
+++ b/spec/ruby/core/encoding/find_spec.rb
@@ -7,18 +7,18 @@ describe "Encoding.find" do
it "returns the corresponding Encoding object if given a valid encoding name" do
@encodings.each do |enc|
- Encoding.find(enc).should be_an_instance_of(Encoding)
+ Encoding.find(enc).should.instance_of?(Encoding)
end
end
it "returns the corresponding Encoding object if given a valid alias name" do
Encoding.aliases.keys.each do |enc_alias|
- Encoding.find(enc_alias).should be_an_instance_of(Encoding)
+ Encoding.find(enc_alias).should.instance_of?(Encoding)
end
end
it "raises a TypeError if passed a Symbol" do
- -> { Encoding.find(:"utf-8") }.should raise_error(TypeError)
+ -> { Encoding.find(:"utf-8") }.should.raise(TypeError)
end
it "returns the passed Encoding object" do
@@ -50,7 +50,7 @@ describe "Encoding.find" do
end
it "raises an ArgumentError if the given encoding does not exist" do
- -> { Encoding.find('dh2dh278d') }.should raise_error(ArgumentError)
+ -> { Encoding.find('dh2dh278d') }.should.raise(ArgumentError, 'unknown encoding name - dh2dh278d')
end
# Not sure how to do a better test, since locale depends on weird platform-specific stuff
diff --git a/spec/ruby/core/encoding/fixtures/classes.rb b/spec/ruby/core/encoding/fixtures/classes.rb
index 12e9a4f348..943865e8d8 100644
--- a/spec/ruby/core/encoding/fixtures/classes.rb
+++ b/spec/ruby/core/encoding/fixtures/classes.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
module EncodingSpecs
class UndefinedConversionError
def self.exception
diff --git a/spec/ruby/core/encoding/inspect_spec.rb b/spec/ruby/core/encoding/inspect_spec.rb
index 9a930b2a77..ab7f8cf9fc 100644
--- a/spec/ruby/core/encoding/inspect_spec.rb
+++ b/spec/ruby/core/encoding/inspect_spec.rb
@@ -2,12 +2,26 @@ require_relative '../../spec_helper'
describe "Encoding#inspect" do
it "returns a String" do
- Encoding::UTF_8.inspect.should be_an_instance_of(String)
+ Encoding::UTF_8.inspect.should.instance_of?(String)
end
- it "returns #<Encoding:name> for a non-dummy encoding named 'name'" do
- Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc|
- enc.inspect.should =~ /#<Encoding:#{enc.name}>/
+ ruby_version_is ""..."3.4" do
+ it "returns #<Encoding:name> for a non-dummy encoding named 'name'" do
+ Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc|
+ enc.inspect.should =~ /#<Encoding:#{enc.name}>/
+ end
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "returns #<Encoding:name> for a non-dummy encoding named 'name'" do
+ Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc|
+ if enc.name == "ASCII-8BIT"
+ enc.inspect.should == "#<Encoding:BINARY (ASCII-8BIT)>"
+ else
+ enc.inspect.should =~ /#<Encoding:#{enc.name}>/
+ end
+ end
end
end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb
index f5fa6f55e3..7d3cc77c0b 100644
--- a/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb
@@ -1,3 +1,4 @@
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do
@@ -7,8 +8,8 @@ describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do
end
it "returns a String" do
- @exception.destination_encoding_name.should be_an_instance_of(String)
- @exception2.destination_encoding_name.should be_an_instance_of(String)
+ @exception.destination_encoding_name.should.instance_of?(String)
+ @exception2.destination_encoding_name.should.instance_of?(String)
end
it "is equal to the destination encoding name of the object that raised it" do
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb
index 43be3ddd71..264c409b1c 100644
--- a/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb
@@ -1,3 +1,4 @@
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#destination_encoding" do
@@ -7,8 +8,8 @@ describe "Encoding::InvalidByteSequenceError#destination_encoding" do
end
it "returns an Encoding object" do
- @exception.destination_encoding.should be_an_instance_of(Encoding)
- @exception2.destination_encoding.should be_an_instance_of(Encoding)
+ @exception.destination_encoding.should.instance_of?(Encoding)
+ @exception2.destination_encoding.should.instance_of?(Encoding)
end
it "is equal to the destination encoding of the object that raised it" do
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb
index a8f7354b16..40a9a35caf 100644
--- a/spec/ruby/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb
@@ -1,4 +1,5 @@
-# -*- encoding: binary -*-
+# encoding: binary
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#error_bytes" do
@@ -8,8 +9,8 @@ describe "Encoding::InvalidByteSequenceError#error_bytes" do
end
it "returns a String" do
- @exception.error_bytes.should be_an_instance_of(String)
- @exception2.error_bytes.should be_an_instance_of(String)
+ @exception.error_bytes.should.instance_of?(String)
+ @exception2.error_bytes.should.instance_of?(String)
end
it "returns the bytes that caused the exception" do
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb
index 94201a9b15..143db7b6da 100644
--- a/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb
@@ -1,28 +1,28 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../../spec_helper'
describe "Encoding::InvalidByteSequenceError#incomplete_input?" do
it "returns nil by default" do
- Encoding::InvalidByteSequenceError.new.incomplete_input?.should be_nil
+ Encoding::InvalidByteSequenceError.new.incomplete_input?.should == nil
end
it "returns true if #primitive_convert returned :incomplete_input for the same data" do
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- ec.primitive_convert("\xA1",'').should == :incomplete_input
+ ec.primitive_convert(+"\xA1", +'').should == :incomplete_input
begin
ec.convert("\xA1")
rescue Encoding::InvalidByteSequenceError => e
- e.incomplete_input?.should be_true
+ e.incomplete_input?.should == true
end
end
it "returns false if #primitive_convert returned :invalid_byte_sequence for the same data" do
ec = Encoding::Converter.new("ascii", "utf-8")
- ec.primitive_convert("\xfffffffff",'').should == :invalid_byte_sequence
+ ec.primitive_convert(+"\xfffffffff", +'').should == :invalid_byte_sequence
begin
ec.convert("\xfffffffff")
rescue Encoding::InvalidByteSequenceError => e
- e.incomplete_input?.should be_false
+ e.incomplete_input?.should == false
end
end
end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb
index 93823b5db4..e4fc81aac6 100644
--- a/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb
@@ -1,4 +1,5 @@
-# -*- encoding: binary -*-
+# encoding: binary
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#readagain_bytes" do
@@ -8,17 +9,17 @@ describe "Encoding::InvalidByteSequenceError#readagain_bytes" do
end
it "returns a String" do
- @exception.readagain_bytes.should be_an_instance_of(String)
- @exception2.readagain_bytes.should be_an_instance_of(String)
+ @exception.readagain_bytes.should.instance_of?(String)
+ @exception2.readagain_bytes.should.instance_of?(String)
end
it "returns the bytes to be read again" do
@exception.readagain_bytes.size.should == 1
- @exception.readagain_bytes.should == "a".force_encoding('binary')
+ @exception.readagain_bytes.should == "a".dup.force_encoding('binary')
@exception.readagain_bytes.should == @errinfo[-1]
@exception2.readagain_bytes.size.should == 1
- @exception2.readagain_bytes.should == "\xFF".force_encoding('binary')
+ @exception2.readagain_bytes.should == "\xFF".dup.force_encoding('binary')
@exception2.readagain_bytes.should == @errinfo2[-1]
end
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb
index bd3a51cbc5..b00e1ad4e8 100644
--- a/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb
@@ -1,3 +1,4 @@
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#source_encoding_name" do
@@ -7,7 +8,7 @@ describe "Encoding::UndefinedConversionError#source_encoding_name" do
end
it "returns a String" do
- @exception.source_encoding_name.should be_an_instance_of(String)
+ @exception.source_encoding_name.should.instance_of?(String)
end
it "is equal to the source encoding name of the object that raised it" do
diff --git a/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb
index f43d6d5830..32ad25dbb5 100644
--- a/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb
+++ b/spec/ruby/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb
@@ -1,3 +1,4 @@
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::InvalidByteSequenceError#source_encoding" do
@@ -7,8 +8,8 @@ describe "Encoding::InvalidByteSequenceError#source_encoding" do
end
it "returns an Encoding object" do
- @exception.source_encoding.should be_an_instance_of(Encoding)
- @exception2.source_encoding.should be_an_instance_of(Encoding)
+ @exception.source_encoding.should.instance_of?(Encoding)
+ @exception2.source_encoding.should.instance_of?(Encoding)
end
it "is equal to the source encoding of the object that raised it" do
diff --git a/spec/ruby/core/encoding/list_spec.rb b/spec/ruby/core/encoding/list_spec.rb
index 2a2078974e..9fe336c608 100644
--- a/spec/ruby/core/encoding/list_spec.rb
+++ b/spec/ruby/core/encoding/list_spec.rb
@@ -2,38 +2,46 @@ require_relative '../../spec_helper'
describe "Encoding.list" do
it "returns an Array" do
- Encoding.list.should be_an_instance_of(Array)
+ Encoding.list.should.instance_of?(Array)
end
it "returns an Array of Encoding objects" do
Encoding.list.each do |enc|
- enc.should be_an_instance_of(Encoding)
+ enc.should.instance_of?(Encoding)
end
end
it "returns each encoding only once" do
- orig = Encoding.list.map {|e| e.name}
+ orig = Encoding.list.map { |e| e.name }
orig.should == orig.uniq
end
it "includes the default external encoding" do
- Encoding.list.include?(Encoding.default_external).should be_true
+ Encoding.list.include?(Encoding.default_external).should == true
end
it "does not include any alias names" do
Encoding.aliases.keys.each do |enc_alias|
- Encoding.list.include?(enc_alias).should be_false
+ Encoding.list.include?(enc_alias).should == false
end
end
it "includes all aliased encodings" do
Encoding.aliases.values.each do |enc_alias|
- Encoding.list.include?(Encoding.find(enc_alias)).should be_true
+ Encoding.list.include?(Encoding.find(enc_alias)).should == true
end
end
it "includes dummy encodings" do
- Encoding.list.select {|e| e.dummy?}.should_not == []
+ Encoding.list.select { |e| e.dummy? }.should_not == []
+ end
+
+ it 'includes UTF-8 encoding' do
+ Encoding.list.should.include?(Encoding::UTF_8)
+ end
+
+ it 'includes CESU-8 encoding' do
+ Encoding.list.should.include?(Encoding::CESU_8)
end
# TODO: Find example that illustrates this
diff --git a/spec/ruby/core/encoding/locale_charmap_spec.rb b/spec/ruby/core/encoding/locale_charmap_spec.rb
index 8143b9083a..0d77bf227b 100644
--- a/spec/ruby/core/encoding/locale_charmap_spec.rb
+++ b/spec/ruby/core/encoding/locale_charmap_spec.rb
@@ -2,55 +2,55 @@ require_relative '../../spec_helper'
describe "Encoding.locale_charmap" do
it "returns a String" do
- Encoding.locale_charmap.should be_an_instance_of(String)
+ Encoding.locale_charmap.should.instance_of?(String)
end
- # FIXME: Get this working on Windows
- platform_is :linux do
- platform_is_not :android do
- it "returns a value based on the LC_ALL environment variable" do
- old_lc_all = ENV['LC_ALL']
- ENV['LC_ALL'] = 'C'
- ruby_exe("print Encoding.locale_charmap").should == 'ANSI_X3.4-1968'
- ENV['LC_ALL'] = old_lc_all
+ describe "when setting LC_ALL=C" do
+ before :each do
+ @old_lc_all = ENV['LC_ALL']
+ end
+
+ after :each do
+ ENV['LC_ALL'] = @old_lc_all
+ end
+
+ # FIXME: Get this working on Windows
+ platform_is :linux do
+ platform_is_not :android do
+ it "returns a value based on the LC_ALL environment variable" do
+ ENV['LC_ALL'] = 'C'
+ ruby_exe("print Encoding.locale_charmap").should == 'ANSI_X3.4-1968'
+ end
end
end
- end
- platform_is :freebsd, :openbsd, :darwin do
- it "returns a value based on the LC_ALL environment variable" do
- old_lc_all = ENV['LC_ALL']
- ENV['LC_ALL'] = 'C'
- ruby_exe("print Encoding.locale_charmap").should == 'US-ASCII'
- ENV['LC_ALL'] = old_lc_all
+ platform_is :freebsd, :openbsd, :darwin do
+ it "returns a value based on the LC_ALL environment variable" do
+ ENV['LC_ALL'] = 'C'
+ ruby_exe("print Encoding.locale_charmap").should == 'US-ASCII'
+ end
end
- end
- platform_is :netbsd do
- it "returns a value based on the LC_ALL environment variable" do
- old_lc_all = ENV['LC_ALL']
- ENV['LC_ALL'] = 'C'
- ruby_exe("print Encoding.locale_charmap").should == '646'
- ENV['LC_ALL'] = old_lc_all
+ platform_is :netbsd do
+ it "returns a value based on the LC_ALL environment variable" do
+ ENV['LC_ALL'] = 'C'
+ ruby_exe("print Encoding.locale_charmap").should == '646'
+ end
end
- end
- platform_is :android do
- it "always returns UTF-8" do
- old_lc_all = ENV['LC_ALL']
- ENV['LC_ALL'] = 'C'
- ruby_exe("print Encoding.locale_charmap").should == 'UTF-8'
- ENV['LC_ALL'] = old_lc_all
+ platform_is :android do
+ it "always returns UTF-8" do
+ ENV['LC_ALL'] = 'C'
+ ruby_exe("print Encoding.locale_charmap").should == 'UTF-8'
+ end
end
- end
- platform_is :bsd, :darwin, :linux do
- it "is unaffected by assigning to ENV['LC_ALL'] in the same process" do
- old_charmap = Encoding.locale_charmap
- old_lc_all = ENV['LC_ALL']
- ENV['LC_ALL'] = 'C'
- Encoding.locale_charmap.should == old_charmap
- ENV['LC_ALL'] = old_lc_all
+ platform_is :bsd, :darwin, :linux do
+ it "is unaffected by assigning to ENV['LC_ALL'] in the same process" do
+ old_charmap = Encoding.locale_charmap
+ ENV['LC_ALL'] = 'C'
+ Encoding.locale_charmap.should == old_charmap
+ end
end
end
end
diff --git a/spec/ruby/core/encoding/name_list_spec.rb b/spec/ruby/core/encoding/name_list_spec.rb
index 836381c4d8..1ba8d383bc 100644
--- a/spec/ruby/core/encoding/name_list_spec.rb
+++ b/spec/ruby/core/encoding/name_list_spec.rb
@@ -2,22 +2,22 @@ require_relative '../../spec_helper'
describe "Encoding.name_list" do
it "returns an Array" do
- Encoding.name_list.should be_an_instance_of(Array)
+ Encoding.name_list.should.instance_of?(Array)
end
it "returns encoding names as Strings" do
- Encoding.name_list.each {|e| e.should be_an_instance_of(String) }
+ Encoding.name_list.each {|e| e.should.instance_of?(String) }
end
it "includes all aliases" do
Encoding.aliases.keys.each do |enc_alias|
- Encoding.name_list.include?(enc_alias).should be_true
+ Encoding.name_list.include?(enc_alias).should == true
end
end
it "includes all non-dummy encodings" do
Encoding.list.each do |enc|
- Encoding.name_list.include?(enc.name).should be_true
+ Encoding.name_list.include?(enc.name).should == true
end
end
end
diff --git a/spec/ruby/core/encoding/name_spec.rb b/spec/ruby/core/encoding/name_spec.rb
index 5eadb1d2f5..1d625c9379 100644
--- a/spec/ruby/core/encoding/name_spec.rb
+++ b/spec/ruby/core/encoding/name_spec.rb
@@ -1,5 +1,15 @@
-require_relative 'shared/name'
+require_relative "../../spec_helper"
describe "Encoding#name" do
- it_behaves_like :encoding_name, :name
+ it "returns a String" do
+ Encoding.list.each do |e|
+ e.name.should.instance_of?(String)
+ end
+ end
+
+ it "uniquely identifies an encoding" do
+ Encoding.list.each do |e|
+ e.should == Encoding.find(e.name)
+ end
+ end
end
diff --git a/spec/ruby/core/encoding/names_spec.rb b/spec/ruby/core/encoding/names_spec.rb
index 9ded043bbb..e6bcbf474a 100644
--- a/spec/ruby/core/encoding/names_spec.rb
+++ b/spec/ruby/core/encoding/names_spec.rb
@@ -4,7 +4,7 @@ describe "Encoding#names" do
it "returns an Array" do
Encoding.name_list.each do |name|
e = Encoding.find(name) or next
- e.names.should be_an_instance_of(Array)
+ e.names.should.instance_of?(Array)
end
end
@@ -12,7 +12,7 @@ describe "Encoding#names" do
Encoding.name_list.each do |name|
e = Encoding.find(name) or next
e.names.each do |this_name|
- this_name.should be_an_instance_of(String)
+ this_name.should.instance_of?(String)
end
end
end
@@ -29,7 +29,7 @@ describe "Encoding#names" do
e = Encoding.find(name) or next
aliases = Encoding.aliases.select{|a,n| n == name}.keys
names = e.names
- aliases.each {|a| names.include?(a).should be_true}
+ aliases.each {|a| names.include?(a).should == true}
end
end
end
diff --git a/spec/ruby/core/encoding/replicate_spec.rb b/spec/ruby/core/encoding/replicate_spec.rb
index 717e9cea72..9fe0ba8747 100644
--- a/spec/ruby/core/encoding/replicate_spec.rb
+++ b/spec/ruby/core/encoding/replicate_spec.rb
@@ -1,46 +1,8 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../spec_helper'
describe "Encoding#replicate" do
- before :all do
- @i = 0
- end
-
- before :each do
- @i += 1
- @prefix = "RS#{@i}"
- end
-
- it "returns a replica of ASCII" do
- name = @prefix + '-ASCII'
- e = Encoding::ASCII.replicate(name)
- e.name.should == name
- "a".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
- end
-
- it "returns a replica of UTF-8" do
- name = @prefix + 'UTF-8'
- e = Encoding::UTF_8.replicate(name)
- e.name.should == name
- "a".force_encoding(e).valid_encoding?.should be_true
- "\u3042".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
- end
-
- it "returns a replica of UTF-16BE" do
- name = @prefix + 'UTF-16-BE'
- e = Encoding::UTF_16BE.replicate(name)
- e.name.should == name
- "a".force_encoding(e).valid_encoding?.should be_false
- "\x30\x42".force_encoding(e).valid_encoding?.should be_true
- "\x80".force_encoding(e).valid_encoding?.should be_false
- end
-
- it "returns a replica of ISO-2022-JP" do
- name = @prefix + 'ISO-2022-JP'
- e = Encoding::ISO_2022_JP.replicate(name)
- e.name.should == name
- e.dummy?.should be_true
+ it "has been removed" do
+ Encoding::US_ASCII.should_not.respond_to?(:replicate, true)
end
end
diff --git a/spec/ruby/core/encoding/shared/name.rb b/spec/ruby/core/encoding/shared/name.rb
deleted file mode 100644
index cd37ea06db..0000000000
--- a/spec/ruby/core/encoding/shared/name.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative '../../../spec_helper'
-
-describe :encoding_name, shared: true do
- it "returns a String" do
- Encoding.list.each do |e|
- e.send(@method).should be_an_instance_of(String)
- end
- end
-
- it "uniquely identifies an encoding" do
- Encoding.list.each do |e|
- e.should == Encoding.find(e.send(@method))
- end
- end
-end
diff --git a/spec/ruby/core/encoding/to_s_spec.rb b/spec/ruby/core/encoding/to_s_spec.rb
index 82d282386b..ee5e3b4abe 100644
--- a/spec/ruby/core/encoding/to_s_spec.rb
+++ b/spec/ruby/core/encoding/to_s_spec.rb
@@ -1,5 +1,7 @@
-require_relative 'shared/name'
+require_relative "../../spec_helper"
describe "Encoding#to_s" do
- it_behaves_like :encoding_name, :to_s
+ it "is an alias of Encoding#name" do
+ Encoding.instance_method(:to_s).should == Encoding.instance_method(:name)
+ end
end
diff --git a/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb
index 106fc7ecac..bc36695ca7 100644
--- a/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb
+++ b/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb
@@ -1,3 +1,4 @@
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#destination_encoding_name" do
@@ -6,7 +7,7 @@ describe "Encoding::UndefinedConversionError#destination_encoding_name" do
end
it "returns a String" do
- @exception.destination_encoding_name.should be_an_instance_of(String)
+ @exception.destination_encoding_name.should.instance_of?(String)
end
it "is equal to the destination encoding name of the object that raised it" do
diff --git a/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_spec.rb
index c6e24732fd..c0fcf8de58 100644
--- a/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_spec.rb
+++ b/spec/ruby/core/encoding/undefined_conversion_error/destination_encoding_spec.rb
@@ -1,3 +1,4 @@
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#destination_encoding" do
@@ -6,7 +7,7 @@ describe "Encoding::UndefinedConversionError#destination_encoding" do
end
it "returns an Encoding object" do
- @exception.destination_encoding.should be_an_instance_of(Encoding)
+ @exception.destination_encoding.should.instance_of?(Encoding)
end
it "is equal to the destination encoding of the object that raised it" do
diff --git a/spec/ruby/core/encoding/undefined_conversion_error/error_char_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/error_char_spec.rb
index 780d81c1ee..333acf5ee6 100644
--- a/spec/ruby/core/encoding/undefined_conversion_error/error_char_spec.rb
+++ b/spec/ruby/core/encoding/undefined_conversion_error/error_char_spec.rb
@@ -1,3 +1,4 @@
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#error_char" do
@@ -7,8 +8,8 @@ describe "Encoding::UndefinedConversionError#error_char" do
end
it "returns a String" do
- @exception.error_char.should be_an_instance_of(String)
- @exception2.error_char.should be_an_instance_of(String)
+ @exception.error_char.should.instance_of?(String)
+ @exception2.error_char.should.instance_of?(String)
end
it "returns the one-character String that caused the exception" do
diff --git a/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb
index 3b697cb82f..4932a25ed7 100644
--- a/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb
+++ b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb
@@ -1,3 +1,4 @@
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#source_encoding_name" do
@@ -7,7 +8,7 @@ describe "Encoding::UndefinedConversionError#source_encoding_name" do
end
it "returns a String" do
- @exception.source_encoding_name.should be_an_instance_of(String)
+ @exception.source_encoding_name.should.instance_of?(String)
end
it "is equal to the source encoding name of the object that raised it" do
diff --git a/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb
index 9101d51e11..cf12020ad2 100644
--- a/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb
+++ b/spec/ruby/core/encoding/undefined_conversion_error/source_encoding_spec.rb
@@ -1,3 +1,4 @@
+require_relative "../../../spec_helper"
require_relative '../fixtures/classes'
describe "Encoding::UndefinedConversionError#source_encoding" do
@@ -7,8 +8,8 @@ describe "Encoding::UndefinedConversionError#source_encoding" do
end
it "returns an Encoding object" do
- @exception.source_encoding.should be_an_instance_of(Encoding)
- @exception2.source_encoding.should be_an_instance_of(Encoding)
+ @exception.source_encoding.should.instance_of?(Encoding)
+ @exception2.source_encoding.should.instance_of?(Encoding)
end
it "is equal to the source encoding of the object that raised it" do
diff --git a/spec/ruby/core/enumerable/all_spec.rb b/spec/ruby/core/enumerable/all_spec.rb
index 7e9726fe87..cbdd63f86a 100644
--- a/spec/ruby/core/enumerable/all_spec.rb
+++ b/spec/ruby/core/enumerable/all_spec.rb
@@ -21,19 +21,19 @@ describe "Enumerable#all?" do
end
it "raises an ArgumentError when more than 1 argument is provided" do
- -> { @enum.all?(1, 2, 3) }.should raise_error(ArgumentError)
- -> { [].all?(1, 2, 3) }.should raise_error(ArgumentError)
- -> { {}.all?(1, 2, 3) }.should raise_error(ArgumentError)
+ -> { @enum.all?(1, 2, 3) }.should.raise(ArgumentError)
+ -> { [].all?(1, 2, 3) }.should.raise(ArgumentError)
+ -> { {}.all?(1, 2, 3) }.should.raise(ArgumentError)
end
it "does not hide exceptions out of #each" do
-> {
EnumerableSpecs::ThrowingEach.new.all?
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
-> {
EnumerableSpecs::ThrowingEach.new.all? { false }
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
describe "with no block" do
@@ -60,7 +60,7 @@ describe "Enumerable#all?" do
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMultiWithFalse.new
- multi.all?.should be_true
+ multi.all?.should == true
end
end
@@ -106,7 +106,7 @@ describe "Enumerable#all?" do
it "does not hide exceptions out of the block" do
-> {
@enum.all? { raise "from block" }
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "gathers initial args as elements when each yields multiple" do
@@ -125,21 +125,12 @@ describe "Enumerable#all?" do
end
describe 'when given a pattern argument' do
- it "calls `===` on the pattern the return value " do
+ it "calls `===` on the pattern the return value" do
pattern = EnumerableSpecs::Pattern.new { |x| x >= 0 }
@enum1.all?(pattern).should == false
pattern.yielded.should == [[0], [1], [2], [-1]]
end
- # may raise an exception in future versions
- ruby_version_is ""..."2.6" do
- it "ignores block" do
- @enum2.all?(NilClass) { raise }.should == false
- [1, 2, nil].all?(NilClass) { raise }.should == false
- {a: 1}.all?(Array) { raise }.should == true
- end
- end
-
it "always returns true on empty enumeration" do
@empty.all?(Integer).should == true
[].all?(Integer).should == true
@@ -149,7 +140,7 @@ describe "Enumerable#all?" do
it "does not hide exceptions out of #each" do
-> {
EnumerableSpecs::ThrowingEach.new.all?(Integer)
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "returns true if the pattern never returns false or nil" do
@@ -177,7 +168,7 @@ describe "Enumerable#all?" do
pattern = EnumerableSpecs::Pattern.new { raise "from pattern" }
-> {
@enum.all?(pattern)
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "calls the pattern with gathered array when yielded with multiple arguments" do
@@ -186,5 +177,11 @@ describe "Enumerable#all?" do
multi.all?(pattern).should == true
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end
+
+ it "ignores the block if there is an argument" do
+ -> {
+ EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).all?(String) { true }.should == false
+ }.should complain(/given block not used/)
+ end
end
end
diff --git a/spec/ruby/core/enumerable/any_spec.rb b/spec/ruby/core/enumerable/any_spec.rb
index 64e3159963..4405d4740a 100644
--- a/spec/ruby/core/enumerable/any_spec.rb
+++ b/spec/ruby/core/enumerable/any_spec.rb
@@ -21,19 +21,19 @@ describe "Enumerable#any?" do
end
it "raises an ArgumentError when more than 1 argument is provided" do
- -> { @enum.any?(1, 2, 3) }.should raise_error(ArgumentError)
- -> { [].any?(1, 2, 3) }.should raise_error(ArgumentError)
- -> { {}.any?(1, 2, 3) }.should raise_error(ArgumentError)
+ -> { @enum.any?(1, 2, 3) }.should.raise(ArgumentError)
+ -> { [].any?(1, 2, 3) }.should.raise(ArgumentError)
+ -> { {}.any?(1, 2, 3) }.should.raise(ArgumentError)
end
it "does not hide exceptions out of #each" do
-> {
EnumerableSpecs::ThrowingEach.new.any?
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
-> {
EnumerableSpecs::ThrowingEach.new.any? { false }
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
describe "with no block" do
@@ -60,7 +60,7 @@ describe "Enumerable#any?" do
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMultiWithFalse.new
- multi.any?.should be_true
+ multi.any?.should == true
end
end
@@ -120,7 +120,7 @@ describe "Enumerable#any?" do
it "does not hide exceptions out of the block" do
-> {
@enum.any? { raise "from block" }
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "gathers initial args as elements when each yields multiple" do
@@ -139,21 +139,12 @@ describe "Enumerable#any?" do
end
describe 'when given a pattern argument' do
- it "calls `===` on the pattern the return value " do
+ it "calls `===` on the pattern the return value" do
pattern = EnumerableSpecs::Pattern.new { |x| x == 2 }
@enum1.any?(pattern).should == true
pattern.yielded.should == [[0], [1], [2]]
end
- # may raise an exception in future versions
- ruby_version_is ""..."2.6" do
- it "ignores block" do
- @enum2.any?(NilClass) { raise }.should == true
- [1, 2, nil].any?(NilClass) { raise }.should == true
- {a: 1}.any?(Array) { raise }.should == true
- end
- end
-
it "always returns false on empty enumeration" do
@empty.any?(Integer).should == false
[].any?(Integer).should == false
@@ -163,7 +154,7 @@ describe "Enumerable#any?" do
it "does not hide exceptions out of #each" do
-> {
EnumerableSpecs::ThrowingEach.new.any?(Integer)
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "returns true if the pattern ever returns a truthy value" do
@@ -190,7 +181,7 @@ describe "Enumerable#any?" do
pattern = EnumerableSpecs::Pattern.new { raise "from pattern" }
-> {
@enum.any?(pattern)
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "calls the pattern with gathered array when yielded with multiple arguments" do
@@ -199,5 +190,11 @@ describe "Enumerable#any?" do
multi.any?(pattern).should == false
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end
+
+ it "ignores the block if there is an argument" do
+ -> {
+ EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).any?(String) { true }.should == false
+ }.should complain(/given block not used/)
+ end
end
end
diff --git a/spec/ruby/core/enumerable/chain_spec.rb b/spec/ruby/core/enumerable/chain_spec.rb
index 85c0c03603..a0597e46a1 100644
--- a/spec/ruby/core/enumerable/chain_spec.rb
+++ b/spec/ruby/core/enumerable/chain_spec.rb
@@ -1,25 +1,23 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-ruby_version_is "2.6" do
- describe "Enumerable#chain" do
- before :each do
- ScratchPad.record []
- end
+describe "Enumerable#chain" do
+ before :each do
+ ScratchPad.record []
+ end
- it "returns a chain of self and provided enumerables" do
- one = EnumerableSpecs::Numerous.new(1)
- two = EnumerableSpecs::Numerous.new(2, 3)
- three = EnumerableSpecs::Numerous.new(4, 5, 6)
+ it "returns a chain of self and provided enumerables" do
+ one = EnumerableSpecs::Numerous.new(1)
+ two = EnumerableSpecs::Numerous.new(2, 3)
+ three = EnumerableSpecs::Numerous.new(4, 5, 6)
- chain = one.chain(two, three)
+ chain = one.chain(two, three)
- chain.each { |item| ScratchPad << item }
- ScratchPad.recorded.should == [1, 2, 3, 4, 5, 6]
- end
+ chain.each { |item| ScratchPad << item }
+ ScratchPad.recorded.should == [1, 2, 3, 4, 5, 6]
+ end
- it "returns an Enumerator::Chain if given a block" do
- EnumerableSpecs::Numerous.new.chain.should be_an_instance_of(Enumerator::Chain)
- end
+ it "returns an Enumerator::Chain if given a block" do
+ EnumerableSpecs::Numerous.new.chain.should.instance_of?(Enumerator::Chain)
end
end
diff --git a/spec/ruby/core/enumerable/chunk_spec.rb b/spec/ruby/core/enumerable/chunk_spec.rb
index 548544f4e8..7c9b31c991 100644
--- a/spec/ruby/core/enumerable/chunk_spec.rb
+++ b/spec/ruby/core/enumerable/chunk_spec.rb
@@ -8,13 +8,13 @@ describe "Enumerable#chunk" do
it "returns an Enumerator if called without a block" do
chunk = EnumerableSpecs::Numerous.new(1, 2, 3, 1, 2).chunk
- chunk.should be_an_instance_of(Enumerator)
+ chunk.should.instance_of?(Enumerator)
result = chunk.with_index {|elt, i| elt - i }.to_a
result.should == [[1, [1, 2, 3]], [-2, [1, 2]]]
end
it "returns an Enumerator if given a block" do
- EnumerableSpecs::Numerous.new.chunk {}.should be_an_instance_of(Enumerator)
+ EnumerableSpecs::Numerous.new.chunk {}.should.instance_of?(Enumerator)
end
it "yields the current element and the current chunk to the block" do
@@ -29,12 +29,22 @@ describe "Enumerable#chunk" do
result.should == [[1, [1, 2]], [0, [3]], [1, [2]], [0, [3]], [1, [2, 1]]]
end
+ it "returns a partitioned Array of values" do
+ e = EnumerableSpecs::Numerous.new(1,2,3)
+ e.chunk { |x| x > 2 }.map(&:last).should == [[1, 2], [3]]
+ end
+
it "returns elements for which the block returns :_alone in separate Arrays" do
e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1)
result = e.chunk { |x| x < 2 && :_alone }.to_a
result.should == [[:_alone, [1]], [false, [2, 3, 2]], [:_alone, [1]]]
end
+ it "yields Arrays as a single argument to a rest argument" do
+ e = EnumerableSpecs::Numerous.new([1, 2])
+ result = e.chunk { |*x| x.should == [[1,2]] }.to_a
+ end
+
it "does not return elements for which the block returns :_separator" do
e = EnumerableSpecs::Numerous.new(1, 2, 3, 3, 2, 1)
result = e.chunk { |x| x == 2 ? :_separator : 1 }.to_a
@@ -49,14 +59,14 @@ describe "Enumerable#chunk" do
it "raises a RuntimeError if the block returns a Symbol starting with an underscore other than :_alone or :_separator" do
e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1)
- -> { e.chunk { |x| :_arbitrary }.to_a }.should raise_error(RuntimeError)
+ -> { e.chunk { |x| :_arbitrary }.to_a }.should.raise(RuntimeError)
end
it "does not accept arguments" do
e = EnumerableSpecs::Numerous.new(1, 2, 3)
-> {
e.chunk(1) {}
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it 'returned Enumerator size returns nil' do
diff --git a/spec/ruby/core/enumerable/chunk_while_spec.rb b/spec/ruby/core/enumerable/chunk_while_spec.rb
index 26bcc983db..286f717442 100644
--- a/spec/ruby/core/enumerable/chunk_while_spec.rb
+++ b/spec/ruby/core/enumerable/chunk_while_spec.rb
@@ -11,7 +11,7 @@ describe "Enumerable#chunk_while" do
context "when given a block" do
it "returns an enumerator" do
- @result.should be_an_instance_of(Enumerator)
+ @result.should.instance_of?(Enumerator)
end
it "splits chunks between adjacent elements i and j where the block returns false" do
@@ -30,7 +30,7 @@ describe "Enumerable#chunk_while" do
context "when not given a block" do
it "raises an ArgumentError" do
- -> { @enum.chunk_while }.should raise_error(ArgumentError)
+ -> { @enum.chunk_while }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/enumerable/collect_concat_spec.rb b/spec/ruby/core/enumerable/collect_concat_spec.rb
index 6e34c9eb93..59317cfe34 100644
--- a/spec/ruby/core/enumerable/collect_concat_spec.rb
+++ b/spec/ruby/core/enumerable/collect_concat_spec.rb
@@ -3,5 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/collect_concat'
describe "Enumerable#collect_concat" do
- it_behaves_like :enumerable_collect_concat , :collect_concat
+ it_behaves_like :enumerable_collect_concat, :collect_concat
end
diff --git a/spec/ruby/core/enumerable/collect_spec.rb b/spec/ruby/core/enumerable/collect_spec.rb
index 1016b67798..cfa2895cce 100644
--- a/spec/ruby/core/enumerable/collect_spec.rb
+++ b/spec/ruby/core/enumerable/collect_spec.rb
@@ -3,5 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/collect'
describe "Enumerable#collect" do
- it_behaves_like :enumerable_collect , :collect
+ it_behaves_like :enumerable_collect, :collect
end
diff --git a/spec/ruby/core/enumerable/compact_spec.rb b/spec/ruby/core/enumerable/compact_spec.rb
new file mode 100644
index 0000000000..1895430c4e
--- /dev/null
+++ b/spec/ruby/core/enumerable/compact_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#compact" do
+ it 'returns array without nil elements' do
+ arr = EnumerableSpecs::Numerous.new(nil, 1, 2, nil, true)
+ arr.compact.should == [1, 2, true]
+ end
+end
diff --git a/spec/ruby/core/enumerable/cycle_spec.rb b/spec/ruby/core/enumerable/cycle_spec.rb
index 487086cba3..1fb3cc3d41 100644
--- a/spec/ruby/core/enumerable/cycle_spec.rb
+++ b/spec/ruby/core/enumerable/cycle_spec.rb
@@ -17,7 +17,7 @@ describe "Enumerable#cycle" do
it "returns nil if there are no elements" do
out = EnumerableSpecs::Empty.new.cycle { break :nope }
- out.should be_nil
+ out.should == nil
end
it "yields successive elements of the array repeatedly" do
@@ -44,8 +44,8 @@ describe "Enumerable#cycle" do
describe "passed a number n as an argument" do
it "returns nil and does nothing for non positive n" do
- EnumerableSpecs::ThrowingEach.new.cycle(0) {}.should be_nil
- EnumerableSpecs::NoEach.new.cycle(-22) {}.should be_nil
+ EnumerableSpecs::ThrowingEach.new.cycle(0) {}.should == nil
+ EnumerableSpecs::NoEach.new.cycle(-22) {}.should == nil
end
it "calls each at most once" do
@@ -71,12 +71,12 @@ describe "Enumerable#cycle" do
it "raises a TypeError when the passed n cannot be coerced to Integer" do
enum = EnumerableSpecs::Numerous.new
- ->{ enum.cycle("cat"){} }.should raise_error(TypeError)
+ ->{ enum.cycle("cat"){} }.should.raise(TypeError)
end
it "raises an ArgumentError if more arguments are passed" do
enum = EnumerableSpecs::Numerous.new
- ->{ enum.cycle(1, 2) {} }.should raise_error(ArgumentError)
+ ->{ enum.cycle(1, 2) {} }.should.raise(ArgumentError)
end
it "gathers whole arrays as elements when each yields multiple" do
diff --git a/spec/ruby/core/enumerable/detect_spec.rb b/spec/ruby/core/enumerable/detect_spec.rb
index e912134fed..6959aadc44 100644
--- a/spec/ruby/core/enumerable/detect_spec.rb
+++ b/spec/ruby/core/enumerable/detect_spec.rb
@@ -3,5 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/find'
describe "Enumerable#detect" do
- it_behaves_like :enumerable_find , :detect
+ it_behaves_like :enumerable_find, :detect
end
diff --git a/spec/ruby/core/enumerable/drop_spec.rb b/spec/ruby/core/enumerable/drop_spec.rb
index 423cc0088b..8d95f464b3 100644
--- a/spec/ruby/core/enumerable/drop_spec.rb
+++ b/spec/ruby/core/enumerable/drop_spec.rb
@@ -7,13 +7,13 @@ describe "Enumerable#drop" do
end
it "requires exactly one argument" do
- ->{ @enum.drop{} }.should raise_error(ArgumentError)
- ->{ @enum.drop(1, 2){} }.should raise_error(ArgumentError)
+ ->{ @enum.drop{} }.should.raise(ArgumentError)
+ ->{ @enum.drop(1, 2){} }.should.raise(ArgumentError)
end
describe "passed a number n as an argument" do
it "raises ArgumentError if n < 0" do
- ->{ @enum.drop(-1) }.should raise_error(ArgumentError)
+ ->{ @enum.drop(-1) }.should.raise(ArgumentError)
end
it "tries to convert n to an Integer using #to_int" do
@@ -35,8 +35,8 @@ describe "Enumerable#drop" do
end
it "raises a TypeError when the passed n cannot be coerced to Integer" do
- ->{ @enum.drop("hat") }.should raise_error(TypeError)
- ->{ @enum.drop(nil) }.should raise_error(TypeError)
+ ->{ @enum.drop("hat") }.should.raise(TypeError)
+ ->{ @enum.drop(nil) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/enumerable/drop_while_spec.rb b/spec/ruby/core/enumerable/drop_while_spec.rb
index 636c3d284a..4b4fdf2d4f 100644
--- a/spec/ruby/core/enumerable/drop_while_spec.rb
+++ b/spec/ruby/core/enumerable/drop_while_spec.rb
@@ -8,7 +8,7 @@ describe "Enumerable#drop_while" do
end
it "returns an Enumerator if no block given" do
- @enum.drop_while.should be_an_instance_of(Enumerator)
+ @enum.drop_while.should.instance_of?(Enumerator)
end
it "returns no/all elements for {true/false} block" do
@@ -38,7 +38,7 @@ describe "Enumerable#drop_while" do
it "doesn't return self when it could" do
a = [1,2,3]
- a.drop_while{false}.should_not equal(a)
+ a.drop_while{false}.should_not.equal?(a)
end
it "gathers whole arrays as elements when each yields multiple" do
diff --git a/spec/ruby/core/enumerable/each_cons_spec.rb b/spec/ruby/core/enumerable/each_cons_spec.rb
index 7d44f54f74..c5e299fd00 100644
--- a/spec/ruby/core/enumerable/each_cons_spec.rb
+++ b/spec/ruby/core/enumerable/each_cons_spec.rb
@@ -10,24 +10,24 @@ describe "Enumerable#each_cons" do
it "passes element groups to the block" do
acc = []
- @enum.each_cons(3){|g| acc << g}.should be_nil
+ @enum.each_cons(3){|g| acc << g}
acc.should == @in_threes
end
it "raises an ArgumentError if there is not a single parameter > 0" do
- ->{ @enum.each_cons(0){} }.should raise_error(ArgumentError)
- ->{ @enum.each_cons(-2){} }.should raise_error(ArgumentError)
- ->{ @enum.each_cons{} }.should raise_error(ArgumentError)
- ->{ @enum.each_cons(2,2){} }.should raise_error(ArgumentError)
- ->{ @enum.each_cons(0) }.should raise_error(ArgumentError)
- ->{ @enum.each_cons(-2) }.should raise_error(ArgumentError)
- ->{ @enum.each_cons }.should raise_error(ArgumentError)
- ->{ @enum.each_cons(2,2) }.should raise_error(ArgumentError)
+ ->{ @enum.each_cons(0){} }.should.raise(ArgumentError)
+ ->{ @enum.each_cons(-2){} }.should.raise(ArgumentError)
+ ->{ @enum.each_cons{} }.should.raise(ArgumentError)
+ ->{ @enum.each_cons(2,2){} }.should.raise(ArgumentError)
+ ->{ @enum.each_cons(0) }.should.raise(ArgumentError)
+ ->{ @enum.each_cons(-2) }.should.raise(ArgumentError)
+ ->{ @enum.each_cons }.should.raise(ArgumentError)
+ ->{ @enum.each_cons(2,2) }.should.raise(ArgumentError)
end
it "tries to convert n to an Integer using #to_int" do
acc = []
- @enum.each_cons(3.3){|g| acc << g}.should == nil
+ @enum.each_cons(3.3){|g| acc << g}
acc.should == @in_threes
obj = mock('to_int')
@@ -56,10 +56,14 @@ describe "Enumerable#each_cons" do
multi.each_cons(2).to_a.should == [[[1, 2], [3, 4, 5]], [[3, 4, 5], [6, 7, 8, 9]]]
end
+ it "returns self when a block is given" do
+ @enum.each_cons(3){}.should == @enum
+ end
+
describe "when no block is given" do
it "returns an enumerator" do
e = @enum.each_cons(3)
- e.should be_an_instance_of(Enumerator)
+ e.should.instance_of?(Enumerator)
e.to_a.should == @in_threes
end
diff --git a/spec/ruby/core/enumerable/each_entry_spec.rb b/spec/ruby/core/enumerable/each_entry_spec.rb
index edf00f3137..9dc89ec28e 100644
--- a/spec/ruby/core/enumerable/each_entry_spec.rb
+++ b/spec/ruby/core/enumerable/each_entry_spec.rb
@@ -11,13 +11,13 @@ describe "Enumerable#each_entry" do
it "yields multiple arguments as an array" do
acc = []
- @enum.each_entry {|g| acc << g}.should equal(@enum)
+ @enum.each_entry {|g| acc << g}.should.equal?(@enum)
acc.should == @entries
end
it "returns an enumerator if no block" do
e = @enum.each_entry
- e.should be_an_instance_of(Enumerator)
+ e.should.instance_of?(Enumerator)
e.to_a.should == @entries
end
@@ -27,8 +27,8 @@ describe "Enumerable#each_entry" do
end
it "raises an ArgumentError when extra arguments" do
- -> { @enum.each_entry("one").to_a }.should raise_error(ArgumentError)
- -> { @enum.each_entry("one"){}.to_a }.should raise_error(ArgumentError)
+ -> { @enum.each_entry("one").to_a }.should.raise(ArgumentError)
+ -> { @enum.each_entry("one"){}.to_a }.should.raise(ArgumentError)
end
it "passes extra arguments to #each" do
diff --git a/spec/ruby/core/enumerable/each_slice_spec.rb b/spec/ruby/core/enumerable/each_slice_spec.rb
index ab3b79c344..d05abad1e9 100644
--- a/spec/ruby/core/enumerable/each_slice_spec.rb
+++ b/spec/ruby/core/enumerable/each_slice_spec.rb
@@ -10,24 +10,24 @@ describe "Enumerable#each_slice" do
it "passes element groups to the block" do
acc = []
- @enum.each_slice(3){|g| acc << g}.should be_nil
+ @enum.each_slice(3){|g| acc << g}
acc.should == @sliced
end
it "raises an ArgumentError if there is not a single parameter > 0" do
- ->{ @enum.each_slice(0){} }.should raise_error(ArgumentError)
- ->{ @enum.each_slice(-2){} }.should raise_error(ArgumentError)
- ->{ @enum.each_slice{} }.should raise_error(ArgumentError)
- ->{ @enum.each_slice(2,2){} }.should raise_error(ArgumentError)
- ->{ @enum.each_slice(0) }.should raise_error(ArgumentError)
- ->{ @enum.each_slice(-2) }.should raise_error(ArgumentError)
- ->{ @enum.each_slice }.should raise_error(ArgumentError)
- ->{ @enum.each_slice(2,2) }.should raise_error(ArgumentError)
+ ->{ @enum.each_slice(0){} }.should.raise(ArgumentError)
+ ->{ @enum.each_slice(-2){} }.should.raise(ArgumentError)
+ ->{ @enum.each_slice{} }.should.raise(ArgumentError)
+ ->{ @enum.each_slice(2,2){} }.should.raise(ArgumentError)
+ ->{ @enum.each_slice(0) }.should.raise(ArgumentError)
+ ->{ @enum.each_slice(-2) }.should.raise(ArgumentError)
+ ->{ @enum.each_slice }.should.raise(ArgumentError)
+ ->{ @enum.each_slice(2,2) }.should.raise(ArgumentError)
end
it "tries to convert n to an Integer using #to_int" do
acc = []
- @enum.each_slice(3.3){|g| acc << g}.should == nil
+ @enum.each_slice(3.3){|g| acc << g}
acc.should == @sliced
obj = mock('to_int')
@@ -53,10 +53,14 @@ describe "Enumerable#each_slice" do
it "returns an enumerator if no block" do
e = @enum.each_slice(3)
- e.should be_an_instance_of(Enumerator)
+ e.should.instance_of?(Enumerator)
e.to_a.should == @sliced
end
+ it "returns self when a block is given" do
+ @enum.each_slice(3){}.should == @enum
+ end
+
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMulti.new
multi.each_slice(2).to_a.should == [[[1, 2], [3, 4, 5]], [[6, 7, 8, 9]]]
@@ -65,7 +69,7 @@ describe "Enumerable#each_slice" do
describe "when no block is given" do
it "returns an enumerator" do
e = @enum.each_slice(3)
- e.should be_an_instance_of(Enumerator)
+ e.should.instance_of?(Enumerator)
e.to_a.should == @sliced
end
diff --git a/spec/ruby/core/enumerable/each_with_index_spec.rb b/spec/ruby/core/enumerable/each_with_index_spec.rb
index 122e88eab7..fcb2f82f84 100644
--- a/spec/ruby/core/enumerable/each_with_index_spec.rb
+++ b/spec/ruby/core/enumerable/each_with_index_spec.rb
@@ -26,19 +26,19 @@ describe "Enumerable#each_with_index" do
acc = []
res = @b.each_with_index {|a,i| acc << [a,i]}
[[2, 0], [5, 1], [3, 2], [6, 3], [1, 4], [4, 5]].should == acc
- res.should eql(@b)
+ res.should.eql?(@b)
end
it "binds splat arguments properly" do
acc = []
res = @b.each_with_index { |*b| c,d = b; acc << c; acc << d }
[2, 0, 5, 1, 3, 2, 6, 3, 1, 4, 4, 5].should == acc
- res.should eql(@b)
+ res.should.eql?(@b)
end
it "returns an enumerator if no block" do
e = @b.each_with_index
- e.should be_an_instance_of(Enumerator)
+ e.should.instance_of?(Enumerator)
e.to_a.should == [[2, 0], [5, 1], [3, 2], [6, 3], [1, 4], [4, 5]]
end
diff --git a/spec/ruby/core/enumerable/each_with_object_spec.rb b/spec/ruby/core/enumerable/each_with_object_spec.rb
index 35665e7019..1760d3b267 100644
--- a/spec/ruby/core/enumerable/each_with_object_spec.rb
+++ b/spec/ruby/core/enumerable/each_with_object_spec.rb
@@ -12,10 +12,10 @@ describe "Enumerable#each_with_object" do
it "passes each element and its argument to the block" do
acc = []
@enum.each_with_object(@initial) do |elem, obj|
- obj.should equal(@initial)
+ obj.should.equal?(@initial)
obj = 42
acc << elem
- end.should equal(@initial)
+ end.should.equal?(@initial)
acc.should == @values
end
@@ -23,10 +23,10 @@ describe "Enumerable#each_with_object" do
acc = []
e = @enum.each_with_object(@initial)
e.each do |elem, obj|
- obj.should equal(@initial)
+ obj.should.equal?(@initial)
obj = 42
acc << elem
- end.should equal(@initial)
+ end.should.equal?(@initial)
acc.should == @values
end
diff --git a/spec/ruby/core/enumerable/entries_spec.rb b/spec/ruby/core/enumerable/entries_spec.rb
index 83232cfa06..2de4fc756a 100644
--- a/spec/ruby/core/enumerable/entries_spec.rb
+++ b/spec/ruby/core/enumerable/entries_spec.rb
@@ -3,5 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/entries'
describe "Enumerable#entries" do
- it_behaves_like :enumerable_entries , :entries
+ it_behaves_like :enumerable_entries, :entries
end
diff --git a/spec/ruby/core/enumerable/filter_map_spec.rb b/spec/ruby/core/enumerable/filter_map_spec.rb
index 31acc277b4..1ed131a960 100644
--- a/spec/ruby/core/enumerable/filter_map_spec.rb
+++ b/spec/ruby/core/enumerable/filter_map_spec.rb
@@ -1,26 +1,24 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-ruby_version_is '2.7' do
- describe 'Enumerable#filter_map' do
- before :each do
- @numerous = EnumerableSpecs::Numerous.new(*(1..8).to_a)
- end
+describe 'Enumerable#filter_map' do
+ before :each do
+ @numerous = EnumerableSpecs::Numerous.new(*(1..8).to_a)
+ end
- it 'returns an empty array if there are no elements' do
- EnumerableSpecs::Empty.new.filter_map { true }.should == []
- end
+ it 'returns an empty array if there are no elements' do
+ EnumerableSpecs::Empty.new.filter_map { true }.should == []
+ end
- it 'returns an array with truthy results of passing each element to block' do
- @numerous.filter_map { |i| i * 2 if i.even? }.should == [4, 8, 12, 16]
- @numerous.filter_map { |i| i * 2 }.should == [2, 4, 6, 8, 10, 12, 14, 16]
- @numerous.filter_map { 0 }.should == [0, 0, 0, 0, 0, 0, 0, 0]
- @numerous.filter_map { false }.should == []
- @numerous.filter_map { nil }.should == []
- end
+ it 'returns an array with truthy results of passing each element to block' do
+ @numerous.filter_map { |i| i * 2 if i.even? }.should == [4, 8, 12, 16]
+ @numerous.filter_map { |i| i * 2 }.should == [2, 4, 6, 8, 10, 12, 14, 16]
+ @numerous.filter_map { 0 }.should == [0, 0, 0, 0, 0, 0, 0, 0]
+ @numerous.filter_map { false }.should == []
+ @numerous.filter_map { nil }.should == []
+ end
- it 'returns an enumerator when no block given' do
- @numerous.filter_map.should be_an_instance_of(Enumerator)
- end
+ it 'returns an enumerator when no block given' do
+ @numerous.filter_map.should.instance_of?(Enumerator)
end
end
diff --git a/spec/ruby/core/enumerable/filter_spec.rb b/spec/ruby/core/enumerable/filter_spec.rb
index f2dc7a7b71..1c3a7e9ff5 100644
--- a/spec/ruby/core/enumerable/filter_spec.rb
+++ b/spec/ruby/core/enumerable/filter_spec.rb
@@ -2,8 +2,6 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/find_all'
-ruby_version_is "2.6" do
- describe "Enumerable#filter" do
- it_behaves_like(:enumerable_find_all , :filter)
- end
+describe "Enumerable#filter" do
+ it_behaves_like :enumerable_find_all, :filter
end
diff --git a/spec/ruby/core/enumerable/find_all_spec.rb b/spec/ruby/core/enumerable/find_all_spec.rb
index ce9058fe77..9cd635f247 100644
--- a/spec/ruby/core/enumerable/find_all_spec.rb
+++ b/spec/ruby/core/enumerable/find_all_spec.rb
@@ -3,5 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/find_all'
describe "Enumerable#find_all" do
- it_behaves_like :enumerable_find_all , :find_all
+ it_behaves_like :enumerable_find_all, :find_all
end
diff --git a/spec/ruby/core/enumerable/find_index_spec.rb b/spec/ruby/core/enumerable/find_index_spec.rb
index 542660fe04..2e714367ba 100644
--- a/spec/ruby/core/enumerable/find_index_spec.rb
+++ b/spec/ruby/core/enumerable/find_index_spec.rb
@@ -47,7 +47,7 @@ describe "Enumerable#find_index" do
end
it "returns an Enumerator if no block given" do
- @numerous.find_index.should be_an_instance_of(Enumerator)
+ @numerous.find_index.should.instance_of?(Enumerator)
end
it "uses #== for testing equality" do
diff --git a/spec/ruby/core/enumerable/find_spec.rb b/spec/ruby/core/enumerable/find_spec.rb
index 25aa3bf103..5ddebc05f8 100644
--- a/spec/ruby/core/enumerable/find_spec.rb
+++ b/spec/ruby/core/enumerable/find_spec.rb
@@ -3,5 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/find'
describe "Enumerable#find" do
- it_behaves_like :enumerable_find , :find
+ it_behaves_like :enumerable_find, :find
end
diff --git a/spec/ruby/core/enumerable/first_spec.rb b/spec/ruby/core/enumerable/first_spec.rb
index ed1ba599b4..592dff1ebc 100644
--- a/spec/ruby/core/enumerable/first_spec.rb
+++ b/spec/ruby/core/enumerable/first_spec.rb
@@ -19,7 +19,7 @@ describe "Enumerable#first" do
it "raises a RangeError when passed a Bignum" do
enum = EnumerableSpecs::Empty.new
- -> { enum.first(bignum_value) }.should raise_error(RangeError)
+ -> { enum.first(bignum_value) }.should.raise(RangeError)
end
describe "when passed an argument" do
diff --git a/spec/ruby/core/enumerable/fixtures/classes.rb b/spec/ruby/core/enumerable/fixtures/classes.rb
index 5051196742..b5feafcfb7 100644
--- a/spec/ruby/core/enumerable/fixtures/classes.rb
+++ b/spec/ruby/core/enumerable/fixtures/classes.rb
@@ -38,12 +38,14 @@ module EnumerableSpecs
class Empty
include Enumerable
def each
+ self
end
end
class EmptyWithSize
include Enumerable
def each
+ self
end
def size
0
@@ -251,7 +253,7 @@ module EnumerableSpecs
end
end
- class ComparableWithFixnum
+ class ComparableWithInteger
include Comparable
def initialize(num)
@num = num
@@ -342,4 +344,7 @@ module EnumerableSpecs
@block.call(*args)
end
end
+
+ class SetSubclass < Set
+ end
end # EnumerableSpecs utility classes
diff --git a/spec/ruby/core/enumerable/flat_map_spec.rb b/spec/ruby/core/enumerable/flat_map_spec.rb
index a294b9ddad..bd07eab6c5 100644
--- a/spec/ruby/core/enumerable/flat_map_spec.rb
+++ b/spec/ruby/core/enumerable/flat_map_spec.rb
@@ -3,5 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/collect_concat'
describe "Enumerable#flat_map" do
- it_behaves_like :enumerable_collect_concat , :flat_map
+ it_behaves_like :enumerable_collect_concat, :flat_map
end
diff --git a/spec/ruby/core/enumerable/grep_spec.rb b/spec/ruby/core/enumerable/grep_spec.rb
index c9c0f34e27..965e183766 100644
--- a/spec/ruby/core/enumerable/grep_spec.rb
+++ b/spec/ruby/core/enumerable/grep_spec.rb
@@ -40,15 +40,28 @@ describe "Enumerable#grep" do
$~.should == nil
end
- it "sets $~ to the last match when given no block" do
+ it "does not set $~ when given no block" do
"z" =~ /z/ # Reset $~
["abc", "def"].grep(/b/).should == ["abc"]
+ $&.should == "z"
+ end
- # Set by the failed match of "def"
- $~.should == nil
+ it "does not modify Regexp.last_match without block" do
+ "z" =~ /z/ # Reset last match
+ ["abc", "def"].grep(/b/).should == ["abc"]
+ Regexp.last_match[0].should == "z"
+ end
+
+ it "correctly handles non-string elements" do
+ 'set last match' =~ /set last (.*)/
+ [:a, 'b', 'z', :c, 42, nil].grep(/[a-d]/).should == [:a, 'b', :c]
+ $1.should == 'match'
- ["abc", "def"].grep(/e/)
- $&.should == "e"
+ o = Object.new
+ def o.to_str
+ 'hello'
+ end
+ [o].grep(/ll/).first.should.equal?(o)
end
describe "with a block" do
@@ -68,7 +81,7 @@ describe "Enumerable#grep" do
end
it "raises an ArgumentError when not given a pattern" do
- -> { @numerous.grep { |e| e } }.should raise_error(ArgumentError)
+ -> { @numerous.grep { |e| e } }.should.raise(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/enumerable/grep_v_spec.rb b/spec/ruby/core/enumerable/grep_v_spec.rb
index 6dec487065..ee99a77ac1 100644
--- a/spec/ruby/core/enumerable/grep_v_spec.rb
+++ b/spec/ruby/core/enumerable/grep_v_spec.rb
@@ -20,15 +20,28 @@ describe "Enumerable#grep_v" do
$&.should == "e"
end
- it "sets $~ to the last match when given no block" do
+ it "does not set $~ when given no block" do
"z" =~ /z/ # Reset $~
["abc", "def"].grep_v(/e/).should == ["abc"]
+ $&.should == "z"
+ end
- # Set by the match of "def"
- $&.should == "e"
+ it "does not modify Regexp.last_match without block" do
+ "z" =~ /z/ # Reset last match
+ ["abc", "def"].grep_v(/e/).should == ["abc"]
+ Regexp.last_match[0].should == "z"
+ end
+
+ it "correctly handles non-string elements" do
+ 'set last match' =~ /set last (.*)/
+ [:a, 'b', 'z', :c, 42, nil].grep_v(/[a-d]/).should == ['z', 42, nil]
+ $1.should == 'match'
- ["abc", "def"].grep_v(/b/)
- $&.should == nil
+ o = Object.new
+ def o.to_str
+ 'hello'
+ end
+ [o].grep_v(/mm/).first.should.equal?(o)
end
describe "without block" do
@@ -42,7 +55,7 @@ describe "Enumerable#grep_v" do
end
it "raises an ArgumentError when not given a pattern" do
- -> { @numerous.grep_v }.should raise_error(ArgumentError)
+ -> { @numerous.grep_v }.should.raise(ArgumentError)
end
end
@@ -57,7 +70,7 @@ describe "Enumerable#grep_v" do
end
it "raises an ArgumentError when not given a pattern" do
- -> { @numerous.grep_v { |e| e } }.should raise_error(ArgumentError)
+ -> { @numerous.grep_v { |e| e } }.should.raise(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/enumerable/group_by_spec.rb b/spec/ruby/core/enumerable/group_by_spec.rb
index 52b5a68d64..904e5d6c68 100644
--- a/spec/ruby/core/enumerable/group_by_spec.rb
+++ b/spec/ruby/core/enumerable/group_by_spec.rb
@@ -16,13 +16,13 @@ describe "Enumerable#group_by" do
it "returns a hash without default_proc" do
e = EnumerableSpecs::Numerous.new("foo", "bar", "baz")
h = e.group_by { |word| word[0..0].to_sym }
- h[:some].should be_nil
- h.default_proc.should be_nil
- h.default.should be_nil
+ h[:some].should == nil
+ h.default_proc.should == nil
+ h.default.should == nil
end
it "returns an Enumerator if called without a block" do
- EnumerableSpecs::Numerous.new.group_by.should be_an_instance_of(Enumerator)
+ EnumerableSpecs::Numerous.new.group_by.should.instance_of?(Enumerator)
end
it "gathers whole arrays as elements when each yields multiple" do
@@ -33,15 +33,5 @@ describe "Enumerable#group_by" do
[3, 4, 5] => [[3, 4, 5]] }
end
- ruby_version_is ''...'2.7' do
- it "returns a tainted hash if self is tainted" do
- EnumerableSpecs::Empty.new.taint.group_by {}.tainted?.should be_true
- end
-
- it "returns an untrusted hash if self is untrusted" do
- EnumerableSpecs::Empty.new.untrust.group_by {}.untrusted?.should be_true
- end
- end
-
it_behaves_like :enumerable_enumeratorized_with_origin_size, :group_by
end
diff --git a/spec/ruby/core/enumerable/lazy_spec.rb b/spec/ruby/core/enumerable/lazy_spec.rb
index 9a9ead81a0..935e574067 100644
--- a/spec/ruby/core/enumerable/lazy_spec.rb
+++ b/spec/ruby/core/enumerable/lazy_spec.rb
@@ -5,6 +5,6 @@ require_relative 'fixtures/classes'
describe "Enumerable#lazy" do
it "returns an instance of Enumerator::Lazy" do
- EnumerableSpecs::Numerous.new.lazy.should be_an_instance_of(Enumerator::Lazy)
+ EnumerableSpecs::Numerous.new.lazy.should.instance_of?(Enumerator::Lazy)
end
end
diff --git a/spec/ruby/core/enumerable/map_spec.rb b/spec/ruby/core/enumerable/map_spec.rb
index d65aec238c..98a70781cf 100644
--- a/spec/ruby/core/enumerable/map_spec.rb
+++ b/spec/ruby/core/enumerable/map_spec.rb
@@ -3,5 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/collect'
describe "Enumerable#map" do
- it_behaves_like :enumerable_collect , :map
+ it_behaves_like :enumerable_collect, :map
end
diff --git a/spec/ruby/core/enumerable/max_by_spec.rb b/spec/ruby/core/enumerable/max_by_spec.rb
index ec1738ea3b..f67b5d15ea 100644
--- a/spec/ruby/core/enumerable/max_by_spec.rb
+++ b/spec/ruby/core/enumerable/max_by_spec.rb
@@ -4,7 +4,7 @@ require_relative 'shared/enumerable_enumeratorized'
describe "Enumerable#max_by" do
it "returns an enumerator if no block" do
- EnumerableSpecs::Numerous.new(42).max_by.should be_an_instance_of(Enumerator)
+ EnumerableSpecs::Numerous.new(42).max_by.should.instance_of?(Enumerator)
end
it "returns nil if #each yields no objects" do
@@ -18,7 +18,7 @@ describe "Enumerable#max_by" do
it "returns the object that appears first in #each in case of a tie" do
a, b, c = '1', '2', '2'
- EnumerableSpecs::Numerous.new(a, b, c).max_by {|obj| obj.to_i }.should equal(b)
+ EnumerableSpecs::Numerous.new(a, b, c).max_by {|obj| obj.to_i }.should.equal?(b)
end
it "uses max.<=>(current) to determine order" do
@@ -48,7 +48,7 @@ describe "Enumerable#max_by" do
context "without a block" do
it "returns an enumerator" do
- @enum.max_by(2).should be_an_instance_of(Enumerator)
+ @enum.max_by(2).should.instance_of?(Enumerator)
end
end
@@ -67,7 +67,7 @@ describe "Enumerable#max_by" do
context "when n is negative" do
it "raises an ArgumentError" do
- -> { @enum.max_by(-1) { |i| i.to_s } }.should raise_error(ArgumentError)
+ -> { @enum.max_by(-1) { |i| i.to_s } }.should.raise(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/enumerable/max_spec.rb b/spec/ruby/core/enumerable/max_spec.rb
index 0c11ca0969..d92700258b 100644
--- a/spec/ruby/core/enumerable/max_spec.rb
+++ b/spec/ruby/core/enumerable/max_spec.rb
@@ -38,16 +38,16 @@ describe "Enumerable#max" do
it "raises a NoMethodError for elements without #<=>" do
-> do
EnumerableSpecs::EachDefiner.new(BasicObject.new, BasicObject.new).max
- end.should raise_error(NoMethodError)
+ end.should.raise(NoMethodError)
end
it "raises an ArgumentError for incomparable elements" do
-> do
EnumerableSpecs::EachDefiner.new(11,"22").max
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
-> do
EnumerableSpecs::EachDefiner.new(11,12,22,33).max{|a, b| nil}
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
end
context "when passed a block" do
@@ -106,7 +106,7 @@ describe "Enumerable#max" do
context "that is negative" do
it "raises an ArgumentError" do
- -> { @e_ints.max(-1) }.should raise_error(ArgumentError)
+ -> { @e_ints.max(-1) }.should.raise(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/enumerable/min_by_spec.rb b/spec/ruby/core/enumerable/min_by_spec.rb
index 3ff87e49d8..4f949e2130 100644
--- a/spec/ruby/core/enumerable/min_by_spec.rb
+++ b/spec/ruby/core/enumerable/min_by_spec.rb
@@ -4,7 +4,7 @@ require_relative 'shared/enumerable_enumeratorized'
describe "Enumerable#min_by" do
it "returns an enumerator if no block" do
- EnumerableSpecs::Numerous.new(42).min_by.should be_an_instance_of(Enumerator)
+ EnumerableSpecs::Numerous.new(42).min_by.should.instance_of?(Enumerator)
end
it "returns nil if #each yields no objects" do
@@ -18,7 +18,7 @@ describe "Enumerable#min_by" do
it "returns the object that appears first in #each in case of a tie" do
a, b, c = '2', '1', '1'
- EnumerableSpecs::Numerous.new(a, b, c).min_by {|obj| obj.to_i }.should equal(b)
+ EnumerableSpecs::Numerous.new(a, b, c).min_by {|obj| obj.to_i }.should.equal?(b)
end
it "uses min.<=>(current) to determine order" do
@@ -48,7 +48,7 @@ describe "Enumerable#min_by" do
context "without a block" do
it "returns an enumerator" do
- @enum.min_by(2).should be_an_instance_of(Enumerator)
+ @enum.min_by(2).should.instance_of?(Enumerator)
end
end
@@ -67,7 +67,7 @@ describe "Enumerable#min_by" do
context "when n is negative" do
it "raises an ArgumentError" do
- -> { @enum.min_by(-1) { |i| i.to_s } }.should raise_error(ArgumentError)
+ -> { @enum.min_by(-1) { |i| i.to_s } }.should.raise(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/enumerable/min_spec.rb b/spec/ruby/core/enumerable/min_spec.rb
index 4b6ae248fa..f05d59c2c9 100644
--- a/spec/ruby/core/enumerable/min_spec.rb
+++ b/spec/ruby/core/enumerable/min_spec.rb
@@ -32,22 +32,22 @@ describe "Enumerable#min" do
end
it "returns nil for an empty Enumerable" do
- EnumerableSpecs::EachDefiner.new.min.should be_nil
+ EnumerableSpecs::EachDefiner.new.min.should == nil
end
it "raises a NoMethodError for elements without #<=>" do
-> do
EnumerableSpecs::EachDefiner.new(BasicObject.new, BasicObject.new).min
- end.should raise_error(NoMethodError)
+ end.should.raise(NoMethodError)
end
it "raises an ArgumentError for incomparable elements" do
-> do
EnumerableSpecs::EachDefiner.new(11,"22").min
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
-> do
EnumerableSpecs::EachDefiner.new(11,12,22,33).min{|a, b| nil}
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
end
it "returns the minimum when using a block rule" do
@@ -110,7 +110,7 @@ describe "Enumerable#min" do
context "that is negative" do
it "raises an ArgumentError" do
- -> { @e_ints.min(-1) }.should raise_error(ArgumentError)
+ -> { @e_ints.min(-1) }.should.raise(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/enumerable/minmax_by_spec.rb b/spec/ruby/core/enumerable/minmax_by_spec.rb
index a6a9249270..30c88cfcfb 100644
--- a/spec/ruby/core/enumerable/minmax_by_spec.rb
+++ b/spec/ruby/core/enumerable/minmax_by_spec.rb
@@ -4,7 +4,7 @@ require_relative 'shared/enumerable_enumeratorized'
describe "Enumerable#minmax_by" do
it "returns an enumerator if no block" do
- EnumerableSpecs::Numerous.new(42).minmax_by.should be_an_instance_of(Enumerator)
+ EnumerableSpecs::Numerous.new(42).minmax_by.should.instance_of?(Enumerator)
end
it "returns nil if #each yields no objects" do
@@ -19,8 +19,8 @@ describe "Enumerable#minmax_by" do
it "returns the object that appears first in #each in case of a tie" do
a, b, c, d = '1', '1', '2', '2'
mm = EnumerableSpecs::Numerous.new(a, b, c, d).minmax_by {|obj| obj.to_i }
- mm[0].should equal(a)
- mm[1].should equal(c)
+ mm[0].should.equal?(a)
+ mm[1].should.equal?(c)
end
it "uses min/max.<=>(current) to determine order" do
diff --git a/spec/ruby/core/enumerable/minmax_spec.rb b/spec/ruby/core/enumerable/minmax_spec.rb
index 29f1ecf82c..f5f17ef079 100644
--- a/spec/ruby/core/enumerable/minmax_spec.rb
+++ b/spec/ruby/core/enumerable/minmax_spec.rb
@@ -1,41 +1,17 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative '../../shared/enumerable/minmax'
describe "Enumerable#minmax" do
before :each do
@enum = EnumerableSpecs::Numerous.new(6, 4, 5, 10, 8)
-
+ @empty_enum = EnumerableSpecs::Empty.new
+ @incomparable_enum = EnumerableSpecs::Numerous.new(BasicObject.new, BasicObject.new)
+ @incompatible_enum = EnumerableSpecs::Numerous.new(11,"22")
@strs = EnumerableSpecs::Numerous.new("333", "2", "60", "55555", "1010", "111")
end
- it "min should return the minimum element" do
- @enum.minmax.should == [4, 10]
- @strs.minmax.should == ["1010", "60" ]
- end
-
- it "returns [nil, nil] for an empty Enumerable" do
- EnumerableSpecs::Empty.new.minmax.should == [nil, nil]
- end
-
- it "raises an ArgumentError when elements are incomparable" do
- -> do
- EnumerableSpecs::Numerous.new(11,"22").minmax
- end.should raise_error(ArgumentError)
- -> do
- EnumerableSpecs::Numerous.new(11,12,22,33).minmax{|a, b| nil}
- end.should raise_error(ArgumentError)
- end
-
- it "raises a NoMethodError for elements without #<=>" do
- -> do
- EnumerableSpecs::Numerous.new(BasicObject.new, BasicObject.new).minmax
- end.should raise_error(NoMethodError)
- end
-
- it "returns the minimum when using a block rule" do
- @enum.minmax {|a,b| b <=> a }.should == [10, 4]
- @strs.minmax {|a,b| a.length <=> b.length }.should == ["2", "55555"]
- end
+ it_behaves_like :enumerable_minmax, :minmax
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMulti.new
diff --git a/spec/ruby/core/enumerable/none_spec.rb b/spec/ruby/core/enumerable/none_spec.rb
index ec48eaf0a7..d9ee0b441e 100644
--- a/spec/ruby/core/enumerable/none_spec.rb
+++ b/spec/ruby/core/enumerable/none_spec.rb
@@ -15,35 +15,35 @@ describe "Enumerable#none?" do
end
it "raises an ArgumentError when more than 1 argument is provided" do
- -> { @enum.none?(1, 2, 3) }.should raise_error(ArgumentError)
- -> { [].none?(1, 2, 3) }.should raise_error(ArgumentError)
- -> { {}.none?(1, 2, 3) }.should raise_error(ArgumentError)
+ -> { @enum.none?(1, 2, 3) }.should.raise(ArgumentError)
+ -> { [].none?(1, 2, 3) }.should.raise(ArgumentError)
+ -> { {}.none?(1, 2, 3) }.should.raise(ArgumentError)
end
it "does not hide exceptions out of #each" do
-> {
EnumerableSpecs::ThrowingEach.new.none?
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
-> {
EnumerableSpecs::ThrowingEach.new.none? { false }
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
describe "with no block" do
it "returns true if none of the elements in self are true" do
e = EnumerableSpecs::Numerous.new(false, nil, false)
- e.none?.should be_true
+ e.none?.should == true
end
it "returns false if at least one of the elements in self are true" do
e = EnumerableSpecs::Numerous.new(false, nil, true, false)
- e.none?.should be_false
+ e.none?.should == false
end
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMultiWithFalse.new
- multi.none?.should be_false
+ multi.none?.should == false
end
end
@@ -65,17 +65,17 @@ describe "Enumerable#none?" do
end
it "returns true if the block never returns true" do
- @e.none? {|e| false }.should be_true
+ @e.none? {|e| false }.should == true
end
it "returns false if the block ever returns true" do
- @e.none? {|e| e == 3 ? true : false }.should be_false
+ @e.none? {|e| e == 3 ? true : false }.should == false
end
it "does not hide exceptions out of the block" do
-> {
@enum.none? { raise "from block" }
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "gathers initial args as elements when each yields multiple" do
@@ -94,21 +94,12 @@ describe "Enumerable#none?" do
end
describe 'when given a pattern argument' do
- it "calls `===` on the pattern the return value " do
+ it "calls `===` on the pattern the return value" do
pattern = EnumerableSpecs::Pattern.new { |x| x == 3 }
@enum1.none?(pattern).should == true
pattern.yielded.should == [[0], [1], [2], [-1]]
end
- # may raise an exception in future versions
- ruby_version_is ""..."2.6" do
- it "ignores block" do
- @enum2.none?(Integer) { raise }.should == true
- [1, 2, nil].none?(TrueClass) { raise }.should == true
- {a: 1}.none?(Hash) { raise }.should == true
- end
- end
-
it "always returns true on empty enumeration" do
@empty.none?(Integer).should == true
[].none?(Integer).should == true
@@ -118,7 +109,7 @@ describe "Enumerable#none?" do
it "does not hide exceptions out of #each" do
-> {
EnumerableSpecs::ThrowingEach.new.none?(Integer)
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "returns true if the pattern never returns a truthy value" do
@@ -143,7 +134,7 @@ describe "Enumerable#none?" do
pattern = EnumerableSpecs::Pattern.new { raise "from pattern" }
-> {
@enum.none?(pattern)
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "calls the pattern with gathered array when yielded with multiple arguments" do
@@ -152,5 +143,11 @@ describe "Enumerable#none?" do
multi.none?(pattern).should == true
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end
+
+ it "ignores the block if there is an argument" do
+ -> {
+ EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).none?(String) { true }.should == true
+ }.should complain(/given block not used/)
+ end
end
end
diff --git a/spec/ruby/core/enumerable/one_spec.rb b/spec/ruby/core/enumerable/one_spec.rb
index 6489c84742..cf966d4a9b 100644
--- a/spec/ruby/core/enumerable/one_spec.rb
+++ b/spec/ruby/core/enumerable/one_spec.rb
@@ -15,57 +15,57 @@ describe "Enumerable#one?" do
end
it "raises an ArgumentError when more than 1 argument is provided" do
- -> { @enum.one?(1, 2, 3) }.should raise_error(ArgumentError)
- -> { [].one?(1, 2, 3) }.should raise_error(ArgumentError)
- -> { {}.one?(1, 2, 3) }.should raise_error(ArgumentError)
+ -> { @enum.one?(1, 2, 3) }.should.raise(ArgumentError)
+ -> { [].one?(1, 2, 3) }.should.raise(ArgumentError)
+ -> { {}.one?(1, 2, 3) }.should.raise(ArgumentError)
end
it "does not hide exceptions out of #each" do
-> {
EnumerableSpecs::ThrowingEach.new.one?
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
-> {
EnumerableSpecs::ThrowingEach.new.one? { false }
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
describe "with no block" do
it "returns true if only one element evaluates to true" do
- [false, nil, true].one?.should be_true
+ [false, nil, true].one?.should == true
end
it "returns false if two elements evaluate to true" do
- [false, :value, nil, true].one?.should be_false
+ [false, :value, nil, true].one?.should == false
end
it "returns false if all elements evaluate to false" do
- [false, nil, false].one?.should be_false
+ [false, nil, false].one?.should == false
end
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMultiWithSingleTrue.new
- multi.one?.should be_false
+ multi.one?.should == false
end
end
describe "with a block" do
it "returns true if block returns true once" do
- [:a, :b, :c].one? { |s| s == :a }.should be_true
+ [:a, :b, :c].one? { |s| s == :a }.should == true
end
it "returns false if the block returns true more than once" do
- [:a, :b, :c].one? { |s| s == :a || s == :b }.should be_false
+ [:a, :b, :c].one? { |s| s == :a || s == :b }.should == false
end
it "returns false if the block only returns false" do
- [:a, :b, :c].one? { |s| s == :d }.should be_false
+ [:a, :b, :c].one? { |s| s == :d }.should == false
end
it "does not hide exceptions out of the block" do
-> {
@enum.one? { raise "from block" }
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "gathers initial args as elements when each yields multiple" do
@@ -83,23 +83,13 @@ describe "Enumerable#one?" do
end
end
-
describe 'when given a pattern argument' do
- it "calls `===` on the pattern the return value " do
+ it "calls `===` on the pattern the return value" do
pattern = EnumerableSpecs::Pattern.new { |x| x == 1 }
@enum1.one?(pattern).should == true
pattern.yielded.should == [[0], [1], [2], [-1]]
end
- # may raise an exception in future versions
- ruby_version_is ""..."2.6" do
- it "ignores block" do
- @enum2.one?(NilClass) { raise }.should == true
- [1, 2, nil].one?(NilClass) { raise }.should == true
- {a: 1}.one?(Array) { raise }.should == true
- end
- end
-
it "always returns false on empty enumeration" do
@empty.one?(Integer).should == false
[].one?(Integer).should == false
@@ -109,7 +99,7 @@ describe "Enumerable#one?" do
it "does not hide exceptions out of #each" do
-> {
EnumerableSpecs::ThrowingEach.new.one?(Integer)
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "returns true if the pattern returns a truthy value only once" do
@@ -145,7 +135,7 @@ describe "Enumerable#one?" do
pattern = EnumerableSpecs::Pattern.new { raise "from pattern" }
-> {
@enum.one?(pattern)
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
it "calls the pattern with gathered array when yielded with multiple arguments" do
@@ -154,5 +144,11 @@ describe "Enumerable#one?" do
multi.one?(pattern).should == false
pattern.yielded.should == [[[1, 2]], [[3, 4, 5]], [[6, 7, 8, 9]]]
end
+
+ it "ignores the block if there is an argument" do
+ -> {
+ EnumerableSpecs::Numerous.new(1, 2, 3, 4, "5").one?(String) { false }.should == true
+ }.should complain(/given block not used/)
+ end
end
end
diff --git a/spec/ruby/core/enumerable/partition_spec.rb b/spec/ruby/core/enumerable/partition_spec.rb
index d3d220b4b4..8272ee189e 100644
--- a/spec/ruby/core/enumerable/partition_spec.rb
+++ b/spec/ruby/core/enumerable/partition_spec.rb
@@ -8,7 +8,7 @@ describe "Enumerable#partition" do
end
it "returns an Enumerator if called without a block" do
- EnumerableSpecs::Numerous.new.partition.should be_an_instance_of(Enumerator)
+ EnumerableSpecs::Numerous.new.partition.should.instance_of?(Enumerator)
end
it "gathers whole arrays as elements when each yields multiple" do
diff --git a/spec/ruby/core/enumerable/reject_spec.rb b/spec/ruby/core/enumerable/reject_spec.rb
index 0d86b49ea2..31e89f5b0e 100644
--- a/spec/ruby/core/enumerable/reject_spec.rb
+++ b/spec/ruby/core/enumerable/reject_spec.rb
@@ -13,7 +13,7 @@ describe "Enumerable#reject" do
end
it "returns an Enumerator if called without a block" do
- EnumerableSpecs::Numerous.new.reject.should be_an_instance_of(Enumerator)
+ EnumerableSpecs::Numerous.new.reject.should.instance_of?(Enumerator)
end
it "gathers whole arrays as elements when each yields multiple" do
diff --git a/spec/ruby/core/enumerable/reverse_each_spec.rb b/spec/ruby/core/enumerable/reverse_each_spec.rb
index 2b1c233488..4753956724 100644
--- a/spec/ruby/core/enumerable/reverse_each_spec.rb
+++ b/spec/ruby/core/enumerable/reverse_each_spec.rb
@@ -11,7 +11,7 @@ describe "Enumerable#reverse_each" do
it "returns an Enumerator if no block given" do
enum = EnumerableSpecs::Numerous.new.reverse_each
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.to_a.should == [4, 1, 6, 3, 5, 2]
end
diff --git a/spec/ruby/core/enumerable/select_spec.rb b/spec/ruby/core/enumerable/select_spec.rb
index 11168eb42e..7fc61926f9 100644
--- a/spec/ruby/core/enumerable/select_spec.rb
+++ b/spec/ruby/core/enumerable/select_spec.rb
@@ -3,5 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/find_all'
describe "Enumerable#select" do
- it_behaves_like :enumerable_find_all , :select
+ it_behaves_like :enumerable_find_all, :select
end
diff --git a/spec/ruby/core/enumerable/shared/collect.rb b/spec/ruby/core/enumerable/shared/collect.rb
index 71b8acd526..4696d32454 100644
--- a/spec/ruby/core/enumerable/shared/collect.rb
+++ b/spec/ruby/core/enumerable/shared/collect.rb
@@ -30,7 +30,7 @@ describe :enumerable_collect, shared: true do
it "returns an enumerator when no block given" do
enum = EnumerableSpecs::Numerous.new.send(@method)
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.each { |i| -i }.should == [-2, -5, -3, -6, -1, -4]
end
@@ -51,7 +51,20 @@ describe :enumerable_collect, shared: true do
ScratchPad.recorded.should == [1]
end
- it "yields 2 arguments for a Hash" do
+ it "yields an Array of 2 elements for a Hash when block arity is 1" do
+ c = Class.new do
+ def register(a)
+ ScratchPad << a
+ end
+ end
+ m = c.new.method(:register)
+
+ ScratchPad.record []
+ { 1 => 'a', 2 => 'b' }.map(&m)
+ ScratchPad.recorded.should == [[1, 'a'], [2, 'b']]
+ end
+
+ it "yields 2 arguments for a Hash when block arity is 2" do
c = Class.new do
def register(a, b)
ScratchPad << [a, b]
@@ -64,5 +77,31 @@ describe :enumerable_collect, shared: true do
ScratchPad.recorded.should == [[1, 'a'], [2, 'b']]
end
+ it "raises an error for a Hash when an arity enforcing block of arity >2 is passed in" do
+ c = Class.new do
+ def register(a, b, c)
+ end
+ end
+ m = c.new.method(:register)
+
+ -> do
+ { 1 => 'a', 2 => 'b' }.map(&m)
+ end.should.raise(ArgumentError)
+ end
+
+ it "calls the each method on sub-classes" do
+ c = Class.new(Hash) do
+ def each
+ ScratchPad << 'in each'
+ super
+ end
+ end
+ h = c.new
+ h[1] = 'a'
+ ScratchPad.record []
+ h.send(@method) { |k,v| v }
+ ScratchPad.recorded.should == ['in each']
+ end
+
it_should_behave_like :enumerable_enumeratorized_with_origin_size
end
diff --git a/spec/ruby/core/enumerable/shared/collect_concat.rb b/spec/ruby/core/enumerable/shared/collect_concat.rb
index ddd431baeb..1694e3fdce 100644
--- a/spec/ruby/core/enumerable/shared/collect_concat.rb
+++ b/spec/ruby/core/enumerable/shared/collect_concat.rb
@@ -41,12 +41,12 @@ describe :enumerable_collect_concat, shared: true do
obj = mock("to_ary defined")
obj.should_receive(:to_ary).and_return("array")
- -> { [1, obj, 3].send(@method) { |i| i } }.should raise_error(TypeError)
+ -> { [1, obj, 3].send(@method) { |i| i } }.should.raise(TypeError)
end
it "returns an enumerator when no block given" do
enum = EnumerableSpecs::Numerous.new(1, 2).send(@method)
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.each{ |i| [i] * i }.should == [1, 2, 2]
end
diff --git a/spec/ruby/core/enumerable/shared/entries.rb b/spec/ruby/core/enumerable/shared/entries.rb
index 590ce73bcf..e32eb23d2a 100644
--- a/spec/ruby/core/enumerable/shared/entries.rb
+++ b/spec/ruby/core/enumerable/shared/entries.rb
@@ -13,14 +13,4 @@ describe :enumerable_entries, shared: true do
count.send(@method, :hello, "world").should == [1, 2, 3]
count.arguments_passed.should == [:hello, "world"]
end
-
- ruby_version_is ''...'2.7' do
- it "returns a tainted array if self is tainted" do
- EnumerableSpecs::Empty.new.taint.send(@method).tainted?.should be_true
- end
-
- it "returns an untrusted array if self is untrusted" do
- EnumerableSpecs::Empty.new.untrust.send(@method).untrusted?.should be_true
- end
- end
end
diff --git a/spec/ruby/core/enumerable/shared/find.rb b/spec/ruby/core/enumerable/shared/find.rb
index 61d63ba3d5..cdff640415 100644
--- a/spec/ruby/core/enumerable/shared/find.rb
+++ b/spec/ruby/core/enumerable/shared/find.rb
@@ -59,7 +59,7 @@ describe :enumerable_find, shared: true do
end
it "returns an enumerator when no block given" do
- @numerous.send(@method).should be_an_instance_of(Enumerator)
+ @numerous.send(@method).should.instance_of?(Enumerator)
end
it "passes the ifnone proc to the enumerator" do
diff --git a/spec/ruby/core/enumerable/shared/find_all.rb b/spec/ruby/core/enumerable/shared/find_all.rb
index 1bbe71f372..27f01de6e0 100644
--- a/spec/ruby/core/enumerable/shared/find_all.rb
+++ b/spec/ruby/core/enumerable/shared/find_all.rb
@@ -14,7 +14,7 @@ describe :enumerable_find_all, shared: true do
end
it "returns an enumerator when no block given" do
- @numerous.send(@method).should be_an_instance_of(Enumerator)
+ @numerous.send(@method).should.instance_of?(Enumerator)
end
it "passes through the values yielded by #each_with_index" do
diff --git a/spec/ruby/core/enumerable/shared/include.rb b/spec/ruby/core/enumerable/shared/include.rb
index 569f350fd5..ea250f032b 100644
--- a/spec/ruby/core/enumerable/shared/include.rb
+++ b/spec/ruby/core/enumerable/shared/include.rb
@@ -28,7 +28,7 @@ describe :enumerable_include, shared: true do
it "gathers whole arrays as elements when each yields multiple" do
multi = EnumerableSpecs::YieldsMulti.new
- multi.send(@method, [1,2]).should be_true
+ multi.send(@method, [1,2]).should == true
end
end
diff --git a/spec/ruby/core/enumerable/shared/inject.rb b/spec/ruby/core/enumerable/shared/inject.rb
index 12e0665dda..7da4f0ca99 100644
--- a/spec/ruby/core/enumerable/shared/inject.rb
+++ b/spec/ruby/core/enumerable/shared/inject.rb
@@ -1,3 +1,5 @@
+require_relative '../../array/shared/iterable_and_tolerating_size_increasing'
+
describe :enumerable_inject, shared: true do
it "with argument takes a block with an accumulator (with argument as initial value) and the current element. Value of block becomes new accumulator" do
a = []
@@ -14,14 +16,65 @@ describe :enumerable_inject, shared: true do
it "can take two argument" do
EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-).should == 4
+ EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, "-").should == 4
+
+ [1, 2, 3].send(@method, 10, :-).should == 4
+ [1, 2, 3].send(@method, 10, "-").should == 4
+ end
+
+ it "converts non-Symbol method name argument to String with #to_str if two arguments" do
+ name = Object.new
+ def name.to_str; "-"; end
+
+ EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, name).should == 4
+ [1, 2, 3].send(@method, 10, name).should == 4
+ end
+
+ it "raises TypeError when the second argument is not Symbol or String and it cannot be converted to String if two arguments" do
+ -> { EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, Object.new) }.should.raise(TypeError, /is not a symbol nor a string/)
+ -> { [1, 2, 3].send(@method, 10, Object.new) }.should.raise(TypeError, /is not a symbol nor a string/)
end
it "ignores the block if two arguments" do
- EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-){ raise "we never get here"}.should == 4
+ -> {
+ EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-) { raise "we never get here"}.should == 4
+ }.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true)
+
+ -> {
+ [1, 2, 3].send(@method, 10, :-) { raise "we never get here"}.should == 4
+ }.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true)
+ end
+
+ it "does not warn when given a Symbol with $VERBOSE true" do
+ -> {
+ [1, 2].send(@method, 0, :+)
+ [1, 2].send(@method, :+)
+ EnumerableSpecs::Numerous.new(1, 2).send(@method, 0, :+)
+ EnumerableSpecs::Numerous.new(1, 2).send(@method, :+)
+ }.should_not complain(verbose: true)
end
it "can take a symbol argument" do
EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, :-).should == 4
+ [10, 1, 2, 3].send(@method, :-).should == 4
+ end
+
+ it "can take a String argument" do
+ EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, "-").should == 4
+ [10, 1, 2, 3].send(@method, "-").should == 4
+ end
+
+ it "converts non-Symbol method name argument to String with #to_str" do
+ name = Object.new
+ def name.to_str; "-"; end
+
+ EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, name).should == 4
+ [10, 1, 2, 3].send(@method, name).should == 4
+ end
+
+ it "raises TypeError when passed not Symbol or String method name argument and it cannot be converted to String" do
+ -> { EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, Object.new) }.should.raise(TypeError, /is not a symbol nor a string/)
+ -> { [10, 1, 2, 3].send(@method, Object.new) }.should.raise(TypeError, /is not a symbol nor a string/)
end
it "without argument takes a block with an accumulator (with first element as initial value) and the current element. Value of block becomes new accumulator" do
@@ -30,10 +83,10 @@ describe :enumerable_inject, shared: true do
a.should == [[2, 5], [5, 3], [3, 6], [6, 1], [1, 4]]
end
- it "gathers whole arrays as elements when each yields multiple" do
- multi = EnumerableSpecs::YieldsMulti.new
- multi.send(@method, []) {|acc, e| acc << e }.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
- end
+ it "gathers whole arrays as elements when each yields multiple" do
+ multi = EnumerableSpecs::YieldsMulti.new
+ multi.send(@method, []) {|acc, e| acc << e }.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
+ end
it "with inject arguments(legacy rubycon)" do
# with inject argument
@@ -50,7 +103,7 @@ describe :enumerable_inject, shared: true do
it "without inject arguments(legacy rubycon)" do
# no inject argument
- EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| 999 } .should == 2
+ EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| 999 }.should == 2
EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| acc }.should == 2
EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| x }.should == 2
@@ -60,10 +113,30 @@ describe :enumerable_inject, shared: true do
EnumerableSpecs::EachDefiner.new('a','b','c').send(@method) {|result, i| i+result}.should == "cba"
EnumerableSpecs::EachDefiner.new(3, 4, 5).send(@method) {|result, i| result*i}.should == 60
EnumerableSpecs::EachDefiner.new([1], 2, 'a','b').send(@method){|r,i| r<<i}.should == [1, 2, 'a', 'b']
-
end
it "returns nil when fails(legacy rubycon)" do
EnumerableSpecs::EachDefiner.new().send(@method) {|acc,x| 999 }.should == nil
end
+
+ it "tolerates increasing a collection size during iterating Array" do
+ array = [:a, :b, :c]
+ ScratchPad.record []
+ i = 0
+
+ array.send(@method, nil) do |_, e|
+ ScratchPad << e
+ array << i if i < 100
+ i += 1
+ end
+
+ actual = ScratchPad.recorded
+ expected = [:a, :b, :c] + (0..99).to_a
+ actual.sort_by(&:to_s).should == expected.sort_by(&:to_s)
+ end
+
+ it "raises an ArgumentError when no parameters or block is given" do
+ -> { [1,2].send(@method) }.should.raise(ArgumentError)
+ -> { {one: 1, two: 2}.send(@method) }.should.raise(ArgumentError)
+ end
end
diff --git a/spec/ruby/core/enumerable/shared/take.rb b/spec/ruby/core/enumerable/shared/take.rb
index ce2ace20fa..a6da06325f 100644
--- a/spec/ruby/core/enumerable/shared/take.rb
+++ b/spec/ruby/core/enumerable/shared/take.rb
@@ -25,7 +25,7 @@ describe :enumerable_take, shared: true do
end
it "raises an ArgumentError when count is negative" do
- -> { @enum.send(@method, -1) }.should raise_error(ArgumentError)
+ -> { @enum.send(@method, -1) }.should.raise(ArgumentError)
end
it "returns the entire array when count > length" do
@@ -40,11 +40,11 @@ describe :enumerable_take, shared: true do
end
it "raises a TypeError if the passed argument is not numeric" do
- -> { @enum.send(@method, nil) }.should raise_error(TypeError)
- -> { @enum.send(@method, "a") }.should raise_error(TypeError)
+ -> { @enum.send(@method, nil) }.should.raise(TypeError)
+ -> { @enum.send(@method, "a") }.should.raise(TypeError)
obj = mock("nonnumeric")
- -> { @enum.send(@method, obj) }.should raise_error(TypeError)
+ -> { @enum.send(@method, obj) }.should.raise(TypeError)
end
it "gathers whole arrays as elements when each yields multiple" do
diff --git a/spec/ruby/core/enumerable/shared/value_packing.rb b/spec/ruby/core/enumerable/shared/value_packing.rb
new file mode 100644
index 0000000000..ff77f45cdf
--- /dev/null
+++ b/spec/ruby/core/enumerable/shared/value_packing.rb
@@ -0,0 +1,26 @@
+# This is the behavior of rb_enum_values_pack() in CRuby
+describe :enumerable_value_packing, shared: true do
+ # @take must be set to a Proc that returns the take-result whose #each
+ # yields packed values (e.g. -> e { e.take(1) } or -> e { e.lazy.take(1) }).
+
+ it "yields a single nil for a zero-argument source yield" do
+ e = Enumerator.new { |y| y.yield }
+ args = nil
+ @take.call(e).each { |*a| args = a }
+ args.should == [nil]
+ end
+
+ it "yields the value for a single-argument source yield" do
+ e = Enumerator.new { |y| y.yield :v }
+ args = nil
+ @take.call(e).each { |*a| args = a }
+ args.should == [:v]
+ end
+
+ it "yields a packed Array for a multi-argument source yield" do
+ e = Enumerator.new { |y| y.yield 1, 2 }
+ args = nil
+ @take.call(e).each { |*a| args = a }
+ args.should == [[1, 2]]
+ end
+end
diff --git a/spec/ruby/core/enumerable/slice_after_spec.rb b/spec/ruby/core/enumerable/slice_after_spec.rb
index 0e46688db1..1ef37195e5 100644
--- a/spec/ruby/core/enumerable/slice_after_spec.rb
+++ b/spec/ruby/core/enumerable/slice_after_spec.rb
@@ -11,7 +11,7 @@ describe "Enumerable#slice_after" do
arg = mock("filter")
arg.should_receive(:===).and_return(false, true, false, false, false, true, false)
e = @enum.slice_after(arg)
- e.should be_an_instance_of(Enumerator)
+ e.should.instance_of?(Enumerator)
e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]]
end
@@ -34,21 +34,21 @@ describe "Enumerable#slice_after" do
describe "and no argument" do
it "calls the block to determine when to yield" do
e = @enum.slice_after{ |i| i == 6 || i == 2 }
- e.should be_an_instance_of(Enumerator)
+ e.should.instance_of?(Enumerator)
e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]]
end
end
describe "and an argument" do
it "raises an ArgumentError" do
- -> { @enum.slice_after(42) { |i| i == 6 } }.should raise_error(ArgumentError)
+ -> { @enum.slice_after(42) { |i| i == 6 } }.should.raise(ArgumentError)
end
end
end
it "raises an ArgumentError when given an incorrect number of arguments" do
- -> { @enum.slice_after("one", "two") }.should raise_error(ArgumentError)
- -> { @enum.slice_after }.should raise_error(ArgumentError)
+ -> { @enum.slice_after("one", "two") }.should.raise(ArgumentError)
+ -> { @enum.slice_after }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/enumerable/slice_before_spec.rb b/spec/ruby/core/enumerable/slice_before_spec.rb
index f9b33f7b28..7eb4410a25 100644
--- a/spec/ruby/core/enumerable/slice_before_spec.rb
+++ b/spec/ruby/core/enumerable/slice_before_spec.rb
@@ -12,7 +12,7 @@ describe "Enumerable#slice_before" do
arg = mock "filter"
arg.should_receive(:===).and_return(false, true, false, false, false, true, false)
e = @enum.slice_before(arg)
- e.should be_an_instance_of(Enumerator)
+ e.should.instance_of?(Enumerator)
e.to_a.should == [[7], [6, 5, 4, 3], [2, 1]]
end
@@ -35,7 +35,7 @@ describe "Enumerable#slice_before" do
describe "and no argument" do
it "calls the block to determine when to yield" do
e = @enum.slice_before{|i| i == 6 || i == 2}
- e.should be_an_instance_of(Enumerator)
+ e.should.instance_of?(Enumerator)
e.to_a.should == [[7], [6, 5, 4, 3], [2, 1]]
end
end
@@ -43,13 +43,13 @@ describe "Enumerable#slice_before" do
it "does not accept arguments" do
-> {
@enum.slice_before(1) {}
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
end
it "raises an ArgumentError when given an incorrect number of arguments" do
- -> { @enum.slice_before("one", "two") }.should raise_error(ArgumentError)
- -> { @enum.slice_before }.should raise_error(ArgumentError)
+ -> { @enum.slice_before("one", "two") }.should.raise(ArgumentError)
+ -> { @enum.slice_before }.should.raise(ArgumentError)
end
describe "when an iterator method yields more than one value" do
diff --git a/spec/ruby/core/enumerable/slice_when_spec.rb b/spec/ruby/core/enumerable/slice_when_spec.rb
index 6b8ea0923e..fe1ecd31e2 100644
--- a/spec/ruby/core/enumerable/slice_when_spec.rb
+++ b/spec/ruby/core/enumerable/slice_when_spec.rb
@@ -11,7 +11,7 @@ describe "Enumerable#slice_when" do
context "when given a block" do
it "returns an enumerator" do
- @result.should be_an_instance_of(Enumerator)
+ @result.should.instance_of?(Enumerator)
end
it "splits chunks between adjacent elements i and j where the block returns true" do
@@ -39,7 +39,7 @@ describe "Enumerable#slice_when" do
context "when not given a block" do
it "raises an ArgumentError" do
- -> { @enum.slice_when }.should raise_error(ArgumentError)
+ -> { @enum.slice_when }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/enumerable/sort_by_spec.rb b/spec/ruby/core/enumerable/sort_by_spec.rb
index 8fdd923fb4..62cf38ce3e 100644
--- a/spec/ruby/core/enumerable/sort_by_spec.rb
+++ b/spec/ruby/core/enumerable/sort_by_spec.rb
@@ -18,7 +18,7 @@ describe "Enumerable#sort_by" do
it "returns an Enumerator when a block is not supplied" do
a = EnumerableSpecs::Numerous.new("a","b")
- a.sort_by.should be_an_instance_of(Enumerator)
+ a.sort_by.should.instance_of?(Enumerator)
a.to_a.should == ["a", "b"]
end
diff --git a/spec/ruby/core/enumerable/sort_spec.rb b/spec/ruby/core/enumerable/sort_spec.rb
index cff1a59986..427b1cd8f1 100644
--- a/spec/ruby/core/enumerable/sort_spec.rb
+++ b/spec/ruby/core/enumerable/sort_spec.rb
@@ -16,7 +16,7 @@ describe "Enumerable#sort" do
it "raises a NoMethodError if elements do not define <=>" do
-> do
EnumerableSpecs::Numerous.new(BasicObject.new, BasicObject.new, BasicObject.new).sort
- end.should raise_error(NoMethodError)
+ end.should.raise(NoMethodError)
end
it "sorts enumerables that contain nils" do
@@ -31,16 +31,16 @@ describe "Enumerable#sort" do
it "compare values returned by block with 0" do
EnumerableSpecs::Numerous.new.sort { |n, m| -(n+m) * (n <=> m) }.should == [6, 5, 4, 3, 2, 1]
EnumerableSpecs::Numerous.new.sort { |n, m|
- EnumerableSpecs::ComparableWithFixnum.new(-(n+m) * (n <=> m))
+ EnumerableSpecs::ComparableWithInteger.new(-(n+m) * (n <=> m))
}.should == [6, 5, 4, 3, 2, 1]
-> {
EnumerableSpecs::Numerous.new.sort { |n, m| (n <=> m).to_s }
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "raises an error if objects can't be compared" do
a=EnumerableSpecs::Numerous.new(EnumerableSpecs::Uncomparable.new, EnumerableSpecs::Uncomparable.new)
- -> {a.sort}.should raise_error(ArgumentError)
+ -> {a.sort}.should.raise(ArgumentError)
end
it "gathers whole arrays as elements when each yields multiple" do
diff --git a/spec/ruby/core/enumerable/sum_spec.rb b/spec/ruby/core/enumerable/sum_spec.rb
index c9d7017b45..2eb74db6ac 100644
--- a/spec/ruby/core/enumerable/sum_spec.rb
+++ b/spec/ruby/core/enumerable/sum_spec.rb
@@ -22,7 +22,29 @@ describe 'Enumerable#sum' do
@enum.sum.should == 5/3r
end
- it 'takes a block to transform the elements' do
- @enum.sum { |element| element * 2 }.should == 10/3r
+ context 'with a block' do
+ it 'transforms the elements' do
+ @enum.sum { |element| element * 2 }.should == 10/3r
+ end
+
+ it 'does not destructure array elements' do
+ class << @enum
+ def each
+ yield [1,2]
+ yield [3]
+ end
+ end
+
+ @enum.sum(&:last).should == 5
+ end
+ end
+
+ # https://bugs.ruby-lang.org/issues/12217
+ # https://github.com/ruby/ruby/blob/master/doc/ChangeLog/ChangeLog-2.4.0#L6208-L6214
+ it "uses Kahan's compensated summation algorithm for precise sum of float numbers" do
+ floats = [2.7800000000000002, 5.0, 2.5, 4.44, 3.89, 3.89, 4.44, 7.78, 5.0, 2.7800000000000002, 5.0, 2.5].to_enum
+ naive_sum = floats.reduce { |sum, e| sum + e }
+ naive_sum.should == 50.00000000000001
+ floats.sum.should == 50.0
end
end
diff --git a/spec/ruby/core/enumerable/take_spec.rb b/spec/ruby/core/enumerable/take_spec.rb
index 41a7438330..ca439b750d 100644
--- a/spec/ruby/core/enumerable/take_spec.rb
+++ b/spec/ruby/core/enumerable/take_spec.rb
@@ -1,13 +1,21 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/take'
+require_relative 'shared/value_packing'
describe "Enumerable#take" do
it "requires an argument" do
- ->{ EnumerableSpecs::Numerous.new.take}.should raise_error(ArgumentError)
+ ->{ EnumerableSpecs::Numerous.new.take}.should.raise(ArgumentError)
end
describe "when passed an argument" do
it_behaves_like :enumerable_take, :take
end
+
+ describe "value packing of source yields" do
+ before :each do
+ @take = -> e { e.take(1) }
+ end
+ it_behaves_like :enumerable_value_packing, nil
+ end
end
diff --git a/spec/ruby/core/enumerable/take_while_spec.rb b/spec/ruby/core/enumerable/take_while_spec.rb
index 26db39ac4b..918bfc897d 100644
--- a/spec/ruby/core/enumerable/take_while_spec.rb
+++ b/spec/ruby/core/enumerable/take_while_spec.rb
@@ -8,7 +8,7 @@ describe "Enumerable#take_while" do
end
it "returns an Enumerator if no block given" do
- @enum.take_while.should be_an_instance_of(Enumerator)
+ @enum.take_while.should.instance_of?(Enumerator)
end
it "returns no/all elements for {true/false} block" do
@@ -38,7 +38,7 @@ describe "Enumerable#take_while" do
it "doesn't return self when it could" do
a = [1,2,3]
- a.take_while{true}.should_not equal(a)
+ a.take_while{true}.should_not.equal?(a)
end
it "calls the block with initial args when yielded with multiple arguments" do
diff --git a/spec/ruby/core/enumerable/tally_spec.rb b/spec/ruby/core/enumerable/tally_spec.rb
index 363b3def21..deef741407 100644
--- a/spec/ruby/core/enumerable/tally_spec.rb
+++ b/spec/ruby/core/enumerable/tally_spec.rb
@@ -1,35 +1,91 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-ruby_version_is "2.7" do
- describe "Enumerable#tally" do
- before :each do
- ScratchPad.record []
- end
-
- it "returns a hash with counts according to the value" do
- enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
- enum.tally.should == { 'foo' => 2, 'bar' => 1, 'baz' => 1}
- end
-
- it "returns a hash without default" do
- hash = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz').tally
- hash.default_proc.should be_nil
- hash.default.should be_nil
- end
-
- it "returns an empty hash for empty enumerables" do
- EnumerableSpecs::Empty.new.tally.should == {}
- end
-
- it "counts values as gathered array when yielded with multiple arguments" do
- EnumerableSpecs::YieldsMixed2.new.tally.should == EnumerableSpecs::YieldsMixed2.gathered_yields.group_by(&:itself).transform_values(&:size)
- end
-
- it "does not call given block" do
- enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
- enum.tally { |v| ScratchPad << v }
- ScratchPad.recorded.should == []
- end
+describe "Enumerable#tally" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "returns a hash with counts according to the value" do
+ enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
+ enum.tally.should == { 'foo' => 2, 'bar' => 1, 'baz' => 1}
+ end
+
+ it "returns a hash without default" do
+ hash = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz').tally
+ hash.default_proc.should == nil
+ hash.default.should == nil
+ end
+
+ it "returns an empty hash for empty enumerables" do
+ EnumerableSpecs::Empty.new.tally.should == {}
+ end
+
+ it "counts values as gathered array when yielded with multiple arguments" do
+ EnumerableSpecs::YieldsMixed2.new.tally.should == EnumerableSpecs::YieldsMixed2.gathered_yields.group_by(&:itself).transform_values(&:size)
+ end
+
+ it "does not call given block" do
+ enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
+ enum.tally { |v| ScratchPad << v }
+ ScratchPad.recorded.should == []
+ end
+end
+
+describe "Enumerable#tally with a hash" do
+ before :each do
+ ScratchPad.record []
+ end
+
+ it "returns a hash with counts according to the value" do
+ enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
+ enum.tally({ 'foo' => 1 }).should == { 'foo' => 3, 'bar' => 1, 'baz' => 1}
+ end
+
+ it "returns the given hash" do
+ enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
+ hash = { 'foo' => 1 }
+ enum.tally(hash).should.equal?(hash)
+ end
+
+ it "calls #to_hash to convert argument to Hash implicitly if passed not a Hash" do
+ enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
+ object = Object.new
+ def object.to_hash; { 'foo' => 1 }; end
+ enum.tally(object).should == { 'foo' => 3, 'bar' => 1, 'baz' => 1}
+ end
+
+ it "raises a FrozenError and does not update the given hash when the hash is frozen" do
+ enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
+ hash = { 'foo' => 1 }.freeze
+ -> { enum.tally(hash) }.should.raise(FrozenError)
+ hash.should == { 'foo' => 1 }
+ end
+
+ it "raises a FrozenError even if enumerable is empty" do
+ enum = EnumerableSpecs::Numerous.new()
+ hash = { 'foo' => 1 }.freeze
+ -> { enum.tally(hash) }.should.raise(FrozenError)
+ end
+
+ it "does not call given block" do
+ enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
+ enum.tally({ 'foo' => 1 }) { |v| ScratchPad << v }
+ ScratchPad.recorded.should == []
+ end
+
+ it "ignores the default value" do
+ enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
+ enum.tally(Hash.new(100)).should == { 'foo' => 2, 'bar' => 1, 'baz' => 1}
+ end
+
+ it "ignores the default proc" do
+ enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
+ enum.tally(Hash.new {100}).should == { 'foo' => 2, 'bar' => 1, 'baz' => 1}
+ end
+
+ it "needs the values counting each elements to be an integer" do
+ enum = EnumerableSpecs::Numerous.new('foo')
+ -> { enum.tally({ 'foo' => 'bar' }) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/enumerable/to_a_spec.rb b/spec/ruby/core/enumerable/to_a_spec.rb
index 0f3060dc48..723f922574 100644
--- a/spec/ruby/core/enumerable/to_a_spec.rb
+++ b/spec/ruby/core/enumerable/to_a_spec.rb
@@ -3,5 +3,5 @@ require_relative 'fixtures/classes'
require_relative 'shared/entries'
describe "Enumerable#to_a" do
- it_behaves_like :enumerable_entries , :to_a
+ it_behaves_like :enumerable_entries, :to_a
end
diff --git a/spec/ruby/core/enumerable/to_h_spec.rb b/spec/ruby/core/enumerable/to_h_spec.rb
index 63bfdf19af..38847eccfb 100644
--- a/spec/ruby/core/enumerable/to_h_spec.rb
+++ b/spec/ruby/core/enumerable/to_h_spec.rb
@@ -36,55 +36,61 @@ describe "Enumerable#to_h" do
it "raises TypeError if an element is not an array" do
enum = EnumerableSpecs::EachDefiner.new(:x)
- -> { enum.to_h }.should raise_error(TypeError)
+ -> { enum.to_h }.should.raise(TypeError)
end
it "raises ArgumentError if an element is not a [key, value] pair" do
enum = EnumerableSpecs::EachDefiner.new([:x])
- -> { enum.to_h }.should raise_error(ArgumentError)
+ -> { enum.to_h }.should.raise(ArgumentError)
end
- ruby_version_is "2.6" do
- context "with block" do
- before do
- @enum = EnumerableSpecs::EachDefiner.new(:a, :b)
- end
-
- it "converts [key, value] pairs returned by the block to a hash" do
- @enum.to_h { |k| [k, k.to_s] }.should == { a: 'a', b: 'b' }
- end
-
- it "raises ArgumentError if block returns longer or shorter array" do
- -> do
- @enum.to_h { |k| [k, k.to_s, 1] }
- end.should raise_error(ArgumentError, /element has wrong array length/)
-
- -> do
- @enum.to_h { |k| [k] }
- end.should raise_error(ArgumentError, /element has wrong array length/)
- end
-
- it "raises TypeError if block returns something other than Array" do
- -> do
- @enum.to_h { |k| "not-array" }
- end.should raise_error(TypeError, /wrong element type String/)
- end
-
- it "coerces returned pair to Array with #to_ary" do
- x = mock('x')
- x.stub!(:to_ary).and_return([:b, 'b'])
-
- @enum.to_h { |k| x }.should == { :b => 'b' }
- end
-
- it "does not coerce returned pair to Array with #to_a" do
- x = mock('x')
- x.stub!(:to_a).and_return([:b, 'b'])
-
- -> do
- @enum.to_h { |k| x }
- end.should raise_error(TypeError, /wrong element type MockObject/)
- end
+ context "with block" do
+ before do
+ @enum = EnumerableSpecs::EachDefiner.new(:a, :b)
+ end
+
+ it "converts [key, value] pairs returned by the block to a hash" do
+ @enum.to_h { |k| [k, k.to_s] }.should == { a: 'a', b: 'b' }
+ end
+
+ it "passes to a block each element as a single argument" do
+ enum_of_arrays = EnumerableSpecs::EachDefiner.new([:a, 1], [:b, 2])
+
+ ScratchPad.record []
+ enum_of_arrays.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
+ ScratchPad.recorded.sort.should == [[[:a, 1]], [[:b, 2]]]
+ end
+
+ it "raises ArgumentError if block returns longer or shorter array" do
+ -> do
+ @enum.to_h { |k| [k, k.to_s, 1] }
+ end.should.raise(ArgumentError, /element has wrong array length/)
+
+ -> do
+ @enum.to_h { |k| [k] }
+ end.should.raise(ArgumentError, /element has wrong array length/)
+ end
+
+ it "raises TypeError if block returns something other than Array" do
+ -> do
+ @enum.to_h { |k| "not-array" }
+ end.should.raise(TypeError, /wrong element type String/)
+ end
+
+ it "coerces returned pair to Array with #to_ary" do
+ x = mock('x')
+ x.stub!(:to_ary).and_return([:b, 'b'])
+
+ @enum.to_h { |k| x }.should == { :b => 'b' }
+ end
+
+ it "does not coerce returned pair to Array with #to_a" do
+ x = mock('x')
+ x.stub!(:to_a).and_return([:b, 'b'])
+
+ -> do
+ @enum.to_h { |k| x }
+ end.should.raise(TypeError, /wrong element type MockObject/)
end
end
end
diff --git a/spec/ruby/core/enumerable/to_set_spec.rb b/spec/ruby/core/enumerable/to_set_spec.rb
new file mode 100644
index 0000000000..7b04c72bce
--- /dev/null
+++ b/spec/ruby/core/enumerable/to_set_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerable#to_set" do
+ it "returns a new Set created from self" do
+ [1, 2, 3].to_set.should == Set[1, 2, 3]
+ {a: 1, b: 2}.to_set.should == Set[[:b, 2], [:a, 1]]
+ end
+
+ it "passes down passed blocks" do
+ [1, 2, 3].to_set { |x| x * x }.should == Set[1, 4, 9]
+ end
+
+ ruby_version_is "4.0"..."4.1" do
+ it "instantiates an object of provided as the first argument set class" do
+ set = nil
+ proc{set = [1, 2, 3].to_set(EnumerableSpecs::SetSubclass)}.should complain(/Enumerable#to_set/)
+ set.should.is_a?(EnumerableSpecs::SetSubclass)
+ set.to_a.sort.should == [1, 2, 3]
+ end
+ end
+
+ ruby_version_is ""..."4.0" do
+ it "instantiates an object of provided as the first argument set class" do
+ set = [1, 2, 3].to_set(EnumerableSpecs::SetSubclass)
+ set.should.is_a?(EnumerableSpecs::SetSubclass)
+ set.to_a.sort.should == [1, 2, 3]
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerable/uniq_spec.rb b/spec/ruby/core/enumerable/uniq_spec.rb
index e58dd36366..a1ed44796f 100644
--- a/spec/ruby/core/enumerable/uniq_spec.rb
+++ b/spec/ruby/core/enumerable/uniq_spec.rb
@@ -31,76 +31,32 @@ describe 'Enumerable#uniq' do
[x, y].to_enum.uniq.should == [x, y]
end
- ruby_version_is '2.7' do
- it "compares elements with matching hash codes with #eql?" do
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
-
- def obj.eql?(o)
- false
- end
-
- obj
- end
-
- a.uniq.should == a
+ it "compares elements with matching hash codes with #eql?" do
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
-
- def obj.eql?(o)
- true
- end
-
- obj
+ def obj.eql?(o)
+ false
end
- a.to_enum.uniq.size.should == 1
+ obj
end
- end
- ruby_version_is ''...'2.7' do
- it "compares elements with matching hash codes with #eql?" do
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
-
- def obj.eql?(o)
- # It's undefined whether the impl does a[0].eql?(a[1]) or
- # a[1].eql?(a[0]) so we taint both.
- taint
- o.taint
- false
- end
-
- obj
- end
-
- a.uniq.should == a
- a[0].should.tainted?
- a[1].should.tainted?
+ a.uniq.should == a
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
- def obj.eql?(o)
- # It's undefined whether the impl does a[0].eql?(a[1]) or
- # a[1].eql?(a[0]) so we taint both.
- taint
- o.taint
- true
- end
-
- obj
+ def obj.eql?(o)
+ true
end
- a.to_enum.uniq.size.should == 1
- a[0].should.tainted?
- a[1].should.tainted?
+ obj
end
+
+ a.to_enum.uniq.size.should == 1
end
context 'when yielded with multiple arguments' do
diff --git a/spec/ruby/core/enumerable/zip_spec.rb b/spec/ruby/core/enumerable/zip_spec.rb
index 9ec15aa030..c5f9a3e4d4 100644
--- a/spec/ruby/core/enumerable/zip_spec.rb
+++ b/spec/ruby/core/enumerable/zip_spec.rb
@@ -38,4 +38,9 @@ describe "Enumerable#zip" do
multi.zip(multi).should == [[[1, 2], [1, 2]], [[3, 4, 5], [3, 4, 5]], [[6, 7, 8, 9], [6, 7, 8, 9]]]
end
+ it "raises TypeError when some argument isn't Array and doesn't respond to #to_ary and #to_enum" do
+ -> { EnumerableSpecs::Numerous.new(1,2,3).zip(Object.new) }.should.raise(TypeError, "wrong argument type Object (must respond to :each)")
+ -> { EnumerableSpecs::Numerous.new(1,2,3).zip(1) }.should.raise(TypeError, "wrong argument type Integer (must respond to :each)")
+ -> { EnumerableSpecs::Numerous.new(1,2,3).zip(true) }.should.raise(TypeError, "wrong argument type TrueClass (must respond to :each)")
+ end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/begin_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/begin_spec.rb
index c8d91ebaec..bd243fa0b5 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/begin_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/begin_spec.rb
@@ -1,11 +1,16 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#begin" do
- it "returns the begin of the sequence" do
- 1.step(10).begin.should == 1
- (1..10).step.begin.should == 1
- (1...10).step.begin.should == 1
+describe "Enumerator::ArithmeticSequence#begin" do
+ it "returns the begin of the sequence" do
+ 1.step(10).begin.should == 1
+ (1..10).step.begin.should == 1
+ (1...10).step.begin.should == 1
+ end
+
+ context "with beginless" do
+ it "returns nil as begin of the sequence" do
+ (..10).step(1).begin.should == nil
+ (...10).step(1).begin.should == nil
end
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/each_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/each_spec.rb
index d7edf3a21f..0a83019d49 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/each_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/each_spec.rb
@@ -1,19 +1,17 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#each" do
- before :each do
- ScratchPad.record []
- @seq = 1.step(10, 4)
- end
+describe "Enumerator::ArithmeticSequence#each" do
+ before :each do
+ ScratchPad.record []
+ @seq = 1.step(10, 4)
+ end
- it "calls given block on each item of the sequence" do
- @seq.each { |item| ScratchPad << item }
- ScratchPad.recorded.should == [1, 5, 9]
- end
+ it "calls given block on each item of the sequence" do
+ @seq.each { |item| ScratchPad << item }
+ ScratchPad.recorded.should == [1, 5, 9]
+ end
- it "returns self" do
- @seq.each { |item| }.should equal(@seq)
- end
+ it "returns self" do
+ @seq.each { |item| }.should.equal?(@seq)
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/end_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/end_spec.rb
index 5a436e8167..05429cac3e 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/end_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/end_spec.rb
@@ -1,11 +1,16 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#end" do
- it "returns the end of the sequence" do
- 1.step(10).end.should == 10
- (1..10).step.end.should == 10
- (1...10).step(17).end.should == 10
+describe "Enumerator::ArithmeticSequence#end" do
+ it "returns the end of the sequence" do
+ 1.step(10).end.should == 10
+ (1..10).step.end.should == 10
+ (1...10).step(17).end.should == 10
+ end
+
+ context "with endless" do
+ it "returns nil as end of the sequence" do
+ (1..).step(1).end.should == nil
+ (1...).step(1).end.should == nil
end
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/eq_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/eq_spec.rb
index 7895f98047..77eed02d8b 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/eq_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/eq_spec.rb
@@ -1,20 +1,18 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#==" do
- it "returns true if begin, end, step and exclude_end? are equal" do
- 1.step(10).should == 1.step(10)
- 1.step(10, 5).should == 1.step(10, 5)
+describe "Enumerator::ArithmeticSequence#==" do
+ it "returns true if begin, end, step and exclude_end? are equal" do
+ 1.step(10).should == 1.step(10)
+ 1.step(10, 5).should == 1.step(10, 5)
- (1..10).step.should == (1..10).step
- (1...10).step(8).should == (1...10).step(8)
+ (1..10).step.should == (1..10).step
+ (1...10).step(8).should == (1...10).step(8)
- # both have exclude_end? == false
- (1..10).step(100).should == 1.step(10, 100)
+ # both have exclude_end? == false
+ (1..10).step(100).should == 1.step(10, 100)
- ((1..10).step == (1..11).step).should == false
- ((1..10).step == (1...10).step).should == false
- ((1..10).step == (1..10).step(2)).should == false
- end
+ ((1..10).step == (1..11).step).should == false
+ ((1..10).step == (1...10).step).should == false
+ ((1..10).step == (1..10).step(2)).should == false
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/exclude_end_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/exclude_end_spec.rb
index 5be189e993..021fe7d90f 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/exclude_end_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/exclude_end_spec.rb
@@ -1,19 +1,17 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#exclude_end?" do
- context "when created using Numeric#step" do
- it "always returns false" do
- 1.step(10).should_not.exclude_end?
- 10.step(1).should_not.exclude_end?
- end
+describe "Enumerator::ArithmeticSequence#exclude_end?" do
+ context "when created using Numeric#step" do
+ it "always returns false" do
+ 1.step(10).should_not.exclude_end?
+ 10.step(1).should_not.exclude_end?
end
+ end
- context "when created using Range#step" do
- it "mirrors range.exclude_end?" do
- (1...10).step.should.exclude_end?
- (1..10).step.should_not.exclude_end?
- end
+ context "when created using Range#step" do
+ it "mirrors range.exclude_end?" do
+ (1...10).step.should.exclude_end?
+ (1..10).step.should_not.exclude_end?
end
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/first_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/first_spec.rb
index 43c520d1f0..ccd02be020 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/first_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/first_spec.rb
@@ -1,11 +1,9 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#first" do
- it "returns the first element of the sequence" do
- 1.step(10).first.should == 1
- (1..10).step.first.should == 1
- (1...10).step.first.should == 1
- end
+describe "Enumerator::ArithmeticSequence#first" do
+ it "returns the first element of the sequence" do
+ 1.step(10).first.should == 1
+ (1..10).step.first.should == 1
+ (1...10).step.first.should == 1
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/hash_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/hash_spec.rb
index 236f845f41..a18c554fb3 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/hash_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/hash_spec.rb
@@ -1,22 +1,20 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#hash" do
- it "is based on begin, end, step and exclude_end?" do
- 1.step(10).hash.should be_an_instance_of(Integer)
+describe "Enumerator::ArithmeticSequence#hash" do
+ it "is based on begin, end, step and exclude_end?" do
+ 1.step(10).hash.should.instance_of?(Integer)
- 1.step(10).hash.should == 1.step(10).hash
- 1.step(10, 5).hash.should == 1.step(10, 5).hash
+ 1.step(10).hash.should == 1.step(10).hash
+ 1.step(10, 5).hash.should == 1.step(10, 5).hash
- (1..10).step.hash.should == (1..10).step.hash
- (1...10).step(8).hash.should == (1...10).step(8).hash
+ (1..10).step.hash.should == (1..10).step.hash
+ (1...10).step(8).hash.should == (1...10).step(8).hash
- # both have exclude_end? == false
- (1..10).step(100).hash.should == 1.step(10, 100).hash
+ # both have exclude_end? == false
+ (1..10).step(100).hash.should == 1.step(10, 100).hash
- ((1..10).step.hash == (1..11).step.hash).should == false
- ((1..10).step.hash == (1...10).step.hash).should == false
- ((1..10).step.hash == (1..10).step(2).hash).should == false
- end
+ ((1..10).step.hash == (1..11).step.hash).should == false
+ ((1..10).step.hash == (1...10).step.hash).should == false
+ ((1..10).step.hash == (1..10).step(2).hash).should == false
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/inspect_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/inspect_spec.rb
index 21e64a6b58..b73b49d272 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/inspect_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/inspect_spec.rb
@@ -1,22 +1,20 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#inspect" do
- context 'when Numeric#step is used' do
- it "returns '(begin.step(end{, step}))'" do
- 1.step(10).inspect.should == "(1.step(10))"
- 1.step(10, 3).inspect.should == "(1.step(10, 3))"
- end
+describe "Enumerator::ArithmeticSequence#inspect" do
+ context 'when Numeric#step is used' do
+ it "returns '(begin.step(end{, step}))'" do
+ 1.step(10).inspect.should == "(1.step(10))"
+ 1.step(10, 3).inspect.should == "(1.step(10, 3))"
end
+ end
- context 'when Range#step is used' do
- it "returns '((range).step{(step)})'" do
- (1..10).step.inspect.should == "((1..10).step)"
- (1..10).step(3).inspect.should == "((1..10).step(3))"
+ context 'when Range#step is used' do
+ it "returns '((range).step{(step)})'" do
+ (1..10).step.inspect.should == "((1..10).step)"
+ (1..10).step(3).inspect.should == "((1..10).step(3))"
- (1...10).step.inspect.should == "((1...10).step)"
- (1...10).step(3).inspect.should == "((1...10).step(3))"
- end
+ (1...10).step.inspect.should == "((1...10).step)"
+ (1...10).step(3).inspect.should == "((1...10).step(3))"
end
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/last_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/last_spec.rb
index ebb20090fc..31f982b7c4 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/last_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/last_spec.rb
@@ -1,11 +1,9 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#last" do
- it "returns the last element of the sequence" do
- 1.step(10).last.should == 10
- (1..10).step.last.should == 10
- (1...10).step(4).last.should == 9
- end
+describe "Enumerator::ArithmeticSequence#last" do
+ it "returns the last element of the sequence" do
+ 1.step(10).last.should == 10
+ (1..10).step.last.should == 10
+ (1...10).step(4).last.should == 9
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/new_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/new_spec.rb
index 7227581fb9..1bd2f7f0f7 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/new_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/new_spec.rb
@@ -1,19 +1,17 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence.new" do
- it "is not defined" do
- -> {
- Enumerator::ArithmeticSequence.new
- }.should raise_error(NoMethodError)
- end
+describe "Enumerator::ArithmeticSequence.new" do
+ it "is not defined" do
+ -> {
+ Enumerator::ArithmeticSequence.new
+ }.should.raise(NoMethodError)
end
+end
- describe "Enumerator::ArithmeticSequence.allocate" do
- it "is not defined" do
- -> {
- Enumerator::ArithmeticSequence.allocate
- }.should raise_error(TypeError, 'allocator undefined for Enumerator::ArithmeticSequence')
- end
+describe "Enumerator::ArithmeticSequence.allocate" do
+ it "is not defined" do
+ -> {
+ Enumerator::ArithmeticSequence.allocate
+ }.should.raise(TypeError, 'allocator undefined for Enumerator::ArithmeticSequence')
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/size_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/size_spec.rb
index 00403b0238..7e03edd961 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/size_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/size_spec.rb
@@ -1,19 +1,17 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#size" do
- context "for finite sequence" do
- it "returns the number of elements in this arithmetic sequence" do
- 1.step(10).size.should == 10
- (1...10).step.size.should == 9
- end
+describe "Enumerator::ArithmeticSequence#size" do
+ context "for finite sequence" do
+ it "returns the number of elements in this arithmetic sequence" do
+ 1.step(10).size.should == 10
+ (1...10).step.size.should == 9
end
+ end
- context "for infinite sequence" do
- it "returns Infinity" do
- 1.step(Float::INFINITY).size.should == Float::INFINITY
- (1..Float::INFINITY).step.size.should == Float::INFINITY
- end
+ context "for infinite sequence" do
+ it "returns Infinity" do
+ 1.step(Float::INFINITY).size.should == Float::INFINITY
+ (1..Float::INFINITY).step.size.should == Float::INFINITY
end
end
end
diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/step_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/step_spec.rb
index 20a5cb6e7b..c1f2d9173f 100644
--- a/spec/ruby/core/enumerator/arithmetic_sequence/step_spec.rb
+++ b/spec/ruby/core/enumerator/arithmetic_sequence/step_spec.rb
@@ -1,15 +1,11 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::ArithmeticSequence#step" do
- it "returns the original value given to step method" do
- (1..10).step.step.should == 1
- (1..10).step(3).step.should == 3
- (1..10).step(0).step.should == 0
+describe "Enumerator::ArithmeticSequence#step" do
+ it "returns the original value given to step method" do
+ (1..10).step.step.should == 1
+ (1..10).step(3).step.should == 3
- 1.step(10).step.should == 1
- 1.step(10, 3).step.should == 3
- 1.step(10, 0).step.should == 0
- end
+ 1.step(10).step.should == 1
+ 1.step(10, 3).step.should == 3
end
end
diff --git a/spec/ruby/core/enumerator/chain/each_spec.rb b/spec/ruby/core/enumerator/chain/each_spec.rb
index ab4d355f22..cc93cbac60 100644
--- a/spec/ruby/core/enumerator/chain/each_spec.rb
+++ b/spec/ruby/core/enumerator/chain/each_spec.rb
@@ -1,17 +1,15 @@
require_relative '../../../spec_helper'
require_relative '../../enumerable/fixtures/classes'
-ruby_version_is "2.6" do
- describe "Enumerator::Chain#each" do
- it "calls each on its constituents as needed" do
- a = EnumerableSpecs::EachCounter.new(:a, :b)
- b = EnumerableSpecs::EachCounter.new(:c, :d)
+describe "Enumerator::Chain#each" do
+ it "calls each on its constituents as needed" do
+ a = EnumerableSpecs::EachCounter.new(:a, :b)
+ b = EnumerableSpecs::EachCounter.new(:c, :d)
- ScratchPad.record []
- Enumerator::Chain.new(a, b).each do |elem|
- ScratchPad << elem << b.times_yielded
- end
- ScratchPad.recorded.should == [:a, 0, :b, 0, :c, 1, :d, 2]
+ ScratchPad.record []
+ Enumerator::Chain.new(a, b).each do |elem|
+ ScratchPad << elem << b.times_yielded
end
+ ScratchPad.recorded.should == [:a, 0, :b, 0, :c, 1, :d, 2]
end
end
diff --git a/spec/ruby/core/enumerator/chain/initialize_spec.rb b/spec/ruby/core/enumerator/chain/initialize_spec.rb
index e5aa32fd02..1df1dec5f8 100644
--- a/spec/ruby/core/enumerator/chain/initialize_spec.rb
+++ b/spec/ruby/core/enumerator/chain/initialize_spec.rb
@@ -1,33 +1,31 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::Chain#initialize" do
- before :each do
- @uninitialized = Enumerator::Chain.allocate
- end
+describe "Enumerator::Chain#initialize" do
+ before :each do
+ @uninitialized = Enumerator::Chain.allocate
+ end
- it "is a private method" do
- Enumerator::Chain.should have_private_instance_method(:initialize, false)
- end
+ it "is a private method" do
+ Enumerator::Chain.private_instance_methods(false).should.include?(:initialize)
+ end
- it "returns self" do
- @uninitialized.send(:initialize).should equal(@uninitialized)
- end
+ it "returns self" do
+ @uninitialized.send(:initialize).should.equal?(@uninitialized)
+ end
- it "accepts many arguments" do
- @uninitialized.send(:initialize, 0..1, 2..3, 4..5).should equal(@uninitialized)
- end
+ it "accepts many arguments" do
+ @uninitialized.send(:initialize, 0..1, 2..3, 4..5).should.equal?(@uninitialized)
+ end
- it "accepts arguments that are not Enumerable nor responding to :each" do
- @uninitialized.send(:initialize, Object.new).should equal(@uninitialized)
- end
+ it "accepts arguments that are not Enumerable nor responding to :each" do
+ @uninitialized.send(:initialize, Object.new).should.equal?(@uninitialized)
+ end
- describe "on frozen instance" do
- it "raises a RuntimeError" do
- -> {
- @uninitialized.freeze.send(:initialize)
- }.should raise_error(RuntimeError)
- end
+ describe "on frozen instance" do
+ it "raises a FrozenError" do
+ -> {
+ @uninitialized.freeze.send(:initialize)
+ }.should.raise(FrozenError)
end
end
end
diff --git a/spec/ruby/core/enumerator/chain/inspect_spec.rb b/spec/ruby/core/enumerator/chain/inspect_spec.rb
index a644d88c6f..9b5a442b75 100644
--- a/spec/ruby/core/enumerator/chain/inspect_spec.rb
+++ b/spec/ruby/core/enumerator/chain/inspect_spec.rb
@@ -1,16 +1,18 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::Chain#inspect" do
- it "shows a representation of the Enumerator" do
- Enumerator::Chain.new.inspect.should == "#<Enumerator::Chain: []>"
- Enumerator::Chain.new(1..2, 3..4).inspect.should == "#<Enumerator::Chain: [1..2, 3..4]>"
- end
+describe "Enumerator::Chain#inspect" do
+ it "shows a representation of the Enumerator" do
+ Enumerator::Chain.new.inspect.should == "#<Enumerator::Chain: []>"
+ Enumerator::Chain.new(1..2, 3..4).inspect.should == "#<Enumerator::Chain: [1..2, 3..4]>"
+ end
+
+ it "calls inspect on its chain elements" do
+ obj = mock('inspect')
+ obj.should_receive(:inspect).and_return('some desc')
+ Enumerator::Chain.new(obj).inspect.should == "#<Enumerator::Chain: [some desc]>"
+ end
- it "calls inspect on its chain elements" do
- obj = mock('inspect')
- obj.should_receive(:inspect).and_return('some desc')
- Enumerator::Chain.new(obj).inspect.should == "#<Enumerator::Chain: [some desc]>"
- end
+ it "returns a not initialized representation if #initialized is not called yet" do
+ Enumerator::Chain.allocate.inspect.should == "#<Enumerator::Chain: uninitialized>"
end
end
diff --git a/spec/ruby/core/enumerator/chain/rewind_spec.rb b/spec/ruby/core/enumerator/chain/rewind_spec.rb
index 951b364f07..4525b82f7b 100644
--- a/spec/ruby/core/enumerator/chain/rewind_spec.rb
+++ b/spec/ruby/core/enumerator/chain/rewind_spec.rb
@@ -1,53 +1,51 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator::Chain#rewind" do
- before(:each) do
- @obj = mock('obj')
- @obj.should_receive(:each).any_number_of_times.and_yield
- @second = mock('obj')
- @second.should_receive(:each).any_number_of_times.and_yield
- @enum = Enumerator::Chain.new(@obj, @second)
- end
-
- it "returns self" do
- @enum.rewind.should equal @enum
- end
-
- it "does nothing if receiver has not been iterated" do
- @obj.should_not_receive(:rewind)
- @obj.respond_to?(:rewind).should == true # sanity check
- @enum.rewind
- end
-
- it "does nothing on objects that don't respond_to rewind" do
- @obj.respond_to?(:rewind).should == false # sanity check
- @enum.each {}
- @enum.rewind
- end
-
- it "calls_rewind its objects" do
- @obj.should_receive(:rewind)
- @enum.each {}
- @enum.rewind
- end
-
- it "calls_rewind in reverse order" do
- @obj.should_not_receive(:rewind)
- @second.should_receive(:rewind).and_raise(RuntimeError)
- @enum.each {}
- -> { @enum.rewind }.should raise_error(RuntimeError)
- end
-
- it "calls rewind only for objects that have actually been iterated on" do
- @obj = mock('obj')
- @obj.should_receive(:each).any_number_of_times.and_raise(RuntimeError)
- @enum = Enumerator::Chain.new(@obj, @second)
-
- @obj.should_receive(:rewind)
- @second.should_not_receive(:rewind)
- -> { @enum.each {} }.should raise_error(RuntimeError)
- @enum.rewind
- end
+describe "Enumerator::Chain#rewind" do
+ before(:each) do
+ @obj = mock('obj')
+ @obj.should_receive(:each).any_number_of_times.and_yield
+ @second = mock('obj')
+ @second.should_receive(:each).any_number_of_times.and_yield
+ @enum = Enumerator::Chain.new(@obj, @second)
+ end
+
+ it "returns self" do
+ @enum.rewind.should.equal? @enum
+ end
+
+ it "does nothing if receiver has not been iterated" do
+ @obj.should_not_receive(:rewind)
+ @obj.respond_to?(:rewind).should == true # sanity check
+ @enum.rewind
+ end
+
+ it "does nothing on objects that don't respond_to rewind" do
+ @obj.respond_to?(:rewind).should == false # sanity check
+ @enum.each {}
+ @enum.rewind
+ end
+
+ it "calls_rewind its objects" do
+ @obj.should_receive(:rewind)
+ @enum.each {}
+ @enum.rewind
+ end
+
+ it "calls_rewind in reverse order" do
+ @obj.should_not_receive(:rewind)
+ @second.should_receive(:rewind).and_raise(RuntimeError)
+ @enum.each {}
+ -> { @enum.rewind }.should.raise(RuntimeError)
+ end
+
+ it "calls rewind only for objects that have actually been iterated on" do
+ @obj = mock('obj')
+ @obj.should_receive(:each).any_number_of_times.and_raise(RuntimeError)
+ @enum = Enumerator::Chain.new(@obj, @second)
+
+ @obj.should_receive(:rewind)
+ @second.should_not_receive(:rewind)
+ -> { @enum.each {} }.should.raise(RuntimeError)
+ @enum.rewind
end
end
diff --git a/spec/ruby/core/enumerator/chain/size_spec.rb b/spec/ruby/core/enumerator/chain/size_spec.rb
index 42c31ac10b..d85b88ee8b 100644
--- a/spec/ruby/core/enumerator/chain/size_spec.rb
+++ b/spec/ruby/core/enumerator/chain/size_spec.rb
@@ -1,24 +1,22 @@
require_relative '../../../spec_helper'
require_relative '../../enumerable/fixtures/classes'
-ruby_version_is "2.6" do
- describe "Enumerator::Chain#size" do
- it "returns the sum of the sizes of the elements" do
+describe "Enumerator::Chain#size" do
+ it "returns the sum of the sizes of the elements" do
+ a = mock('size')
+ a.should_receive(:size).and_return(40)
+ Enumerator::Chain.new(a, [:a, :b]).size.should == 42
+ end
+
+ it "returns nil or Infinity for the first element of such a size" do
+ [nil, Float::INFINITY].each do |special|
a = mock('size')
a.should_receive(:size).and_return(40)
- Enumerator::Chain.new(a, [:a, :b]).size.should == 42
- end
-
- it "returns nil or Infinity for the first element of such a size" do
- [nil, Float::INFINITY].each do |special|
- a = mock('size')
- a.should_receive(:size).and_return(40)
- b = mock('special')
- b.should_receive(:size).and_return(special)
- c = mock('not called')
- c.should_not_receive(:size)
- Enumerator::Chain.new(a, b, c).size.should == special
- end
+ b = mock('special')
+ b.should_receive(:size).and_return(special)
+ c = mock('not called')
+ c.should_not_receive(:size)
+ Enumerator::Chain.new(a, b, c).size.should == special
end
end
end
diff --git a/spec/ruby/core/enumerator/each_spec.rb b/spec/ruby/core/enumerator/each_spec.rb
index 99ac3120af..03be53fe05 100644
--- a/spec/ruby/core/enumerator/each_spec.rb
+++ b/spec/ruby/core/enumerator/each_spec.rb
@@ -10,58 +10,58 @@ describe "Enumerator#each" do
@enum_with_arguments = object_each_with_arguments.to_enum(:each_with_arguments, :arg0, :arg1, :arg2)
- @enum_with_yielder = Enumerator.new {|y| y.yield :ok}
+ @enum_with_yielder = Enumerator.new { |y| y.yield :ok }
end
it "yields each element of self to the given block" do
acc = []
- [1,2,3].to_enum.each {|e| acc << e }
- acc.should == [1,2,3]
+ [1, 2, 3].to_enum.each { |e| acc << e }
+ acc.should == [1, 2, 3]
end
it "calls #each on the object given in the constructor by default" do
each = mock('each')
each.should_receive(:each)
- each.to_enum.each {|e| e }
+ each.to_enum.each { |e| e }
end
it "calls #each on the underlying object until it's exhausted" do
each = mock('each')
each.should_receive(:each).and_yield(1).and_yield(2).and_yield(3)
acc = []
- each.to_enum.each {|e| acc << e }
- acc.should == [1,2,3]
+ each.to_enum.each { |e| acc << e }
+ acc.should == [1, 2, 3]
end
it "calls the method given in the constructor instead of #each" do
each = mock('peach')
each.should_receive(:peach)
- each.to_enum(:peach).each {|e| e }
+ each.to_enum(:peach).each { |e| e }
end
it "calls the method given in the constructor until it's exhausted" do
each = mock('peach')
each.should_receive(:peach).and_yield(1).and_yield(2).and_yield(3)
acc = []
- each.to_enum(:peach).each {|e| acc << e }
- acc.should == [1,2,3]
+ each.to_enum(:peach).each { |e| acc << e }
+ acc.should == [1, 2, 3]
end
it "raises a NoMethodError if the object doesn't respond to #each" do
enum = Object.new.to_enum
-> do
enum.each { |e| e }
- end.should raise_error(NoMethodError)
+ end.should.raise(NoMethodError)
end
it "returns self if not given arguments and not given a block" do
- @enum_with_arguments.each.should equal(@enum_with_arguments)
+ @enum_with_arguments.each.should.equal?(@enum_with_arguments)
- @enum_with_yielder.each.should equal(@enum_with_yielder)
+ @enum_with_yielder.each.should.equal?(@enum_with_yielder)
end
it "returns the same value from receiver.each if block is given" do
- @enum_with_arguments.each {}.should equal(:method_returned)
+ @enum_with_arguments.each {}.should.equal?(:method_returned)
end
it "passes given arguments at initialized to receiver.each" do
@@ -78,12 +78,27 @@ describe "Enumerator#each" do
end
it "returns the same value from receiver.each if block and arguments are given" do
- @enum_with_arguments.each(:each1, :each2) {}.should equal(:method_returned)
+ @enum_with_arguments.each(:each1, :each2) {}.should.equal?(:method_returned)
end
it "returns new Enumerator if given arguments but not given a block" do
ret = @enum_with_arguments.each 1
- ret.should be_an_instance_of(Enumerator)
- ret.should_not equal(@enum_with_arguments)
+ ret.should.instance_of?(Enumerator)
+ ret.should_not.equal?(@enum_with_arguments)
+ end
+
+ it "does not destructure yielded array values when chaining each.map" do
+ result = [[[1]]].each.map { |a, b| [a, b] }
+ result.should == [[[1], nil]]
+ end
+
+ it "preserves array values yielded from the enumerator" do
+ result = [[1, 2]].each.map { |a| a }
+ result.should == [[1, 2]]
+ end
+
+ it "allows destructuring to occur in the block, not the enumerator" do
+ result = [[1, 2]].each.map { |a, b| a }
+ result.should == [1]
end
end
diff --git a/spec/ruby/core/enumerator/each_with_index_spec.rb b/spec/ruby/core/enumerator/each_with_index_spec.rb
index 96e53a2804..0630b7045e 100644
--- a/spec/ruby/core/enumerator/each_with_index_spec.rb
+++ b/spec/ruby/core/enumerator/each_with_index_spec.rb
@@ -1,5 +1,5 @@
require_relative '../../spec_helper'
-require_relative '../../shared/enumerator/with_index'
+require_relative 'shared/with_index'
require_relative '../enumerable/shared/enumeratorized'
describe "Enumerator#each_with_index" do
@@ -9,28 +9,28 @@ describe "Enumerator#each_with_index" do
it "returns a new Enumerator when no block is given" do
enum1 = [1,2,3].select
enum2 = enum1.each_with_index
- enum2.should be_an_instance_of(Enumerator)
+ enum2.should.instance_of?(Enumerator)
enum1.should_not == enum2
end
it "raises an ArgumentError if passed extra arguments" do
-> do
[1].to_enum.each_with_index(:glark)
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
end
it "passes on the given block's return value" do
arr = [1,2,3]
- arr.delete_if.with_index { |a,b| false }
+ arr.delete_if.each_with_index { |a,b| false }
arr.should == [1,2,3]
end
it "returns the iterator's return value" do
- [1,2,3].select.with_index { |a,b| false }.should == []
+ [1,2,3].select.each_with_index { |a,b| false }.should == []
+ [1,2,3].select.each_with_index { |a,b| true }.should == [1,2,3]
end
it "returns the correct value if chained with itself" do
[:a].each_with_index.each_with_index.to_a.should == [[[:a,0],0]]
- [:a].each.with_index.with_index.to_a.should == [[[:a,0],0]]
end
end
diff --git a/spec/ruby/core/enumerator/each_with_object_spec.rb b/spec/ruby/core/enumerator/each_with_object_spec.rb
index 68524dc74a..84a45ae89d 100644
--- a/spec/ruby/core/enumerator/each_with_object_spec.rb
+++ b/spec/ruby/core/enumerator/each_with_object_spec.rb
@@ -1,5 +1,5 @@
require_relative '../../spec_helper'
-require_relative '../../shared/enumerator/with_object'
+require_relative 'shared/with_object'
describe "Enumerator#each_with_object" do
it_behaves_like :enum_with_object, :each_with_object
diff --git a/spec/ruby/core/enumerator/enum_for_spec.rb b/spec/ruby/core/enumerator/enum_for_spec.rb
index fd33f463bf..fbdf98545a 100644
--- a/spec/ruby/core/enumerator/enum_for_spec.rb
+++ b/spec/ruby/core/enumerator/enum_for_spec.rb
@@ -1,5 +1,5 @@
require_relative '../../spec_helper'
-require_relative '../../shared/enumerator/enum_for'
+require_relative 'shared/enum_for'
describe "Enumerator#enum_for" do
it_behaves_like :enum_for, :enum_for
diff --git a/spec/ruby/core/enumerator/feed_spec.rb b/spec/ruby/core/enumerator/feed_spec.rb
index e387c6cd39..781947a8c7 100644
--- a/spec/ruby/core/enumerator/feed_spec.rb
+++ b/spec/ruby/core/enumerator/feed_spec.rb
@@ -33,20 +33,20 @@ describe "Enumerator#feed" do
end
it "returns nil" do
- @enum.feed(:a).should be_nil
+ @enum.feed(:a).should == nil
end
it "raises a TypeError if called more than once without advancing the enumerator" do
@enum.feed :a
@enum.next
- -> { @enum.feed :b }.should raise_error(TypeError)
+ -> { @enum.feed :b }.should.raise(TypeError)
end
it "sets the return value of Yielder#yield" do
enum = Enumerator.new { |y| ScratchPad << y.yield }
enum.next
enum.feed :a
- -> { enum.next }.should raise_error(StopIteration)
+ -> { enum.next }.should.raise(StopIteration)
ScratchPad.recorded.should == [:a]
end
end
diff --git a/spec/ruby/fixtures/enumerator/classes.rb b/spec/ruby/core/enumerator/fixtures/classes.rb
index 6f285b8efa..6f285b8efa 100644
--- a/spec/ruby/fixtures/enumerator/classes.rb
+++ b/spec/ruby/core/enumerator/fixtures/classes.rb
diff --git a/spec/ruby/core/enumerator/generator/each_spec.rb b/spec/ruby/core/enumerator/generator/each_spec.rb
deleted file mode 100644
index a43805dd16..0000000000
--- a/spec/ruby/core/enumerator/generator/each_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require_relative '../../../spec_helper'
-
-describe "Enumerator::Generator#each" do
- before :each do
- @generator = Enumerator::Generator.new do |y, *args|
- y << 3 << 2 << 1
- y << args unless args.empty?
- :block_returned
- end
- end
-
- it "is an enumerable" do
- @generator.should be_kind_of(Enumerable)
- end
-
- it "supports enumeration with a block" do
- r = []
- @generator.each { |v| r << v }
-
- r.should == [3, 2, 1]
- end
-
- it "raises a LocalJumpError if no block given" do
- -> { @generator.each }.should raise_error(LocalJumpError)
- end
-
- it "returns the block returned value" do
- @generator.each {}.should equal(:block_returned)
- end
-
- it "requires multiple arguments" do
- Enumerator::Generator.instance_method(:each).arity.should < 0
- end
-
- it "appends given arguments to receiver.each" do
- yields = []
- @generator.each(:each0, :each1) { |yielded| yields << yielded }
- yields.should == [3, 2, 1, [:each0, :each1]]
- end
-end
diff --git a/spec/ruby/core/enumerator/generator/initialize_spec.rb b/spec/ruby/core/enumerator/generator/initialize_spec.rb
deleted file mode 100644
index f75c7d6f26..0000000000
--- a/spec/ruby/core/enumerator/generator/initialize_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- encoding: us-ascii -*-
-
-require_relative '../../../spec_helper'
-
-describe "Enumerator::Generator#initialize" do
- before :each do
- @class = Enumerator::Generator
- @uninitialized = @class.allocate
- end
-
- it "is a private method" do
- @class.should have_private_instance_method(:initialize, false)
- end
-
- it "returns self when given a block" do
- @uninitialized.send(:initialize) {}.should equal(@uninitialized)
- end
-
- describe "on frozen instance" do
- it "raises a RuntimeError" do
- -> {
- @uninitialized.freeze.send(:initialize) {}
- }.should raise_error(RuntimeError)
- end
- end
-end
diff --git a/spec/ruby/core/enumerator/initialize_spec.rb b/spec/ruby/core/enumerator/initialize_spec.rb
index 53bf5d4ef2..9929494b5a 100644
--- a/spec/ruby/core/enumerator/initialize_spec.rb
+++ b/spec/ruby/core/enumerator/initialize_spec.rb
@@ -8,15 +8,11 @@ describe "Enumerator#initialize" do
end
it "is a private method" do
- Enumerator.should have_private_instance_method(:initialize, false)
- end
-
- it "returns self when given an object" do
- @uninitialized.send(:initialize, Object.new).should equal(@uninitialized)
+ Enumerator.private_instance_methods(false).should.include?(:initialize)
end
it "returns self when given a block" do
- @uninitialized.send(:initialize) {}.should equal(@uninitialized)
+ @uninitialized.send(:initialize) {}.should.equal?(@uninitialized)
end
# Maybe spec should be broken up?
@@ -25,25 +21,25 @@ describe "Enumerator#initialize" do
r = yielder.yield 3
yielder << r << 2 << 1
end
- @uninitialized.should be_an_instance_of(Enumerator)
+ @uninitialized.should.instance_of?(Enumerator)
r = []
@uninitialized.each{|x| r << x; x * 2}
r.should == [3, 6, 2, 1]
end
it "sets size to nil if size is not given" do
- @uninitialized.send(:initialize) {}.size.should be_nil
+ @uninitialized.send(:initialize) {}.size.should == nil
end
it "sets size to nil if the given size is nil" do
- @uninitialized.send(:initialize, nil) {}.size.should be_nil
+ @uninitialized.send(:initialize, nil) {}.size.should == nil
end
it "sets size to the given size if the given size is Float::INFINITY" do
- @uninitialized.send(:initialize, Float::INFINITY) {}.size.should equal(Float::INFINITY)
+ @uninitialized.send(:initialize, Float::INFINITY) {}.size.should.equal?(Float::INFINITY)
end
- it "sets size to the given size if the given size is a Fixnum" do
+ it "sets size to the given size if the given size is an Integer" do
@uninitialized.send(:initialize, 100) {}.size.should == 100
end
@@ -52,10 +48,10 @@ describe "Enumerator#initialize" do
end
describe "on frozen instance" do
- it "raises a RuntimeError" do
+ it "raises a FrozenError" do
-> {
@uninitialized.freeze.send(:initialize) {}
- }.should raise_error(RuntimeError)
+ }.should.raise(FrozenError)
end
end
end
diff --git a/spec/ruby/core/enumerator/inspect_spec.rb b/spec/ruby/core/enumerator/inspect_spec.rb
index 3bcf07e754..7e97864246 100644
--- a/spec/ruby/core/enumerator/inspect_spec.rb
+++ b/spec/ruby/core/enumerator/inspect_spec.rb
@@ -14,4 +14,9 @@ describe "Enumerator#inspect" do
(1..3).each.each_slice(2).inspect.should == "#<Enumerator: #<Enumerator: 1..3:each>:each_slice(2)>"
end
end
+
+ it "returns a not initialized representation if #initialized is not called yet" do
+ Enumerator.allocate.inspect.should == "#<Enumerator: uninitialized>"
+ Enumerator::Lazy.allocate.inspect.should == "#<Enumerator::Lazy: uninitialized>"
+ end
end
diff --git a/spec/ruby/core/enumerator/lazy/chunk_spec.rb b/spec/ruby/core/enumerator/lazy/chunk_spec.rb
index 87d2b0c206..d0179d32b6 100644
--- a/spec/ruby/core/enumerator/lazy/chunk_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/chunk_spec.rb
@@ -17,8 +17,8 @@ describe "Enumerator::Lazy#chunk" do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.chunk {}
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "sets #size to nil" do
@@ -27,7 +27,7 @@ describe "Enumerator::Lazy#chunk" do
it "returns an Enumerator if called without a block" do
chunk = @yieldsmixed.chunk
- chunk.should be_an_instance_of(Enumerator::Lazy)
+ chunk.should.instance_of?(Enumerator::Lazy)
res = chunk.each { |v| true }.force
res.should == [[true, EnumeratorLazySpecs::YieldsMixed.gathered_yields]]
diff --git a/spec/ruby/core/enumerator/lazy/chunk_while_spec.rb b/spec/ruby/core/enumerator/lazy/chunk_while_spec.rb
index d555089872..edba8e1363 100644
--- a/spec/ruby/core/enumerator/lazy/chunk_while_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/chunk_while_spec.rb
@@ -6,4 +6,9 @@ describe "Enumerator::Lazy#chunk_while" do
s.lazy.chunk_while { |a, b| false }.first(100).should ==
s.first(100).chunk_while { |a, b| false }.to_a
end
+
+ it "should return a lazy enumerator" do
+ s = 0..Float::INFINITY
+ s.lazy.chunk_while { |a, b| false }.should.is_a?(Enumerator::Lazy)
+ end
end
diff --git a/spec/ruby/core/enumerator/lazy/compact_spec.rb b/spec/ruby/core/enumerator/lazy/compact_spec.rb
new file mode 100644
index 0000000000..7305e1c9c4
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/compact_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#compact" do
+ it 'returns array without nil elements' do
+ arr = [1, nil, 3, false, 5].to_enum.lazy.compact
+ arr.should.instance_of?(Enumerator::Lazy)
+ arr.force.should == [1, 3, false, 5]
+ end
+
+ it "sets #size to nil" do
+ Enumerator::Lazy.new(Object.new, 100) {}.compact.size.should == nil
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/drop_spec.rb b/spec/ruby/core/enumerator/lazy/drop_spec.rb
index 822b8034fb..95ac7e9ecc 100644
--- a/spec/ruby/core/enumerator/lazy/drop_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/drop_spec.rb
@@ -16,8 +16,8 @@ describe "Enumerator::Lazy#drop" do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.drop(1)
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "sets difference of given count with old size to new size" do
diff --git a/spec/ruby/core/enumerator/lazy/drop_while_spec.rb b/spec/ruby/core/enumerator/lazy/drop_while_spec.rb
index 4f6e366f88..65f3007dec 100644
--- a/spec/ruby/core/enumerator/lazy/drop_while_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/drop_while_spec.rb
@@ -16,8 +16,8 @@ describe "Enumerator::Lazy#drop_while" do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.drop_while {}
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "sets #size to nil" do
@@ -40,7 +40,7 @@ describe "Enumerator::Lazy#drop_while" do
end
it "raises an ArgumentError when not given a block" do
- -> { @yieldsmixed.drop_while }.should raise_error(ArgumentError)
+ -> { @yieldsmixed.drop_while }.should.raise(ArgumentError)
end
describe "on a nested Lazy" do
diff --git a/spec/ruby/core/enumerator/lazy/eager_spec.rb b/spec/ruby/core/enumerator/lazy/eager_spec.rb
index 30ba2dfe0e..592da4fd8c 100644
--- a/spec/ruby/core/enumerator/lazy/eager_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/eager_spec.rb
@@ -1,29 +1,27 @@
require_relative '../../../spec_helper'
-ruby_version_is "2.7" do
- describe "Enumerator::Lazy#eager" do
- it "returns a non-lazy Enumerator converted from the lazy enumerator" do
- enum = [1, 2, 3].lazy
+describe "Enumerator::Lazy#eager" do
+ it "returns a non-lazy Enumerator converted from the lazy enumerator" do
+ enum = [1, 2, 3].lazy
- enum.class.should == Enumerator::Lazy
- enum.eager.class.should == Enumerator
- end
+ enum.class.should == Enumerator::Lazy
+ enum.eager.class.should == Enumerator
+ end
- it "does not enumerate an enumerator" do
- ScratchPad.record []
+ it "does not enumerate an enumerator" do
+ ScratchPad.record []
- sequence = [1, 2, 3]
- enum_lazy = Enumerator::Lazy.new(sequence) do |yielder, value|
- yielder << value
- ScratchPad << value
- end
+ sequence = [1, 2, 3]
+ enum_lazy = Enumerator::Lazy.new(sequence) do |yielder, value|
+ yielder << value
+ ScratchPad << value
+ end
- ScratchPad.recorded.should == []
- enum = enum_lazy.eager
- ScratchPad.recorded.should == []
+ ScratchPad.recorded.should == []
+ enum = enum_lazy.eager
+ ScratchPad.recorded.should == []
- enum.map { |i| i }.should == [1, 2, 3]
- ScratchPad.recorded.should == [1, 2, 3]
- end
+ enum.map { |i| i }.should == [1, 2, 3]
+ ScratchPad.recorded.should == [1, 2, 3]
end
end
diff --git a/spec/ruby/core/enumerator/lazy/filter_map_spec.rb b/spec/ruby/core/enumerator/lazy/filter_map_spec.rb
new file mode 100644
index 0000000000..99dd59cebe
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/filter_map_spec.rb
@@ -0,0 +1,14 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#filter_map" do
+ it "maps only truthy results" do
+ (1..Float::INFINITY).lazy.filter_map { |i| i if i.odd? }.first(4).should == [1, 3, 5, 7]
+ end
+
+ it "does not map false results" do
+ (1..Float::INFINITY).lazy.filter_map { |i| i.odd? ? i : false }.first(4).should == [1, 3, 5, 7]
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/filter_spec.rb b/spec/ruby/core/enumerator/lazy/filter_spec.rb
index 2ababa69cc..43128241e0 100644
--- a/spec/ruby/core/enumerator/lazy/filter_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/filter_spec.rb
@@ -1,8 +1,6 @@
require_relative '../../../spec_helper'
require_relative 'shared/select'
-ruby_version_is "2.6" do
- describe "Enumerator::Lazy#filter" do
- it_behaves_like :enumerator_lazy_select, :filter
- end
+describe "Enumerator::Lazy#filter" do
+ it_behaves_like :enumerator_lazy_select, :filter
end
diff --git a/spec/ruby/core/enumerator/lazy/grep_spec.rb b/spec/ruby/core/enumerator/lazy/grep_spec.rb
index e67686c9a3..383f80a918 100644
--- a/spec/ruby/core/enumerator/lazy/grep_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/grep_spec.rb
@@ -20,12 +20,12 @@ describe "Enumerator::Lazy#grep" do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.grep(Object) {}
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
ret = @yieldsmixed.grep(Object)
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "sets #size to nil" do
diff --git a/spec/ruby/core/enumerator/lazy/grep_v_spec.rb b/spec/ruby/core/enumerator/lazy/grep_v_spec.rb
index 67173021bb..19c917f254 100644
--- a/spec/ruby/core/enumerator/lazy/grep_v_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/grep_v_spec.rb
@@ -18,12 +18,12 @@ describe "Enumerator::Lazy#grep_v" do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.grep_v(Object) {}
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
ret = @yieldsmixed.grep_v(Object)
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "sets #size to nil" do
diff --git a/spec/ruby/core/enumerator/lazy/initialize_spec.rb b/spec/ruby/core/enumerator/lazy/initialize_spec.rb
index 88c66530b9..47765d32c3 100644
--- a/spec/ruby/core/enumerator/lazy/initialize_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/initialize_spec.rb
@@ -16,11 +16,11 @@ describe "Enumerator::Lazy#initialize" do
end
it "is a private method" do
- Enumerator::Lazy.should have_private_instance_method(:initialize, false)
+ Enumerator::Lazy.private_instance_methods(false).should.include?(:initialize)
end
it "returns self" do
- @uninitialized.send(:initialize, @receiver) {}.should equal(@uninitialized)
+ @uninitialized.send(:initialize, @receiver) {}.should.equal?(@uninitialized)
end
describe "when the returned lazy enumerator is evaluated by Enumerable#first" do
@@ -32,18 +32,18 @@ describe "Enumerator::Lazy#initialize" do
end
it "sets #size to nil if not given a size" do
- @uninitialized.send(:initialize, @receiver) {}.size.should be_nil
+ @uninitialized.send(:initialize, @receiver) {}.size.should == nil
end
it "sets #size to nil if given size is nil" do
- @uninitialized.send(:initialize, @receiver, nil) {}.size.should be_nil
+ @uninitialized.send(:initialize, @receiver, nil) {}.size.should == nil
end
it "sets given size to own size if the given size is Float::INFINITY" do
- @uninitialized.send(:initialize, @receiver, Float::INFINITY) {}.size.should equal(Float::INFINITY)
+ @uninitialized.send(:initialize, @receiver, Float::INFINITY) {}.size.should.equal?(Float::INFINITY)
end
- it "sets given size to own size if the given size is a Fixnum" do
+ it "sets given size to own size if the given size is an Integer" do
@uninitialized.send(:initialize, @receiver, 100) {}.size.should == 100
end
@@ -52,12 +52,12 @@ describe "Enumerator::Lazy#initialize" do
end
it "raises an ArgumentError when block is not given" do
- -> { @uninitialized.send :initialize, @receiver }.should raise_error(ArgumentError)
+ -> { @uninitialized.send :initialize, @receiver }.should.raise(ArgumentError)
end
describe "on frozen instance" do
- it "raises a RuntimeError" do
- -> { @uninitialized.freeze.send(:initialize, @receiver) {} }.should raise_error(RuntimeError)
+ it "raises a FrozenError" do
+ -> { @uninitialized.freeze.send(:initialize, @receiver) {} }.should.raise(FrozenError)
end
end
end
diff --git a/spec/ruby/core/enumerator/lazy/lazy_spec.rb b/spec/ruby/core/enumerator/lazy/lazy_spec.rb
index cde9b31066..12107383d6 100644
--- a/spec/ruby/core/enumerator/lazy/lazy_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/lazy_spec.rb
@@ -4,25 +4,24 @@ require_relative '../../../spec_helper'
describe "Enumerator::Lazy" do
it "is a subclass of Enumerator" do
- Enumerator::Lazy.superclass.should equal(Enumerator)
+ Enumerator::Lazy.superclass.should.equal?(Enumerator)
end
it "defines lazy versions of a whitelist of Enumerator methods" do
- lazy_methods = [
- :chunk, :collect, :collect_concat, :drop, :drop_while, :enum_for,
+ lazy_methods = Set[
+ :chunk, :chunk_while, :collect, :collect_concat, :compact, :drop, :drop_while, :enum_for,
:find_all, :flat_map, :force, :grep, :grep_v, :lazy, :map, :reject,
:select, :slice_after, :slice_before, :slice_when, :take, :take_while,
- :to_enum, :zip
+ :to_enum, :uniq, :zip
]
- lazy_methods += [:chunk_while, :uniq]
- Enumerator::Lazy.instance_methods(false).should include(*lazy_methods)
+ Enumerator::Lazy.instance_methods(false).to_set.should >= lazy_methods
end
end
describe "Enumerator::Lazy#lazy" do
it "returns self" do
lazy = (1..3).to_enum.lazy
- lazy.lazy.should equal(lazy)
+ lazy.lazy.should.equal?(lazy)
end
end
diff --git a/spec/ruby/core/enumerator/lazy/reject_spec.rb b/spec/ruby/core/enumerator/lazy/reject_spec.rb
index 0e1632d667..374d4df14e 100644
--- a/spec/ruby/core/enumerator/lazy/reject_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/reject_spec.rb
@@ -16,8 +16,8 @@ describe "Enumerator::Lazy#reject" do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.reject {}
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "sets #size to nil" do
@@ -42,7 +42,7 @@ describe "Enumerator::Lazy#reject" do
-> {
lazy.first
- }.should raise_error(RuntimeError, "foo")
+ }.should.raise(RuntimeError, "foo")
end
it "calls the block with a gathered array when yield with multiple arguments" do
@@ -52,7 +52,7 @@ describe "Enumerator::Lazy#reject" do
end
it "raises an ArgumentError when not given a block" do
- -> { @yieldsmixed.reject }.should raise_error(ArgumentError)
+ -> { @yieldsmixed.reject }.should.raise(ArgumentError)
end
describe "on a nested Lazy" do
diff --git a/spec/ruby/core/enumerator/lazy/shared/collect.rb b/spec/ruby/core/enumerator/lazy/shared/collect.rb
index 5690255a0c..0ed04c8e02 100644
--- a/spec/ruby/core/enumerator/lazy/shared/collect.rb
+++ b/spec/ruby/core/enumerator/lazy/shared/collect.rb
@@ -16,8 +16,8 @@ describe :enumerator_lazy_collect, shared: true do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.send(@method) {}
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "keeps size" do
diff --git a/spec/ruby/core/enumerator/lazy/shared/collect_concat.rb b/spec/ruby/core/enumerator/lazy/shared/collect_concat.rb
index 00d7941a61..685e6d0594 100644
--- a/spec/ruby/core/enumerator/lazy/shared/collect_concat.rb
+++ b/spec/ruby/core/enumerator/lazy/shared/collect_concat.rb
@@ -16,8 +16,8 @@ describe :enumerator_lazy_collect_concat, shared: true do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.send(@method) {}
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "sets #size to nil" do
@@ -34,7 +34,7 @@ describe :enumerator_lazy_collect_concat, shared: true do
it "flattens elements when the given block returned an array or responding to .each and .force" do
(0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.chars }.first(6).should == %w[0 1 0 2 0 3]
- (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should be_true
+ (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should == true
(0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.each_char.lazy }.first(6).should == %w[0 1 0 2 0 3]
end
end
@@ -46,7 +46,7 @@ describe :enumerator_lazy_collect_concat, shared: true do
end
it "raises an ArgumentError when not given a block" do
- -> { @yieldsmixed.send(@method) }.should raise_error(ArgumentError)
+ -> { @yieldsmixed.send(@method) }.should.raise(ArgumentError)
end
describe "on a nested Lazy" do
@@ -64,7 +64,7 @@ describe :enumerator_lazy_collect_concat, shared: true do
it "flattens elements when the given block returned an array or responding to .each and .force" do
(0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.chars }.first(6).should == %w[0 1 0 2 0 3]
- (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should be_true
+ (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should == true
(0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.each_char.lazy }.first(6).should == %w[0 1 0 2 0 3]
end
end
diff --git a/spec/ruby/core/enumerator/lazy/shared/select.rb b/spec/ruby/core/enumerator/lazy/shared/select.rb
index 50a00bcbf4..2d15176620 100644
--- a/spec/ruby/core/enumerator/lazy/shared/select.rb
+++ b/spec/ruby/core/enumerator/lazy/shared/select.rb
@@ -16,8 +16,8 @@ describe :enumerator_lazy_select, shared: true do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.send(@method) {}
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "sets #size to nil" do
@@ -40,7 +40,7 @@ describe :enumerator_lazy_select, shared: true do
end
it "raises an ArgumentError when not given a block" do
- -> { @yieldsmixed.send(@method) }.should raise_error(ArgumentError)
+ -> { @yieldsmixed.send(@method) }.should.raise(ArgumentError)
end
describe "on a nested Lazy" do
diff --git a/spec/ruby/core/enumerator/lazy/shared/to_enum.rb b/spec/ruby/core/enumerator/lazy/shared/to_enum.rb
index 0c91ea55b9..75b9aefe8c 100644
--- a/spec/ruby/core/enumerator/lazy/shared/to_enum.rb
+++ b/spec/ruby/core/enumerator/lazy/shared/to_enum.rb
@@ -13,8 +13,8 @@ describe :enumerator_lazy_to_enum, shared: true do
it "returns a new instance of Enumerator::Lazy" do
ret = @infinite.send @method
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@infinite)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@infinite)
end
it "sets #size to nil when not given a block" do
@@ -43,7 +43,7 @@ describe :enumerator_lazy_to_enum, shared: true do
each_entry: [],
each_cons: [2]
}.each_pair do |method, args|
- @infinite.send(method, *args).should be_an_instance_of(Enumerator::Lazy)
+ @infinite.send(method, *args).should.instance_of?(Enumerator::Lazy)
end
end
diff --git a/spec/ruby/core/enumerator/lazy/slice_after_spec.rb b/spec/ruby/core/enumerator/lazy/slice_after_spec.rb
index 438df8d550..f8cd97178f 100644
--- a/spec/ruby/core/enumerator/lazy/slice_after_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/slice_after_spec.rb
@@ -6,4 +6,9 @@ describe "Enumerator::Lazy#slice_after" do
s.lazy.slice_after { |n| true }.first(100).should ==
s.first(100).slice_after { |n| true }.to_a
end
+
+ it "should return a lazy enumerator" do
+ s = 0..Float::INFINITY
+ s.lazy.slice_after { |n| true }.should.is_a?(Enumerator::Lazy)
+ end
end
diff --git a/spec/ruby/core/enumerator/lazy/slice_before_spec.rb b/spec/ruby/core/enumerator/lazy/slice_before_spec.rb
index 6c8660c1a1..192e853343 100644
--- a/spec/ruby/core/enumerator/lazy/slice_before_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/slice_before_spec.rb
@@ -6,4 +6,9 @@ describe "Enumerator::Lazy#slice_before" do
s.lazy.slice_before { |n| true }.first(100).should ==
s.first(100).slice_before { |n| true }.to_a
end
+
+ it "should return a lazy enumerator" do
+ s = 0..Float::INFINITY
+ s.lazy.slice_before { |n| true }.should.is_a?(Enumerator::Lazy)
+ end
end
diff --git a/spec/ruby/core/enumerator/lazy/slice_when_spec.rb b/spec/ruby/core/enumerator/lazy/slice_when_spec.rb
index e7673def47..fc9d5f5069 100644
--- a/spec/ruby/core/enumerator/lazy/slice_when_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/slice_when_spec.rb
@@ -6,4 +6,9 @@ describe "Enumerator::Lazy#slice_when" do
s.lazy.slice_when { |a, b| true }.first(100).should ==
s.first(100).slice_when { |a, b| true }.to_a
end
+
+ it "should return a lazy enumerator" do
+ s = 0..Float::INFINITY
+ s.lazy.slice_when { |a, b| true }.should.is_a?(Enumerator::Lazy)
+ end
end
diff --git a/spec/ruby/core/enumerator/lazy/take_spec.rb b/spec/ruby/core/enumerator/lazy/take_spec.rb
index 9fc17e969f..2dd5b939e2 100644
--- a/spec/ruby/core/enumerator/lazy/take_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/take_spec.rb
@@ -2,8 +2,16 @@
require_relative '../../../spec_helper'
require_relative 'fixtures/classes'
+require_relative '../../enumerable/shared/value_packing'
describe "Enumerator::Lazy#take" do
+ describe "value packing of source yields (matches Enumerable#take)" do
+ before :each do
+ @take = -> e { e.lazy.take(1) }
+ end
+ it_behaves_like :enumerable_value_packing, nil
+ end
+
before :each do
@yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy
@eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy
@@ -16,8 +24,8 @@ describe "Enumerator::Lazy#take" do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.take(1)
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "sets given count to size if the given count is less than old size" do
diff --git a/spec/ruby/core/enumerator/lazy/take_while_spec.rb b/spec/ruby/core/enumerator/lazy/take_while_spec.rb
index bcea0b1419..c369712c56 100644
--- a/spec/ruby/core/enumerator/lazy/take_while_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/take_while_spec.rb
@@ -16,8 +16,8 @@ describe "Enumerator::Lazy#take_while" do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.take_while {}
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "sets #size to nil" do
@@ -40,7 +40,7 @@ describe "Enumerator::Lazy#take_while" do
end
it "raises an ArgumentError when not given a block" do
- -> { @yieldsmixed.take_while }.should raise_error(ArgumentError)
+ -> { @yieldsmixed.take_while }.should.raise(ArgumentError)
end
describe "on a nested Lazy" do
diff --git a/spec/ruby/core/enumerator/lazy/uniq_spec.rb b/spec/ruby/core/enumerator/lazy/uniq_spec.rb
index ce67ace5ab..d30ed8df2f 100644
--- a/spec/ruby/core/enumerator/lazy/uniq_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/uniq_spec.rb
@@ -8,7 +8,7 @@ describe 'Enumerator::Lazy#uniq' do
end
it 'returns a lazy enumerator' do
- @lazy.should be_an_instance_of(Enumerator::Lazy)
+ @lazy.should.instance_of?(Enumerator::Lazy)
@lazy.force.should == [0, 1]
end
@@ -28,7 +28,7 @@ describe 'Enumerator::Lazy#uniq' do
end
it 'returns a lazy enumerator' do
- @lazy.should be_an_instance_of(Enumerator::Lazy)
+ @lazy.should.instance_of?(Enumerator::Lazy)
@lazy.force.should == [0, 1]
end
diff --git a/spec/ruby/core/enumerator/lazy/with_index_spec.rb b/spec/ruby/core/enumerator/lazy/with_index_spec.rb
new file mode 100644
index 0000000000..2e983fd3b1
--- /dev/null
+++ b/spec/ruby/core/enumerator/lazy/with_index_spec.rb
@@ -0,0 +1,36 @@
+# -*- encoding: us-ascii -*-
+
+require_relative '../../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Enumerator::Lazy#with_index" do
+ it "enumerates with an index" do
+ (0..Float::INFINITY).lazy.with_index.map { |i, idx| [i, idx] }.first(3).should == [[0, 0], [1, 1], [2, 2]]
+ end
+
+ it "enumerates with an index starting at a given offset" do
+ (0..Float::INFINITY).lazy.with_index(3).map { |i, idx| [i, idx] }.first(3).should == [[0, 3], [1, 4], [2, 5]]
+ end
+
+ it "enumerates with an index starting at 0 when offset is nil" do
+ (0..Float::INFINITY).lazy.with_index(nil).map { |i, idx| [i, idx] }.first(3).should == [[0, 0], [1, 1], [2, 2]]
+ end
+
+ it "raises TypeError when offset does not convert to Integer" do
+ -> { (0..Float::INFINITY).lazy.with_index(false).map { |i, idx| i }.first(3) }.should.raise(TypeError)
+ end
+
+ it "enumerates with a given block" do
+ result = []
+ (0..Float::INFINITY).lazy.with_index { |i, idx| result << [i * 2, idx] }.first(3)
+ result.should == [[0,0],[2,1],[4,2]]
+ end
+
+ it "resets after a new call to each" do
+ enum = (0..2).lazy.with_index.map { |i, idx| [i, idx] }
+ result = []
+ enum.each { |i, idx| result << [i, idx] }
+ enum.each { |i, idx| result << [i, idx] }
+ result.should == [[0,0], [1,1], [2,2], [0,0], [1,1], [2,2]]
+ end
+end
diff --git a/spec/ruby/core/enumerator/lazy/zip_spec.rb b/spec/ruby/core/enumerator/lazy/zip_spec.rb
index 5a828c1dcc..9f612542d7 100644
--- a/spec/ruby/core/enumerator/lazy/zip_spec.rb
+++ b/spec/ruby/core/enumerator/lazy/zip_spec.rb
@@ -16,8 +16,8 @@ describe "Enumerator::Lazy#zip" do
it "returns a new instance of Enumerator::Lazy" do
ret = @yieldsmixed.zip []
- ret.should be_an_instance_of(Enumerator::Lazy)
- ret.should_not equal(@yieldsmixed)
+ ret.should.instance_of?(Enumerator::Lazy)
+ ret.should_not.equal?(@yieldsmixed)
end
it "keeps size" do
@@ -40,11 +40,11 @@ describe "Enumerator::Lazy#zip" do
end
it "returns a Lazy when no arguments given" do
- @yieldsmixed.zip.should be_an_instance_of(Enumerator::Lazy)
+ @yieldsmixed.zip.should.instance_of?(Enumerator::Lazy)
end
it "raises a TypeError if arguments contain non-list object" do
- -> { @yieldsmixed.zip [], Object.new, [] }.should raise_error(TypeError)
+ -> { @yieldsmixed.zip [], Object.new, [] }.should.raise(TypeError)
end
describe "on a nested Lazy" do
diff --git a/spec/ruby/core/enumerator/new_spec.rb b/spec/ruby/core/enumerator/new_spec.rb
index 100edc8455..eb6c13759e 100644
--- a/spec/ruby/core/enumerator/new_spec.rb
+++ b/spec/ruby/core/enumerator/new_spec.rb
@@ -1,42 +1,10 @@
require_relative '../../spec_helper'
describe "Enumerator.new" do
- it "creates a new custom enumerator with the given object, iterator and arguments" do
- enum = Enumerator.new(1, :upto, 3)
- enum.should be_an_instance_of(Enumerator)
- end
-
- it "creates a new custom enumerator that responds to #each" do
- enum = Enumerator.new(1, :upto, 3)
- enum.respond_to?(:each).should == true
- end
-
- it "creates a new custom enumerator that runs correctly" do
- Enumerator.new(1, :upto, 3).map{|x|x}.should == [1,2,3]
- end
-
- it "aliases the second argument to :each" do
- Enumerator.new(1..2).to_a.should == Enumerator.new(1..2, :each).to_a
- end
-
- it "doesn't check for the presence of the iterator method" do
- Enumerator.new(nil).should be_an_instance_of(Enumerator)
- end
-
- it "uses the latest define iterator method" do
- class StrangeEach
- def each
- yield :foo
- end
+ context "no block given" do
+ it "raises" do
+ -> { Enumerator.new(1, :upto, 3) }.should.raise(ArgumentError)
end
- enum = Enumerator.new(StrangeEach.new)
- enum.to_a.should == [:foo]
- class StrangeEach
- def each
- yield :bar
- end
- end
- enum.to_a.should == [:bar]
end
context "when passed a block" do
@@ -66,45 +34,82 @@ describe "Enumerator.new" do
enum.take(3).should == [1, 2, 3]
end
- ruby_version_is "2.7" do
- it "defines iteration with block, yielder argument and treating it as a proc" do
- enum = Enumerator.new do |yielder|
- "a\nb\nc".each_line(&yielder)
- end
-
- enum.to_a.should == ["a\n", "b\n", "c"]
+ it "defines iteration with block, yielder argument and treating it as a proc" do
+ enum = Enumerator.new do |yielder|
+ "a\nb\nc".each_line(&yielder)
end
+
+ enum.to_a.should == ["a\n", "b\n", "c"]
end
- describe 'yielded values' do
- it 'handles yield arguments properly' do
+ describe '#yield' do
+ it 'accepts a single argument' do
Enumerator.new { |y| y.yield(1) }.to_a.should == [1]
Enumerator.new { |y| y.yield(1) }.first.should == 1
+ end
- Enumerator.new { |y| y.yield([1]) }.to_a.should == [[1]]
- Enumerator.new { |y| y.yield([1]) }.first.should == [1]
-
+ it 'accepts multiple arguments' do
Enumerator.new { |y| y.yield(1, 2) }.to_a.should == [[1, 2]]
Enumerator.new { |y| y.yield(1, 2) }.first.should == [1, 2]
+ end
+
+ it "doesn't double-wrap arrays" do
+ Enumerator.new { |y| y.yield([1]) }.to_a.should == [[1]]
+ Enumerator.new { |y| y.yield([1]) }.first.should == [1]
Enumerator.new { |y| y.yield([1, 2]) }.to_a.should == [[1, 2]]
Enumerator.new { |y| y.yield([1, 2]) }.first.should == [1, 2]
end
- it 'handles << arguments properly' do
+ it 'returns nil' do
+ ScratchPad.record []
+ Enumerator.new do |y|
+ ScratchPad << y.yield(1)
+ end.to_a
+
+ ScratchPad.recorded.should == [nil]
+ end
+
+ it 'accepts keyword arguments and treats them as a positional hash' do
+ Enumerator.new { |y| y.yield(foo: 42) }.to_a.should == [{ foo: 42 }]
+ Enumerator.new { |y| y.yield(foo: 42) }.first.should == { foo: 42 }
+
+ Enumerator.new { |y| y.yield(123, foo: 42) }.to_a.should == [[123, { foo: 42 }]]
+ Enumerator.new { |y| y.yield(123, foo: 42) }.first.should == [123, { foo: 42 }]
+ end
+ end
+
+ describe '#<<' do
+ it 'accepts a single argument' do
Enumerator.new { |y| y.<<(1) }.to_a.should == [1]
Enumerator.new { |y| y.<<(1) }.first.should == 1
+ end
+ it "doesn't double-wrap arrays" do
Enumerator.new { |y| y.<<([1]) }.to_a.should == [[1]]
Enumerator.new { |y| y.<<([1]) }.first.should == [1]
- # << doesn't accept multiple arguments
- # Enumerator.new { |y| y.<<(1, 2) }.to_a.should == [[1, 2]]
- # Enumerator.new { |y| y.<<(1, 2) }.first.should == [1, 2]
-
Enumerator.new { |y| y.<<([1, 2]) }.to_a.should == [[1, 2]]
Enumerator.new { |y| y.<<([1, 2]) }.first.should == [1, 2]
end
+
+ it 'accepts keyword arguments and treats them as a positional hash' do
+ Enumerator.new { |y| y.<<(foo: 42) }.to_a.should == [{ foo: 42 }]
+ Enumerator.new { |y| y.<<(foo: 42) }.first.should == { foo: 42 }
+ end
+
+ it 'can be chained' do
+ enum = Enumerator.new do |y|
+ y << 1 << 2
+ end
+ enum.to_a.should == [1, 2]
+ end
+
+ it 'raises ArgumentError when given more than one argument' do
+ -> {
+ Enumerator.new { |y| y.<<(1, 2) }.to_a
+ }.should.raise(ArgumentError, "wrong number of arguments (given 2, expected 1)")
+ end
end
end
end
diff --git a/spec/ruby/core/enumerator/next_spec.rb b/spec/ruby/core/enumerator/next_spec.rb
index a5e01a399d..77e79185a9 100644
--- a/spec/ruby/core/enumerator/next_spec.rb
+++ b/spec/ruby/core/enumerator/next_spec.rb
@@ -13,14 +13,14 @@ describe "Enumerator#next" do
it "raises a StopIteration exception at the end of the stream" do
3.times { @enum.next }
- -> { @enum.next }.should raise_error(StopIteration)
+ -> { @enum.next }.should.raise(StopIteration)
end
it "cannot be called again until the enumerator is rewound" do
3.times { @enum.next }
- -> { @enum.next }.should raise_error(StopIteration)
- -> { @enum.next }.should raise_error(StopIteration)
- -> { @enum.next }.should raise_error(StopIteration)
+ -> { @enum.next }.should.raise(StopIteration)
+ -> { @enum.next }.should.raise(StopIteration)
+ -> { @enum.next }.should.raise(StopIteration)
@enum.rewind
@enum.next.should == 1
end
diff --git a/spec/ruby/core/enumerator/next_values_spec.rb b/spec/ruby/core/enumerator/next_values_spec.rb
index 201b5d323f..63e024e2b4 100644
--- a/spec/ruby/core/enumerator/next_values_spec.rb
+++ b/spec/ruby/core/enumerator/next_values_spec.rb
@@ -11,6 +11,7 @@ describe "Enumerator#next_values" do
yield :e1, :e2, :e3
yield nil
yield
+ yield [:f1, :f2]
end
@e = o.to_enum
@@ -48,8 +49,13 @@ describe "Enumerator#next_values" do
@e.next_values.should == []
end
- it "raises StopIteration if called on a finished enumerator" do
+ it "returns an array of array if yield is called with an array" do
7.times { @e.next }
- -> { @e.next_values }.should raise_error(StopIteration)
+ @e.next_values.should == [[:f1, :f2]]
+ end
+
+ it "raises StopIteration if called on a finished enumerator" do
+ 8.times { @e.next }
+ -> { @e.next_values }.should.raise(StopIteration)
end
end
diff --git a/spec/ruby/core/enumerator/peek_spec.rb b/spec/ruby/core/enumerator/peek_spec.rb
index 2334385437..096fd2b10c 100644
--- a/spec/ruby/core/enumerator/peek_spec.rb
+++ b/spec/ruby/core/enumerator/peek_spec.rb
@@ -31,6 +31,6 @@ describe "Enumerator#peek" do
it "raises StopIteration if called on a finished enumerator" do
5.times { @e.next }
- -> { @e.peek }.should raise_error(StopIteration)
+ -> { @e.peek }.should.raise(StopIteration)
end
end
diff --git a/spec/ruby/core/enumerator/peek_values_spec.rb b/spec/ruby/core/enumerator/peek_values_spec.rb
index 7865546515..63f7988bcd 100644
--- a/spec/ruby/core/enumerator/peek_values_spec.rb
+++ b/spec/ruby/core/enumerator/peek_values_spec.rb
@@ -11,6 +11,7 @@ describe "Enumerator#peek_values" do
yield :e1, :e2, :e3
yield nil
yield
+ yield [:f1, :f2]
end
@e = o.to_enum
@@ -50,8 +51,13 @@ describe "Enumerator#peek_values" do
@e.peek_values.should == []
end
- it "raises StopIteration if called on a finished enumerator" do
+ it "returns an array of array if yield is called with an array" do
7.times { @e.next }
- -> { @e.peek_values }.should raise_error(StopIteration)
+ @e.peek_values.should == [[:f1, :f2]]
+ end
+
+ it "raises StopIteration if called on a finished enumerator" do
+ 8.times { @e.next }
+ -> { @e.peek_values }.should.raise(StopIteration)
end
end
diff --git a/spec/ruby/core/enumerator/plus_spec.rb b/spec/ruby/core/enumerator/plus_spec.rb
index c9bae08b04..d6c0fa93ac 100644
--- a/spec/ruby/core/enumerator/plus_spec.rb
+++ b/spec/ruby/core/enumerator/plus_spec.rb
@@ -1,35 +1,33 @@
require_relative '../../spec_helper'
-ruby_version_is "2.6" do
- describe "Enumerator#+" do
- before :each do
- ScratchPad.record []
- end
+describe "Enumerator#+" do
+ before :each do
+ ScratchPad.record []
+ end
- it "returns a chain of self and provided enumerators" do
- one = Enumerator.new { |y| y << 1 }
- two = Enumerator.new { |y| y << 2 }
- three = Enumerator.new { |y| y << 3 }
+ it "returns a chain of self and provided enumerators" do
+ one = Enumerator.new { |y| y << 1 }
+ two = Enumerator.new { |y| y << 2 }
+ three = Enumerator.new { |y| y << 3 }
- chain = one + two + three
+ chain = one + two + three
- chain.should be_an_instance_of(Enumerator::Chain)
- chain.each { |item| ScratchPad << item }
- ScratchPad.recorded.should == [1, 2, 3]
- end
+ chain.should.instance_of?(Enumerator::Chain)
+ chain.each { |item| ScratchPad << item }
+ ScratchPad.recorded.should == [1, 2, 3]
+ end
- it "calls #each on each argument" do
- enum = Enumerator.new { |y| y << "one" }
+ it "calls #each on each argument" do
+ enum = Enumerator.new { |y| y << "one" }
- obj1 = mock("obj1")
- obj1.should_receive(:each).once.and_yield("two")
+ obj1 = mock("obj1")
+ obj1.should_receive(:each).once.and_yield("two")
- obj2 = mock("obj2")
- obj2.should_receive(:each).once.and_yield("three")
+ obj2 = mock("obj2")
+ obj2.should_receive(:each).once.and_yield("three")
- chain = enum + obj1 + obj2
- chain.each { |item| ScratchPad << item }
- ScratchPad.recorded.should == ["one", "two", "three"]
- end
+ chain = enum + obj1 + obj2
+ chain.each { |item| ScratchPad << item }
+ ScratchPad.recorded.should == ["one", "two", "three"]
end
end
diff --git a/spec/ruby/core/enumerator/produce_spec.rb b/spec/ruby/core/enumerator/produce_spec.rb
index f6f1dcd429..eb1b09294e 100644
--- a/spec/ruby/core/enumerator/produce_spec.rb
+++ b/spec/ruby/core/enumerator/produce_spec.rb
@@ -1,35 +1,77 @@
require_relative '../../spec_helper'
-ruby_version_is "2.7" do
- describe "Enumerator.produce" do
- it "creates an infinite enumerator" do
- enum = Enumerator.produce(0) { |prev| prev + 1 }
- enum.take(5).should == [0, 1, 2, 3, 4]
+describe "Enumerator.produce" do
+ it "creates an infinite enumerator" do
+ enum = Enumerator.produce(0) { |prev| prev + 1 }
+
+ enum.size.should == Float::INFINITY
+ enum.take(5).should == [0, 1, 2, 3, 4]
+ end
+
+ it "terminates iteration when block raises StopIteration exception" do
+ enum = Enumerator.produce(0) do | prev|
+ raise StopIteration if prev >= 2
+ prev + 1
end
- it "terminates iteration when block raises StopIteration exception" do
- enum = Enumerator.produce(0) do | prev|
- raise StopIteration if prev >= 2
- prev + 1
- end
+ enum.to_a.should == [0, 1, 2]
+ end
+
+ context "when initial value skipped" do
+ it "uses nil instead" do
+ ScratchPad.record []
+ enum = Enumerator.produce { |prev| ScratchPad << prev; (prev || 0) + 1 }
- enum.to_a.should == [0, 1, 2]
+ enum.take(3).should == [1, 2, 3]
+ ScratchPad.recorded.should == [nil, 1, 2]
end
- context "when initial value skipped" do
- it "uses nil instead" do
- ScratchPad.record []
- enum = Enumerator.produce { |prev| ScratchPad << prev; (prev || 0) + 1 }
+ it "starts enumerable from result of first block call" do
+ array = "a\nb\nc\nd".lines
+ lines = Enumerator.produce { array.shift }.take_while { |s| s }
+
+ lines.should == ["a\n", "b\n", "c\n", "d"]
+ end
+ end
+
+ it "raises ArgumentError when no block is given" do
+ -> { Enumerator.produce }.should.raise(ArgumentError, "no block given")
+ end
+
+ ruby_version_is ""..."4.0" do
+ it "accepts keyword arguments as the initial value" do
+ enum = Enumerator.produce(a: 1, b: 1) {}
+ enum.take(1).should == [{a: 1, b: 1}]
+ end
+ end
+
+ ruby_version_is "4.0" do
+ it "raises ArgumentError for unknown keyword arguments" do
+ -> { Enumerator.produce(a: 1, b: 1) {} }.should.raise(ArgumentError, /unknown keywords/)
+ end
+ end
+
+ ruby_version_is "4.0" do
+ context "with size keyword argument" do
+ it "sets the size of the enumerator" do
+ enum = Enumerator.produce(0, size: 10) { |n| n + 1 }
+
+ enum.size.should == 10
+ enum.take(5).should == [0, 1, 2, 3, 4]
+ end
+
+ it "accepts a callable" do
+ enum = Enumerator.produce(0, size: -> { 5 * 5 }) { |n| n + 1 }
- enum.take(3).should == [1, 2, 3]
- ScratchPad.recorded.should == [nil, 1, 2]
+ enum.size.should == 25
+ enum.take(5).should == [0, 1, 2, 3, 4]
end
- it "starts enumerable from result of first block call" do
- array = "a\nb\nc\nd".lines
- lines = Enumerator.produce { array.shift }.take_while { |s| s }
+ it "accepts nil" do
+ enum = Enumerator.produce(0, size: nil) { |n| n + 1 }
- lines.should == ["a\n", "b\n", "c\n", "d"]
+ enum.size.should == nil
+ enum.take(5).should == [0, 1, 2, 3, 4]
end
end
end
diff --git a/spec/ruby/core/enumerator/product/each_spec.rb b/spec/ruby/core/enumerator/product/each_spec.rb
new file mode 100644
index 0000000000..a5dced4db1
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/each_spec.rb
@@ -0,0 +1,85 @@
+require_relative '../../../spec_helper'
+require_relative '../../enumerable/shared/enumeratorized'
+
+describe "Enumerator::Product#each" do
+ it_behaves_like :enumeratorized_with_origin_size, :each, Enumerator::Product.new([1, 2], [:a, :b])
+
+ it "yields each element of Cartesian product of enumerators" do
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+ acc = []
+ enum.each { |e| acc << e }
+ acc.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "calls #each_entry method on enumerators" do
+ object1 = Object.new
+ def object1.each_entry
+ yield 1
+ yield 2
+ end
+
+ object2 = Object.new
+ def object2.each_entry
+ yield :a
+ yield :b
+ end
+
+ enum = Enumerator::Product.new(object1, object2)
+ acc = []
+ enum.each { |e| acc << e }
+ acc.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "raises a NoMethodError if the object doesn't respond to #each_entry" do
+ -> {
+ Enumerator::Product.new(Object.new).each {}
+ }.should.raise(NoMethodError, /undefined method [`']each_entry' for/)
+ end
+
+ it "returns enumerator if not given a block" do
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+ enum.each.should.kind_of?(Enumerator)
+
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+ enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "returns self if given a block" do
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+ enum.each {}.should.equal?(enum)
+ end
+
+ it "doesn't accept arguments" do
+ Enumerator::Product.instance_method(:each).arity.should == 0
+ end
+
+ it "yields each element to a block that takes multiple arguments" do
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+
+ acc = []
+ enum.each { |x, y| acc << x }
+ acc.should == [1, 1, 2, 2]
+
+ acc = []
+ enum.each { |x, y| acc << y }
+ acc.should == [:a, :b, :a, :b]
+
+ acc = []
+ enum.each { |x, y, z| acc << z }
+ acc.should == [nil, nil, nil, nil]
+ end
+
+ it "yields no element when any enumerable is empty" do
+ enum = Enumerator::Product.new([], [1])
+
+ acc = []
+ enum.each { |x| acc << x }
+ acc.should == []
+
+ enum = Enumerator::Product.new([1], [])
+
+ acc = []
+ enum.each { |x| acc << x }
+ acc.should == []
+ end
+end
diff --git a/spec/ruby/core/enumerator/product/initialize_copy_spec.rb b/spec/ruby/core/enumerator/product/initialize_copy_spec.rb
new file mode 100644
index 0000000000..b5d2b345a9
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/initialize_copy_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Product#initialize_copy" do
+ it "replaces content of the receiver with content of the other object" do
+ enum = Enumerator::Product.new([true, false])
+ enum2 = Enumerator::Product.new([1, 2], [:a, :b])
+
+ enum.send(:initialize_copy, enum2)
+ enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "returns self" do
+ enum = Enumerator::Product.new([true, false])
+ enum2 = Enumerator::Product.new([1, 2], [:a, :b])
+
+ enum.send(:initialize_copy, enum2).should.equal?(enum)
+ end
+
+ it "is a private method" do
+ Enumerator::Product.private_instance_methods(false).should.include?(:initialize_copy)
+ end
+
+ it "does nothing if the argument is the same as the receiver" do
+ enum = Enumerator::Product.new(1..2)
+ enum.send(:initialize_copy, enum).should.equal?(enum)
+
+ enum.freeze
+ enum.send(:initialize_copy, enum).should.equal?(enum)
+ end
+
+ it "raises FrozenError if the receiver is frozen" do
+ enum = Enumerator::Product.new(1..2)
+ enum2 = Enumerator::Product.new(3..4)
+
+ -> { enum.freeze.send(:initialize_copy, enum2) }.should.raise(FrozenError)
+ end
+
+ it "raises TypeError if the objects are of different class" do
+ enum = Enumerator::Product.new(1..2)
+ enum2 = Class.new(Enumerator::Product).new(3..4)
+
+ -> { enum.send(:initialize_copy, enum2) }.should.raise(TypeError, 'initialize_copy should take same class object')
+ -> { enum2.send(:initialize_copy, enum) }.should.raise(TypeError, 'initialize_copy should take same class object')
+ end
+
+ it "raises ArgumentError if the argument is not initialized yet" do
+ enum = Enumerator::Product.new(1..2)
+ enum2 = Enumerator::Product.allocate
+
+ -> { enum.send(:initialize_copy, enum2) }.should.raise(ArgumentError, 'uninitialized product')
+ end
+end
diff --git a/spec/ruby/core/enumerator/product/initialize_spec.rb b/spec/ruby/core/enumerator/product/initialize_spec.rb
new file mode 100644
index 0000000000..8814f9d3c7
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/initialize_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Product#initialize" do
+ before :each do
+ @uninitialized = Enumerator::Product.allocate
+ end
+
+ it "is a private method" do
+ Enumerator::Product.private_instance_methods(false).should.include?(:initialize)
+ end
+
+ it "returns self" do
+ @uninitialized.send(:initialize).should.equal?(@uninitialized)
+ end
+
+ it "accepts many arguments" do
+ @uninitialized.send(:initialize, 0..1, 2..3, 4..5).should.equal?(@uninitialized)
+ end
+
+ it "accepts arguments that are not Enumerable nor responding to :each_entry" do
+ @uninitialized.send(:initialize, Object.new).should.equal?(@uninitialized)
+ end
+
+ describe "on frozen instance" do
+ it "raises a FrozenError" do
+ -> {
+ @uninitialized.freeze.send(:initialize, 0..1)
+ }.should.raise(FrozenError)
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/product/inspect_spec.rb b/spec/ruby/core/enumerator/product/inspect_spec.rb
new file mode 100644
index 0000000000..e0d7441f26
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/inspect_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Product#inspect" do
+ it "returns a String including enumerators" do
+ enum = Enumerator::Product.new([1, 2], [:a, :b])
+ enum.inspect.should == "#<Enumerator::Product: [[1, 2], [:a, :b]]>"
+ end
+
+ it "represents a recursive element with '[...]'" do
+ enum = [1, 2]
+ enum_recursive = Enumerator::Product.new(enum)
+
+ enum << enum_recursive
+ enum_recursive.inspect.should == "#<Enumerator::Product: [[1, 2, #<Enumerator::Product: ...>]]>"
+ end
+
+ it "returns a not initialized representation if #initialized is not called yet" do
+ Enumerator::Product.allocate.inspect.should == "#<Enumerator::Product: uninitialized>"
+ end
+end
diff --git a/spec/ruby/core/enumerator/product/rewind_spec.rb b/spec/ruby/core/enumerator/product/rewind_spec.rb
new file mode 100644
index 0000000000..2beffaf5c1
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/rewind_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Product#rewind" do
+ before :each do
+ @enum = Enumerator::Product.new([1, 2].each.to_enum, [:a, :b].each.to_enum)
+ end
+
+ it "resets the enumerator to its initial state" do
+ @enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ @enum.rewind
+ @enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "returns self" do
+ @enum.rewind.should.equal? @enum
+ end
+
+ it "has no effect on a new enumerator" do
+ @enum.rewind
+ @enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "has no effect if called multiple, consecutive times" do
+ @enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ @enum.rewind
+ @enum.rewind
+ @enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
+ end
+
+ it "calls the enclosed object's rewind method if one exists" do
+ obj = mock('rewinder')
+ enum = Enumerator::Product.new(obj.to_enum)
+
+ obj.should_receive(:rewind)
+ enum.rewind
+ end
+
+ it "does nothing if the object doesn't have a #rewind method" do
+ obj = mock('rewinder')
+ enum = Enumerator::Product.new(obj.to_enum)
+
+ enum.rewind.should == enum
+ end
+
+ it "calls a rewind method on each enumerable in direct order" do
+ ScratchPad.record []
+
+ object1 = Object.new
+ def object1.rewind; ScratchPad << :object1; end
+
+ object2 = Object.new
+ def object2.rewind; ScratchPad << :object2; end
+
+ object3 = Object.new
+ def object3.rewind; ScratchPad << :object3; end
+
+ enum = Enumerator::Product.new(object1, object2, object3)
+ enum.rewind
+
+ ScratchPad.recorded.should == [:object1, :object2, :object3]
+ end
+end
diff --git a/spec/ruby/core/enumerator/product/size_spec.rb b/spec/ruby/core/enumerator/product/size_spec.rb
new file mode 100644
index 0000000000..0ba427af9a
--- /dev/null
+++ b/spec/ruby/core/enumerator/product/size_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../../spec_helper'
+
+describe "Enumerator::Product#size" do
+ it "returns the total size of the enumerator product calculated by multiplying the sizes of enumerables in the product" do
+ product = Enumerator::Product.new(1..2, 1..3, 1..4)
+ product.size.should == 24 # 2 * 3 * 4
+ end
+
+ it "returns nil if any enumerable reports its size as nil" do
+ enum = Object.new
+ def enum.size; nil; end
+
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == nil
+ end
+
+ it "returns Float::INFINITY if any enumerable reports its size as Float::INFINITY" do
+ enum = Object.new
+ def enum.size; Float::INFINITY; end
+
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == Float::INFINITY
+ end
+
+ it "returns nil if any enumerable reports its size as Float::NAN" do
+ enum = Object.new
+ def enum.size; Float::NAN; end
+
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == nil
+ end
+
+ it "returns nil if any enumerable doesn't respond to #size" do
+ enum = Object.new
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == nil
+ end
+
+ it "returns nil if any enumerable reports a not-convertible to Integer" do
+ enum = Object.new
+ def enum.size; :symbol; end
+
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == nil
+ end
+
+ it "returns nil if any enumerable reports a non-Integer but convertible to Integer size" do
+ enum = Object.new
+ def enum.size; 1.0; end
+
+ product = Enumerator::Product.new(1..2, enum)
+ product.size.should == nil
+ end
+
+ ruby_version_is "3.4" do
+ it "returns zero when any enumerable reports zero" do
+ enum = Enumerator::Product.new(1...1, ["A", "B"])
+ enum.size.should == 0
+
+ enum = Enumerator::Product.new(["A", "B"], 1...1)
+ enum.size.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/product_spec.rb b/spec/ruby/core/enumerator/product_spec.rb
new file mode 100644
index 0000000000..0f37ef76e7
--- /dev/null
+++ b/spec/ruby/core/enumerator/product_spec.rb
@@ -0,0 +1,91 @@
+require_relative '../../spec_helper'
+
+describe "Enumerator.product" do
+ it "returns a Cartesian product of enumerators" do
+ enum = Enumerator.product(1..2, ["A", "B"])
+ enum.to_a.should == [[1, "A"], [1, "B"], [2, "A"], [2, "B"]]
+ end
+
+ it "accepts a list of enumerators of any length" do
+ enum = Enumerator.product(1..2)
+ enum.to_a.should == [[1], [2]]
+
+ enum = Enumerator.product(1..2, ["A"])
+ enum.to_a.should == [[1, "A"], [2, "A"]]
+
+ enum = Enumerator.product(1..2, ["A"], ["B"])
+ enum.to_a.should == [[1, "A", "B"], [2, "A", "B"]]
+
+ enum = Enumerator.product(2..3, ["A"], ["B"], ["C"])
+ enum.to_a.should == [[2, "A", "B", "C"], [3, "A", "B", "C"]]
+ end
+
+ it "returns an enumerator with an empty array when no arguments passed" do
+ enum = Enumerator.product
+ enum.to_a.should == [[]]
+ end
+
+ it "returns an instance of Enumerator::Product" do
+ enum = Enumerator.product
+ enum.class.should == Enumerator::Product
+ end
+
+ it "accepts infinite enumerators and returns infinite enumerator" do
+ enum = Enumerator.product(1.., ["A", "B"])
+ enum.take(5).should == [[1, "A"], [1, "B"], [2, "A"], [2, "B"], [3, "A"]]
+ enum.size.should == Float::INFINITY
+ end
+
+ it "accepts a block" do
+ elems = []
+ enum = Enumerator.product(1..2, ["X", "Y"]) { elems << _1 }
+
+ elems.should == [[1, "X"], [1, "Y"], [2, "X"], [2, "Y"]]
+ end
+
+ it "returns nil when a block passed" do
+ Enumerator.product(1..2) {}.should == nil
+ end
+
+ # https://bugs.ruby-lang.org/issues/19829
+ it "reject keyword arguments" do
+ -> {
+ Enumerator.product(1..3, foo: 1, bar: 2)
+ }.should.raise(ArgumentError, "unknown keywords: :foo, :bar")
+ end
+
+ it "calls only #each_entry method on arguments" do
+ object = Object.new
+ def object.each_entry
+ yield 1
+ yield 2
+ end
+
+ enum = Enumerator.product(object, ["A", "B"])
+ enum.to_a.should == [[1, "A"], [1, "B"], [2, "A"], [2, "B"]]
+ end
+
+ it "raises NoMethodError when argument doesn't respond to #each_entry" do
+ -> {
+ Enumerator.product(Object.new).to_a
+ }.should.raise(NoMethodError, /undefined method [`']each_entry' for/)
+ end
+
+ it "calls #each_entry lazily" do
+ Enumerator.product(Object.new).should.is_a?(Enumerator)
+ end
+
+ it "iterates through consuming enumerator elements only once" do
+ a = [1, 2, 3]
+ i = 0
+
+ enum = Enumerator.new do |y|
+ while i < a.size
+ y << a[i]
+ i += 1
+ end
+ end
+
+ Enumerator.product(['a', 'b'], enum).to_a.should == [["a", 1], ["a", 2], ["a", 3]]
+ end
+end
diff --git a/spec/ruby/core/enumerator/rewind_spec.rb b/spec/ruby/core/enumerator/rewind_spec.rb
index a105f2c619..6ba0edf174 100644
--- a/spec/ruby/core/enumerator/rewind_spec.rb
+++ b/spec/ruby/core/enumerator/rewind_spec.rb
@@ -14,7 +14,7 @@ describe "Enumerator#rewind" do
end
it "returns self" do
- @enum.rewind.should == @enum
+ @enum.rewind.should.equal? @enum
end
it "has no effect on a new enumerator" do
@@ -49,7 +49,7 @@ describe "Enumerator#rewind" do
obj = mock('rewinder')
enum = obj.to_enum
obj.should_receive(:each).at_most(1)
- -> { enum.rewind.should == enum }.should_not raise_error
+ enum.rewind.should == enum
end
end
diff --git a/spec/ruby/core/enumerator/shared/enum_for.rb b/spec/ruby/core/enumerator/shared/enum_for.rb
new file mode 100644
index 0000000000..4388103ecf
--- /dev/null
+++ b/spec/ruby/core/enumerator/shared/enum_for.rb
@@ -0,0 +1,57 @@
+describe :enum_for, shared: true do
+ it "is defined in Kernel" do
+ Kernel.method_defined?(@method).should == true
+ end
+
+ it "returns a new enumerator" do
+ "abc".send(@method).should.instance_of?(Enumerator)
+ end
+
+ it "defaults the first argument to :each" do
+ enum = [1,2].send(@method)
+ enum.map { |v| v }.should == [1,2].each { |v| v }
+ end
+
+ it "sets regexp matches in the caller" do
+ "wawa".send(@method, :scan, /./).map {|o| $& }.should == ["w", "a", "w", "a"]
+ a = []
+ "wawa".send(@method, :scan, /./).each {|o| a << $& }
+ a.should == ["w", "a", "w", "a"]
+ end
+
+ it "exposes multi-arg yields as an array" do
+ o = Object.new
+ def o.each
+ yield :a
+ yield :b1, :b2
+ yield [:c]
+ yield :d1, :d2
+ yield :e1, :e2, :e3
+ end
+
+ enum = o.send(@method)
+ enum.next.should == :a
+ enum.next.should == [:b1, :b2]
+ enum.next.should == [:c]
+ enum.next.should == [:d1, :d2]
+ enum.next.should == [:e1, :e2, :e3]
+ end
+
+ it "uses the passed block's value to calculate the size of the enumerator" do
+ Object.new.enum_for { 100 }.size.should == 100
+ end
+
+ it "defers the evaluation of the passed block until #size is called" do
+ ScratchPad.record []
+
+ enum = Object.new.enum_for do
+ ScratchPad << :called
+ 100
+ end
+
+ ScratchPad.recorded.should.empty?
+
+ enum.size
+ ScratchPad.recorded.should == [:called]
+ end
+end
diff --git a/spec/ruby/core/enumerator/shared/with_index.rb b/spec/ruby/core/enumerator/shared/with_index.rb
new file mode 100644
index 0000000000..0992397e95
--- /dev/null
+++ b/spec/ruby/core/enumerator/shared/with_index.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+
+describe :enum_with_index, shared: true do
+
+ require_relative '../fixtures/classes'
+
+ before :each do
+ @origin = [1, 2, 3, 4]
+ @enum = @origin.to_enum
+ end
+
+ it "passes each element and its index to block" do
+ a = []
+ @enum.send(@method) { |o, i| a << [o, i] }
+ a.should == [[1, 0], [2, 1], [3, 2], [4, 3]]
+ end
+
+ it "returns the object being enumerated when given a block" do
+ @enum.send(@method) { |o, i| :glark }.should.equal?(@origin)
+ end
+
+ it "binds splat arguments properly" do
+ acc = []
+ @enum.send(@method) { |*b| c,d = b; acc << c; acc << d }
+ [1, 0, 2, 1, 3, 2, 4, 3].should == acc
+ end
+
+ it "returns an enumerator if no block is supplied" do
+ ewi = @enum.send(@method)
+ ewi.should.instance_of?(Enumerator)
+ ewi.to_a.should == [[1, 0], [2, 1], [3, 2], [4, 3]]
+ end
+end
diff --git a/spec/ruby/core/enumerator/shared/with_object.rb b/spec/ruby/core/enumerator/shared/with_object.rb
new file mode 100644
index 0000000000..50d4f24eb3
--- /dev/null
+++ b/spec/ruby/core/enumerator/shared/with_object.rb
@@ -0,0 +1,42 @@
+require_relative '../../../spec_helper'
+
+describe :enum_with_object, shared: true do
+ before :each do
+ @enum = [:a, :b].to_enum
+ @memo = ''
+ @block_params = @enum.send(@method, @memo).to_a
+ end
+
+ it "receives an argument" do
+ @enum.method(@method).arity.should == 1
+ end
+
+ context "with block" do
+ it "returns the given object" do
+ ret = @enum.send(@method, @memo) do |elm, memo|
+ # nothing
+ end
+ ret.should.equal?(@memo)
+ end
+
+ context "the block parameter" do
+ it "passes each element to first parameter" do
+ @block_params[0][0].should.equal?(:a)
+ @block_params[1][0].should.equal?(:b)
+ end
+
+ it "passes the given object to last parameter" do
+ @block_params[0][1].should.equal?(@memo)
+ @block_params[1][1].should.equal?(@memo)
+ end
+ end
+ end
+
+ context "without block" do
+ it "returns new Enumerator" do
+ ret = @enum.send(@method, @memo)
+ ret.should.instance_of?(Enumerator)
+ ret.should_not.equal?(@enum)
+ end
+ end
+end
diff --git a/spec/ruby/core/enumerator/size_spec.rb b/spec/ruby/core/enumerator/size_spec.rb
index 6accd26a4e..4b2beffbbe 100644
--- a/spec/ruby/core/enumerator/size_spec.rb
+++ b/spec/ruby/core/enumerator/size_spec.rb
@@ -6,7 +6,7 @@ describe "Enumerator#size" do
end
it "returns nil if set size is nil" do
- Enumerator.new(nil) {}.size.should be_nil
+ Enumerator.new(nil) {}.size.should == nil
end
it "returns returning value from size.call if set size is a Proc" do
diff --git a/spec/ruby/core/enumerator/to_enum_spec.rb b/spec/ruby/core/enumerator/to_enum_spec.rb
index cadfcf6314..7fb73d0c3c 100644
--- a/spec/ruby/core/enumerator/to_enum_spec.rb
+++ b/spec/ruby/core/enumerator/to_enum_spec.rb
@@ -1,6 +1,6 @@
require_relative '../../spec_helper'
-require_relative '../../shared/enumerator/enum_for'
+require_relative 'shared/enum_for'
describe "Enumerator#to_enum" do
- it_behaves_like :enum_for, :enum_for
+ it_behaves_like :enum_for, :to_enum
end
diff --git a/spec/ruby/core/enumerator/with_index_spec.rb b/spec/ruby/core/enumerator/with_index_spec.rb
index ac37cee508..ca90fd18f7 100644
--- a/spec/ruby/core/enumerator/with_index_spec.rb
+++ b/spec/ruby/core/enumerator/with_index_spec.rb
@@ -1,5 +1,5 @@
require_relative '../../spec_helper'
-require_relative '../../shared/enumerator/with_index'
+require_relative 'shared/with_index'
require_relative '../enumerable/shared/enumeratorized'
describe "Enumerator#with_index" do
@@ -9,20 +9,20 @@ describe "Enumerator#with_index" do
it "returns a new Enumerator when no block is given" do
enum1 = [1,2,3].select
enum2 = enum1.with_index
- enum2.should be_an_instance_of(Enumerator)
+ enum2.should.instance_of?(Enumerator)
enum1.should_not === enum2
end
it "accepts an optional argument when given a block" do
-> do
@enum.with_index(1) { |f| f}
- end.should_not raise_error(ArgumentError)
+ end.should_not.raise(ArgumentError)
end
it "accepts an optional argument when not given a block" do
-> do
@enum.with_index(1)
- end.should_not raise_error(ArgumentError)
+ end.should_not.raise(ArgumentError)
end
it "numbers indices from the given index when given an offset but no block" do
@@ -38,7 +38,7 @@ describe "Enumerator#with_index" do
it "raises a TypeError when the argument cannot be converted to numeric" do
-> do
@enum.with_index('1') {|*i| i}
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it "converts non-numeric arguments to Integer via #to_int" do
@@ -69,4 +69,21 @@ describe "Enumerator#with_index" do
@enum.with_index(-1) { |*x| res << x}
res.should == [[1,-1], [2,0], [3,1], [4,2]]
end
+
+ it "passes on the given block's return value" do
+ arr = [1,2,3]
+ arr.delete_if.with_index { |a,b| false }
+ arr.should == [1,2,3]
+
+ arr.delete_if.with_index { |a,b| true }
+ arr.should == []
+ end
+
+ it "returns the iterator's return value" do
+ @enum.select.with_index { |a,b| false }.should == []
+ end
+
+ it "returns the correct value if chained with itself" do
+ [:a].each.with_index.with_index.to_a.should == [[[:a,0],0]]
+ end
end
diff --git a/spec/ruby/core/enumerator/with_object_spec.rb b/spec/ruby/core/enumerator/with_object_spec.rb
index e7ba83fd9f..58031fd765 100644
--- a/spec/ruby/core/enumerator/with_object_spec.rb
+++ b/spec/ruby/core/enumerator/with_object_spec.rb
@@ -1,5 +1,5 @@
require_relative '../../spec_helper'
-require_relative '../../shared/enumerator/with_object'
+require_relative 'shared/with_object'
describe "Enumerator#with_object" do
it_behaves_like :enum_with_object, :with_object
diff --git a/spec/ruby/core/enumerator/yielder/append_spec.rb b/spec/ruby/core/enumerator/yielder/append_spec.rb
deleted file mode 100644
index 7f7059774d..0000000000
--- a/spec/ruby/core/enumerator/yielder/append_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require_relative '../../../spec_helper'
-
-describe "Enumerator::Yielder#<<" do
- # TODO: There's some common behavior between yield and <<; move to a shared spec
- it "yields the value to the block" do
- ary = []
- y = Enumerator::Yielder.new {|x| ary << x}
- y << 1
-
- ary.should == [1]
- end
-
- it "doesn't double-wrap Arrays" do
- yields = []
- y = Enumerator::Yielder.new {|args| yields << args }
- y << [1]
- yields.should == [[1]]
- end
-
- it "returns self" do
- y = Enumerator::Yielder.new {|x| x + 1}
- (y << 1).should equal(y)
- end
-
- context "when multiple arguments passed" do
- ruby_version_is '' ... '2.6' do
- it "yields the arguments list to the block" do
- ary = []
- y = Enumerator::Yielder.new { |*x| ary << x }
- y.<<(1, 2)
-
- ary.should == [[1, 2]]
- end
- end
-
- ruby_version_is '2.6' do
- it "raises an ArgumentError" do
- ary = []
- y = Enumerator::Yielder.new { |*x| ary << x }
-
- -> {
- y.<<(1, 2)
- }.should raise_error(ArgumentError, /wrong number of arguments/)
- end
- end
- end
-end
diff --git a/spec/ruby/core/enumerator/yielder/initialize_spec.rb b/spec/ruby/core/enumerator/yielder/initialize_spec.rb
deleted file mode 100644
index 5a6eee2d0f..0000000000
--- a/spec/ruby/core/enumerator/yielder/initialize_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- encoding: us-ascii -*-
-
-require_relative '../../../spec_helper'
-
-describe "Enumerator::Yielder#initialize" do
- before :each do
- @class = Enumerator::Yielder
- @uninitialized = @class.allocate
- end
-
- it "is a private method" do
- @class.should have_private_instance_method(:initialize, false)
- end
-
- it "returns self when given a block" do
- @uninitialized.send(:initialize) {}.should equal(@uninitialized)
- end
-end
diff --git a/spec/ruby/core/enumerator/yielder/to_proc_spec.rb b/spec/ruby/core/enumerator/yielder/to_proc_spec.rb
deleted file mode 100644
index 0ed1645853..0000000000
--- a/spec/ruby/core/enumerator/yielder/to_proc_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require_relative '../../../spec_helper'
-
-ruby_version_is "2.7" do
- describe "Enumerator::Yielder#to_proc" do
- it "returns a Proc object that takes an argument and yields it to the block" do
- ScratchPad.record []
- y = Enumerator::Yielder.new { |*args| ScratchPad << args; "foobar" }
-
- callable = y.to_proc
- callable.class.should == Proc
-
- result = callable.call(1, 2)
- ScratchPad.recorded.should == [[1, 2]]
-
- result.should == "foobar"
- end
- end
-end
diff --git a/spec/ruby/core/enumerator/yielder/yield_spec.rb b/spec/ruby/core/enumerator/yielder/yield_spec.rb
deleted file mode 100644
index acfdf114b6..0000000000
--- a/spec/ruby/core/enumerator/yielder/yield_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require_relative '../../../spec_helper'
-
-describe "Enumerator::Yielder#yield" do
- it "yields the value to the block" do
- ary = []
- y = Enumerator::Yielder.new {|x| ary << x}
- y.yield 1
-
- ary.should == [1]
- end
-
- it "yields with passed arguments" do
- yields = []
- y = Enumerator::Yielder.new {|*args| yields << args }
- y.yield 1, 2
- yields.should == [[1, 2]]
- end
-
- it "returns the result of the block for the given value" do
- y = Enumerator::Yielder.new {|x| x + 1}
- y.yield(1).should == 2
- end
-
- context "when multiple arguments passed" do
- it "yields the arguments list to the block" do
- ary = []
- y = Enumerator::Yielder.new { |*x| ary << x }
- y.yield(1, 2)
-
- ary.should == [[1, 2]]
- end
- end
-end
diff --git a/spec/ruby/core/env/assoc_spec.rb b/spec/ruby/core/env/assoc_spec.rb
index c7a388db75..b81be7ddf2 100644
--- a/spec/ruby/core/env/assoc_spec.rb
+++ b/spec/ruby/core/env/assoc_spec.rb
@@ -26,6 +26,6 @@ describe "ENV.assoc" do
end
it "raises TypeError if the argument is not a String and does not respond to #to_str" do
- -> { ENV.assoc(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.assoc(Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String")
end
end
diff --git a/spec/ruby/core/env/clear_spec.rb b/spec/ruby/core/env/clear_spec.rb
index 48b034ba1d..c0d20193ad 100644
--- a/spec/ruby/core/env/clear_spec.rb
+++ b/spec/ruby/core/env/clear_spec.rb
@@ -4,7 +4,7 @@ describe "ENV.clear" do
it "deletes all environment variables" do
orig = ENV.to_hash
begin
- ENV.clear.should equal(ENV)
+ ENV.clear.should.equal?(ENV)
# This used 'env' the helper before. That shells out to 'env' which
# itself sets up certain environment variables before it runs, because
diff --git a/spec/ruby/core/env/clone_spec.rb b/spec/ruby/core/env/clone_spec.rb
new file mode 100644
index 0000000000..bb3c7ff2f8
--- /dev/null
+++ b/spec/ruby/core/env/clone_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../../spec_helper'
+
+describe "ENV#clone" do
+ it "raises ArgumentError when keyword argument 'freeze' is neither nil nor boolean" do
+ -> {
+ ENV.clone(freeze: 1)
+ }.should.raise(ArgumentError)
+ end
+
+ it "raises ArgumentError when keyword argument is not 'freeze'" do
+ -> {
+ ENV.clone(foo: nil)
+ }.should.raise(ArgumentError)
+ end
+
+ it "raises TypeError" do
+ -> {
+ ENV.clone
+ }.should.raise(TypeError, /Cannot clone ENV, use ENV.to_h to get a copy of ENV as a hash/)
+ end
+end
diff --git a/spec/ruby/core/env/delete_if_spec.rb b/spec/ruby/core/env/delete_if_spec.rb
index d2de51c225..4b05064eba 100644
--- a/spec/ruby/core/env/delete_if_spec.rb
+++ b/spec/ruby/core/env/delete_if_spec.rb
@@ -22,15 +22,15 @@ describe "ENV.delete_if" do
end
it "returns ENV when block given" do
- ENV.delete_if { |k, v| ["foo", "bar"].include?(k) }.should equal(ENV)
+ ENV.delete_if { |k, v| ["foo", "bar"].include?(k) }.should.equal?(ENV)
end
it "returns ENV even if nothing deleted" do
- ENV.delete_if { false }.should equal(ENV)
+ ENV.delete_if { false }.should.equal?(ENV)
end
it "returns an Enumerator if no block given" do
- ENV.delete_if.should be_an_instance_of(Enumerator)
+ ENV.delete_if.should.instance_of?(Enumerator)
end
it "deletes pairs through enumerator" do
@@ -42,12 +42,12 @@ describe "ENV.delete_if" do
it "returns ENV from enumerator" do
enum = ENV.delete_if
- enum.each { |k, v| ["foo", "bar"].include?(k) }.should equal(ENV)
+ enum.each { |k, v| ["foo", "bar"].include?(k) }.should.equal?(ENV)
end
it "returns ENV from enumerator even if nothing deleted" do
enum = ENV.delete_if
- enum.each { false }.should equal(ENV)
+ enum.each { false }.should.equal?(ENV)
end
it_behaves_like :enumeratorized_with_origin_size, :delete_if, ENV
diff --git a/spec/ruby/core/env/delete_spec.rb b/spec/ruby/core/env/delete_spec.rb
index 36a1f2624b..db6d07b057 100644
--- a/spec/ruby/core/env/delete_spec.rb
+++ b/spec/ruby/core/env/delete_spec.rb
@@ -30,11 +30,9 @@ describe "ENV.delete" do
ScratchPad.recorded.should == "foo"
end
- ruby_version_is "2.8" do
- it "returns the result of given block if the named environment variable does not exist" do
- ENV.delete("foo")
- ENV.delete("foo") { |name| "bar" }.should == "bar"
- end
+ it "returns the result of given block if the named environment variable does not exist" do
+ ENV.delete("foo")
+ ENV.delete("foo") { |name| "bar" }.should == "bar"
end
it "does not evaluate the block if the environment variable exists" do
@@ -43,7 +41,15 @@ describe "ENV.delete" do
ENV["foo"].should == nil
end
+ it "removes the variable coerced with #to_str" do
+ ENV["foo"] = "bar"
+ k = mock('key')
+ k.should_receive(:to_str).and_return("foo")
+ ENV.delete(k)
+ ENV["foo"].should == nil
+ end
+
it "raises TypeError if the argument is not a String and does not respond to #to_str" do
- -> { ENV.delete(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.delete(Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String")
end
end
diff --git a/spec/ruby/core/env/dup_spec.rb b/spec/ruby/core/env/dup_spec.rb
new file mode 100644
index 0000000000..d8cfac891d
--- /dev/null
+++ b/spec/ruby/core/env/dup_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "ENV#dup" do
+ it "raises TypeError" do
+ -> {
+ ENV.dup
+ }.should.raise(TypeError, /Cannot dup ENV, use ENV.to_h to get a copy of ENV as a hash/)
+ end
+end
diff --git a/spec/ruby/core/env/each_key_spec.rb b/spec/ruby/core/env/each_key_spec.rb
index 0efcb09900..ad2cb560a0 100644
--- a/spec/ruby/core/env/each_key_spec.rb
+++ b/spec/ruby/core/env/each_key_spec.rb
@@ -10,9 +10,9 @@ describe "ENV.each_key" do
ENV.clear
ENV["1"] = "3"
ENV["2"] = "4"
- ENV.each_key { |k| e << k }.should equal(ENV)
- e.should include("1")
- e.should include("2")
+ ENV.each_key { |k| e << k }.should.equal?(ENV)
+ e.should.include?("1")
+ e.should.include?("2")
ensure
ENV.replace orig
end
@@ -20,7 +20,7 @@ describe "ENV.each_key" do
it "returns an Enumerator if called without a block" do
enum = ENV.each_key
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.to_a.should == ENV.keys
end
diff --git a/spec/ruby/core/env/each_value_spec.rb b/spec/ruby/core/env/each_value_spec.rb
index cc3c9ebfb8..6f65e923e6 100644
--- a/spec/ruby/core/env/each_value_spec.rb
+++ b/spec/ruby/core/env/each_value_spec.rb
@@ -10,9 +10,9 @@ describe "ENV.each_value" do
ENV.clear
ENV["1"] = "3"
ENV["2"] = "4"
- ENV.each_value { |v| e << v }.should equal(ENV)
- e.should include("3")
- e.should include("4")
+ ENV.each_value { |v| e << v }.should.equal?(ENV)
+ e.should.include?("3")
+ e.should.include?("4")
ensure
ENV.replace orig
end
@@ -20,7 +20,7 @@ describe "ENV.each_value" do
it "returns an Enumerator if called without a block" do
enum = ENV.each_value
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.to_a.should == ENV.values
end
diff --git a/spec/ruby/core/env/element_reference_spec.rb b/spec/ruby/core/env/element_reference_spec.rb
index 1cd58ace54..e3ac979418 100644
--- a/spec/ruby/core/env/element_reference_spec.rb
+++ b/spec/ruby/core/env/element_reference_spec.rb
@@ -1,5 +1,6 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../spec_helper'
+require_relative 'fixtures/common'
describe "ENV.[]" do
before :each do
@@ -27,7 +28,7 @@ describe "ENV.[]" do
end
it "raises TypeError if the argument is not a String and does not respond to #to_str" do
- -> { ENV[Object.new] }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV[Object.new] }.should.raise(TypeError, "no implicit conversion of Object into String")
end
platform_is :windows do
@@ -58,7 +59,7 @@ describe "ENV.[]" do
it "uses the locale encoding if Encoding.default_internal is nil" do
Encoding.default_internal = nil
- locale = Encoding.find('locale')
+ locale = ENVSpecs.encoding
locale = Encoding::BINARY if locale == Encoding::US_ASCII
ENV[@variable] = "\xC3\xB8"
ENV[@variable].encoding.should == locale
@@ -70,6 +71,6 @@ describe "ENV.[]" do
ENV[@variable] = ""
Encoding.default_internal = Encoding::IBM437
- ENV[@variable].encoding.should equal(Encoding::IBM437)
+ ENV[@variable].encoding.should.equal?(Encoding::IBM437)
end
end
diff --git a/spec/ruby/core/env/except_spec.rb b/spec/ruby/core/env/except_spec.rb
new file mode 100644
index 0000000000..fb8f3b7536
--- /dev/null
+++ b/spec/ruby/core/env/except_spec.rb
@@ -0,0 +1,34 @@
+require_relative 'spec_helper'
+require_relative 'shared/to_hash'
+
+describe "ENV.except" do
+ before do
+ @orig_hash = ENV.to_hash
+ end
+
+ after do
+ ENV.replace @orig_hash
+ end
+
+ # Testing the method without arguments is covered via
+ it_behaves_like :env_to_hash, :except
+
+ it "returns a hash without the requested subset" do
+ ENV.clear
+
+ ENV['one'] = '1'
+ ENV['two'] = '2'
+ ENV['three'] = '3'
+
+ ENV.except('one', 'three').should == { 'two' => '2' }
+ end
+
+ it "ignores keys not present in the original hash" do
+ ENV.clear
+
+ ENV['one'] = '1'
+ ENV['two'] = '2'
+
+ ENV.except('one', 'three').should == { 'two' => '2' }
+ end
+end
diff --git a/spec/ruby/core/env/fetch_spec.rb b/spec/ruby/core/env/fetch_spec.rb
index ef8f0a4ed3..a2ec79c62b 100644
--- a/spec/ruby/core/env/fetch_spec.rb
+++ b/spec/ruby/core/env/fetch_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative '../../shared/hash/key_error'
+require_relative 'fixtures/common'
describe "ENV.fetch" do
before :each do
@@ -15,7 +16,7 @@ describe "ENV.fetch" do
end
it "raises a TypeError if the key is not a String" do
- -> { ENV.fetch Object.new }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.fetch Object.new }.should.raise(TypeError, "no implicit conversion of Object into String")
end
context "when the key is not found" do
@@ -24,7 +25,7 @@ describe "ENV.fetch" do
it "formats the object with #inspect in the KeyError message" do
-> {
ENV.fetch('foo')
- }.should raise_error(KeyError, 'key not found: "foo"')
+ }.should.raise(KeyError, 'key not found: "foo"')
end
end
@@ -46,7 +47,7 @@ describe "ENV.fetch" do
it "warns on block and default parameter given" do
-> do
- ENV.fetch("foo", "default") { "bar" }.should == "bar"
+ ENV.fetch("foo", "default") { "bar" }.should == "bar"
end.should complain(/block supersedes default value argument/)
end
@@ -57,6 +58,6 @@ describe "ENV.fetch" do
it "uses the locale encoding" do
ENV["foo"] = "bar"
- ENV.fetch("foo").encoding.should == Encoding.find('locale')
+ ENV.fetch("foo").encoding.should == ENVSpecs.encoding
end
end
diff --git a/spec/ruby/core/env/fetch_values_spec.rb b/spec/ruby/core/env/fetch_values_spec.rb
new file mode 100644
index 0000000000..302cde2fd1
--- /dev/null
+++ b/spec/ruby/core/env/fetch_values_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+ruby_version_is "4.1" do
+ describe "ENV.fetch_values" do
+ before :each do
+ @saved_foo = ENV["foo"]
+ @saved_bar = ENV["bar"]
+ ENV.delete("foo")
+ ENV.delete("bar")
+ end
+
+ after :each do
+ ENV["foo"] = @saved_foo
+ ENV["bar"] = @saved_bar
+ end
+
+ it "returns an array of the values corresponding to the given keys" do
+ ENV["foo"] = "oof"
+ ENV["bar"] = "rab"
+ ENV.fetch_values("bar", "foo").should == ["rab", "oof"]
+ end
+
+ it "returns the default value from block" do
+ ENV["foo"] = "oof"
+ ENV.fetch_values("bar") { |key| "`#{key}' is not found" }.should == ["`bar' is not found"]
+ ENV.fetch_values("bar", "foo") { |key| "`#{key}' is not found" }.should == ["`bar' is not found", "oof"]
+ end
+
+ it "returns an empty array if no keys specified" do
+ ENV.fetch_values.should == []
+ end
+
+ it "raises KeyError when there is no matching key" do
+ ENV["foo"] = "oof"
+ ENV["bar"] = "rab"
+ -> {
+ ENV.fetch_values("bar", "y", "foo", "z")
+ }.should.raise(KeyError, 'key not found: "y"')
+ end
+
+ it "uses the locale encoding" do
+ ENV.fetch_values(ENV.keys.first).first.encoding.should == ENVSpecs.encoding
+ end
+
+ it "raises TypeError when a key is not coercible to String" do
+ ENV["foo"] = "oof"
+ -> { ENV.fetch_values("foo", Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String")
+ end
+ end
+end
diff --git a/spec/ruby/core/env/filter_spec.rb b/spec/ruby/core/env/filter_spec.rb
index ba18a3b33b..52f8b79a0b 100644
--- a/spec/ruby/core/env/filter_spec.rb
+++ b/spec/ruby/core/env/filter_spec.rb
@@ -2,14 +2,12 @@ require_relative '../../spec_helper'
require_relative '../enumerable/shared/enumeratorized'
require_relative 'shared/select'
-ruby_version_is "2.6" do
- describe "ENV.filter!" do
- it_behaves_like :env_select!, :filter!
- it_behaves_like :enumeratorized_with_origin_size, :filter!, ENV
- end
+describe "ENV.filter!" do
+ it_behaves_like :env_select!, :filter!
+ it_behaves_like :enumeratorized_with_origin_size, :filter!, ENV
+end
- describe "ENV.filter" do
- it_behaves_like :env_select, :filter
- it_behaves_like :enumeratorized_with_origin_size, :filter, ENV
- end
+describe "ENV.filter" do
+ it_behaves_like :env_select, :filter
+ it_behaves_like :enumeratorized_with_origin_size, :filter, ENV
end
diff --git a/spec/ruby/core/env/fixtures/common.rb b/spec/ruby/core/env/fixtures/common.rb
new file mode 100644
index 0000000000..8d5057614d
--- /dev/null
+++ b/spec/ruby/core/env/fixtures/common.rb
@@ -0,0 +1,9 @@
+module ENVSpecs
+ def self.encoding
+ locale = Encoding.find('locale')
+ if ruby_version_is '3' and platform_is :windows
+ locale = Encoding::UTF_8
+ end
+ locale
+ end
+end
diff --git a/spec/ruby/core/env/index_spec.rb b/spec/ruby/core/env/index_spec.rb
deleted file mode 100644
index 43875f5a50..0000000000
--- a/spec/ruby/core/env/index_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'shared/key'
-
-describe "ENV.index" do
- it_behaves_like :env_key, :index
-
- it "warns about deprecation" do
- -> do
- ENV.index("foo")
- end.should complain(/warning: ENV.index is deprecated; use ENV.key/)
- end
-end
diff --git a/spec/ruby/core/env/indexes_spec.rb b/spec/ruby/core/env/indexes_spec.rb
deleted file mode 100644
index e724feaa39..0000000000
--- a/spec/ruby/core/env/indexes_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../spec_helper'
diff --git a/spec/ruby/core/env/indices_spec.rb b/spec/ruby/core/env/indices_spec.rb
deleted file mode 100644
index e724feaa39..0000000000
--- a/spec/ruby/core/env/indices_spec.rb
+++ /dev/null
@@ -1 +0,0 @@
-require_relative '../../spec_helper'
diff --git a/spec/ruby/core/env/inspect_spec.rb b/spec/ruby/core/env/inspect_spec.rb
index 3c611c24a1..7dd92b120f 100644
--- a/spec/ruby/core/env/inspect_spec.rb
+++ b/spec/ruby/core/env/inspect_spec.rb
@@ -4,7 +4,7 @@ describe "ENV.inspect" do
it "returns a String that looks like a Hash with real data" do
ENV["foo"] = "bar"
- ENV.inspect.should =~ /\{.*"foo"=>"bar".*\}/
+ ENV.inspect.should =~ /\{.*"foo" *=> *"bar".*\}/
ENV.delete "foo"
end
diff --git a/spec/ruby/core/env/keep_if_spec.rb b/spec/ruby/core/env/keep_if_spec.rb
index 64b6a207d0..f24981e216 100644
--- a/spec/ruby/core/env/keep_if_spec.rb
+++ b/spec/ruby/core/env/keep_if_spec.rb
@@ -22,15 +22,15 @@ describe "ENV.keep_if" do
end
it "returns ENV when block given" do
- ENV.keep_if { |k, v| !["foo", "bar"].include?(k) }.should equal(ENV)
+ ENV.keep_if { |k, v| !["foo", "bar"].include?(k) }.should.equal?(ENV)
end
it "returns ENV even if nothing deleted" do
- ENV.keep_if { true }.should equal(ENV)
+ ENV.keep_if { true }.should.equal?(ENV)
end
it "returns an Enumerator if no block given" do
- ENV.keep_if.should be_an_instance_of(Enumerator)
+ ENV.keep_if.should.instance_of?(Enumerator)
end
it "deletes pairs through enumerator" do
@@ -42,12 +42,12 @@ describe "ENV.keep_if" do
it "returns ENV from enumerator" do
enum = ENV.keep_if
- enum.each { |k, v| !["foo", "bar"].include?(k) }.should equal(ENV)
+ enum.each { |k, v| !["foo", "bar"].include?(k) }.should.equal?(ENV)
end
it "returns ENV from enumerator even if nothing deleted" do
enum = ENV.keep_if
- enum.each { true }.should equal(ENV)
+ enum.each { true }.should.equal?(ENV)
end
it_behaves_like :enumeratorized_with_origin_size, :keep_if, ENV
diff --git a/spec/ruby/core/env/key_spec.rb b/spec/ruby/core/env/key_spec.rb
index 82cfbefa39..677cf35216 100644
--- a/spec/ruby/core/env/key_spec.rb
+++ b/spec/ruby/core/env/key_spec.rb
@@ -1,11 +1,39 @@
require_relative '../../spec_helper'
require_relative 'shared/include'
-require_relative 'shared/key'
describe "ENV.key?" do
it_behaves_like :env_include, :key?
end
describe "ENV.key" do
- it_behaves_like :env_key, :key
+ before :each do
+ @saved_foo = ENV["foo"]
+ end
+
+ after :each do
+ ENV["foo"] = @saved_foo
+ end
+
+ it "returns the index associated with the passed value" do
+ ENV["foo"] = "bar"
+ ENV.key("bar").should == "foo"
+ end
+
+ it "returns nil if the passed value is not found" do
+ ENV.delete("foo")
+ ENV.key("foo").should == nil
+ end
+
+ it "coerces the key element with #to_str" do
+ ENV["foo"] = "bar"
+ k = mock('key')
+ k.should_receive(:to_str).and_return("bar")
+ ENV.key(k).should == "foo"
+ end
+
+ it "raises TypeError if the argument is not a String and does not respond to #to_str" do
+ -> {
+ ENV.key(Object.new)
+ }.should.raise(TypeError, "no implicit conversion of Object into String")
+ end
end
diff --git a/spec/ruby/core/env/length_spec.rb b/spec/ruby/core/env/length_spec.rb
index 536af9edf5..c6f9062892 100644
--- a/spec/ruby/core/env/length_spec.rb
+++ b/spec/ruby/core/env/length_spec.rb
@@ -2,5 +2,5 @@ require_relative '../../spec_helper'
require_relative 'shared/length'
describe "ENV.length" do
- it_behaves_like :env_length, :length
+ it_behaves_like :env_length, :length
end
diff --git a/spec/ruby/core/env/merge_spec.rb b/spec/ruby/core/env/merge_spec.rb
index b418cd11f4..f10662cf79 100644
--- a/spec/ruby/core/env/merge_spec.rb
+++ b/spec/ruby/core/env/merge_spec.rb
@@ -1,8 +1,6 @@
require_relative '../../spec_helper'
require_relative 'shared/update'
-ruby_version_is "2.7" do
- describe "ENV.merge!" do
- it_behaves_like :env_update, :merge!
- end
+describe "ENV.merge!" do
+ it_behaves_like :env_update, :merge!
end
diff --git a/spec/ruby/core/env/rassoc_spec.rb b/spec/ruby/core/env/rassoc_spec.rb
index ab9fe68088..e4ac5ce5e2 100644
--- a/spec/ruby/core/env/rassoc_spec.rb
+++ b/spec/ruby/core/env/rassoc_spec.rb
@@ -22,7 +22,7 @@ describe "ENV.rassoc" do
[
["foo", "bar"],
["baz", "bar"],
- ].should include(ENV.rassoc("bar"))
+ ].should.include?(ENV.rassoc("bar"))
end
it "returns nil if no environment variable with the given value exists" do
diff --git a/spec/ruby/core/env/reject_spec.rb b/spec/ruby/core/env/reject_spec.rb
index 6a9794925d..4df864493d 100644
--- a/spec/ruby/core/env/reject_spec.rb
+++ b/spec/ruby/core/env/reject_spec.rb
@@ -25,15 +25,15 @@ describe "ENV.reject!" do
it "returns itself or nil" do
ENV.reject! { false }.should == nil
ENV["foo"] = "bar"
- ENV.reject! { |k, v| k == "foo" }.should equal(ENV)
+ ENV.reject! { |k, v| k == "foo" }.should.equal?(ENV)
ENV["foo"].should == nil
end
it "returns an Enumerator if called without a block" do
ENV["foo"] = "bar"
enum = ENV.reject!
- enum.should be_an_instance_of(Enumerator)
- enum.each { |k, v| k == "foo" }.should equal(ENV)
+ enum.should.instance_of?(Enumerator)
+ enum.each { |k, v| k == "foo" }.should.equal?(ENV)
ENV["foo"].should == nil
end
@@ -41,7 +41,7 @@ describe "ENV.reject!" do
orig = ENV.to_hash
begin
ENV.clear
- -> { ENV.reject! }.should_not raise_error(LocalJumpError)
+ -> { ENV.reject! }.should_not.raise(LocalJumpError)
ensure
ENV.replace orig
end
@@ -76,13 +76,13 @@ describe "ENV.reject" do
end
it "returns a Hash" do
- ENV.reject { false }.should be_kind_of(Hash)
+ ENV.reject { false }.should.is_a?(Hash)
end
it "returns an Enumerator if called without a block" do
ENV["foo"] = "bar"
enum = ENV.reject
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.each { |k, v| k == "foo"}
ENV["foo"] = nil
end
@@ -91,7 +91,7 @@ describe "ENV.reject" do
orig = ENV.to_hash
begin
ENV.clear
- -> { ENV.reject }.should_not raise_error(LocalJumpError)
+ -> { ENV.reject }.should_not.raise(LocalJumpError)
ensure
ENV.replace orig
end
diff --git a/spec/ruby/core/env/replace_spec.rb b/spec/ruby/core/env/replace_spec.rb
index 9fc67643d1..27eb3e45dd 100644
--- a/spec/ruby/core/env/replace_spec.rb
+++ b/spec/ruby/core/env/replace_spec.rb
@@ -11,41 +11,41 @@ describe "ENV.replace" do
end
it "replaces ENV with a Hash" do
- ENV.replace("foo" => "0", "bar" => "1").should equal(ENV)
+ ENV.replace("foo" => "0", "bar" => "1").should.equal?(ENV)
ENV.size.should == 2
ENV["foo"].should == "0"
ENV["bar"].should == "1"
end
it "raises TypeError if the argument is not a Hash" do
- -> { ENV.replace(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into Hash")
+ -> { ENV.replace(Object.new) }.should.raise(TypeError, "no implicit conversion of Object into Hash")
ENV.to_hash.should == @orig
end
it "raises TypeError if a key is not a String" do
- -> { ENV.replace(Object.new => "0") }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.replace(Object.new => "0") }.should.raise(TypeError, "no implicit conversion of Object into String")
ENV.to_hash.should == @orig
end
it "raises TypeError if a value is not a String" do
- -> { ENV.replace("foo" => Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.replace("foo" => Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String")
ENV.to_hash.should == @orig
end
it "raises Errno::EINVAL when the key contains the '=' character" do
- -> { ENV.replace("foo=" =>"bar") }.should raise_error(Errno::EINVAL)
+ -> { ENV.replace("foo=" =>"bar") }.should.raise(Errno::EINVAL)
end
it "raises Errno::EINVAL when the key is an empty string" do
- -> { ENV.replace("" => "bar") }.should raise_error(Errno::EINVAL)
+ -> { ENV.replace("" => "bar") }.should.raise(Errno::EINVAL)
end
it "does not accept good data preceding an error" do
- -> { ENV.replace("foo" => "1", Object.new => Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.replace("foo" => "1", Object.new => Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String")
end
it "does not accept good data following an error" do
- -> { ENV.replace(Object.new => Object.new, "foo" => "0") }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.replace(Object.new => Object.new, "foo" => "0") }.should.raise(TypeError, "no implicit conversion of Object into String")
ENV.to_hash.should == @orig
end
end
diff --git a/spec/ruby/core/env/shared/each.rb b/spec/ruby/core/env/shared/each.rb
index d901b854c4..0661ca924c 100644
--- a/spec/ruby/core/env/shared/each.rb
+++ b/spec/ruby/core/env/shared/each.rb
@@ -8,9 +8,9 @@ describe :env_each, shared: true do
ENV.clear
ENV["foo"] = "bar"
ENV["baz"] = "boo"
- ENV.send(@method) { |k, v| e << [k, v] }.should equal(ENV)
- e.should include(["foo", "bar"])
- e.should include(["baz", "boo"])
+ ENV.send(@method) { |k, v| e << [k, v] }.should.equal?(ENV)
+ e.should.include?(["foo", "bar"])
+ e.should.include?(["baz", "boo"])
ensure
ENV.replace orig
end
@@ -18,7 +18,7 @@ describe :env_each, shared: true do
it "returns an Enumerator if called without a block" do
enum = ENV.send(@method)
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.each do |name, value|
ENV[name].should == value
end
@@ -55,9 +55,9 @@ describe :env_each, shared: true do
Encoding.default_internal = internal = Encoding::IBM437
ENV.send(@method) do |key, value|
- key.encoding.should equal(internal)
+ key.encoding.should.equal?(internal)
if value.ascii_only?
- value.encoding.should equal(internal)
+ value.encoding.should.equal?(internal)
end
end
end
diff --git a/spec/ruby/core/env/shared/include.rb b/spec/ruby/core/env/shared/include.rb
index 3efcd523d6..ceca02e3eb 100644
--- a/spec/ruby/core/env/shared/include.rb
+++ b/spec/ruby/core/env/shared/include.rb
@@ -17,7 +17,14 @@ describe :env_include, shared: true do
ENV.send(@method, "foo").should == false
end
+ it "coerces the key with #to_str" do
+ ENV["foo"] = "bar"
+ k = mock('key')
+ k.should_receive(:to_str).and_return("foo")
+ ENV.send(@method, k).should == true
+ end
+
it "raises TypeError if the argument is not a String and does not respond to #to_str" do
- -> { ENV.send(@method, Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.send(@method, Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String")
end
end
diff --git a/spec/ruby/core/env/shared/key.rb b/spec/ruby/core/env/shared/key.rb
deleted file mode 100644
index fcb3a9b8c5..0000000000
--- a/spec/ruby/core/env/shared/key.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-describe :env_key, shared: true do
- before :each do
- @saved_foo = ENV["foo"]
- end
-
- after :each do
- ENV["foo"] = @saved_foo
- end
-
- it "returns the index associated with the passed value" do
- ENV["foo"] = "bar"
- ENV.send(@method, "bar").should == "foo"
- end
-
- it "returns nil if the passed value is not found" do
- ENV.delete("foo")
- ENV.send(@method, "foo").should be_nil
- end
-
- it "raises TypeError if the argument is not a String and does not respond to #to_str" do
- -> { ENV.send(@method, Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
- end
-end
diff --git a/spec/ruby/core/env/shared/select.rb b/spec/ruby/core/env/shared/select.rb
index 75ba112a32..8ec648a637 100644
--- a/spec/ruby/core/env/shared/select.rb
+++ b/spec/ruby/core/env/shared/select.rb
@@ -14,7 +14,7 @@ describe :env_select, shared: true do
it "returns an Enumerator when no block is given" do
enum = ENV.send(@method)
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
end
it "selects via the enumerator" do
@@ -49,7 +49,7 @@ describe :env_select!, shared: true do
end
it "returns an Enumerator if called without a block" do
- ENV.send(@method).should be_an_instance_of(Enumerator)
+ ENV.send(@method).should.instance_of?(Enumerator)
end
it "selects via the enumerator" do
diff --git a/spec/ruby/core/env/shared/store.rb b/spec/ruby/core/env/shared/store.rb
index d6265c66a5..388208a8ac 100644
--- a/spec/ruby/core/env/shared/store.rb
+++ b/spec/ruby/core/env/shared/store.rb
@@ -14,13 +14,13 @@ describe :env_store, shared: true do
it "returns the value" do
value = "bar"
- ENV.send(@method, "foo", value).should equal(value)
+ ENV.send(@method, "foo", value).should.equal?(value)
end
it "deletes the environment variable when the value is nil" do
ENV["foo"] = "bar"
ENV.send(@method, "foo", nil)
- ENV.key?("foo").should be_false
+ ENV.key?("foo").should == false
end
it "coerces the key argument with #to_str" do
@@ -38,23 +38,23 @@ describe :env_store, shared: true do
end
it "raises TypeError when the key is not coercible to String" do
- -> { ENV.send(@method, Object.new, "bar") }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.send(@method, Object.new, "bar") }.should.raise(TypeError, "no implicit conversion of Object into String")
end
it "raises TypeError when the value is not coercible to String" do
- -> { ENV.send(@method, "foo", Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.send(@method, "foo", Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String")
end
it "raises Errno::EINVAL when the key contains the '=' character" do
- -> { ENV.send(@method, "foo=", "bar") }.should raise_error(Errno::EINVAL)
+ -> { ENV.send(@method, "foo=", "bar") }.should.raise(Errno::EINVAL)
end
it "raises Errno::EINVAL when the key is an empty string" do
- -> { ENV.send(@method, "", "bar") }.should raise_error(Errno::EINVAL)
+ -> { ENV.send(@method, "", "bar") }.should.raise(Errno::EINVAL)
end
it "does nothing when the key is not a valid environment variable key and the value is nil" do
ENV.send(@method, "foo=", nil)
- ENV.key?("foo=").should be_false
+ ENV.key?("foo=").should == false
end
end
diff --git a/spec/ruby/core/env/shared/to_hash.rb b/spec/ruby/core/env/shared/to_hash.rb
index a0d4d7ce69..7868690c38 100644
--- a/spec/ruby/core/env/shared/to_hash.rb
+++ b/spec/ruby/core/env/shared/to_hash.rb
@@ -10,7 +10,7 @@ describe :env_to_hash, shared: true do
it "returns the ENV as a hash" do
ENV["foo"] = "bar"
h = ENV.send(@method)
- h.should be_an_instance_of(Hash)
+ h.should.instance_of?(Hash)
h["foo"].should == "bar"
end
@@ -24,7 +24,7 @@ describe :env_to_hash, shared: true do
it "duplicates the ENV when converting to a Hash" do
h = ENV.send(@method)
- h.should_not equal ENV
+ h.should_not.equal? ENV
h.size.should == ENV.size
h.each_pair do |k, v|
ENV[k].should == v
diff --git a/spec/ruby/core/env/shared/update.rb b/spec/ruby/core/env/shared/update.rb
index 129a56544c..112cc2505d 100644
--- a/spec/ruby/core/env/shared/update.rb
+++ b/spec/ruby/core/env/shared/update.rb
@@ -10,13 +10,19 @@ describe :env_update, shared: true do
end
it "adds the parameter hash to ENV, returning ENV" do
- ENV.send(@method, "foo" => "0", "bar" => "1").should equal(ENV)
+ ENV.send(@method, "foo" => "0", "bar" => "1").should.equal?(ENV)
ENV["foo"].should == "0"
ENV["bar"].should == "1"
end
+ it "adds the multiple parameter hashes to ENV, returning ENV" do
+ ENV.send(@method, {"foo" => "multi1"}, {"bar" => "multi2"}).should.equal?(ENV)
+ ENV["foo"].should == "multi1"
+ ENV["bar"].should == "multi2"
+ end
+
it "returns ENV when no block given" do
- ENV.send(@method, {"foo" => "0", "bar" => "1"}).should equal(ENV)
+ ENV.send(@method, {"foo" => "0", "bar" => "1"}).should.equal?(ENV)
end
it "yields key, the old value and the new value when replacing an entry" do
@@ -39,43 +45,41 @@ describe :env_update, shared: true do
ENV["bar"].should == "5"
end
- ruby_version_is "2.7" do
- # BUG: https://bugs.ruby-lang.org/issues/16192
- it "does not evaluate the block when the name is new" do
- ENV.delete("bar")
- ENV.send @method, {"foo" => "0"}
- ENV.send(@method, "bar" => "1") { |key, old, new| fail "Should not get here" }
- ENV["bar"].should == "1"
- end
+ # BUG: https://bugs.ruby-lang.org/issues/16192
+ it "does not evaluate the block when the name is new" do
+ ENV.delete("bar")
+ ENV.send @method, {"foo" => "0"}
+ ENV.send(@method, "bar" => "1") { |key, old, new| fail "Should not get here" }
+ ENV["bar"].should == "1"
+ end
- # BUG: https://bugs.ruby-lang.org/issues/16192
- it "does not use the block's return value as the value when the name is new" do
- ENV.delete("bar")
- ENV.send @method, {"foo" => "0"}
- ENV.send(@method, "bar" => "1") { |key, old, new| "Should not use this value" }
- ENV["foo"].should == "0"
- ENV["bar"].should == "1"
- end
+ # BUG: https://bugs.ruby-lang.org/issues/16192
+ it "does not use the block's return value as the value when the name is new" do
+ ENV.delete("bar")
+ ENV.send @method, {"foo" => "0"}
+ ENV.send(@method, "bar" => "1") { |key, old, new| "Should not use this value" }
+ ENV["foo"].should == "0"
+ ENV["bar"].should == "1"
end
it "returns ENV when block given" do
- ENV.send(@method, {"foo" => "0", "bar" => "1"}){}.should equal(ENV)
+ ENV.send(@method, {"foo" => "0", "bar" => "1"}){}.should.equal?(ENV)
end
it "raises TypeError when a name is not coercible to String" do
- -> { ENV.send @method, Object.new => "0" }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.send @method, Object.new => "0" }.should.raise(TypeError, "no implicit conversion of Object into String")
end
it "raises TypeError when a value is not coercible to String" do
- -> { ENV.send @method, "foo" => Object.new }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.send @method, "foo" => Object.new }.should.raise(TypeError, "no implicit conversion of Object into String")
end
it "raises Errno::EINVAL when a name contains the '=' character" do
- -> { ENV.send(@method, "foo=" => "bar") }.should raise_error(Errno::EINVAL)
+ -> { ENV.send(@method, "foo=" => "bar") }.should.raise(Errno::EINVAL)
end
it "raises Errno::EINVAL when a name is an empty string" do
- -> { ENV.send(@method, "" => "bar") }.should raise_error(Errno::EINVAL)
+ -> { ENV.send(@method, "" => "bar") }.should.raise(Errno::EINVAL)
end
it "updates good data preceding an error" do
diff --git a/spec/ruby/core/env/shared/value.rb b/spec/ruby/core/env/shared/value.rb
index bef96b5fef..c2b5025465 100644
--- a/spec/ruby/core/env/shared/value.rb
+++ b/spec/ruby/core/env/shared/value.rb
@@ -16,6 +16,13 @@ describe :env_value, shared: true do
ENV.send(@method, "foo").should == false
end
+ it "coerces the value element with #to_str" do
+ ENV["foo"] = "bar"
+ v = mock('value')
+ v.should_receive(:to_str).and_return("bar")
+ ENV.send(@method, v).should == true
+ end
+
it "returns nil if the argument is not a String and does not respond to #to_str" do
ENV.send(@method, Object.new).should == nil
end
diff --git a/spec/ruby/core/env/shift_spec.rb b/spec/ruby/core/env/shift_spec.rb
index 0fe08d4f6d..52bd0f9139 100644
--- a/spec/ruby/core/env/shift_spec.rb
+++ b/spec/ruby/core/env/shift_spec.rb
@@ -1,28 +1,5 @@
require_relative '../../spec_helper'
-
-describe "ENV.shift" do
- it "returns a pair and deletes it" do
- ENV.should_not.empty?
- orig = ENV.to_hash
- begin
- pair = ENV.shift
- ENV.has_key?(pair.first).should == false
- ensure
- ENV.replace orig
- end
- ENV.has_key?(pair.first).should == true
- end
-
- it "returns nil if ENV.empty?" do
- orig = ENV.to_hash
- begin
- ENV.clear
- ENV.shift.should == nil
- ensure
- ENV.replace orig
- end
- end
-end
+require_relative 'fixtures/common'
describe "ENV.shift" do
before :each do
@@ -31,6 +8,7 @@ describe "ENV.shift" do
@internal = Encoding.default_internal
Encoding.default_external = Encoding::BINARY
+ ENV.replace({"FOO"=>"BAR"})
end
after :each do
@@ -39,19 +17,31 @@ describe "ENV.shift" do
ENV.replace @orig
end
+ it "returns a pair and deletes it" do
+ ENV.should.has_key?("FOO")
+ pair = ENV.shift
+ pair.should == ["FOO", "BAR"]
+ ENV.should_not.has_key?("FOO")
+ end
+
+ it "returns nil if ENV.empty?" do
+ ENV.clear
+ ENV.shift.should == nil
+ end
+
it "uses the locale encoding if Encoding.default_internal is nil" do
Encoding.default_internal = nil
pair = ENV.shift
- pair.first.encoding.should equal(Encoding.find("locale"))
- pair.last.encoding.should equal(Encoding.find("locale"))
+ pair.first.encoding.should.equal?(ENVSpecs.encoding)
+ pair.last.encoding.should.equal?(ENVSpecs.encoding)
end
it "transcodes from the locale encoding to Encoding.default_internal if set" do
Encoding.default_internal = Encoding::IBM437
pair = ENV.shift
- pair.first.encoding.should equal(Encoding::IBM437)
- pair.last.encoding.should equal(Encoding::IBM437)
+ pair.first.encoding.should.equal?(Encoding::IBM437)
+ pair.last.encoding.should.equal?(Encoding::IBM437)
end
end
diff --git a/spec/ruby/core/env/size_spec.rb b/spec/ruby/core/env/size_spec.rb
index f050e9e5a9..7c8072481e 100644
--- a/spec/ruby/core/env/size_spec.rb
+++ b/spec/ruby/core/env/size_spec.rb
@@ -2,5 +2,5 @@ require_relative '../../spec_helper'
require_relative 'shared/length'
describe "ENV.size" do
- it_behaves_like :env_length, :size
+ it_behaves_like :env_length, :size
end
diff --git a/spec/ruby/core/env/slice_spec.rb b/spec/ruby/core/env/slice_spec.rb
index 1ac8a303f8..4c0416547d 100644
--- a/spec/ruby/core/env/slice_spec.rb
+++ b/spec/ruby/core/env/slice_spec.rb
@@ -1,29 +1,37 @@
require_relative '../../spec_helper'
-ruby_version_is "2.6" do
- describe "ENV.slice" do
- before :each do
- @saved_foo = ENV["foo"]
- @saved_bar = ENV["bar"]
- ENV["foo"] = "0"
- ENV["bar"] = "1"
- end
+describe "ENV.slice" do
+ before :each do
+ @saved_foo = ENV["foo"]
+ @saved_bar = ENV["bar"]
+ ENV["foo"] = "0"
+ ENV["bar"] = "1"
+ end
+
+ after :each do
+ ENV["foo"] = @saved_foo
+ ENV["bar"] = @saved_bar
+ end
- after :each do
- ENV["foo"] = @saved_foo
- ENV["bar"] = @saved_bar
- end
+ it "returns a hash of the given environment variable names and their values" do
+ ENV.slice("foo", "bar").should == {"foo" => "0", "bar" => "1"}
+ end
- it "returns a hash of the given environment variable names and their values" do
- ENV.slice("foo", "bar").should == {"foo" => "0", "bar" => "1"}
- end
+ it "ignores each String that is not an environment variable name" do
+ ENV.slice("foo", "boo", "bar").should == {"foo" => "0", "bar" => "1"}
+ end
- it "ignores each String that is not an environment variable name" do
- ENV.slice("foo", "boo", "bar").should == {"foo" => "0", "bar" => "1"}
- end
+ it "returns the values for the keys coerced with #to_str, but keeps the original objects as result keys" do
+ foo = mock('key 1')
+ foo.should_receive(:to_str).and_return("foo")
+ boo = mock('key 2')
+ boo.should_receive(:to_str).and_return("boo")
+ bar = mock('key 3')
+ bar.should_receive(:to_str).and_return("bar")
+ ENV.slice(foo, boo, bar).should == {foo => "0", bar => "1"}
+ end
- it "raises TypeError if any argument is not a String and does not respond to #to_str" do
- -> { ENV.slice(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
- end
+ it "raises TypeError if any argument is not a String and does not respond to #to_str" do
+ -> { ENV.slice(Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String")
end
end
diff --git a/spec/ruby/core/env/to_a_spec.rb b/spec/ruby/core/env/to_a_spec.rb
index 39e3877b48..2b1649281f 100644
--- a/spec/ruby/core/env/to_a_spec.rb
+++ b/spec/ruby/core/env/to_a_spec.rb
@@ -6,7 +6,10 @@ describe "ENV.to_a" do
a = ENV.to_a
a.is_a?(Array).should == true
a.size.should == ENV.size
- ENV.each_pair { |k, v| a.should include([k, v])}
+ a.each { |k,v| ENV[k].should == v }
+
+ a.first.should.is_a?(Array)
+ a.first.size.should == 2
end
it "returns the entries in the locale encoding" do
diff --git a/spec/ruby/core/env/to_h_spec.rb b/spec/ruby/core/env/to_h_spec.rb
index 65cdb59951..7e0fef2120 100644
--- a/spec/ruby/core/env/to_h_spec.rb
+++ b/spec/ruby/core/env/to_h_spec.rb
@@ -4,57 +4,67 @@ require_relative 'shared/to_hash'
describe "ENV.to_h" do
it_behaves_like :env_to_hash, :to_h
- ruby_version_is "2.6" do
- context "with block" do
- before do
- @orig_hash = ENV.to_hash
- end
-
- after do
- ENV.replace @orig_hash
- end
-
- it "converts [key, value] pairs returned by the block to a hash" do
- ENV.replace("a" => "b", "c" => "d")
- ENV.to_h { |k, v| [k, v.upcase] }.should == { 'a' => "B", 'c' => "D" }
- end
-
- it "does not require the array elements to be strings" do
- ENV.replace("a" => "b", "c" => "d")
- ENV.to_h { |k, v| [k.to_sym, v.to_sym] }.should == { :a => :b, :c => :d }
- end
-
- it "raises ArgumentError if block returns longer or shorter array" do
- -> do
- ENV.to_h { |k, v| [k, v.upcase, 1] }
- end.should raise_error(ArgumentError, /element has wrong array length/)
-
- -> do
- ENV.to_h { |k, v| [k] }
- end.should raise_error(ArgumentError, /element has wrong array length/)
- end
-
- it "raises TypeError if block returns something other than Array" do
- -> do
- ENV.to_h { |k, v| "not-array" }
- end.should raise_error(TypeError, /wrong element type String/)
- end
-
- it "coerces returned pair to Array with #to_ary" do
- x = mock('x')
- x.stub!(:to_ary).and_return([:b, 'b'])
-
- ENV.to_h { |k| x }.should == { :b => 'b' }
- end
-
- it "does not coerce returned pair to Array with #to_a" do
- x = mock('x')
- x.stub!(:to_a).and_return([:b, 'b'])
-
- -> do
- ENV.to_h { |k| x }
- end.should raise_error(TypeError, /wrong element type MockObject/)
- end
+ context "with block" do
+ before do
+ @orig_hash = ENV.to_hash
+ end
+
+ after do
+ ENV.replace @orig_hash
+ end
+
+ it "converts [key, value] pairs returned by the block to a hash" do
+ ENV.replace("a" => "b", "c" => "d")
+ ENV.to_h { |k, v| [k, v.upcase] }.should == { 'a' => "B", 'c' => "D" }
+ end
+
+ it "passes to a block each pair's key and value as separate arguments" do
+ ENV.replace("a" => "b", "c" => "d")
+
+ ScratchPad.record []
+ ENV.to_h { |k, v| ScratchPad << [k, v]; [k, v] }
+ ScratchPad.recorded.sort.should == [["a", "b"], ["c", "d"]]
+
+ ScratchPad.record []
+ ENV.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
+ ScratchPad.recorded.sort.should == [["a", "b"], ["c", "d"]]
+ end
+
+ it "does not require the array elements to be strings" do
+ ENV.replace("a" => "b", "c" => "d")
+ ENV.to_h { |k, v| [k.to_sym, v.to_sym] }.should == { :a => :b, :c => :d }
+ end
+
+ it "raises ArgumentError if block returns longer or shorter array" do
+ -> do
+ ENV.to_h { |k, v| [k, v.upcase, 1] }
+ end.should.raise(ArgumentError, /element has wrong array length/)
+
+ -> do
+ ENV.to_h { |k, v| [k] }
+ end.should.raise(ArgumentError, /element has wrong array length/)
+ end
+
+ it "raises TypeError if block returns something other than Array" do
+ -> do
+ ENV.to_h { |k, v| "not-array" }
+ end.should.raise(TypeError, /wrong element type String/)
+ end
+
+ it "coerces returned pair to Array with #to_ary" do
+ x = mock('x')
+ x.stub!(:to_ary).and_return([:b, 'b'])
+
+ ENV.to_h { |k| x }.should == { :b => 'b' }
+ end
+
+ it "does not coerce returned pair to Array with #to_a" do
+ x = mock('x')
+ x.stub!(:to_a).and_return([:b, 'b'])
+
+ -> do
+ ENV.to_h { |k| x }
+ end.should.raise(TypeError, /wrong element type MockObject/)
end
end
end
diff --git a/spec/ruby/core/env/values_at_spec.rb b/spec/ruby/core/env/values_at_spec.rb
index ee970e5f65..4a733f4ed5 100644
--- a/spec/ruby/core/env/values_at_spec.rb
+++ b/spec/ruby/core/env/values_at_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/common'
describe "ENV.values_at" do
before :each do
@@ -28,10 +29,10 @@ describe "ENV.values_at" do
end
it "uses the locale encoding" do
- ENV.values_at(ENV.keys.first).first.encoding.should == Encoding.find('locale')
+ ENV.values_at(ENV.keys.first).first.encoding.should == ENVSpecs.encoding
end
it "raises TypeError when a key is not coercible to String" do
- -> { ENV.values_at("foo", Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String")
+ -> { ENV.values_at("foo", Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String")
end
end
diff --git a/spec/ruby/core/exception/backtrace_locations_spec.rb b/spec/ruby/core/exception/backtrace_locations_spec.rb
index 86eb9d3413..62eab8a0f3 100644
--- a/spec/ruby/core/exception/backtrace_locations_spec.rb
+++ b/spec/ruby/core/exception/backtrace_locations_spec.rb
@@ -7,15 +7,15 @@ describe "Exception#backtrace_locations" do
end
it "returns nil if no backtrace was set" do
- Exception.new.backtrace_locations.should be_nil
+ Exception.new.backtrace_locations.should == nil
end
it "returns an Array" do
- @backtrace.should be_an_instance_of(Array)
+ @backtrace.should.instance_of?(Array)
end
it "sets each element to a Thread::Backtrace::Location" do
- @backtrace.each {|l| l.should be_an_instance_of(Thread::Backtrace::Location)}
+ @backtrace.each {|l| l.should.instance_of?(Thread::Backtrace::Location)}
end
it "produces a backtrace for an exception captured using $!" do
diff --git a/spec/ruby/core/exception/backtrace_spec.rb b/spec/ruby/core/exception/backtrace_spec.rb
index 3f74c4cefe..a4a8272030 100644
--- a/spec/ruby/core/exception/backtrace_spec.rb
+++ b/spec/ruby/core/exception/backtrace_spec.rb
@@ -7,15 +7,15 @@ describe "Exception#backtrace" do
end
it "returns nil if no backtrace was set" do
- Exception.new.backtrace.should be_nil
+ Exception.new.backtrace.should == nil
end
it "returns an Array" do
- @backtrace.should be_an_instance_of(Array)
+ @backtrace.should.instance_of?(Array)
end
it "sets each element to a String" do
- @backtrace.each {|l| l.should be_an_instance_of(String)}
+ @backtrace.each {|l| l.should.instance_of?(String)}
end
it "includes the filename of the location where self raised in the first element" do
@@ -27,7 +27,7 @@ describe "Exception#backtrace" do
end
it "includes the name of the method from where self raised in the first element" do
- @backtrace.first.should =~ /in `backtrace'/
+ @backtrace.first.should =~ /in [`'](?:ExceptionSpecs::Backtrace\.)?backtrace'/
end
it "includes the filename of the location immediately prior to where self raised in the second element" do
@@ -38,12 +38,25 @@ describe "Exception#backtrace" do
@backtrace[1].should =~ /:6(:in )?/
end
- it "contains lines of the same format for each prior position in the stack" do
- @backtrace[2..-1].each do |line|
- # This regexp is deliberately imprecise to account for the need to abstract out
- # the paths of the included mspec files and the desire to avoid specifying in any
- # detail what the in `...' portion looks like.
- line.should =~ /^.+:\d+:in `[^`]+'$/
+ ruby_version_is ""..."3.4" do
+ it "contains lines of the same format for each prior position in the stack" do
+ @backtrace[2..-1].each do |line|
+ # This regexp is deliberately imprecise to account for the need to abstract out
+ # the paths of the included mspec files and the desire to avoid specifying in any
+ # detail what the in `...' portion looks like.
+ line.should =~ /^.+:\d+:in `[^`]+'$/
+ end
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "contains lines of the same format for each prior position in the stack" do
+ @backtrace[2..-1].each do |line|
+ # This regexp is deliberately imprecise to account for the need to abstract out
+ # the paths of the included mspec files and the desire to avoid specifying in any
+ # detail what the in '...' portion looks like.
+ line.should =~ /^.+:\d+:in '[^`]+'$/
+ end
end
end
@@ -81,13 +94,13 @@ describe "Exception#backtrace" do
raise
rescue RuntimeError => err
bt = err.backtrace
- err.dup.backtrace.should equal(bt)
+ err.dup.backtrace.should.equal?(bt)
new_bt = ['hi']
err.set_backtrace new_bt
err.backtrace.should == new_bt
- err.dup.backtrace.should equal(new_bt)
+ err.dup.backtrace.should.equal?(new_bt)
end
end
end
diff --git a/spec/ruby/core/exception/case_compare_spec.rb b/spec/ruby/core/exception/case_compare_spec.rb
index 87b9dee3ca..5fd11ae741 100644
--- a/spec/ruby/core/exception/case_compare_spec.rb
+++ b/spec/ruby/core/exception/case_compare_spec.rb
@@ -26,13 +26,11 @@ describe "SystemCallError.===" do
end
it "returns true if receiver is generic and arg is kind of SystemCallError" do
- unknown_error_number = Errno.constants.size
e = SystemCallError.new('foo', @example_errno)
SystemCallError.===(e).should == true
end
it "returns false if receiver is generic and arg is not kind of SystemCallError" do
- unknown_error_number = Errno.constants.size
e = Object.new
SystemCallError.===(e).should == false
end
diff --git a/spec/ruby/core/exception/cause_spec.rb b/spec/ruby/core/exception/cause_spec.rb
index cf4aaeb188..cfc15bdda3 100644
--- a/spec/ruby/core/exception/cause_spec.rb
+++ b/spec/ruby/core/exception/cause_spec.rb
@@ -4,53 +4,35 @@ describe "Exception#cause" do
it "returns the active exception when an exception is raised" do
begin
raise Exception, "the cause"
- rescue Exception
- begin
+ rescue Exception => cause
+ -> {
raise RuntimeError, "the consequence"
- rescue RuntimeError => e
- e.should be_an_instance_of(RuntimeError)
- e.message.should == "the consequence"
-
- e.cause.should be_an_instance_of(Exception)
- e.cause.message.should == "the cause"
- end
+ }.should.raise(RuntimeError, "the consequence", cause:)
end
end
it "is set for user errors caused by internal errors" do
- -> {
- begin
- 1 / 0
- rescue
- raise "foo"
- end
- }.should raise_error(RuntimeError) { |e|
- e.cause.should be_kind_of(ZeroDivisionError)
- }
+ begin
+ 1 / 0
+ rescue => cause
+ -> { raise "foo" }.should.raise(RuntimeError, cause:)
+ end
end
it "is set for internal errors caused by user errors" do
cause = RuntimeError.new "cause"
- -> {
- begin
- raise cause
- rescue
- 1 / 0
- end
- }.should raise_error(ZeroDivisionError) { |e|
- e.cause.should equal(cause)
- }
+ begin
+ raise cause
+ rescue
+ -> { 1 / 0 }.should.raise(ZeroDivisionError, cause:)
+ end
end
it "is not set to the exception itself when it is re-raised" do
- -> {
- begin
- raise RuntimeError
- rescue RuntimeError => e
- raise e
- end
- }.should raise_error(RuntimeError) { |e|
- e.cause.should == nil
- }
+ begin
+ raise RuntimeError
+ rescue RuntimeError => e
+ -> { raise e }.should.raise(RuntimeError, cause: nil)
+ end
end
end
diff --git a/spec/ruby/core/exception/detailed_message_spec.rb b/spec/ruby/core/exception/detailed_message_spec.rb
new file mode 100644
index 0000000000..9df164a1cf
--- /dev/null
+++ b/spec/ruby/core/exception/detailed_message_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/common'
+
+describe "Exception#detailed_message" do
+ it "returns decorated message" do
+ RuntimeError.new("new error").detailed_message.should == "new error (RuntimeError)"
+ end
+
+ it "is called by #full_message to allow message customization" do
+ exception = Exception.new("new error")
+ def exception.detailed_message(**)
+ "<prefix>#{message}<suffix>"
+ end
+ exception.full_message(highlight: false).should.include? "<prefix>new error<suffix>"
+ end
+
+ it "returns just a message if exception class is anonymous" do
+ Class.new(RuntimeError).new("message").detailed_message.should == "message"
+ end
+
+ it "returns 'unhandled exception' for an instance of RuntimeError with empty message" do
+ RuntimeError.new("").detailed_message.should == "unhandled exception"
+ end
+
+ it "returns just class name for an instance other than RuntimeError with empty message" do
+ DetailedMessageSpec::C.new("").detailed_message.should == "DetailedMessageSpec::C"
+ StandardError.new("").detailed_message.should == "StandardError"
+ end
+
+ it "returns a generated class name for an instance of RuntimeError anonymous subclass with empty message" do
+ klass = Class.new(RuntimeError)
+ klass.new("").detailed_message.should =~ /\A#<Class:0x\h+>\z/
+ end
+
+ it "accepts highlight keyword argument and adds escape control sequences" do
+ RuntimeError.new("new error").detailed_message(highlight: true).should == "\e[1mnew error (\e[1;4mRuntimeError\e[m\e[1m)\e[m"
+ end
+
+ it "accepts highlight keyword argument and adds escape control sequences for an instance of RuntimeError with empty message" do
+ RuntimeError.new("").detailed_message(highlight: true).should == "\e[1;4munhandled exception\e[m"
+ end
+
+ it "accepts highlight keyword argument and adds escape control sequences for an instance other than RuntimeError with empty message" do
+ StandardError.new("").detailed_message(highlight: true).should == "\e[1;4mStandardError\e[m"
+ end
+
+ it "allows and ignores other keyword arguments" do
+ RuntimeError.new("new error").detailed_message(foo: true).should == "new error (RuntimeError)"
+ end
+end
diff --git a/spec/ruby/core/exception/dup_spec.rb b/spec/ruby/core/exception/dup_spec.rb
index edd54bfb37..b53ad79bf3 100644
--- a/spec/ruby/core/exception/dup_spec.rb
+++ b/spec/ruby/core/exception/dup_spec.rb
@@ -20,7 +20,7 @@ describe "Exception#dup" do
it "does not copy singleton methods" do
def @obj.special() :the_one end
dup = @obj.dup
- -> { dup.special }.should raise_error(NameError)
+ -> { dup.special }.should.raise(NameError)
end
it "does not copy modules included in the singleton class" do
@@ -29,7 +29,7 @@ describe "Exception#dup" do
end
dup = @obj.dup
- -> { dup.repr }.should raise_error(NameError)
+ -> { dup.repr }.should.raise(NameError)
end
it "does not copy constants defined in the singleton class" do
@@ -38,7 +38,7 @@ describe "Exception#dup" do
end
dup = @obj.dup
- -> { class << dup; CLONE; end }.should raise_error(NameError)
+ -> { class << dup; CLONE; end }.should.raise(NameError)
end
it "does copy the message" do
@@ -61,13 +61,13 @@ describe "Exception#dup" do
it "does copy the cause" do
begin
- raise StandardError, "the cause"
+ raise StandardError
rescue StandardError => cause
begin
- raise RuntimeError, "the consequence"
+ raise RuntimeError
rescue RuntimeError => e
- e.cause.should equal(cause)
- e.dup.cause.should equal(cause)
+ e.cause.should.equal?(cause)
+ e.dup.cause.should.equal?(cause)
end
end
end
diff --git a/spec/ruby/core/exception/equal_value_spec.rb b/spec/ruby/core/exception/equal_value_spec.rb
index 7f2065511a..b76b3bcd4a 100644
--- a/spec/ruby/core/exception/equal_value_spec.rb
+++ b/spec/ruby/core/exception/equal_value_spec.rb
@@ -22,19 +22,19 @@ describe "Exception#==" do
it "returns true if both exceptions have the same class, the same message, and the same backtrace" do
one = TypeError.new("message")
- one.set_backtrace [File.dirname(__FILE__)]
+ one.set_backtrace [__dir__]
two = TypeError.new("message")
- two.set_backtrace [File.dirname(__FILE__)]
+ two.set_backtrace [__dir__]
one.should == two
end
it "returns false if the two exceptions inherit from Exception but have different classes" do
one = RuntimeError.new("message")
- one.set_backtrace [File.dirname(__FILE__)]
- one.should be_kind_of(Exception)
+ one.set_backtrace [__dir__]
+ one.should.is_a?(Exception)
two = TypeError.new("message")
- two.set_backtrace [File.dirname(__FILE__)]
- two.should be_kind_of(Exception)
+ two.set_backtrace [__dir__]
+ two.should.is_a?(Exception)
one.should_not == two
end
@@ -52,7 +52,7 @@ describe "Exception#==" do
it "returns false if the two exceptions differ only in their backtrace" do
one = RuntimeError.new("message")
- one.set_backtrace [File.dirname(__FILE__)]
+ one.set_backtrace [__dir__]
two = RuntimeError.new("message")
two.set_backtrace nil
one.should_not == two
@@ -60,9 +60,9 @@ describe "Exception#==" do
it "returns false if the two exceptions differ only in their message" do
one = RuntimeError.new("message")
- one.set_backtrace [File.dirname(__FILE__)]
+ one.set_backtrace [__dir__]
two = RuntimeError.new("message2")
- two.set_backtrace [File.dirname(__FILE__)]
+ two.set_backtrace [__dir__]
one.should_not == two
end
end
diff --git a/spec/ruby/core/exception/errno_spec.rb b/spec/ruby/core/exception/errno_spec.rb
index 78b3eafc2a..36beae9976 100644
--- a/spec/ruby/core/exception/errno_spec.rb
+++ b/spec/ruby/core/exception/errno_spec.rb
@@ -4,21 +4,21 @@ require_relative 'fixtures/common'
describe "Errno::EINVAL.new" do
it "can be called with no arguments" do
exc = Errno::EINVAL.new
- exc.should be_an_instance_of(Errno::EINVAL)
+ exc.should.instance_of?(Errno::EINVAL)
exc.errno.should == Errno::EINVAL::Errno
exc.message.should == "Invalid argument"
end
it "accepts an optional custom message" do
exc = Errno::EINVAL.new('custom message')
- exc.should be_an_instance_of(Errno::EINVAL)
+ exc.should.instance_of?(Errno::EINVAL)
exc.errno.should == Errno::EINVAL::Errno
exc.message.should == "Invalid argument - custom message"
end
it "accepts an optional custom message and location" do
exc = Errno::EINVAL.new('custom message', 'location')
- exc.should be_an_instance_of(Errno::EINVAL)
+ exc.should.instance_of?(Errno::EINVAL)
exc.errno.should == Errno::EINVAL::Errno
exc.message.should == "Invalid argument @ location - custom message"
end
@@ -28,7 +28,9 @@ describe "Errno::EMFILE" do
it "can be subclassed" do
ExceptionSpecs::EMFILESub = Class.new(Errno::EMFILE)
exc = ExceptionSpecs::EMFILESub.new
- exc.should be_an_instance_of(ExceptionSpecs::EMFILESub)
+ exc.should.instance_of?(ExceptionSpecs::EMFILESub)
+ ensure
+ ExceptionSpecs.send(:remove_const, :EMFILESub)
end
end
@@ -45,6 +47,23 @@ end
describe "Errno::ENOTSUP" do
it "is defined" do
- Errno.should have_constant(:ENOTSUP)
+ Errno.should.const_defined?(:ENOTSUP, false)
+ end
+
+ it "is the same class as Errno::EOPNOTSUPP if they represent the same errno value" do
+ if Errno::ENOTSUP::Errno == Errno::EOPNOTSUPP::Errno
+ Errno::ENOTSUP.should == Errno::EOPNOTSUPP
+ else
+ Errno::ENOTSUP.should_not == Errno::EOPNOTSUPP
+ end
+ end
+end
+
+describe "Errno::ENOENT" do
+ it "lets subclasses inherit the default error message" do
+ c = Class.new(Errno::ENOENT)
+ raise c, "custom message"
+ rescue => e
+ e.message.should == "No such file or directory - custom message"
end
end
diff --git a/spec/ruby/core/exception/exception_spec.rb b/spec/ruby/core/exception/exception_spec.rb
index d6f5283bd9..f5424cdabd 100644
--- a/spec/ruby/core/exception/exception_spec.rb
+++ b/spec/ruby/core/exception/exception_spec.rb
@@ -20,7 +20,7 @@ describe "Exception#exception" do
it "returns an exception of the same class as self with the message given as argument" do
e = RuntimeError.new
e2 = e.exception("message")
- e2.should be_an_instance_of(RuntimeError)
+ e2.should.instance_of?(RuntimeError)
e2.message.should == "message"
end
@@ -62,7 +62,7 @@ describe "Exception#exception" do
it "returns an exception of the same class as self with the message given as argument, but without reinitializing" do
e = CustomArgumentError.new(:boom)
e2 = e.exception("message")
- e2.should be_an_instance_of(CustomArgumentError)
+ e2.should.instance_of?(CustomArgumentError)
e2.val.should == :boom
e2.message.should == "message"
end
diff --git a/spec/ruby/core/exception/exit_value_spec.rb b/spec/ruby/core/exception/exit_value_spec.rb
index 99987dd1bc..bb6cff1831 100644
--- a/spec/ruby/core/exception/exit_value_spec.rb
+++ b/spec/ruby/core/exception/exit_value_spec.rb
@@ -6,7 +6,7 @@ describe "LocalJumpError#exit_value" do
end
it "returns the value given to return" do
- -> { get_me_a_return.call }.should raise_error(LocalJumpError) { |e|
+ -> { get_me_a_return.call }.should.raise(LocalJumpError) { |e|
e.exit_value.should == 42
}
end
diff --git a/spec/ruby/core/exception/fixtures/common.rb b/spec/ruby/core/exception/fixtures/common.rb
index 0ffb3ed855..3d8a3c3430 100644
--- a/spec/ruby/core/exception/fixtures/common.rb
+++ b/spec/ruby/core/exception/fixtures/common.rb
@@ -84,6 +84,9 @@ module NoMethodErrorSpecs
class InstanceException < Exception
end
+
+ class AClass; end
+ module AModule; end
end
class NameErrorSpecs
@@ -93,3 +96,7 @@ class NameErrorSpecs
end
end
end
+
+module DetailedMessageSpec
+ C = Class.new(RuntimeError)
+end
diff --git a/spec/ruby/core/exception/fixtures/syntax_error.rb b/spec/ruby/core/exception/fixtures/syntax_error.rb
new file mode 100644
index 0000000000..ccec62f7a1
--- /dev/null
+++ b/spec/ruby/core/exception/fixtures/syntax_error.rb
@@ -0,0 +1,3 @@
+# rubocop:disable Lint/Syntax
+1+1=2
+# rubocop:enable Lint/Syntax
diff --git a/spec/ruby/core/exception/fixtures/thread_fiber_ensure.rb b/spec/ruby/core/exception/fixtures/thread_fiber_ensure.rb
new file mode 100644
index 0000000000..c109ec6247
--- /dev/null
+++ b/spec/ruby/core/exception/fixtures/thread_fiber_ensure.rb
@@ -0,0 +1,22 @@
+ready = false
+t = Thread.new do
+ f = Fiber.new do
+ begin
+ Fiber.yield
+ ensure
+ STDERR.puts "suspended fiber ensure"
+ end
+ end
+ f.resume
+
+ begin
+ ready = true
+ sleep
+ ensure
+ STDERR.puts "current fiber ensure"
+ end
+end
+
+Thread.pass until ready && t.stop?
+
+# let the program end, it's the same as #exit or an exception for this behavior
diff --git a/spec/ruby/core/exception/fixtures/thread_fiber_ensure_non_root_fiber.rb b/spec/ruby/core/exception/fixtures/thread_fiber_ensure_non_root_fiber.rb
new file mode 100644
index 0000000000..3364ed06d0
--- /dev/null
+++ b/spec/ruby/core/exception/fixtures/thread_fiber_ensure_non_root_fiber.rb
@@ -0,0 +1,25 @@
+ready = false
+t = Thread.new do
+ f = Fiber.new do
+ begin
+ Fiber.yield
+ ensure
+ STDERR.puts "suspended fiber ensure"
+ end
+ end
+ f.resume
+
+ f2 = Fiber.new do
+ begin
+ ready = true
+ sleep
+ ensure
+ STDERR.puts "current fiber ensure"
+ end
+ end
+ f2.resume
+end
+
+Thread.pass until ready && t.stop?
+
+# let the program end, it's the same as #exit or an exception for this behavior
diff --git a/spec/ruby/core/exception/frozen_error_spec.rb b/spec/ruby/core/exception/frozen_error_spec.rb
index f27b33295c..a28f524b54 100644
--- a/spec/ruby/core/exception/frozen_error_spec.rb
+++ b/spec/ruby/core/exception/frozen_error_spec.rb
@@ -1,26 +1,54 @@
require_relative '../../spec_helper'
describe "FrozenError.new" do
- ruby_version_is "2.7" do
- it "should take optional receiver argument" do
- o = Object.new
- FrozenError.new("msg", receiver: o).receiver.should equal(o)
- end
+ it "should take optional receiver argument" do
+ o = Object.new
+ FrozenError.new("msg", receiver: o).receiver.should.equal?(o)
end
end
describe "FrozenError#receiver" do
- ruby_version_is "2.7" do
- it "should return frozen object that modification was attempted on" do
- o = Object.new.freeze
- begin
- def o.x; end
- rescue => e
- e.should be_kind_of(FrozenError)
- e.receiver.should equal(o)
- else
- raise
- end
+ it "should return frozen object that modification was attempted on" do
+ o = Object.new.freeze
+ begin
+ def o.x; end
+ rescue => e
+ e.should.is_a?(FrozenError)
+ e.receiver.should.equal?(o)
+ else
+ raise
+ end
+ end
+end
+
+describe "FrozenError#message" do
+ it "includes a receiver" do
+ object = Object.new
+ object.freeze
+
+ msg_class = ruby_version_is("4.0") ? "Object" : "object"
+
+ -> {
+ def object.x; end
+ }.should.raise(FrozenError, "can't modify frozen #{msg_class}: #{object}")
+
+ object = [].freeze
+ -> { object << nil }.should.raise(FrozenError, "can't modify frozen Array: []")
+ end
+end
+
+describe "Modifying a frozen object" do
+ context "#inspect is redefined and modifies the object" do
+ it "returns ... instead of String representation of object" do
+ object = Object.new
+ def object.inspect; @a = 1 end
+ def object.modify; @a = 2 end
+
+ object.freeze
+
+ # CRuby's message contains multiple whitespaces before '...'.
+ # So handle both multiple and single whitespace.
+ -> { object.modify }.should.raise(FrozenError, /can't modify frozen .*?: \s*.../)
end
end
end
diff --git a/spec/ruby/core/exception/full_message_spec.rb b/spec/ruby/core/exception/full_message_spec.rb
index 9cac9fb037..5a5e0a2b3a 100644
--- a/spec/ruby/core/exception/full_message_spec.rb
+++ b/spec/ruby/core/exception/full_message_spec.rb
@@ -6,87 +6,221 @@ describe "Exception#full_message" do
e.set_backtrace(["a.rb:1", "b.rb:2"])
full_message = e.full_message
- full_message.should include "RuntimeError"
- full_message.should include "Some runtime error"
- full_message.should include "a.rb:1"
- full_message.should include "b.rb:2"
+ full_message.should.include? "RuntimeError"
+ full_message.should.include? "Some runtime error"
+ full_message.should.include? "a.rb:1"
+ full_message.should.include? "b.rb:2"
end
- ruby_version_is "2.5.1" do
- it "supports :highlight option and adds escape sequences to highlight some strings" do
- e = RuntimeError.new("Some runtime error")
+ it "supports :highlight option and adds escape sequences to highlight some strings" do
+ e = RuntimeError.new("Some runtime error")
+
+ full_message = e.full_message(highlight: true, order: :top).lines
+ full_message[0].should.end_with? "\e[1mSome runtime error (\e[1;4mRuntimeError\e[m\e[1m)\e[m\n"
+
+ full_message = e.full_message(highlight: true, order: :bottom).lines
+ full_message[0].should == "\e[1mTraceback\e[m (most recent call last):\n"
+ full_message[-1].should.end_with? "\e[1mSome runtime error (\e[1;4mRuntimeError\e[m\e[1m)\e[m\n"
+
+ full_message = e.full_message(highlight: false, order: :top).lines
+ full_message[0].should.end_with? "Some runtime error (RuntimeError)\n"
+
+ full_message = e.full_message(highlight: false, order: :bottom).lines
+ full_message[0].should == "Traceback (most recent call last):\n"
+ full_message[-1].should.end_with? "Some runtime error (RuntimeError)\n"
+ end
+
+ it "supports :order option and places the error message and the backtrace at the top or the bottom" do
+ e = RuntimeError.new("Some runtime error")
+ e.set_backtrace(["a.rb:1", "b.rb:2"])
+
+ e.full_message(order: :top, highlight: false).should =~ /a.rb:1.*b.rb:2/m
+ e.full_message(order: :bottom, highlight: false).should =~ /b.rb:2.*a.rb:1/m
+ end
+
+ it "shows the caller if the exception has no backtrace" do
+ e = RuntimeError.new("Some runtime error")
+ e.backtrace.should == nil
+ full_message = e.full_message(highlight: false, order: :top).lines
+ full_message[0].should.start_with?("#{__FILE__}:#{__LINE__-1}:in ")
+ full_message[0].should.end_with?("': Some runtime error (RuntimeError)\n")
+ end
+
+ describe "includes details about whether an exception was handled" do
+ describe "RuntimeError" do
+ it "should report as unhandled if message is empty" do
+ err = RuntimeError.new("")
+
+ err.full_message.should =~ /unhandled exception/
+ err.full_message(highlight: true).should =~ /unhandled exception/
+ err.full_message(highlight: false).should =~ /unhandled exception/
+ end
+
+ it "should not report as unhandled if the message is not empty" do
+ err = RuntimeError.new("non-empty")
+
+ err.full_message.should !~ /unhandled exception/
+ err.full_message(highlight: true).should !~ /unhandled exception/
+ err.full_message(highlight: false).should !~ /unhandled exception/
+ end
+
+ it "should not report as unhandled if the message is nil" do
+ err = RuntimeError.new(nil)
+
+ err.full_message.should !~ /unhandled exception/
+ err.full_message(highlight: true).should !~ /unhandled exception/
+ err.full_message(highlight: false).should !~ /unhandled exception/
+ end
+
+ it "should not report as unhandled if the message is not specified" do
+ err = RuntimeError.new()
- full_message = e.full_message(highlight: true, order: :bottom)
- full_message.should include "\e[1mTraceback\e[m (most recent call last)"
- full_message.should include "\e[1mSome runtime error (\e[1;4mRuntimeError\e[m\e[1m)"
+ err.full_message.should !~ /unhandled exception/
+ err.full_message(highlight: true).should !~ /unhandled exception/
+ err.full_message(highlight: false).should !~ /unhandled exception/
+ end
+
+ it "adds escape sequences to highlight some strings if the message is not specified and :highlight option is specified" do
+ e = RuntimeError.new("")
+
+ full_message = e.full_message(highlight: true, order: :top).lines
+ full_message[0].should.end_with? "\e[1;4munhandled exception\e[m\n"
+
+ full_message = e.full_message(highlight: true, order: :bottom).lines
+ full_message[0].should == "\e[1mTraceback\e[m (most recent call last):\n"
+ full_message[-1].should.end_with? "\e[1;4munhandled exception\e[m\n"
- full_message = e.full_message(highlight: false, order: :bottom)
- full_message.should include "Traceback (most recent call last)"
- full_message.should include "Some runtime error (RuntimeError)"
+ full_message = e.full_message(highlight: false, order: :top).lines
+ full_message[0].should.end_with? "unhandled exception\n"
+
+ full_message = e.full_message(highlight: false, order: :bottom).lines
+ full_message[0].should == "Traceback (most recent call last):\n"
+ full_message[-1].should.end_with? "unhandled exception\n"
+ end
end
- it "supports :order option and places the error message and the backtrace at the top or the bottom" do
- e = RuntimeError.new("Some runtime error")
- e.set_backtrace(["a.rb:1", "b.rb:2"])
+ describe "generic Error" do
+ it "should not report as unhandled in any event" do
+ StandardError.new("").full_message.should !~ /unhandled exception/
+ StandardError.new("non-empty").full_message.should !~ /unhandled exception/
+ end
+ end
+ end
- e.full_message(order: :top, highlight: false).should =~ /a.rb:1.*b.rb:2/m
- e.full_message(order: :bottom, highlight: false).should =~ /b.rb:2.*a.rb:1/m
+ it "shows the exception class at the end of the first line of the message when the message contains multiple lines" do
+ begin
+ line = __LINE__; raise "first line\nsecond line"
+ rescue => e
+ full_message = e.full_message(highlight: false, order: :top).lines
+ full_message[0].should.start_with?("#{__FILE__}:#{line}:in ")
+ full_message[0].should.end_with?(": first line (RuntimeError)\n")
+ full_message[1].should == "second line\n"
end
+ end
- it "shows the caller if the exception has no backtrace" do
- e = RuntimeError.new("Some runtime error")
- e.backtrace.should == nil
- full_message = e.full_message(highlight: false, order: :top)
- full_message.should include("#{__FILE__}:#{__LINE__-1}:in `")
- full_message.should include("': Some runtime error (RuntimeError)\n")
+ it "highlights the entire message when the message contains multiple lines" do
+ begin
+ line = __LINE__; raise "first line\nsecond line\nthird line"
+ rescue => e
+ full_message = e.full_message(highlight: true, order: :top).lines
+ full_message[0].should.start_with?("#{__FILE__}:#{line}:in ")
+ full_message[0].should.end_with?(": \e[1mfirst line (\e[1;4mRuntimeError\e[m\e[1m)\e[m\n")
+ full_message[1].should == "\e[1msecond line\e[m\n"
+ full_message[2].should == "\e[1mthird line\e[m\n"
end
+ end
- it "shows the exception class at the end of the first line of the message when the message contains multiple lines" do
+ it "contains cause of exception" do
+ begin
begin
- line = __LINE__; raise "first line\nsecond line"
- rescue => e
- full_message = e.full_message(highlight: false, order: :top).lines
- full_message[0].should include("#{__FILE__}:#{line}:in `")
- full_message[0].should include(": first line (RuntimeError)\n")
- full_message[1].should == "second line\n"
+ raise 'the cause'
+ rescue
+ raise 'main exception'
end
+ rescue => e
+ exception = e
end
+
+ exception.full_message.should.include? "main exception"
+ exception.full_message.should.include? "the cause"
end
- ruby_version_is "2.6" do
- it "contains cause of exception" do
+ it 'contains all the chain of exceptions' do
+ begin
begin
begin
- raise 'the cause'
+ raise 'origin exception'
rescue
- raise 'main exception'
+ raise 'intermediate exception'
end
- rescue => e
- exception = e
+ rescue
+ raise 'last exception'
end
+ rescue => e
+ exception = e
+ end
+
+ exception.full_message.should.include? "last exception"
+ exception.full_message.should.include? "intermediate exception"
+ exception.full_message.should.include? "origin exception"
+ end
+
+ it "relies on #detailed_message" do
+ e = RuntimeError.new("new error")
+ e.define_singleton_method(:detailed_message) { |**| "DETAILED MESSAGE" }
+
+ e.full_message.lines.first.should =~ /DETAILED MESSAGE/
+ end
- exception.full_message.should include "main exception"
- exception.full_message.should include "the cause"
+ it "passes all its own keyword arguments (with :highlight default value and without :order default value) to #detailed_message" do
+ e = RuntimeError.new("new error")
+ options_passed = nil
+ e.define_singleton_method(:detailed_message) do |**options|
+ options_passed = options
+ "DETAILED MESSAGE"
end
- it 'contains all the chain of exceptions' do
- begin
- begin
- begin
- raise 'origin exception'
- rescue
- raise 'intermediate exception'
- end
- rescue
- raise 'last exception'
- end
- rescue => e
- exception = e
- end
+ e.full_message(foo: "bar")
+ options_passed.should == { foo: "bar", highlight: Exception.to_tty? }
+ end
+
+ it "converts #detailed_message returned value to String if it isn't a String" do
+ message = Object.new
+ def message.to_str; "DETAILED MESSAGE"; end
+
+ e = RuntimeError.new("new error")
+ e.define_singleton_method(:detailed_message) { |**| message }
- exception.full_message.should include "last exception"
- exception.full_message.should include "intermediate exception"
- exception.full_message.should include "origin exception"
+ e.full_message.lines.first.should =~ /DETAILED MESSAGE/
+ end
+
+ it "uses class name if #detailed_message returns nil" do
+ e = RuntimeError.new("new error")
+ e.define_singleton_method(:detailed_message) { |**| nil }
+
+ e.full_message(highlight: false).lines.first.should =~ /RuntimeError/
+ e.full_message(highlight: true).lines.first.should =~ /#{Regexp.escape("\e[1;4mRuntimeError\e[m")}/
+ end
+
+ it "uses class name if exception object doesn't respond to #detailed_message" do
+ e = RuntimeError.new("new error")
+ class << e
+ undef :detailed_message
end
+
+ e.full_message(highlight: false).lines.first.should =~ /RuntimeError/
+ e.full_message(highlight: true).lines.first.should =~ /#{Regexp.escape("\e[1;4mRuntimeError\e[m")}/
+ end
+
+ it "allows cause with empty backtrace" do
+ begin
+ raise RuntimeError.new("Some runtime error"), cause: RuntimeError.new("Some other runtime error")
+ rescue => e
+ end
+
+ full_message = e.full_message
+ full_message.should.include? "RuntimeError"
+ full_message.should.include? "Some runtime error"
+ full_message.should.include? "Some other runtime error"
end
end
diff --git a/spec/ruby/core/exception/interrupt_spec.rb b/spec/ruby/core/exception/interrupt_spec.rb
index 14f294bec6..90d261e470 100644
--- a/spec/ruby/core/exception/interrupt_spec.rb
+++ b/spec/ruby/core/exception/interrupt_spec.rb
@@ -29,7 +29,32 @@ describe "rescuing Interrupt" do
sleep
rescue Interrupt => e
e.signo.should == Signal.list["INT"]
- e.signm.should == ""
+ ["", "Interrupt"].should.include?(e.message)
+ end
+ end
+end
+
+describe "Interrupt" do
+ # This spec is basically the same as above,
+ # but it does not rely on Signal.trap(:INT, :SIG_DFL) which can be tricky
+ it "is raised on the main Thread by the default SIGINT handler" do
+ out = ruby_exe(<<-'RUBY', args: "2>&1")
+ begin
+ Process.kill :INT, Process.pid
+ sleep
+ rescue Interrupt => e
+ puts "Interrupt: #{e.signo}"
+ end
+ RUBY
+ out.should == "Interrupt: #{Signal.list["INT"]}\n"
+ end
+
+ platform_is_not :windows do
+ it "shows the backtrace and has a signaled exit status" do
+ err = IO.popen([*ruby_exe, '-e', 'Process.kill :INT, Process.pid; sleep'], err: [:child, :out], &:read)
+ $?.termsig.should == Signal.list.fetch('INT')
+ err.should.include? ': Interrupt'
+ err.should =~ /from -e:1:in [`']<main>'/
end
end
end
diff --git a/spec/ruby/core/exception/io_error_spec.rb b/spec/ruby/core/exception/io_error_spec.rb
index ab8a72518f..940d5be876 100644
--- a/spec/ruby/core/exception/io_error_spec.rb
+++ b/spec/ruby/core/exception/io_error_spec.rb
@@ -3,14 +3,14 @@ require_relative '../../spec_helper'
describe "IO::EAGAINWaitReadable" do
it "combines Errno::EAGAIN and IO::WaitReadable" do
IO::EAGAINWaitReadable.superclass.should == Errno::EAGAIN
- IO::EAGAINWaitReadable.ancestors.should include IO::WaitReadable
+ IO::EAGAINWaitReadable.ancestors.should.include? IO::WaitReadable
end
it "is the same as IO::EWOULDBLOCKWaitReadable if Errno::EAGAIN is the same as Errno::EWOULDBLOCK" do
if Errno::EAGAIN.equal? Errno::EWOULDBLOCK
- IO::EAGAINWaitReadable.should equal IO::EWOULDBLOCKWaitReadable
+ IO::EAGAINWaitReadable.should.equal? IO::EWOULDBLOCKWaitReadable
else
- IO::EAGAINWaitReadable.should_not equal IO::EWOULDBLOCKWaitReadable
+ IO::EAGAINWaitReadable.should_not.equal? IO::EWOULDBLOCKWaitReadable
end
end
end
@@ -18,21 +18,21 @@ end
describe "IO::EWOULDBLOCKWaitReadable" do
it "combines Errno::EWOULDBLOCK and IO::WaitReadable" do
IO::EWOULDBLOCKWaitReadable.superclass.should == Errno::EWOULDBLOCK
- IO::EAGAINWaitReadable.ancestors.should include IO::WaitReadable
+ IO::EAGAINWaitReadable.ancestors.should.include? IO::WaitReadable
end
end
describe "IO::EAGAINWaitWritable" do
it "combines Errno::EAGAIN and IO::WaitWritable" do
IO::EAGAINWaitWritable.superclass.should == Errno::EAGAIN
- IO::EAGAINWaitWritable.ancestors.should include IO::WaitWritable
+ IO::EAGAINWaitWritable.ancestors.should.include? IO::WaitWritable
end
it "is the same as IO::EWOULDBLOCKWaitWritable if Errno::EAGAIN is the same as Errno::EWOULDBLOCK" do
if Errno::EAGAIN.equal? Errno::EWOULDBLOCK
- IO::EAGAINWaitWritable.should equal IO::EWOULDBLOCKWaitWritable
+ IO::EAGAINWaitWritable.should.equal? IO::EWOULDBLOCKWaitWritable
else
- IO::EAGAINWaitWritable.should_not equal IO::EWOULDBLOCKWaitWritable
+ IO::EAGAINWaitWritable.should_not.equal? IO::EWOULDBLOCKWaitWritable
end
end
end
@@ -40,6 +40,6 @@ end
describe "IO::EWOULDBLOCKWaitWritable" do
it "combines Errno::EWOULDBLOCK and IO::WaitWritable" do
IO::EWOULDBLOCKWaitWritable.superclass.should == Errno::EWOULDBLOCK
- IO::EAGAINWaitWritable.ancestors.should include IO::WaitWritable
+ IO::EAGAINWaitWritable.ancestors.should.include? IO::WaitWritable
end
end
diff --git a/spec/ruby/core/exception/key_error_spec.rb b/spec/ruby/core/exception/key_error_spec.rb
index 71bf2b46ff..c5e2b1efbc 100644
--- a/spec/ruby/core/exception/key_error_spec.rb
+++ b/spec/ruby/core/exception/key_error_spec.rb
@@ -1,21 +1,19 @@
require_relative '../../spec_helper'
describe "KeyError" do
- ruby_version_is "2.6" do
- it "accepts :receiver and :key options" do
- receiver = mock("receiver")
- key = mock("key")
+ it "accepts :receiver and :key options" do
+ receiver = mock("receiver")
+ key = mock("key")
- error = KeyError.new(receiver: receiver, key: key)
+ error = KeyError.new(receiver: receiver, key: key)
- error.receiver.should == receiver
- error.key.should == key
+ error.receiver.should == receiver
+ error.key.should == key
- error = KeyError.new("message", receiver: receiver, key: key)
+ error = KeyError.new("message", receiver: receiver, key: key)
- error.message.should == "message"
- error.receiver.should == receiver
- error.key.should == key
- end
+ error.message.should == "message"
+ error.receiver.should == receiver
+ error.key.should == key
end
end
diff --git a/spec/ruby/core/exception/name_error_spec.rb b/spec/ruby/core/exception/name_error_spec.rb
index e901a80c42..ddd51a92e5 100644
--- a/spec/ruby/core/exception/name_error_spec.rb
+++ b/spec/ruby/core/exception/name_error_spec.rb
@@ -5,15 +5,13 @@ describe "NameError.new" do
NameError.new("msg","name").name.should == "name"
end
- ruby_version_is "2.6" do
- it "accepts a :receiver keyword argument" do
- receiver = mock("receiver")
+ it "accepts a :receiver keyword argument" do
+ receiver = mock("receiver")
- error = NameError.new("msg", :name, receiver: receiver)
+ error = NameError.new("msg", :name, receiver: receiver)
- error.receiver.should == receiver
- error.name.should == :name
- end
+ error.receiver.should == receiver
+ error.name.should == :name
end
end
diff --git a/spec/ruby/core/exception/name_spec.rb b/spec/ruby/core/exception/name_spec.rb
index c8a49b40e2..6e0e99d194 100644
--- a/spec/ruby/core/exception/name_spec.rb
+++ b/spec/ruby/core/exception/name_spec.rb
@@ -4,25 +4,25 @@ describe "NameError#name" do
it "returns a method name as a symbol" do
-> {
doesnt_exist
- }.should raise_error(NameError) {|e| e.name.should == :doesnt_exist }
+ }.should.raise(NameError) {|e| e.name.should == :doesnt_exist }
end
it "returns a constant name as a symbol" do
-> {
DoesntExist
- }.should raise_error(NameError) {|e| e.name.should == :DoesntExist }
+ }.should.raise(NameError) {|e| e.name.should == :DoesntExist }
end
it "returns a constant name without namespace as a symbol" do
-> {
Object::DoesntExist
- }.should raise_error(NameError) {|e| e.name.should == :DoesntExist }
+ }.should.raise(NameError) {|e| e.name.should == :DoesntExist }
end
it "returns a class variable name as a symbol" do
-> {
eval("class singleton_class::A; @@doesnt_exist end", binding, __FILE__, __LINE__)
- }.should raise_error(NameError) { |e| e.name.should == :@@doesnt_exist }
+ }.should.raise(NameError) { |e| e.name.should == :@@doesnt_exist }
end
it "returns the first argument passed to the method when a NameError is raised from #instance_variable_get" do
@@ -30,7 +30,7 @@ describe "NameError#name" do
-> {
Object.new.instance_variable_get(invalid_ivar_name)
- }.should raise_error(NameError) {|e| e.name.should equal(invalid_ivar_name) }
+ }.should.raise(NameError) {|e| e.name.should.equal?(invalid_ivar_name) }
end
it "returns the first argument passed to the method when a NameError is raised from #class_variable_get" do
@@ -38,6 +38,6 @@ describe "NameError#name" do
-> {
Object.class_variable_get(invalid_cvar_name)
- }.should raise_error(NameError) {|e| e.name.should equal(invalid_cvar_name) }
+ }.should.raise(NameError) {|e| e.name.should.equal?(invalid_cvar_name) }
end
end
diff --git a/spec/ruby/core/exception/no_method_error_spec.rb b/spec/ruby/core/exception/no_method_error_spec.rb
index b7ee5434fd..9f92104082 100644
--- a/spec/ruby/core/exception/no_method_error_spec.rb
+++ b/spec/ruby/core/exception/no_method_error_spec.rb
@@ -10,15 +10,13 @@ describe "NoMethodError.new" do
NoMethodError.new("msg").message.should == "msg"
end
- ruby_version_is "2.6" do
- it "accepts a :receiver keyword argument" do
- receiver = mock("receiver")
+ it "accepts a :receiver keyword argument" do
+ receiver = mock("receiver")
- error = NoMethodError.new("msg", :name, receiver: receiver)
+ error = NoMethodError.new("msg", :name, receiver: receiver)
- error.receiver.should == receiver
- error.name.should == :name
- end
+ error.receiver.should == receiver
+ error.name.should == :name
end
end
@@ -37,7 +35,7 @@ describe "NoMethodError#args" do
NoMethodErrorSpecs::NoMethodErrorB.new.foo(1,a)
rescue Exception => e
e.args.should == [1,a]
- e.args[1].should equal a
+ e.args[1].should.equal? a
end
end
end
@@ -47,7 +45,7 @@ describe "NoMethodError#message" do
begin
NoMethodErrorSpecs::NoMethodErrorD.new.foo
rescue Exception => e
- e.should be_kind_of(NoMethodError)
+ e.should.is_a?(NoMethodError)
end
end
@@ -55,7 +53,7 @@ describe "NoMethodError#message" do
begin
NoMethodErrorSpecs::NoMethodErrorC.new.a_protected_method
rescue Exception => e
- e.should be_kind_of(NoMethodError)
+ e.should.is_a?(NoMethodError)
end
end
@@ -63,62 +61,150 @@ describe "NoMethodError#message" do
begin
NoMethodErrorSpecs::NoMethodErrorC.new.a_private_method
rescue Exception => e
- e.should be_kind_of(NoMethodError)
- e.message.lines[0].should =~ /private method `a_private_method' called for #<NoMethodErrorSpecs::NoMethodErrorC:0x[\h]+>/
+ e.should.is_a?(NoMethodError)
+ e.message.lines[0].should =~ /private method [`']a_private_method' called for /
end
end
- it "calls receiver.inspect only when calling Exception#message" do
- ScratchPad.record []
- test_class = Class.new do
- def inspect
- ScratchPad << :inspect_called
- "<inspect>"
- end
+ it "uses a literal name when receiver is nil" do
+ begin
+ nil.foo
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']foo' for nil\Z/
end
- instance = test_class.new
+ end
+
+ it "uses a literal name when receiver is true" do
+ begin
+ true.foo
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']foo' for true\Z/
+ end
+ end
+
+ it "uses a literal name when receiver is false" do
+ begin
+ false.foo
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']foo' for false\Z/
+ end
+ end
+
+ it "uses #name when receiver is a class" do
+ klass = Class.new { def self.name; "MyClass"; end }
+
+ begin
+ klass.foo
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']foo' for class MyClass\Z/
+ end
+ end
+
+ it "uses class' string representation when receiver is an anonymous class" do
+ klass = Class.new
+
+ begin
+ klass.foo
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']foo' for class #<Class:0x\h+>\Z/
+ end
+ end
+
+ it "uses class' string representation when receiver is a singleton class" do
+ obj = Object.new
+ singleton_class = obj.singleton_class
+
+ begin
+ singleton_class.foo
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']foo' for class #<Class:#<Object:0x\h+>>\Z/
+ end
+ end
+
+ it "uses #name when receiver is a module" do
+ mod = Module.new { def self.name; "MyModule"; end }
+
+ begin
+ mod.foo
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']foo' for module MyModule\Z/
+ end
+ end
+
+ it "uses module's string representation when receiver is an anonymous module" do
+ m = Module.new
+
+ begin
+ m.foo
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']foo' for module #<Module:0x\h+>\Z/
+ end
+ end
+
+ it "uses class #name when receiver is an ordinary object" do
+ klass = Class.new { def self.name; "MyClass"; end }
+ instance = klass.new
+
begin
instance.bar
- rescue Exception => e
- e.name.should == :bar
- ScratchPad.recorded.should == []
- e.message.should =~ /undefined method.+\bbar\b/
- ScratchPad.recorded.should == [:inspect_called]
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']bar' for an instance of MyClass\Z/
+ end
+ end
+
+ it "uses class string representation when receiver is an instance of anonymous class" do
+ klass = Class.new
+ instance = klass.new
+
+ begin
+ instance.bar
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']bar' for an instance of #<Class:0x\h+>\Z/
end
end
- it "fallbacks to a simpler representation of the receiver when receiver.inspect raises an exception" do
+ it "uses class name when receiver has a singleton class" do
+ instance = NoMethodErrorSpecs::NoMethodErrorA.new
+ def instance.foo; end
+
+ begin
+ instance.bar
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']bar' for #<NoMethodErrorSpecs::NoMethodErrorA:0x\h+>\Z/
+ end
+ end
+
+ it "does not call #inspect when calling Exception#message" do
+ ScratchPad.record []
test_class = Class.new do
def inspect
- raise NoMethodErrorSpecs::InstanceException
+ ScratchPad << :inspect_called
+ "<inspect>"
end
end
instance = test_class.new
+
begin
instance.bar
- rescue Exception => e
- e.name.should == :bar
- message = e.message
- message.should =~ /undefined method.+\bbar\b/
- message.should include test_class.inspect
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']bar' for an instance of #<Class:0x\h+>\Z/
+ ScratchPad.recorded.should == []
end
end
- ruby_version_is "2.8" do
- it "uses #name to display the receiver if it is a class or a module" do
- klass = Class.new { def self.name; "MyClass"; end }
- begin
- klass.foo
- rescue NoMethodError => error
- error.message.lines.first.should == "undefined method `foo' for MyClass:Class"
- end
+ it "does not truncate long class names" do
+ class_name = 'ExceptionSpecs::A' + 'a'*100
- mod = Module.new { def self.name; "MyModule"; end }
- begin
- mod.foo
- rescue NoMethodError => error
- error.message.lines.first.should == "undefined method `foo' for MyModule:Module"
- end
+ begin
+ eval <<~RUBY
+ class #{class_name}
+ end
+
+ obj = #{class_name}.new
+ obj.foo
+ RUBY
+ rescue NoMethodError => error
+ error.message.should =~ /\Aundefined method [`']foo' for an instance of #{class_name}\Z/
end
end
end
diff --git a/spec/ruby/core/exception/reason_spec.rb b/spec/ruby/core/exception/reason_spec.rb
index 210bbc9725..d7022768b6 100644
--- a/spec/ruby/core/exception/reason_spec.rb
+++ b/spec/ruby/core/exception/reason_spec.rb
@@ -6,7 +6,7 @@ describe "LocalJumpError#reason" do
end
it "returns 'return' for a return" do
- -> { get_me_a_return.call }.should raise_error(LocalJumpError) { |e|
+ -> { get_me_a_return.call }.should.raise(LocalJumpError) { |e|
e.reason.should == :return
}
end
diff --git a/spec/ruby/core/exception/receiver_spec.rb b/spec/ruby/core/exception/receiver_spec.rb
index d1c23b67be..6ecf640ad8 100644
--- a/spec/ruby/core/exception/receiver_spec.rb
+++ b/spec/ruby/core/exception/receiver_spec.rb
@@ -11,31 +11,31 @@ describe "NameError#receiver" do
-> {
receiver.doesnt_exist
- }.should raise_error(NameError) {|e| e.receiver.should equal(receiver) }
+ }.should.raise(NameError) {|e| e.receiver.should.equal?(receiver) }
end
it "returns the Object class when an undefined constant is called without namespace" do
-> {
DoesntExist
- }.should raise_error(NameError) {|e| e.receiver.should equal(Object) }
+ }.should.raise(NameError) {|e| e.receiver.should.equal?(Object) }
end
it "returns a class when an undefined constant is called" do
-> {
NameErrorSpecs::ReceiverClass::DoesntExist
- }.should raise_error(NameError) {|e| e.receiver.should equal(NameErrorSpecs::ReceiverClass) }
+ }.should.raise(NameError) {|e| e.receiver.should.equal?(NameErrorSpecs::ReceiverClass) }
end
it "returns the Object class when an undefined class variable is called" do
-> {
eval("class singleton_class::A; @@doesnt_exist end", binding, __FILE__, __LINE__)
- }.should raise_error(NameError) {|e| e.receiver.should equal(singleton_class::A) }
+ }.should.raise(NameError) {|e| e.receiver.should.equal?(singleton_class::A) }
end
it "returns a class when an undefined class variable is called in a subclass' namespace" do
-> {
NameErrorSpecs::ReceiverClass.new.call_undefined_class_variable
- }.should raise_error(NameError) {|e| e.receiver.should equal(NameErrorSpecs::ReceiverClass) }
+ }.should.raise(NameError) {|e| e.receiver.should.equal?(NameErrorSpecs::ReceiverClass) }
end
it "returns the receiver when raised from #instance_variable_get" do
@@ -43,16 +43,16 @@ describe "NameError#receiver" do
-> {
receiver.instance_variable_get("invalid_ivar_name")
- }.should raise_error(NameError) {|e| e.receiver.should equal(receiver) }
+ }.should.raise(NameError) {|e| e.receiver.should.equal?(receiver) }
end
it "returns the receiver when raised from #class_variable_get" do
-> {
Object.class_variable_get("invalid_cvar_name")
- }.should raise_error(NameError) {|e| e.receiver.should equal(Object) }
+ }.should.raise(NameError) {|e| e.receiver.should.equal?(Object) }
end
it "raises an ArgumentError when the receiver is none" do
- -> { NameError.new.receiver }.should raise_error(ArgumentError)
+ -> { NameError.new.receiver }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/exception/result_spec.rb b/spec/ruby/core/exception/result_spec.rb
index 2f12673295..451ff43af5 100644
--- a/spec/ruby/core/exception/result_spec.rb
+++ b/spec/ruby/core/exception/result_spec.rb
@@ -14,10 +14,8 @@ describe "StopIteration#result" do
it "returns the method-returned-object from an Enumerator" do
@enum.next
@enum.next
- -> { @enum.next }.should(
- raise_error(StopIteration) do |error|
- error.result.should equal(:method_returned)
- end
- )
+ -> { @enum.next }.should.raise(StopIteration) { |error|
+ error.result.should.equal?(:method_returned)
+ }
end
end
diff --git a/spec/ruby/core/exception/set_backtrace_spec.rb b/spec/ruby/core/exception/set_backtrace_spec.rb
index ba2e1bf7aa..2cd93326ec 100644
--- a/spec/ruby/core/exception/set_backtrace_spec.rb
+++ b/spec/ruby/core/exception/set_backtrace_spec.rb
@@ -1,56 +1,23 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'
+require_relative 'shared/set_backtrace'
describe "Exception#set_backtrace" do
- it "accepts an Array of Strings" do
- err = RuntimeError.new
- err.set_backtrace ["unhappy"]
- err.backtrace.should == ["unhappy"]
- end
-
it "allows the user to set the backtrace from a rescued exception" do
bt = ExceptionSpecs::Backtrace.backtrace
err = RuntimeError.new
+ err.backtrace.should == nil
+ err.backtrace_locations.should == nil
err.set_backtrace bt
- err.backtrace.should == bt
- end
-
- it "accepts an empty Array" do
- err = RuntimeError.new
- err.set_backtrace []
- err.backtrace.should == []
- end
-
- it "accepts a String" do
- err = RuntimeError.new
- err.set_backtrace "unhappy"
- err.backtrace.should == ["unhappy"]
- end
- it "accepts nil" do
- err = RuntimeError.new
- err.set_backtrace nil
- err.backtrace.should be_nil
- end
-
- it "raises a TypeError when passed a Symbol" do
- err = RuntimeError.new
- -> { err.set_backtrace :unhappy }.should raise_error(TypeError)
+ err.backtrace.should == bt
+ err.backtrace_locations.should == nil
end
- it "raises a TypeError when the Array contains a Symbol" do
+ it_behaves_like :exception_set_backtrace, -> backtrace {
err = RuntimeError.new
- -> { err.set_backtrace ["String", :unhappy] }.should raise_error(TypeError)
- end
-
- it "raises a TypeError when the array contains nil" do
- err = Exception.new
- -> { err.set_backtrace ["String", nil] }.should raise_error(TypeError)
- end
-
- it "raises a TypeError when the argument is a nested array" do
- err = Exception.new
- -> { err.set_backtrace ["String", ["String"]] }.should raise_error(TypeError)
- end
+ err.set_backtrace(backtrace)
+ err
+ }
end
diff --git a/spec/ruby/core/exception/shared/new.rb b/spec/ruby/core/exception/shared/new.rb
index bcde8ee4b2..048fd14dd2 100644
--- a/spec/ruby/core/exception/shared/new.rb
+++ b/spec/ruby/core/exception/shared/new.rb
@@ -1,6 +1,6 @@
describe :exception_new, shared: true do
it "creates a new instance of Exception" do
- Exception.should be_ancestor_of(Exception.send(@method).class)
+ Exception.send(@method).class.ancestors.should.include?(Exception)
end
it "sets the message of the Exception when passes a message" do
@@ -12,7 +12,7 @@ describe :exception_new, shared: true do
end
it "returns the exception when it has a custom constructor" do
- ExceptionSpecs::ConstructorException.send(@method).should be_kind_of(ExceptionSpecs::ConstructorException)
+ ExceptionSpecs::ConstructorException.send(@method).should.is_a?(ExceptionSpecs::ConstructorException)
end
end
diff --git a/spec/ruby/core/exception/shared/set_backtrace.rb b/spec/ruby/core/exception/shared/set_backtrace.rb
new file mode 100644
index 0000000000..934bf3dc5f
--- /dev/null
+++ b/spec/ruby/core/exception/shared/set_backtrace.rb
@@ -0,0 +1,64 @@
+require_relative '../fixtures/common'
+
+describe :exception_set_backtrace, shared: true do
+ it "accepts an Array of Strings" do
+ err = @method.call(["unhappy"])
+ err.backtrace.should == ["unhappy"]
+ end
+
+ it "allows the user to set the backtrace from a rescued exception" do
+ bt = ExceptionSpecs::Backtrace.backtrace
+ err = @method.call(bt)
+ err.backtrace.should == bt
+ end
+
+ ruby_version_is "3.4" do
+ it "allows the user to set backtrace locations from a rescued exception" do
+ bt_locations = ExceptionSpecs::Backtrace.backtrace_locations
+ err = @method.call(bt_locations)
+ err.backtrace_locations.size.should == bt_locations.size
+ err.backtrace_locations.each_with_index do |loc, index|
+ other_loc = bt_locations[index]
+
+ loc.path.should == other_loc.path
+ loc.label.should == other_loc.label
+ loc.base_label.should == other_loc.base_label
+ loc.lineno.should == other_loc.lineno
+ loc.absolute_path.should == other_loc.absolute_path
+ loc.to_s.should == other_loc.to_s
+ end
+ err.backtrace.size.should == err.backtrace_locations.size
+ end
+ end
+
+ it "accepts an empty Array" do
+ err = @method.call([])
+ err.backtrace.should == []
+ end
+
+ it "accepts a String" do
+ err = @method.call("unhappy")
+ err.backtrace.should == ["unhappy"]
+ end
+
+ it "accepts nil" do
+ err = @method.call(nil)
+ err.backtrace.should == nil
+ end
+
+ it "raises a TypeError when passed a Symbol" do
+ -> { @method.call(:unhappy) }.should.raise(TypeError)
+ end
+
+ it "raises a TypeError when the Array contains a Symbol" do
+ -> { @method.call(["String", :unhappy]) }.should.raise(TypeError)
+ end
+
+ it "raises a TypeError when the array contains nil" do
+ -> { @method.call(["String", nil]) }.should.raise(TypeError)
+ end
+
+ it "raises a TypeError when the argument is a nested array" do
+ -> { @method.call(["String", ["String"]]) }.should.raise(TypeError)
+ end
+end
diff --git a/spec/ruby/core/exception/signal_exception_spec.rb b/spec/ruby/core/exception/signal_exception_spec.rb
index e494e18cde..010181bc55 100644
--- a/spec/ruby/core/exception/signal_exception_spec.rb
+++ b/spec/ruby/core/exception/signal_exception_spec.rb
@@ -9,7 +9,7 @@ describe "SignalException.new" do
end
it "raises an exception with an invalid signal number" do
- -> { SignalException.new(100000) }.should raise_error(ArgumentError)
+ -> { SignalException.new(100000) }.should.raise(ArgumentError)
end
it "takes a signal name without SIG prefix as the first argument" do
@@ -27,13 +27,11 @@ describe "SignalException.new" do
end
it "raises an exception with an invalid signal name" do
- -> { SignalException.new("NONEXISTENT") }.should raise_error(ArgumentError)
+ -> { SignalException.new("NONEXISTENT") }.should.raise(ArgumentError)
end
- ruby_version_is "2.6" do
- it "raises an exception with an invalid first argument type" do
- -> { SignalException.new(Object.new) }.should raise_error(ArgumentError)
- end
+ it "raises an exception with an invalid first argument type" do
+ -> { SignalException.new(Object.new) }.should.raise(ArgumentError)
end
it "takes a signal symbol without SIG prefix as the first argument" do
@@ -51,7 +49,7 @@ describe "SignalException.new" do
end
it "raises an exception with an invalid signal name" do
- -> { SignalException.new(:NONEXISTENT) }.should raise_error(ArgumentError)
+ -> { SignalException.new(:NONEXISTENT) }.should.raise(ArgumentError)
end
it "takes an optional message argument with a signal number" do
@@ -62,7 +60,7 @@ describe "SignalException.new" do
end
it "raises an exception for an optional argument with a signal name" do
- -> { SignalException.new("INT","name") }.should raise_error(ArgumentError)
+ -> { SignalException.new("INT","name") }.should.raise(ArgumentError)
end
end
@@ -95,7 +93,7 @@ describe "SignalException" do
platform_is_not :windows do
it "runs after at_exit" do
- output = ruby_exe(<<-RUBY)
+ output = ruby_exe(<<-RUBY, exit_status: :SIGKILL)
at_exit do
puts "hello"
$stdout.flush
@@ -109,7 +107,7 @@ describe "SignalException" do
end
it "cannot be trapped with Signal.trap" do
- ruby_exe(<<-RUBY)
+ ruby_exe(<<-RUBY, exit_status: :SIGPROF)
Signal.trap("PROF") {}
raise(SignalException, "PROF")
RUBY
@@ -118,7 +116,7 @@ describe "SignalException" do
end
it "self-signals for USR1" do
- ruby_exe("raise(SignalException, 'USR1')")
+ ruby_exe("raise(SignalException, 'USR1')", exit_status: :SIGUSR1)
$?.termsig.should == Signal.list.fetch('USR1')
end
end
diff --git a/spec/ruby/core/exception/signm_spec.rb b/spec/ruby/core/exception/signm_spec.rb
index 4adff3b5ee..cabcc7ad58 100644
--- a/spec/ruby/core/exception/signm_spec.rb
+++ b/spec/ruby/core/exception/signm_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "SignalException#signm" do
it "returns the signal name" do
- -> { Process.kill(:TERM, Process.pid) }.should raise_error(SignalException) { |e|
+ -> { Process.kill(:TERM, Process.pid) }.should.raise(SignalException) { |e|
e.signm.should == 'SIGTERM'
}
end
diff --git a/spec/ruby/core/exception/signo_spec.rb b/spec/ruby/core/exception/signo_spec.rb
index 62fc321516..46e79a8daf 100644
--- a/spec/ruby/core/exception/signo_spec.rb
+++ b/spec/ruby/core/exception/signo_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "SignalException#signo" do
it "returns the signal number" do
- -> { Process.kill(:TERM, Process.pid) }.should raise_error(SignalException) { |e|
+ -> { Process.kill(:TERM, Process.pid) }.should.raise(SignalException) { |e|
e.signo.should == Signal.list['TERM']
}
end
diff --git a/spec/ruby/core/exception/standard_error_spec.rb b/spec/ruby/core/exception/standard_error_spec.rb
index 17e98ce7f0..b05d247f67 100644
--- a/spec/ruby/core/exception/standard_error_spec.rb
+++ b/spec/ruby/core/exception/standard_error_spec.rb
@@ -18,6 +18,6 @@ describe "StandardError" do
end
it "does not rescue superclass of StandardError" do
- -> { begin; raise Exception; rescue; end }.should raise_error(Exception)
+ -> { begin; raise Exception; rescue; end }.should.raise(Exception)
end
end
diff --git a/spec/ruby/core/exception/status_spec.rb b/spec/ruby/core/exception/status_spec.rb
index 8ace00fe10..7369b0815d 100644
--- a/spec/ruby/core/exception/status_spec.rb
+++ b/spec/ruby/core/exception/status_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "SystemExit#status" do
it "returns the exit status" do
- -> { exit 42 }.should raise_error(SystemExit) { |e|
+ -> { exit 42 }.should.raise(SystemExit) { |e|
e.status.should == 42
}
end
diff --git a/spec/ruby/core/exception/success_spec.rb b/spec/ruby/core/exception/success_spec.rb
index 6f21743340..5ab8f94454 100644
--- a/spec/ruby/core/exception/success_spec.rb
+++ b/spec/ruby/core/exception/success_spec.rb
@@ -2,13 +2,13 @@ require_relative '../../spec_helper'
describe "SystemExit#success?" do
it "returns true if the process exited successfully" do
- -> { exit 0 }.should raise_error(SystemExit) { |e|
+ -> { exit 0 }.should.raise(SystemExit) { |e|
e.should.success?
}
end
it "returns false if the process exited unsuccessfully" do
- -> { exit(-1) }.should raise_error(SystemExit) { |e|
+ -> { exit(-1) }.should.raise(SystemExit) { |e|
e.should_not.success?
}
end
diff --git a/spec/ruby/core/exception/syntax_error_spec.rb b/spec/ruby/core/exception/syntax_error_spec.rb
new file mode 100644
index 0000000000..66eb5649aa
--- /dev/null
+++ b/spec/ruby/core/exception/syntax_error_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+
+describe "SyntaxError#path" do
+ it "returns the file path provided to eval" do
+ filename = "speccing.rb"
+
+ -> {
+ eval("if true", TOPLEVEL_BINDING, filename)
+ }.should.raise(SyntaxError) { |e|
+ e.path.should == filename
+ }
+ end
+
+ it "returns the file path that raised an exception" do
+ expected_path = fixture(__FILE__, "syntax_error.rb")
+
+ -> {
+ require_relative "fixtures/syntax_error"
+ }.should.raise(SyntaxError) { |e| e.path.should == expected_path }
+ end
+
+ it "returns nil when constructed directly" do
+ SyntaxError.new.path.should == nil
+ end
+end
diff --git a/spec/ruby/core/exception/system_call_error_spec.rb b/spec/ruby/core/exception/system_call_error_spec.rb
index f27977a547..da01c5b6b0 100644
--- a/spec/ruby/core/exception/system_call_error_spec.rb
+++ b/spec/ruby/core/exception/system_call_error_spec.rb
@@ -14,8 +14,10 @@ describe "SystemCallError" do
end
exc = ExceptionSpecs::SCESub.new
- ScratchPad.recorded.should equal(:initialize)
- exc.should be_an_instance_of(ExceptionSpecs::SCESub)
+ ScratchPad.recorded.should.equal?(:initialize)
+ exc.should.instance_of?(ExceptionSpecs::SCESub)
+ ensure
+ ExceptionSpecs.send(:remove_const, :SCESub)
end
end
@@ -25,13 +27,14 @@ describe "SystemCallError.new" do
@example_errno_class = Errno::EINVAL
@last_known_errno = Errno.constants.size - 1
@unknown_errno = Errno.constants.size
+ @some_human_readable = /[[:graph:]]+/
end
it "requires at least one argument" do
- -> { SystemCallError.new }.should raise_error(ArgumentError)
+ -> { SystemCallError.new }.should.raise(ArgumentError)
end
- it "accepts single Fixnum argument as errno" do
+ it "accepts single Integer argument as errno" do
SystemCallError.new(-2**24).errno.should == -2**24
SystemCallError.new(-1).errno.should == -1
SystemCallError.new(0).errno.should == 0
@@ -41,28 +44,33 @@ describe "SystemCallError.new" do
end
it "constructs a SystemCallError for an unknown error number" do
- SystemCallError.new(-2**24).should be_an_instance_of(SystemCallError)
- SystemCallError.new(-1).should be_an_instance_of(SystemCallError)
- SystemCallError.new(@unknown_errno).should be_an_instance_of(SystemCallError)
- SystemCallError.new(2**24).should be_an_instance_of(SystemCallError)
+ SystemCallError.new(-2**24).should.instance_of?(SystemCallError)
+ SystemCallError.new(-1).should.instance_of?(SystemCallError)
+ SystemCallError.new(@unknown_errno).should.instance_of?(SystemCallError)
+ SystemCallError.new(2**24).should.instance_of?(SystemCallError)
end
it "constructs the appropriate Errno class" do
e = SystemCallError.new(@example_errno)
- e.should be_kind_of(SystemCallError)
- e.should be_an_instance_of(@example_errno_class)
+ e.should.is_a?(SystemCallError)
+ e.should.instance_of?(@example_errno_class)
+ end
+
+ it "sets an error message corresponding to an appropriate Errno class" do
+ e = SystemCallError.new(@example_errno)
+ e.message.should == 'Invalid argument'
end
it "accepts an optional custom message preceding the errno" do
exc = SystemCallError.new("custom message", @example_errno)
- exc.should be_an_instance_of(@example_errno_class)
+ exc.should.instance_of?(@example_errno_class)
exc.errno.should == @example_errno
exc.message.should == 'Invalid argument - custom message'
end
it "accepts an optional third argument specifying the location" do
exc = SystemCallError.new("custom message", @example_errno, "location")
- exc.should be_an_instance_of(@example_errno_class)
+ exc.should.instance_of?(@example_errno_class)
exc.errno.should == @example_errno
exc.message.should == 'Invalid argument @ location - custom message'
end
@@ -81,20 +89,37 @@ describe "SystemCallError.new" do
SystemCallError.new('foo', 2.9).should == SystemCallError.new('foo', 2)
end
+ it "treats nil errno as unknown error value" do
+ SystemCallError.new(nil).should.instance_of?(SystemCallError)
+ end
+
+ it "treats nil custom message as if it is not passed at all" do
+ exc = SystemCallError.new(nil, @example_errno)
+ exc.message.should == 'Invalid argument'
+ end
+
+ it "sets an 'unknown error' message when an unknown error number" do
+ SystemCallError.new(-1).message.should =~ @some_human_readable
+ end
+
+ it "adds a custom error message to an 'unknown error' message when an unknown error number and a custom message specified" do
+ SystemCallError.new("custom message", -1).message.should =~ /#{@some_human_readable}.* - custom message/
+ end
+
it "converts to Integer if errno is a Complex convertible to Integer" do
SystemCallError.new('foo', Complex(2.9, 0)).should == SystemCallError.new('foo', 2)
end
it "raises TypeError if message is not a String" do
- -> { SystemCallError.new(:foo, 1) }.should raise_error(TypeError, /no implicit conversion of Symbol into String/)
+ -> { SystemCallError.new(:foo, 1) }.should.raise(TypeError, /no implicit conversion of Symbol into String/)
end
it "raises TypeError if errno is not an Integer" do
- -> { SystemCallError.new('foo', 'bar') }.should raise_error(TypeError, /no implicit conversion of String into Integer/)
+ -> { SystemCallError.new('foo', 'bar') }.should.raise(TypeError, /no implicit conversion of String into Integer/)
end
it "raises RangeError if errno is a Complex not convertible to Integer" do
- -> { SystemCallError.new('foo', Complex(2.9, 1)) }.should raise_error(RangeError, /can't convert/)
+ -> { SystemCallError.new('foo', Complex(2.9, 1)) }.should.raise(RangeError, /can't convert/)
end
end
@@ -115,12 +140,7 @@ end
describe "SystemCallError#message" do
it "returns the default message when no message is given" do
- platform_is :aix do
- SystemCallError.new(2**28).message.should =~ /Error .*occurred/i
- end
- platform_is_not :aix do
- SystemCallError.new(2**28).message.should =~ /Unknown error/i
- end
+ SystemCallError.new(2**28).message.should =~ @some_human_readable
end
it "returns the message given as an argument to new" do
diff --git a/spec/ruby/core/exception/system_exit_spec.rb b/spec/ruby/core/exception/system_exit_spec.rb
new file mode 100644
index 0000000000..d899844c4e
--- /dev/null
+++ b/spec/ruby/core/exception/system_exit_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../../spec_helper'
+
+describe "SystemExit" do
+ describe "#initialize" do
+ it "accepts a status and message" do
+ exc = SystemExit.new(42, "message")
+ exc.status.should == 42
+ exc.message.should == "message"
+
+ exc = SystemExit.new(true, "message")
+ exc.status.should == 0
+ exc.message.should == "message"
+
+ exc = SystemExit.new(false, "message")
+ exc.status.should == 1
+ exc.message.should == "message"
+ end
+
+ it "accepts a status only" do
+ exc = SystemExit.new(42)
+ exc.status.should == 42
+ exc.message.should == "SystemExit"
+
+ exc = SystemExit.new(true)
+ exc.status.should == 0
+ exc.message.should == "SystemExit"
+
+ exc = SystemExit.new(false)
+ exc.status.should == 1
+ exc.message.should == "SystemExit"
+ end
+
+ it "accepts a message only" do
+ exc = SystemExit.new("message")
+ exc.status.should == 0
+ exc.message.should == "message"
+ end
+
+ it "accepts no arguments" do
+ exc = SystemExit.new
+ exc.status.should == 0
+ exc.message.should == "SystemExit"
+ end
+ end
+
+ it "sets the exit status and exits silently when raised" do
+ code = 'raise SystemExit.new(7)'
+ result = ruby_exe(code, args: "2>&1", exit_status: 7)
+ result.should == ""
+ $?.exitstatus.should == 7
+ end
+
+ it "sets the exit status and exits silently when raised when subclassed" do
+ code = 'class CustomExit < SystemExit; end; raise CustomExit.new(8)'
+ result = ruby_exe(code, args: "2>&1", exit_status: 8)
+ result.should == ""
+ $?.exitstatus.should == 8
+ end
+end
diff --git a/spec/ruby/core/exception/to_s_spec.rb b/spec/ruby/core/exception/to_s_spec.rb
index 4c4c7ab432..65c0d73a98 100644
--- a/spec/ruby/core/exception/to_s_spec.rb
+++ b/spec/ruby/core/exception/to_s_spec.rb
@@ -23,7 +23,7 @@ describe "NameError#to_s" do
begin
puts not_defined
rescue => exception
- exception.message.should =~ /undefined local variable or method `not_defined'/
+ exception.message.should =~ /undefined local variable or method [`']not_defined'/
end
end
diff --git a/spec/ruby/core/exception/top_level_spec.rb b/spec/ruby/core/exception/top_level_spec.rb
index 96f957411e..cc961d06d5 100644
--- a/spec/ruby/core/exception/top_level_spec.rb
+++ b/spec/ruby/core/exception/top_level_spec.rb
@@ -2,7 +2,38 @@ require_relative '../../spec_helper'
describe "An Exception reaching the top level" do
it "is printed on STDERR" do
- ruby_exe('raise "foo"', args: "2>&1").should.include?("in `<main>': foo (RuntimeError)")
+ ruby_exe('raise "foo"', args: "2>&1", exit_status: 1).should =~ /in [`']<main>': foo \(RuntimeError\)/
+ end
+
+ it "the Exception#cause is printed to STDERR with backtraces" do
+ code = <<-RUBY
+ def raise_cause
+ raise "the cause" # 2
+ end
+ def raise_wrapped
+ raise "wrapped" # 5
+ end
+ begin
+ raise_cause # 8
+ rescue
+ raise_wrapped # 10
+ end
+ RUBY
+ lines = ruby_exe(code, args: "2>&1", exit_status: 1).lines
+
+ lines.map! { |l| l.chomp[/:(\d+:in.+)/, 1] }
+ lines[0].should =~ /\A5:in [`'](?:Object#)?raise_wrapped': wrapped \(RuntimeError\)\z/
+ if lines[1].include? 'rescue in'
+ # CRuby < 3.4 has an extra 'rescue in' backtrace entry
+ lines[1].should =~ /\A10:in [`']rescue in <main>'\z/
+ lines.delete_at 1
+ lines[1].should =~ /\A7:in [`']<main>'\z/
+ else
+ lines[1].should =~ /\A10:in [`']<main>'\z/
+ end
+ lines[2].should =~ /\A2:in [`'](?:Object#)?raise_cause': the cause \(RuntimeError\)\z/
+ lines[3].should =~ /\A8:in [`']<main>'\z/
+ lines.size.should == 4
end
describe "with a custom backtrace" do
@@ -13,10 +44,22 @@ describe "An Exception reaching the top level" do
"/dir/bar.rb:20:in `caller'",
]
RUBY
- ruby_exe(code, args: "2>&1").should == <<-EOS
+ ruby_exe(code, args: "2>&1", exit_status: 1).should == <<-EOS
/dir/foo.rb:10:in `raising': foo (RuntimeError)
\tfrom /dir/bar.rb:20:in `caller'
EOS
end
end
+
+ describe "kills all threads and fibers, ensure clauses are only run for threads current fibers, not for suspended fibers" do
+ it "with ensure on the root fiber" do
+ file = fixture(__FILE__, "thread_fiber_ensure.rb")
+ ruby_exe(file, args: "2>&1", exit_status: 0).should == "current fiber ensure\n"
+ end
+
+ it "with ensure on non-root fiber" do
+ file = fixture(__FILE__, "thread_fiber_ensure_non_root_fiber.rb")
+ ruby_exe(file, args: "2>&1", exit_status: 0).should == "current fiber ensure\n"
+ end
+ end
end
diff --git a/spec/ruby/core/false/case_compare_spec.rb b/spec/ruby/core/false/case_compare_spec.rb
new file mode 100644
index 0000000000..0bd0ab44ae
--- /dev/null
+++ b/spec/ruby/core/false/case_compare_spec.rb
@@ -0,0 +1,14 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass#===" do
+ it "returns true for false" do
+ (false === false).should == true
+ end
+
+ it "returns false for non-false object" do
+ (false === 0).should == false
+ (false === "").should == false
+ (false === Object).should == false
+ (false === nil).should == false
+ end
+end
diff --git a/spec/ruby/core/false/dup_spec.rb b/spec/ruby/core/false/dup_spec.rb
index 1a569a2f4f..b0eb85529e 100644
--- a/spec/ruby/core/false/dup_spec.rb
+++ b/spec/ruby/core/false/dup_spec.rb
@@ -2,6 +2,6 @@ require_relative '../../spec_helper'
describe "FalseClass#dup" do
it "returns self" do
- false.dup.should equal(false)
+ false.dup.should.equal?(false)
end
end
diff --git a/spec/ruby/core/false/falseclass_spec.rb b/spec/ruby/core/false/falseclass_spec.rb
index c018ef2421..8dfe5ae891 100644
--- a/spec/ruby/core/false/falseclass_spec.rb
+++ b/spec/ruby/core/false/falseclass_spec.rb
@@ -4,12 +4,12 @@ describe "FalseClass" do
it ".allocate raises a TypeError" do
-> do
FalseClass.allocate
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it ".new is undefined" do
-> do
FalseClass.new
- end.should raise_error(NoMethodError)
+ end.should.raise(NoMethodError)
end
end
diff --git a/spec/ruby/core/false/singleton_method_spec.rb b/spec/ruby/core/false/singleton_method_spec.rb
new file mode 100644
index 0000000000..72aee8609a
--- /dev/null
+++ b/spec/ruby/core/false/singleton_method_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "FalseClass#singleton_method" do
+ it "raises regardless of whether FalseClass defines the method" do
+ -> { false.singleton_method(:foo) }.should.raise(NameError)
+ begin
+ def (false).foo; end
+ -> { false.singleton_method(:foo) }.should.raise(NameError)
+ ensure
+ FalseClass.send(:remove_method, :foo)
+ end
+ end
+end
diff --git a/spec/ruby/core/false/to_s_spec.rb b/spec/ruby/core/false/to_s_spec.rb
index 4cae278891..9e24be26a2 100644
--- a/spec/ruby/core/false/to_s_spec.rb
+++ b/spec/ruby/core/false/to_s_spec.rb
@@ -5,13 +5,11 @@ describe "FalseClass#to_s" do
false.to_s.should == "false"
end
- ruby_version_is "2.7" do
- it "returns a frozen string" do
- false.to_s.should.frozen?
- end
+ it "returns a frozen string" do
+ false.to_s.should.frozen?
+ end
- it "always returns the same string" do
- false.to_s.should equal(false.to_s)
- end
+ it "always returns the same string" do
+ false.to_s.should.equal?(false.to_s)
end
end
diff --git a/spec/ruby/core/fiber/alive_spec.rb b/spec/ruby/core/fiber/alive_spec.rb
new file mode 100644
index 0000000000..6fb1229d95
--- /dev/null
+++ b/spec/ruby/core/fiber/alive_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+
+describe "Fiber#alive?" do
+ it "returns true for a Fiber that hasn't had #resume called" do
+ fiber = Fiber.new { true }
+ fiber.alive?.should == true
+ end
+
+ # FIXME: Better description?
+ it "returns true for a Fiber that's yielded to the caller" do
+ fiber = Fiber.new { Fiber.yield }
+ fiber.resume
+ fiber.alive?.should == true
+ end
+
+ it "returns true when called from its Fiber" do
+ fiber = Fiber.new { fiber.alive?.should == true }
+ fiber.resume
+ end
+
+ it "doesn't invoke the block associated with the Fiber" do
+ offthehook = mock('do not call')
+ offthehook.should_not_receive(:ring)
+ fiber = Fiber.new { offthehook.ring }
+ fiber.alive?
+ end
+
+ it "returns false for a Fiber that's dead" do
+ fiber = Fiber.new { true }
+ fiber.resume
+ -> { fiber.resume }.should.raise(FiberError)
+ fiber.alive?.should == false
+ end
+
+ it "always returns false for a dead Fiber" do
+ fiber = Fiber.new { true }
+ fiber.resume
+ -> { fiber.resume }.should.raise(FiberError)
+ fiber.alive?.should == false
+ -> { fiber.resume }.should.raise(FiberError)
+ fiber.alive?.should == false
+ fiber.alive?.should == false
+ end
+end
diff --git a/spec/ruby/core/fiber/blocking_spec.rb b/spec/ruby/core/fiber/blocking_spec.rb
new file mode 100644
index 0000000000..d5caf81fbe
--- /dev/null
+++ b/spec/ruby/core/fiber/blocking_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../spec_helper'
+require_relative 'shared/blocking'
+
+describe "Fiber.blocking?" do
+ it_behaves_like :non_blocking_fiber, -> { Fiber.blocking? }
+
+ context "when fiber is blocking" do
+ context "root Fiber of the main thread" do
+ it "returns 1 for blocking: true" do
+ fiber = Fiber.new(blocking: true) { Fiber.blocking? }
+ blocking = fiber.resume
+
+ blocking.should == 1
+ end
+ end
+
+ context "root Fiber of a new thread" do
+ it "returns 1 for blocking: true" do
+ thread = Thread.new do
+ fiber = Fiber.new(blocking: true) { Fiber.blocking? }
+ blocking = fiber.resume
+
+ blocking.should == 1
+ end
+
+ thread.join
+ end
+ end
+ end
+end
+
+describe "Fiber#blocking?" do
+ it_behaves_like :non_blocking_fiber, -> { Fiber.current.blocking? }
+
+ context "when fiber is blocking" do
+ context "root Fiber of the main thread" do
+ it "returns true for blocking: true" do
+ fiber = Fiber.new(blocking: true) { Fiber.current.blocking? }
+ blocking = fiber.resume
+
+ blocking.should == true
+ end
+ end
+
+ context "root Fiber of a new thread" do
+ it "returns true for blocking: true" do
+ thread = Thread.new do
+ fiber = Fiber.new(blocking: true) { Fiber.current.blocking? }
+ blocking = fiber.resume
+
+ blocking.should == true
+ end
+
+ thread.join
+ end
+ end
+ end
+end
+
+describe "Fiber.blocking" do
+ context "when fiber is non-blocking" do
+ it "can become blocking" do
+ fiber = Fiber.new(blocking: false) do
+ Fiber.blocking do |f|
+ f.blocking? ? :blocking : :non_blocking
+ end
+ end
+
+ blocking = fiber.resume
+ blocking.should == :blocking
+ end
+ end
+end
diff --git a/spec/ruby/core/fiber/current_spec.rb b/spec/ruby/core/fiber/current_spec.rb
new file mode 100644
index 0000000000..cc5c9117b6
--- /dev/null
+++ b/spec/ruby/core/fiber/current_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../../spec_helper'
+
+describe "Fiber.current" do
+ it "returns the root Fiber when called outside of a Fiber" do
+ root = Fiber.current
+ root.should.instance_of?(Fiber)
+ # We can always transfer to the root Fiber; it will never die
+ 5.times do
+ root.transfer.should == nil
+ root.alive?.should == true
+ end
+ end
+
+ it "returns the current Fiber when called from a Fiber" do
+ fiber = Fiber.new do
+ this = Fiber.current
+ this.should.instance_of?(Fiber)
+ this.should == fiber
+ this.alive?.should == true
+ end
+ fiber.resume
+ end
+
+ it "returns the current Fiber when called from a Fiber that transferred to another" do
+ states = []
+ fiber = Fiber.new do
+ states << :fiber
+ this = Fiber.current
+ this.should.instance_of?(Fiber)
+ this.should == fiber
+ this.alive?.should == true
+ end
+
+ fiber2 = Fiber.new do
+ states << :fiber2
+ fiber.transfer
+ flunk
+ end
+
+ fiber3 = Fiber.new do
+ states << :fiber3
+ fiber2.transfer
+ states << :fiber3_terminated
+ end
+
+ fiber3.resume
+
+ states.should == [:fiber3, :fiber2, :fiber, :fiber3_terminated]
+ end
+end
diff --git a/spec/ruby/core/fiber/fixtures/classes.rb b/spec/ruby/core/fiber/fixtures/classes.rb
new file mode 100644
index 0000000000..6b0e0fbc42
--- /dev/null
+++ b/spec/ruby/core/fiber/fixtures/classes.rb
@@ -0,0 +1,22 @@
+module FiberSpecs
+
+ class NewFiberToRaise
+ def self.raise(*args, **kwargs, &block)
+ fiber = Fiber.new do
+ if block_given?
+ block.call do
+ Fiber.yield
+ end
+ else
+ Fiber.yield
+ end
+ end
+
+ fiber.resume
+
+ fiber.raise(*args, **kwargs)
+ end
+ end
+
+ class CustomError < StandardError; end
+end
diff --git a/spec/ruby/core/fiber/fixtures/scheduler.rb b/spec/ruby/core/fiber/fixtures/scheduler.rb
new file mode 100644
index 0000000000..16bd2f6b44
--- /dev/null
+++ b/spec/ruby/core/fiber/fixtures/scheduler.rb
@@ -0,0 +1,35 @@
+module FiberSpecs
+
+ class LoggingScheduler
+ attr_reader :events
+ def initialize
+ @events = []
+ end
+
+ def block(*args)
+ @events << { event: :block, fiber: Fiber.current, args: args }
+ Fiber.yield
+ end
+
+ def io_wait(*args)
+ @events << { event: :io_wait, fiber: Fiber.current, args: args }
+ Fiber.yield
+ end
+
+ def kernel_sleep(*args)
+ @events << { event: :kernel_sleep, fiber: Fiber.current, args: args }
+ Fiber.yield
+ end
+
+ def unblock(*args)
+ @events << { event: :unblock, fiber: Fiber.current, args: args }
+ Fiber.yield
+ end
+
+ def fiber_interrupt(*args)
+ @events << { event: :fiber_interrupt, fiber: Fiber.current, args: args }
+ Fiber.yield
+ end
+ end
+
+end
diff --git a/spec/ruby/core/fiber/inspect_spec.rb b/spec/ruby/core/fiber/inspect_spec.rb
new file mode 100644
index 0000000000..fcfef20716
--- /dev/null
+++ b/spec/ruby/core/fiber/inspect_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+
+describe "Fiber#inspect" do
+ describe "status" do
+ it "is resumed for the root Fiber of a Thread" do
+ inspected = Thread.new { Fiber.current.inspect }.value
+ inspected.should =~ /\A#<Fiber:0x\h+ .*\(resumed\)>\z/
+ end
+
+ it "is created for a Fiber which did not run yet" do
+ inspected = Fiber.new {}.inspect
+ inspected.should =~ /\A#<Fiber:0x\h+ .+ \(created\)>\z/
+ end
+
+ it "is resumed for a Fiber which was resumed" do
+ inspected = Fiber.new { Fiber.current.inspect }.resume
+ inspected.should =~ /\A#<Fiber:0x\h+ .+ \(resumed\)>\z/
+ end
+
+ it "is resumed for a Fiber which was transferred" do
+ inspected = Fiber.new { Fiber.current.inspect }.transfer
+ inspected.should =~ /\A#<Fiber:0x\h+ .+ \(resumed\)>\z/
+ end
+
+ it "is suspended for a Fiber which was resumed and yielded" do
+ inspected = Fiber.new { Fiber.yield }.tap(&:resume).inspect
+ inspected.should =~ /\A#<Fiber:0x\h+ .+ \(suspended\)>\z/
+ end
+
+ it "is terminated for a Fiber which has terminated" do
+ inspected = Fiber.new {}.tap(&:resume).inspect
+ inspected.should =~ /\A#<Fiber:0x\h+ .+ \(terminated\)>\z/
+ end
+ end
+end
diff --git a/spec/ruby/core/fiber/kill_spec.rb b/spec/ruby/core/fiber/kill_spec.rb
new file mode 100644
index 0000000000..abf23ff176
--- /dev/null
+++ b/spec/ruby/core/fiber/kill_spec.rb
@@ -0,0 +1,88 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/kernel/raise'
+
+describe "Fiber#kill" do
+ it "kills a non-resumed fiber" do
+ fiber = Fiber.new{}
+
+ fiber.alive?.should == true
+
+ fiber.kill
+ fiber.alive?.should == false
+ end
+
+ it "kills a resumed fiber" do
+ fiber = Fiber.new{while true; Fiber.yield; end}
+ fiber.resume
+
+ fiber.alive?.should == true
+
+ fiber.kill
+ fiber.alive?.should == false
+ end
+
+ it "can kill itself" do
+ fiber = Fiber.new do
+ Fiber.current.kill
+ end
+
+ fiber.alive?.should == true
+
+ fiber.resume
+ fiber.alive?.should == false
+ end
+
+ it "kills a resumed fiber from a child" do
+ parent = Fiber.new do
+ child = Fiber.new do
+ parent.kill
+ parent.alive?.should == true
+ end
+
+ child.resume
+ end
+
+ parent.resume
+ parent.alive?.should == false
+ end
+
+ it "executes the ensure block" do
+ ensure_executed = false
+
+ fiber = Fiber.new do
+ while true; Fiber.yield; end
+ ensure
+ ensure_executed = true
+ end
+
+ fiber.resume
+ fiber.kill
+ ensure_executed.should == true
+ end
+
+ it "does not execute rescue block" do
+ rescue_executed = false
+
+ fiber = Fiber.new do
+ while true; Fiber.yield; end
+ rescue Exception
+ rescue_executed = true
+ end
+
+ fiber.resume
+ fiber.kill
+ rescue_executed.should == false
+ end
+
+ it "repeatedly kills a fiber" do
+ fiber = Fiber.new do
+ while true; Fiber.yield; end
+ ensure
+ while true; Fiber.yield; end
+ end
+
+ fiber.kill
+ fiber.alive?.should == false
+ end
+end
diff --git a/spec/ruby/core/fiber/new_spec.rb b/spec/ruby/core/fiber/new_spec.rb
index b43c1386be..d31167496d 100644
--- a/spec/ruby/core/fiber/new_spec.rb
+++ b/spec/ruby/core/fiber/new_spec.rb
@@ -4,7 +4,7 @@ describe "Fiber.new" do
it "creates a fiber from the given block" do
fiber = Fiber.new {}
fiber.resume
- fiber.should be_an_instance_of(Fiber)
+ fiber.should.instance_of?(Fiber)
end
it "creates a fiber from a subclass" do
@@ -12,17 +12,17 @@ describe "Fiber.new" do
end
fiber = MyFiber.new {}
fiber.resume
- fiber.should be_an_instance_of(MyFiber)
+ fiber.should.instance_of?(MyFiber)
end
it "raises an ArgumentError if called without a block" do
- -> { Fiber.new }.should raise_error(ArgumentError)
+ -> { Fiber.new }.should.raise(ArgumentError)
end
it "does not invoke the block" do
invoked = false
fiber = Fiber.new { invoked = true }
- invoked.should be_false
+ invoked.should == false
fiber.resume
end
diff --git a/spec/ruby/core/fiber/raise_spec.rb b/spec/ruby/core/fiber/raise_spec.rb
new file mode 100644
index 0000000000..107e5bd4ce
--- /dev/null
+++ b/spec/ruby/core/fiber/raise_spec.rb
@@ -0,0 +1,141 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/kernel/raise'
+
+describe "Fiber#raise" do
+ it "is a public method" do
+ Fiber.public_instance_methods.should.include?(:raise)
+ end
+
+ it_behaves_like :kernel_raise, :raise, FiberSpecs::NewFiberToRaise
+ it_behaves_like :kernel_raise_across_contexts, :raise, FiberSpecs::NewFiberToRaise
+ ruby_version_is "4.0" do
+ it_behaves_like :kernel_raise_with_cause, :raise, FiberSpecs::NewFiberToRaise
+ end
+
+ it 'raises RuntimeError by default' do
+ -> { FiberSpecs::NewFiberToRaise.raise }.should.raise(RuntimeError)
+ end
+
+ it "raises FiberError if Fiber is not born" do
+ fiber = Fiber.new { true }
+ -> { fiber.raise }.should.raise(FiberError, "cannot raise exception on unborn fiber")
+ end
+
+ it "raises FiberError if Fiber is dead" do
+ fiber = Fiber.new { true }
+ fiber.resume
+ -> { fiber.raise }.should.raise(FiberError, /dead fiber called|attempt to resume a terminated fiber/)
+ end
+
+ it 'accepts error class' do
+ -> { FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError }.should.raise(FiberSpecs::CustomError)
+ end
+
+ it 'accepts error message' do
+ -> { FiberSpecs::NewFiberToRaise.raise "error message" }.should.raise(RuntimeError, "error message")
+ end
+
+ it 'does not accept array of backtrace information only' do
+ -> { FiberSpecs::NewFiberToRaise.raise ['foo'] }.should.raise(TypeError)
+ end
+
+ it 'does not accept integer' do
+ -> { FiberSpecs::NewFiberToRaise.raise 100 }.should.raise(TypeError)
+ end
+
+ it 'accepts error class with error message' do
+ -> { FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError, 'test error' }.should.raise(FiberSpecs::CustomError, 'test error')
+ end
+
+ it 'accepts error class with error message and backtrace information' do
+ -> {
+ FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError, 'test error', ['foo', 'boo']
+ }.should.raise(FiberSpecs::CustomError) { |e|
+ e.message.should == 'test error'
+ e.backtrace.should == ['foo', 'boo']
+ }
+ end
+
+ it 'does not accept only error message and backtrace information' do
+ -> { FiberSpecs::NewFiberToRaise.raise 'test error', ['foo', 'boo'] }.should.raise(TypeError)
+ end
+
+ it "raises a FiberError if invoked from a different Thread" do
+ fiber = Fiber.new { Fiber.yield }
+ fiber.resume
+ Thread.new do
+ -> {
+ fiber.raise
+ }.should.raise(FiberError, "fiber called across threads")
+ end.join
+ end
+
+ it "kills Fiber" do
+ fiber = Fiber.new { Fiber.yield :first; :second }
+ fiber.resume
+ -> { fiber.raise }.should.raise
+ -> { fiber.resume }.should.raise(FiberError, /dead fiber called|attempt to resume a terminated fiber/)
+ end
+
+ it "returns to calling fiber after raise" do
+ fiber_one = Fiber.new do
+ Fiber.yield :yield_one
+ :unreachable
+ end
+
+ fiber_two = Fiber.new do
+ results = []
+ results << fiber_one.resume
+ begin
+ fiber_one.raise
+ rescue
+ results << :rescued
+ end
+ results
+ end
+
+ fiber_two.resume.should == [:yield_one, :rescued]
+ end
+
+ ruby_version_is "3.4" do
+ it "raises on the resumed fiber" do
+ root_fiber = Fiber.current
+ f1 = Fiber.new { root_fiber.transfer }
+ f2 = Fiber.new { f1.resume }
+ f2.transfer
+
+ -> do
+ f2.raise(RuntimeError, "Expected error")
+ end.should.raise(RuntimeError, "Expected error")
+ end
+
+ it "raises on itself" do
+ -> do
+ Fiber.current.raise(RuntimeError, "Expected error")
+ end.should.raise(RuntimeError, "Expected error")
+ end
+
+ it "should raise on parent fiber" do
+ f2 = nil
+ f1 = Fiber.new do
+ # This is equivalent to Kernel#raise:
+ f2.raise(RuntimeError, "Expected error")
+ end
+ f2 = Fiber.new do
+ f1.resume
+ end
+
+ -> do
+ f2.resume
+ end.should.raise(RuntimeError, "Expected error")
+ end
+ end
+
+ it "transfers and raises on a transferring fiber" do
+ root = Fiber.current
+ fiber = Fiber.new { root.transfer }
+ fiber.transfer
+ -> { fiber.raise "msg" }.should.raise(RuntimeError, "msg")
+ end
+end
diff --git a/spec/ruby/core/fiber/resume_spec.rb b/spec/ruby/core/fiber/resume_spec.rb
index 97495c5059..e183cc10d9 100644
--- a/spec/ruby/core/fiber/resume_spec.rb
+++ b/spec/ruby/core/fiber/resume_spec.rb
@@ -1,14 +1,36 @@
require_relative '../../spec_helper'
-require_relative '../../shared/fiber/resume'
+require_relative 'shared/resume'
describe "Fiber#resume" do
it_behaves_like :fiber_resume, :resume
end
describe "Fiber#resume" do
+ it "runs until Fiber.yield" do
+ obj = mock('obj')
+ obj.should_not_receive(:do)
+ fiber = Fiber.new { 1 + 2; Fiber.yield; obj.do }
+ fiber.resume
+ end
+
+ it "resumes from the last call to Fiber.yield on subsequent invocations" do
+ fiber = Fiber.new { Fiber.yield :first; :second }
+ fiber.resume.should == :first
+ fiber.resume.should == :second
+ end
+
+ it "sets the block parameters to its arguments on the first invocation" do
+ first = mock('first')
+ first.should_receive(:arg).with(:first).twice
+
+ fiber = Fiber.new { |arg| first.arg arg; Fiber.yield; first.arg arg; }
+ fiber.resume :first
+ fiber.resume :second
+ end
+
it "raises a FiberError if the Fiber tries to resume itself" do
fiber = Fiber.new { fiber.resume }
- -> { fiber.resume }.should raise_error(FiberError, /double resume/)
+ -> { fiber.resume }.should.raise(FiberError, /current fiber/)
end
it "returns control to the calling Fiber if called from one" do
@@ -45,4 +67,17 @@ describe "Fiber#resume" do
ruby_exe(code).should == "ensure executed\n"
end
+
+ it "can work with Fiber#transfer" do
+ fiber1 = Fiber.new { true }
+ fiber2 = Fiber.new { fiber1.transfer; Fiber.yield 10 ; Fiber.yield 20; raise }
+ fiber2.resume.should == 10
+ fiber2.resume.should == 20
+ end
+
+ it "raises a FiberError if the Fiber attempts to resume a resuming fiber" do
+ root_fiber = Fiber.current
+ fiber1 = Fiber.new { root_fiber.resume }
+ -> { fiber1.resume }.should.raise(FiberError, /attempt to resume a resuming fiber/)
+ end
end
diff --git a/spec/ruby/core/fiber/scheduler_spec.rb b/spec/ruby/core/fiber/scheduler_spec.rb
new file mode 100644
index 0000000000..15a03c1479
--- /dev/null
+++ b/spec/ruby/core/fiber/scheduler_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'shared/scheduler'
+
+require "fiber"
+
+describe "Fiber.scheduler" do
+ it_behaves_like :scheduler, :scheduler
+end
diff --git a/spec/ruby/core/fiber/set_scheduler_spec.rb b/spec/ruby/core/fiber/set_scheduler_spec.rb
new file mode 100644
index 0000000000..82f6acbe86
--- /dev/null
+++ b/spec/ruby/core/fiber/set_scheduler_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'shared/scheduler'
+
+require "fiber"
+
+describe "Fiber.scheduler" do
+ it_behaves_like :scheduler, :set_scheduler
+end
diff --git a/spec/ruby/core/fiber/shared/blocking.rb b/spec/ruby/core/fiber/shared/blocking.rb
new file mode 100644
index 0000000000..21707e1ea7
--- /dev/null
+++ b/spec/ruby/core/fiber/shared/blocking.rb
@@ -0,0 +1,41 @@
+describe :non_blocking_fiber, shared: true do
+ context "root Fiber of the main thread" do
+ it "returns false" do
+ fiber = Fiber.new { @method.call }
+ blocking = fiber.resume
+
+ blocking.should == false
+ end
+
+ it "returns false for blocking: false" do
+ fiber = Fiber.new(blocking: false) { @method.call }
+ blocking = fiber.resume
+
+ blocking.should == false
+ end
+ end
+
+ context "root Fiber of a new thread" do
+ it "returns false" do
+ thread = Thread.new do
+ fiber = Fiber.new { @method.call }
+ blocking = fiber.resume
+
+ blocking.should == false
+ end
+
+ thread.join
+ end
+
+ it "returns false for blocking: false" do
+ thread = Thread.new do
+ fiber = Fiber.new(blocking: false) { @method.call }
+ blocking = fiber.resume
+
+ blocking.should == false
+ end
+
+ thread.join
+ end
+ end
+end
diff --git a/spec/ruby/core/fiber/shared/resume.rb b/spec/ruby/core/fiber/shared/resume.rb
new file mode 100644
index 0000000000..ff4bb72c73
--- /dev/null
+++ b/spec/ruby/core/fiber/shared/resume.rb
@@ -0,0 +1,58 @@
+describe :fiber_resume, shared: true do
+ it "can be invoked from the root Fiber" do
+ fiber = Fiber.new { :fiber }
+ fiber.send(@method).should == :fiber
+ end
+
+ it "raises a FiberError if invoked from a different Thread" do
+ fiber = Fiber.new { 42 }
+ Thread.new do
+ -> {
+ fiber.send(@method)
+ }.should.raise(FiberError)
+ end.join
+
+ # Check the Fiber can still be used
+ fiber.send(@method).should == 42
+ end
+
+ it "passes control to the beginning of the block on first invocation" do
+ invoked = false
+ fiber = Fiber.new { invoked = true }
+ fiber.send(@method)
+ invoked.should == true
+ end
+
+ it "returns the last value encountered on first invocation" do
+ fiber = Fiber.new { 1+1; true }
+ fiber.send(@method).should == true
+ end
+
+ it "runs until the end of the block" do
+ obj = mock('obj')
+ obj.should_receive(:do).once
+ fiber = Fiber.new { 1 + 2; a = "glark"; obj.do }
+ fiber.send(@method)
+ end
+
+ it "accepts any number of arguments" do
+ fiber = Fiber.new { |a| }
+ -> { fiber.send(@method, *(1..10).to_a) }.should_not.raise
+ end
+
+ it "raises a FiberError if the Fiber is dead" do
+ fiber = Fiber.new { true }
+ fiber.send(@method)
+ -> { fiber.send(@method) }.should.raise(FiberError)
+ end
+
+ it "raises a LocalJumpError if the block includes a return statement" do
+ fiber = Fiber.new { return; }
+ -> { fiber.send(@method) }.should.raise(LocalJumpError)
+ end
+
+ it "raises a LocalJumpError if the block includes a break statement" do
+ fiber = Fiber.new { break; }
+ -> { fiber.send(@method) }.should.raise(LocalJumpError)
+ end
+end
diff --git a/spec/ruby/core/fiber/shared/scheduler.rb b/spec/ruby/core/fiber/shared/scheduler.rb
new file mode 100644
index 0000000000..04bdded53a
--- /dev/null
+++ b/spec/ruby/core/fiber/shared/scheduler.rb
@@ -0,0 +1,51 @@
+describe :scheduler, shared: true do
+ it "validates the scheduler for required methods" do
+ required_methods = [:block, :unblock, :kernel_sleep, :io_wait]
+ required_methods.each do |missing_method|
+ scheduler = Object.new
+ required_methods.difference([missing_method]).each do |method|
+ scheduler.define_singleton_method(method) {}
+ end
+ -> {
+ suppress_warning { Fiber.set_scheduler(scheduler) }
+ }.should.raise(ArgumentError, /Scheduler must implement ##{missing_method}/)
+ end
+ end
+
+ it "can set and get the scheduler" do
+ required_methods = [:block, :unblock, :kernel_sleep, :io_wait]
+ scheduler = Object.new
+ required_methods.each do |method|
+ scheduler.define_singleton_method(method) {}
+ end
+ suppress_warning { Fiber.set_scheduler(scheduler) }
+ Fiber.scheduler.should == scheduler
+ end
+
+ it "returns the scheduler after setting it" do
+ required_methods = [:block, :unblock, :kernel_sleep, :io_wait]
+ scheduler = Object.new
+ required_methods.each do |method|
+ scheduler.define_singleton_method(method) {}
+ end
+ result = suppress_warning { Fiber.set_scheduler(scheduler) }
+ result.should == scheduler
+ end
+
+ it "can remove the scheduler" do
+ required_methods = [:block, :unblock, :kernel_sleep, :io_wait]
+ scheduler = Object.new
+ required_methods.each do |method|
+ scheduler.define_singleton_method(method) {}
+ end
+ suppress_warning { Fiber.set_scheduler(scheduler) }
+ Fiber.set_scheduler(nil)
+ Fiber.scheduler.should == nil
+ end
+
+ it "can assign a nil scheduler multiple times" do
+ Fiber.set_scheduler(nil)
+ Fiber.set_scheduler(nil)
+ Fiber.scheduler.should == nil
+ end
+end
diff --git a/spec/ruby/core/fiber/storage_spec.rb b/spec/ruby/core/fiber/storage_spec.rb
new file mode 100644
index 0000000000..a3f6bf9cad
--- /dev/null
+++ b/spec/ruby/core/fiber/storage_spec.rb
@@ -0,0 +1,177 @@
+require_relative '../../spec_helper'
+
+describe "Fiber.new(storage:)" do
+ it "creates a Fiber with the given storage" do
+ storage = {life: 42}
+ fiber = Fiber.new(storage: storage) { Fiber.current.storage }
+ fiber.resume.should == storage
+ end
+
+ it "creates a fiber with lazily initialized storage" do
+ Fiber.new(storage: nil) { Fiber[:x] = 10; Fiber.current.storage }.resume.should == {x: 10}
+ end
+
+ it "creates a fiber by inheriting the storage of the parent fiber" do
+ fiber = Fiber.new(storage: {life: 42}) do
+ Fiber.new { Fiber.current.storage }.resume
+ end
+ fiber.resume.should == {life: 42}
+ end
+
+ it "cannot create a fiber with non-hash storage" do
+ -> { Fiber.new(storage: 42) {} }.should.raise(TypeError)
+ end
+
+ it "cannot create a fiber with a frozen hash as storage" do
+ -> { Fiber.new(storage: {life: 43}.freeze) {} }.should.raise(FrozenError)
+ end
+
+ it "cannot create a fiber with a storage hash with non-symbol keys" do
+ -> { Fiber.new(storage: {life: 43, Object.new => 44}) {} }.should.raise(TypeError)
+ end
+end
+
+describe "Fiber#storage" do
+ it "cannot be accessed from a different fiber" do
+ f = Fiber.new(storage: {life: 42}) { nil }
+ -> {
+ f.storage
+ }.should.raise(ArgumentError, /Fiber storage can only be accessed from the Fiber it belongs to/)
+ end
+end
+
+describe "Fiber#storage=" do
+ it "can clear the storage of the fiber" do
+ fiber = Fiber.new(storage: {life: 42}) do
+ Fiber.current.storage = nil
+ Fiber[:x] = 10
+ Fiber.current.storage
+ end
+ fiber.resume.should == {x: 10}
+ end
+
+ it "can set the storage of the fiber" do
+ fiber = Fiber.new(storage: {life: 42}) do
+ Fiber.current.storage = {life: 43}
+ Fiber.current.storage
+ end
+ fiber.resume.should == {life: 43}
+ end
+
+ it "can't set the storage of the fiber to non-hash" do
+ -> { Fiber.current.storage = 42 }.should.raise(TypeError)
+ end
+
+ it "can't set the storage of the fiber to a frozen hash" do
+ -> { Fiber.current.storage = {life: 43}.freeze }.should.raise(FrozenError)
+ end
+
+ it "can't set the storage of the fiber to a hash with non-symbol keys" do
+ -> { Fiber.current.storage = {life: 43, Object.new => 44} }.should.raise(TypeError)
+ end
+end
+
+describe "Fiber.[]" do
+ it "returns the value of the given key in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:life] }.resume.should == 42
+ end
+
+ it "returns nil if the key is not present in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:death] }.resume.should == nil
+ end
+
+ it "returns nil if the current fiber has no storage" do
+ Fiber.new { Fiber[:life] }.resume.should == nil
+ end
+
+ it "can use dynamically defined keys" do
+ key = :"#{self.class.name}#.#{self.object_id}"
+ Fiber.new { Fiber[key] = 42; Fiber[key] }.resume.should == 42
+ end
+
+ it "can't use invalid keys" do
+ invalid_keys = [Object.new, 12]
+ invalid_keys.each do |key|
+ -> { Fiber[key] }.should.raise(TypeError)
+ end
+ end
+
+ ruby_bug "#20978", ""..."3.4" do
+ it "can use keys as strings" do
+ key = Object.new
+ def key.to_str; "Foo"; end
+ Fiber.new { Fiber[key] = 42; Fiber["Foo"] }.resume.should == 42
+ end
+
+ it "converts a String key into a Symbol" do
+ Fiber.new { Fiber["key"] = 42; Fiber[:key] }.resume.should == 42
+ Fiber.new { Fiber[:key] = 42; Fiber["key"] }.resume.should == 42
+ end
+
+ it "can use any object that responds to #to_str as a key" do
+ key = mock("key")
+ key.should_receive(:to_str).twice.and_return("key")
+ Fiber.new { Fiber[key] = 42; Fiber[key] }.resume.should == 42
+ end
+ end
+
+ it "does not call #to_sym on the key" do
+ key = mock("key")
+ key.should_not_receive(:to_sym)
+ -> { Fiber[key] }.should.raise(TypeError)
+ end
+
+ it "can access the storage of the parent fiber" do
+ f = Fiber.new(storage: {life: 42}) do
+ Fiber.new { Fiber[:life] }.resume
+ end
+ f.resume.should == 42
+ end
+
+ it "can't access the storage of the fiber with non-symbol keys" do
+ -> { Fiber[Object.new] }.should.raise(TypeError)
+ end
+end
+
+describe "Fiber.[]=" do
+ it "sets the value of the given key in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:life] = 43; Fiber[:life] }.resume.should == 43
+ end
+
+ it "sets the value of the given key in the storage of the current fiber" do
+ Fiber.new(storage: {life: 42}) { Fiber[:death] = 43; Fiber[:death] }.resume.should == 43
+ end
+
+ it "sets the value of the given key in the storage of the current fiber" do
+ Fiber.new { Fiber[:life] = 43; Fiber[:life] }.resume.should == 43
+ end
+
+ it "does not overwrite the storage of the parent fiber" do
+ f = Fiber.new(storage: {life: 42}) do
+ Fiber.yield Fiber.new { Fiber[:life] = 43; Fiber[:life] }.resume
+ Fiber[:life]
+ end
+ f.resume.should == 43 # Value of the inner fiber
+ f.resume.should == 42 # Value of the outer fiber
+ end
+
+ it "can't access the storage of the fiber with non-symbol keys" do
+ -> { Fiber[Object.new] = 44 }.should.raise(TypeError)
+ end
+
+ it "deletes the fiber storage key when assigning nil" do
+ Fiber.new(storage: {life: 42}) {
+ Fiber[:life] = nil
+ Fiber.current.storage
+ }.resume.should == {}
+ end
+end
+
+describe "Thread.new" do
+ it "creates a thread with the storage of the current fiber" do
+ fiber = Fiber.new(storage: {life: 42}) do
+ Thread.new { Fiber.current.storage }.value
+ end
+ fiber.resume.should == {life: 42}
+ end
+end
diff --git a/spec/ruby/core/fiber/transfer_spec.rb b/spec/ruby/core/fiber/transfer_spec.rb
new file mode 100644
index 0000000000..d8737aeeb3
--- /dev/null
+++ b/spec/ruby/core/fiber/transfer_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../spec_helper'
+require_relative 'shared/resume'
+
+describe "Fiber#transfer" do
+ it_behaves_like :fiber_resume, :transfer
+end
+
+describe "Fiber#transfer" do
+ it "transfers control from one Fiber to another when called from a Fiber" do
+ fiber1 = Fiber.new { :fiber1 }
+ fiber2 = Fiber.new { fiber1.transfer; :fiber2 }
+ fiber2.resume.should == :fiber2
+ end
+
+ it "returns to the root Fiber when finished" do
+ f1 = Fiber.new { :fiber_1 }
+ f2 = Fiber.new { f1.transfer; :fiber_2 }
+
+ f2.transfer.should == :fiber_1
+ f2.transfer.should == :fiber_2
+ end
+
+ it "can be invoked from the same Fiber it transfers control to" do
+ states = []
+ fiber = Fiber.new { states << :start; fiber.transfer; states << :end }
+ fiber.transfer
+ states.should == [:start, :end]
+
+ states = []
+ fiber = Fiber.new { states << :start; fiber.transfer; states << :end }
+ fiber.resume
+ states.should == [:start, :end]
+ end
+
+ it "can not transfer control to a Fiber that has suspended by Fiber.yield" do
+ states = []
+ fiber1 = Fiber.new { states << :fiber1 }
+ fiber2 = Fiber.new { states << :fiber2_start; Fiber.yield fiber1.transfer; states << :fiber2_end}
+ fiber2.resume.should == [:fiber2_start, :fiber1]
+ -> { fiber2.transfer }.should.raise(FiberError)
+ end
+
+ it "raises a FiberError when transferring to a Fiber which resumes itself" do
+ fiber = Fiber.new { fiber.resume }
+ -> { fiber.transfer }.should.raise(FiberError)
+ end
+
+ it "works if Fibers in different Threads each transfer to a Fiber in the same Thread" do
+ # This catches a bug where Fibers are running on a thread-pool
+ # and Fibers from a different Ruby Thread reuse the same native thread.
+ # Caching the Ruby Thread based on the native thread is not correct in that case,
+ # and the check for "fiber called across threads" in Fiber#transfer
+ # might be incorrect based on that.
+ 2.times do
+ Thread.new do
+ io_fiber = Fiber.new do |calling_fiber|
+ calling_fiber.transfer
+ end
+ io_fiber.transfer(Fiber.current)
+ value = Object.new
+ io_fiber.transfer(value).should.equal? value
+ end.join
+ end
+ end
+
+ it "transfers control between a non-main thread's root fiber to a child fiber and back again" do
+ states = []
+ thread = Thread.new do
+ f1 = Fiber.new do |f0|
+ states << 0
+ value2 = f0.transfer(1)
+ states << value2
+ 3
+ end
+
+ value1 = f1.transfer(Fiber.current)
+ states << value1
+ value3 = f1.transfer(2)
+ states << value3
+ end
+ thread.join
+ states.should == [0, 1, 2, 3]
+ end
+end
diff --git a/spec/ruby/core/fiber/yield_spec.rb b/spec/ruby/core/fiber/yield_spec.rb
index b010912c87..12ec6ebcef 100644
--- a/spec/ruby/core/fiber/yield_spec.rb
+++ b/spec/ruby/core/fiber/yield_spec.rb
@@ -18,7 +18,7 @@ describe "Fiber.yield" do
it "returns nil to the caller if given no arguments" do
fiber = Fiber.new { true; Fiber.yield; true }
- fiber.resume.should be_nil
+ fiber.resume.should == nil
fiber.resume
end
@@ -44,6 +44,6 @@ describe "Fiber.yield" do
end
it "raises a FiberError if called from the root Fiber" do
- ->{ Fiber.yield }.should raise_error(FiberError)
+ ->{ Fiber.yield }.should.raise(FiberError)
end
end
diff --git a/spec/ruby/core/file/absolute_path_spec.rb b/spec/ruby/core/file/absolute_path_spec.rb
index 52839cf1cc..fc12985a75 100644
--- a/spec/ruby/core/file/absolute_path_spec.rb
+++ b/spec/ruby/core/file/absolute_path_spec.rb
@@ -1,54 +1,52 @@
require_relative '../../spec_helper'
-ruby_version_is "2.7" do
- describe "File.absolute_path?" do
- before :each do
- @abs = File.expand_path(__FILE__)
- end
+describe "File.absolute_path?" do
+ before :each do
+ @abs = File.expand_path(__FILE__)
+ end
- it "returns true if it's an absolute pathname" do
- File.absolute_path?(@abs).should be_true
- end
+ it "returns true if it's an absolute pathname" do
+ File.absolute_path?(@abs).should == true
+ end
- it "returns false if it's a relative path" do
- File.absolute_path?(File.basename(__FILE__)).should be_false
- end
+ it "returns false if it's a relative path" do
+ File.absolute_path?(File.basename(__FILE__)).should == false
+ end
- it "returns false if it's a tricky relative path" do
- File.absolute_path?("C:foo\\bar").should be_false
- end
+ it "returns false if it's a tricky relative path" do
+ File.absolute_path?("C:foo\\bar").should == false
+ end
- it "does not expand '~' to a home directory." do
- File.absolute_path?('~').should be_false
- end
+ it "does not expand '~' to a home directory." do
+ File.absolute_path?('~').should == false
+ end
- it "does not expand '~user' to a home directory." do
- path = File.dirname(@abs)
- Dir.chdir(path) do
- File.absolute_path?('~user').should be_false
- end
+ it "does not expand '~user' to a home directory." do
+ path = File.dirname(@abs)
+ Dir.chdir(path) do
+ File.absolute_path?('~user').should == false
end
+ end
- it "calls #to_path on its argument" do
- mock = mock_to_path(File.expand_path(__FILE__))
+ it "calls #to_path on its argument" do
+ mock = mock_to_path(File.expand_path(__FILE__))
- File.absolute_path?(mock).should be_true
- end
+ File.absolute_path?(mock).should == true
+ end
- platform_is_not :windows do
- it "takes into consideration the platform's root" do
- File.absolute_path?("C:\\foo\\bar").should be_false
- File.absolute_path?("C:/foo/bar").should be_false
- File.absolute_path?("/foo/bar\\baz").should be_true
- end
+ platform_is_not :windows do
+ it "takes into consideration the platform's root" do
+ File.absolute_path?("C:\\foo\\bar").should == false
+ File.absolute_path?("C:/foo/bar").should == false
+ File.absolute_path?("/foo/bar\\baz").should == true
end
+ end
- platform_is :windows do
- it "takes into consideration the platform path separator(s)" do
- File.absolute_path?("C:\\foo\\bar").should be_true
- File.absolute_path?("C:/foo/bar").should be_true
- File.absolute_path?("/foo/bar\\baz").should be_false
- end
+ platform_is :windows do
+ it "takes into consideration the platform path separator(s)" do
+ File.absolute_path?("C:\\foo\\bar").should == true
+ File.absolute_path?("C:/foo/bar").should == true
+ File.absolute_path?("/foo/bar\\baz").should == false
end
end
end
@@ -73,6 +71,12 @@ describe "File.absolute_path" do
File.absolute_path('~').should_not == File.expand_path('~')
end
+ platform_is_not :windows do
+ it "does not expand '~' when given dir argument" do
+ File.absolute_path('~', '/').should == '/~'
+ end
+ end
+
it "does not expand '~user' to a home directory." do
path = File.dirname(@abs)
Dir.chdir(path) do
@@ -81,7 +85,7 @@ describe "File.absolute_path" do
end
it "accepts a second argument of a directory from which to resolve the path" do
- File.absolute_path(__FILE__, File.dirname(__FILE__)).should == @abs
+ File.absolute_path(__FILE__, __dir__).should == @abs
end
it "calls #to_path on its argument" do
diff --git a/spec/ruby/core/file/atime_spec.rb b/spec/ruby/core/file/atime_spec.rb
index 5663e47266..5c6c110eec 100644
--- a/spec/ruby/core/file/atime_spec.rb
+++ b/spec/ruby/core/file/atime_spec.rb
@@ -12,25 +12,30 @@ describe "File.atime" do
it "returns the last access time for the named file as a Time object" do
File.atime(@file)
- File.atime(@file).should be_kind_of(Time)
+ File.atime(@file).should.is_a?(Time)
end
platform_is :linux, :windows do
- ## NOTE also that some Linux systems disable atime (e.g. via mount params) for better filesystem speed.
- it "returns the last access time for the named file with microseconds" do
- supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d+)/, 1], 10)
- if supports_subseconds != 0
- expected_time = Time.at(Time.now.to_i + 0.123456)
- File.utime expected_time, 0, @file
- File.atime(@file).usec.should == expected_time.usec
- else
- File.atime(__FILE__).usec.should == 0
+ unless ENV.key?('TRAVIS') # https://bugs.ruby-lang.org/issues/17926
+ ## NOTE also that some Linux systems disable atime (e.g. via mount params) for better filesystem speed.
+ it "returns the last access time for the named file with microseconds" do
+ supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10)
+ if supports_subseconds != 0
+ expected_time = Time.at(Time.now.to_i + 0.123456)
+ File.utime expected_time, 0, @file
+ File.atime(@file).usec.should == expected_time.usec
+ else
+ File.atime(__FILE__).usec.should == 0
+ end
+ rescue Errno::ENOENT => e
+ # Native Windows don't have stat command.
+ skip e.message
end
end
end
it "raises an Errno::ENOENT exception if the file is not found" do
- -> { File.atime('a_fake_file') }.should raise_error(Errno::ENOENT)
+ -> { File.atime('a_fake_file') }.should.raise(Errno::ENOENT)
end
it "accepts an object that has a #to_path method" do
@@ -50,6 +55,6 @@ describe "File#atime" do
it "returns the last access time to self" do
@file.atime
- @file.atime.should be_kind_of(Time)
+ @file.atime.should.is_a?(Time)
end
end
diff --git a/spec/ruby/core/file/basename_spec.rb b/spec/ruby/core/file/basename_spec.rb
index 6d7e432086..77afe5c22f 100644
--- a/spec/ruby/core/file/basename_spec.rb
+++ b/spec/ruby/core/file/basename_spec.rb
@@ -42,7 +42,7 @@ describe "File.basename" do
end
it "returns an string" do
- File.basename("foo").should be_kind_of(String)
+ File.basename("foo").should.is_a?(String)
end
it "returns the basename for unix format" do
@@ -105,10 +105,10 @@ describe "File.basename" do
end
it "raises a TypeError if the arguments are not String types" do
- -> { File.basename(nil) }.should raise_error(TypeError)
- -> { File.basename(1) }.should raise_error(TypeError)
- -> { File.basename("bar.txt", 1) }.should raise_error(TypeError)
- -> { File.basename(true) }.should raise_error(TypeError)
+ -> { File.basename(nil) }.should.raise(TypeError)
+ -> { File.basename(1) }.should.raise(TypeError)
+ -> { File.basename("bar.txt", 1) }.should.raise(TypeError)
+ -> { File.basename(true) }.should.raise(TypeError)
end
it "accepts an object that has a #to_path method" do
@@ -116,7 +116,7 @@ describe "File.basename" do
end
it "raises an ArgumentError if passed more than two arguments" do
- -> { File.basename('bar.txt', '.txt', '.txt') }.should raise_error(ArgumentError)
+ -> { File.basename('bar.txt', '.txt', '.txt') }.should.raise(ArgumentError)
end
# specific to MS Windows
@@ -151,8 +151,30 @@ describe "File.basename" do
File.basename("c:\\bar.txt", ".*").should == "bar"
File.basename("c:\\bar.txt.exe", ".*").should == "bar.txt"
end
+
+ it "handles Shift JIS 0x5C (\\) as second byte of a multi-byte sequence" do
+ # dir\fileソname.txt
+ path = "dir\\file\x83\x5cname.txt".b.force_encoding(Encoding::SHIFT_JIS)
+ path.valid_encoding?.should == true
+ File.basename(path).should == "file\x83\x5cname.txt".b.force_encoding(Encoding::SHIFT_JIS)
+ end
+ end
+
+ it "rejects strings encoded with non ASCII-compatible encodings" do
+ Encoding.list.reject(&:ascii_compatible?).reject(&:dummy?).each do |enc|
+ path = "/foo/bar".encode(enc)
+
+ -> {
+ File.basename(path)
+ }.should.raise(Encoding::CompatibilityError)
+ end
end
+ it "works with all ASCII-compatible encodings" do
+ Encoding.list.select(&:ascii_compatible?).each do |enc|
+ File.basename("/foo/bar".encode(enc)).should == "bar".encode(enc)
+ end
+ end
it "returns the extension for a multibyte filename" do
File.basename('/path/Офис.m4a').should == "Офис.m4a"
@@ -164,5 +186,20 @@ describe "File.basename" do
basename.encoding.should == Encoding::Windows_1250
end
+ it "returns a new unfrozen String" do
+ exts = [nil, '.rb', '.*', '.txt']
+ ['foo.rb','//', '/test/', 'test'].each do |example|
+ exts.each do |ext|
+ original = example.freeze
+ result = if ext
+ File.basename(original, ext)
+ else
+ File.basename(original)
+ end
+ result.should_not.equal?(original)
+ result.frozen?.should == false
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/file/birthtime_spec.rb b/spec/ruby/core/file/birthtime_spec.rb
index 755601df64..039fd7572c 100644
--- a/spec/ruby/core/file/birthtime_spec.rb
+++ b/spec/ruby/core/file/birthtime_spec.rb
@@ -1,60 +1,56 @@
require_relative '../../spec_helper'
-describe "File.birthtime" do
- before :each do
- @file = __FILE__
- end
+platform_is :windows, :darwin, :freebsd, :netbsd, :linux do
+ not_implemented_messages = [
+ "birthtime() function is unimplemented", # unsupported OS/version
+ "birthtime is unimplemented", # unsupported filesystem
+ ]
+
+ describe "File.birthtime" do
+ before :each do
+ @file = __FILE__
+ end
- after :each do
- @file = nil
- end
+ after :each do
+ @file = nil
+ end
- platform_is :windows, :darwin, :freebsd, :netbsd do
it "returns the birth time for the named file as a Time object" do
File.birthtime(@file)
- File.birthtime(@file).should be_kind_of(Time)
+ File.birthtime(@file).should.is_a?(Time)
+ rescue NotImplementedError => e
+ e.message.should.start_with?(*not_implemented_messages)
end
it "accepts an object that has a #to_path method" do
+ File.birthtime(@file) # Avoid to failure of mock object with old Kernel and glibc
File.birthtime(mock_to_path(@file))
+ rescue NotImplementedError => e
+ e.message.should.start_with?(*not_implemented_messages)
end
it "raises an Errno::ENOENT exception if the file is not found" do
- -> { File.birthtime('bogus') }.should raise_error(Errno::ENOENT)
+ -> { File.birthtime('bogus') }.should.raise(Errno::ENOENT)
+ rescue NotImplementedError => e
+ e.message.should.start_with?(*not_implemented_messages)
end
end
- platform_is :openbsd do
- it "raises an NotImplementedError" do
- -> { File.birthtime(@file) }.should raise_error(NotImplementedError)
+ describe "File#birthtime" do
+ before :each do
+ @file = File.open(__FILE__)
end
- end
-
- # TODO: depends on Linux kernel version
-end
-describe "File#birthtime" do
- before :each do
- @file = File.open(__FILE__)
- end
-
- after :each do
- @file.close
- @file = nil
- end
+ after :each do
+ @file.close
+ @file = nil
+ end
- platform_is :windows, :darwin, :freebsd, :netbsd do
it "returns the birth time for self" do
@file.birthtime
- @file.birthtime.should be_kind_of(Time)
+ @file.birthtime.should.is_a?(Time)
+ rescue NotImplementedError => e
+ e.message.should.start_with?(*not_implemented_messages)
end
end
-
- platform_is :openbsd do
- it "raises an NotImplementedError" do
- -> { @file.birthtime }.should raise_error(NotImplementedError)
- end
- end
-
- # TODO: depends on Linux kernel version
end
diff --git a/spec/ruby/core/file/chmod_spec.rb b/spec/ruby/core/file/chmod_spec.rb
index 5ca15c9748..e0fd10ceb1 100644
--- a/spec/ruby/core/file/chmod_spec.rb
+++ b/spec/ruby/core/file/chmod_spec.rb
@@ -16,8 +16,8 @@ describe "File#chmod" do
end
it "raises RangeError with too large values" do
- -> { @file.chmod(2**64) }.should raise_error(RangeError)
- -> { @file.chmod(-2**63 - 1) }.should raise_error(RangeError)
+ -> { @file.chmod(2**64) }.should.raise(RangeError)
+ -> { @file.chmod(-2**63 - 1) }.should.raise(RangeError)
end
it "invokes to_int on non-integer argument" do
@@ -97,8 +97,8 @@ describe "File.chmod" do
end
it "raises RangeError with too large values" do
- -> { File.chmod(2**64, @file) }.should raise_error(RangeError)
- -> { File.chmod(-2**63 - 1, @file) }.should raise_error(RangeError)
+ -> { File.chmod(2**64, @file) }.should.raise(RangeError)
+ -> { File.chmod(-2**63 - 1, @file) }.should.raise(RangeError)
end
it "accepts an object that has a #to_path method" do
@@ -106,13 +106,13 @@ describe "File.chmod" do
end
it "throws a TypeError if the given path is not coercible into a string" do
- -> { File.chmod(0, []) }.should raise_error(TypeError)
+ -> { File.chmod(0, []) }.should.raise(TypeError)
end
it "raises an error for a non existent path" do
-> {
File.chmod(0644, "#{@file}.not.existing")
- }.should raise_error(Errno::ENOENT)
+ }.should.raise(Errno::ENOENT)
end
it "invokes to_int on non-integer argument" do
diff --git a/spec/ruby/core/file/chown_spec.rb b/spec/ruby/core/file/chown_spec.rb
index 8cc8f0d04b..3353aafc70 100644
--- a/spec/ruby/core/file/chown_spec.rb
+++ b/spec/ruby/core/file/chown_spec.rb
@@ -68,7 +68,7 @@ describe "File.chown" do
it "raises an error for a non existent path" do
-> {
File.chown(nil, nil, "#{@fname}_not_existing")
- }.should raise_error(Errno::ENOENT)
+ }.should.raise(Errno::ENOENT)
end
end
@@ -78,15 +78,15 @@ describe "File.chown" do
end
describe "File#chown" do
- before :each do
- @fname = tmp('file_chown_test')
- @file = File.open(@fname, 'w')
- end
+ before :each do
+ @fname = tmp('file_chown_test')
+ @file = File.open(@fname, 'w')
+ end
- after :each do
- @file.close unless @file.closed?
- rm_r @fname
- end
+ after :each do
+ @file.close unless @file.closed?
+ rm_r @fname
+ end
as_superuser do
platform_is :windows do
diff --git a/spec/ruby/core/file/constants/constants_spec.rb b/spec/ruby/core/file/constants/constants_spec.rb
index 86946822c5..9d9c1c3b25 100644
--- a/spec/ruby/core/file/constants/constants_spec.rb
+++ b/spec/ruby/core/file/constants/constants_spec.rb
@@ -4,10 +4,10 @@ require_relative '../../../spec_helper'
"FNM_DOTMATCH", "FNM_EXTGLOB", "FNM_NOESCAPE", "FNM_PATHNAME",
"FNM_SYSCASE", "LOCK_EX", "LOCK_NB", "LOCK_SH",
"LOCK_UN", "NONBLOCK", "RDONLY",
- "RDWR", "TRUNC", "WRONLY"].each do |const|
+ "RDWR", "TRUNC", "WRONLY", "SHARE_DELETE"].each do |const|
describe "File::Constants::#{const}" do
it "is defined" do
- File::Constants.const_defined?(const).should be_true
+ File::Constants.const_defined?(const).should == true
end
end
end
@@ -15,7 +15,7 @@ end
platform_is :windows do
describe "File::Constants::BINARY" do
it "is defined" do
- File::Constants.const_defined?(:BINARY).should be_true
+ File::Constants.const_defined?(:BINARY).should == true
end
end
end
@@ -24,7 +24,7 @@ platform_is_not :windows do
["NOCTTY", "SYNC"].each do |const|
describe "File::Constants::#{const}" do
it "is defined" do
- File::Constants.const_defined?(const).should be_true
+ File::Constants.const_defined?(const).should == true
end
end
end
diff --git a/spec/ruby/core/file/ctime_spec.rb b/spec/ruby/core/file/ctime_spec.rb
index b16eb13c1e..cf37d1f4ee 100644
--- a/spec/ruby/core/file/ctime_spec.rb
+++ b/spec/ruby/core/file/ctime_spec.rb
@@ -11,17 +11,20 @@ describe "File.ctime" do
it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself)." do
File.ctime(@file)
- File.ctime(@file).should be_kind_of(Time)
+ File.ctime(@file).should.is_a?(Time)
end
platform_is :linux, :windows do
it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself) with microseconds." do
- supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d+)/, 1], 10)
+ supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10)
if supports_subseconds != 0
File.ctime(__FILE__).usec.should > 0
else
File.ctime(__FILE__).usec.should == 0
end
+ rescue Errno::ENOENT => e
+ # Windows don't have stat command.
+ skip e.message
end
end
@@ -30,7 +33,7 @@ describe "File.ctime" do
end
it "raises an Errno::ENOENT exception if the file is not found" do
- -> { File.ctime('bogus') }.should raise_error(Errno::ENOENT)
+ -> { File.ctime('bogus') }.should.raise(Errno::ENOENT)
end
end
@@ -46,6 +49,6 @@ describe "File#ctime" do
it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself)." do
@file.ctime
- @file.ctime.should be_kind_of(Time)
+ @file.ctime.should.is_a?(Time)
end
end
diff --git a/spec/ruby/core/file/dirname_spec.rb b/spec/ruby/core/file/dirname_spec.rb
index 2ef04a7b64..855148a684 100644
--- a/spec/ruby/core/file/dirname_spec.rb
+++ b/spec/ruby/core/file/dirname_spec.rb
@@ -11,8 +11,37 @@ describe "File.dirname" do
File.dirname('/foo/foo').should == '/foo'
end
+ context "when level is passed" do
+ it "returns all the components of filename except the last parts by the level" do
+ File.dirname('/home/jason', 2).should == '/'
+ File.dirname('/home/jason/poot.txt', 2).should == '/home'
+ end
+
+ it "returns the same String if the level is 0" do
+ File.dirname('poot.txt', 0).should == 'poot.txt'
+ File.dirname('/', 0).should == '/'
+ end
+
+ it "raises ArgumentError if the level is negative" do
+ -> {
+ File.dirname('/home/jason', -1)
+ }.should.raise(ArgumentError, "negative level: -1")
+ end
+
+ it "returns '/' when level exceeds the number of segments in the path" do
+ File.dirname("/home/jason", 100).should == '/'
+ end
+
+ it "calls #to_int if passed not numeric value" do
+ object = Object.new
+ def object.to_int; 2; end
+
+ File.dirname("/a/b/c/d", object).should == '/a/b'
+ end
+ end
+
it "returns a String" do
- File.dirname("foo").should be_kind_of(String)
+ File.dirname("foo").should.is_a?(String)
end
it "does not modify its argument" do
@@ -34,22 +63,48 @@ describe "File.dirname" do
end
it "returns all the components of filename except the last one (edge cases on all platforms)" do
- File.dirname("").should == "."
- File.dirname(".").should == "."
- File.dirname("./").should == "."
- File.dirname("./b/./").should == "./b"
- File.dirname("..").should == "."
- File.dirname("../").should == "."
- File.dirname("/").should == "/"
- File.dirname("/.").should == "/"
- File.dirname("/foo/").should == "/"
- File.dirname("/foo/.").should == "/foo"
- File.dirname("/foo/./").should == "/foo"
- File.dirname("/foo/../.").should == "/foo/.."
- File.dirname("foo/../").should == "foo"
+ File.dirname("").should == "."
+ File.dirname(".").should == "."
+ File.dirname("./").should == "."
+ File.dirname("./b/./").should == "./b"
+ File.dirname("..").should == "."
+ File.dirname("../").should == "."
+ File.dirname("/").should == "/"
+ File.dirname("/.").should == "/"
+ File.dirname("/foo/").should == "/"
+ File.dirname("/foo/.").should == "/foo"
+ File.dirname("/foo/./").should == "/foo"
+ File.dirname("/foo/../.").should == "/foo/.."
+ File.dirname("foo/../").should == "foo"
+ end
+
+ it "rejects strings encoded with non ASCII-compatible encodings" do
+ Encoding.list.reject(&:ascii_compatible?).reject(&:dummy?).each do |enc|
+ path = "/foo/bar".encode(enc)
+ -> {
+ File.dirname(path)
+ }.should.raise(Encoding::CompatibilityError)
+ end
+ end
+
+ it "works with all ASCII-compatible encodings" do
+ Encoding.list.select(&:ascii_compatible?).each do |enc|
+ File.dirname("/foo/bar".encode(enc)).should == "/foo".encode(enc)
+ end
+ end
+
+ it "handles Shift JIS 0x5C (\\) as second byte of a multi-byte sequence" do
+ # dir/fileソname.txt
+ path = "dir/file\x83\x5cname.txt".b.force_encoding(Encoding::SHIFT_JIS)
+ path.valid_encoding?.should == true
+ File.dirname(path).should == "dir"
end
platform_is_not :windows do
+ it "ignores repeated leading / (edge cases on non-windows)" do
+ File.dirname("/////foo/bar/").should == "/foo"
+ end
+
it "returns all the components of filename except the last one (edge cases on non-windows)" do
File.dirname('/////').should == '/'
File.dirname("//foo//").should == "/"
@@ -65,6 +120,13 @@ describe "File.dirname" do
File.dirname("//foo//").should == "//foo"
File.dirname('/////').should == '//'
end
+
+ it "handles Shift JIS 0x5C (\\) as second byte of a multi-byte sequence (windows)" do
+ # dir\fileソname.txt
+ path = "dir\\file\x83\x5cname.txt".b.force_encoding(Encoding::SHIFT_JIS)
+ path.valid_encoding?.should == true
+ File.dirname(path).should == "dir"
+ end
end
it "accepts an object that has a #to_path method" do
@@ -72,10 +134,10 @@ describe "File.dirname" do
end
it "raises a TypeError if not passed a String type" do
- -> { File.dirname(nil) }.should raise_error(TypeError)
- -> { File.dirname(0) }.should raise_error(TypeError)
- -> { File.dirname(true) }.should raise_error(TypeError)
- -> { File.dirname(false) }.should raise_error(TypeError)
+ -> { File.dirname(nil) }.should.raise(TypeError)
+ -> { File.dirname(0) }.should.raise(TypeError)
+ -> { File.dirname(true) }.should.raise(TypeError)
+ -> { File.dirname(false) }.should.raise(TypeError)
end
# Windows specific tests
diff --git a/spec/ruby/core/file/empty_spec.rb b/spec/ruby/core/file/empty_spec.rb
index 77f132303e..e8c9941676 100644
--- a/spec/ruby/core/file/empty_spec.rb
+++ b/spec/ruby/core/file/empty_spec.rb
@@ -4,10 +4,4 @@ require_relative '../../shared/file/zero'
describe "File.empty?" do
it_behaves_like :file_zero, :empty?, File
it_behaves_like :file_zero_missing, :empty?, File
-
- platform_is :solaris do
- it "returns false for /dev/null" do
- File.empty?('/dev/null').should == true
- end
- end
end
diff --git a/spec/ruby/core/file/exist_spec.rb b/spec/ruby/core/file/exist_spec.rb
index ddb5febcba..b5600e5b07 100644
--- a/spec/ruby/core/file/exist_spec.rb
+++ b/spec/ruby/core/file/exist_spec.rb
@@ -4,3 +4,9 @@ require_relative '../../shared/file/exist'
describe "File.exist?" do
it_behaves_like :file_exist, :exist?, File
end
+
+describe "File.exists?" do
+ it "has been removed" do
+ File.should_not.respond_to?(:exists?)
+ end
+end
diff --git a/spec/ruby/core/file/expand_path_spec.rb b/spec/ruby/core/file/expand_path_spec.rb
index c31f885b92..160494f145 100644
--- a/spec/ruby/core/file/expand_path_spec.rb
+++ b/spec/ruby/core/file/expand_path_spec.rb
@@ -92,7 +92,7 @@ describe "File.expand_path" do
end
it "raises an ArgumentError if the path is not valid" do
- -> { File.expand_path("~a_not_existing_user") }.should raise_error(ArgumentError)
+ -> { File.expand_path("~a_not_existing_user") }.should.raise(ArgumentError)
end
it "expands ~ENV['USER'] to the user's home directory" do
@@ -117,9 +117,9 @@ describe "File.expand_path" do
end
it "raises a TypeError if not passed a String type" do
- -> { File.expand_path(1) }.should raise_error(TypeError)
- -> { File.expand_path(nil) }.should raise_error(TypeError)
- -> { File.expand_path(true) }.should raise_error(TypeError)
+ -> { File.expand_path(1) }.should.raise(TypeError)
+ -> { File.expand_path(nil) }.should.raise(TypeError)
+ -> { File.expand_path(true) }.should.raise(TypeError)
end
platform_is_not :windows do
@@ -137,11 +137,11 @@ describe "File.expand_path" do
it "returns a String in the same encoding as the argument" do
Encoding.default_external = Encoding::SHIFT_JIS
- path = "./a".force_encoding Encoding::CP1251
- File.expand_path(path).encoding.should equal(Encoding::CP1251)
+ path = "./a".dup.force_encoding Encoding::CP1251
+ File.expand_path(path).encoding.should.equal?(Encoding::CP1251)
weird_path = [222, 173, 190, 175].pack('C*')
- File.expand_path(weird_path).encoding.should equal(Encoding::BINARY)
+ File.expand_path(weird_path).encoding.should.equal?(Encoding::BINARY)
end
platform_is_not :windows do
@@ -159,7 +159,7 @@ describe "File.expand_path" do
platform_is_not :windows do
it "raises an Encoding::CompatibilityError if the external encoding is not compatible" do
Encoding.default_external = Encoding::UTF_16BE
- -> { File.expand_path("./a") }.should raise_error(Encoding::CompatibilityError)
+ -> { File.expand_path("./a") }.should.raise(Encoding::CompatibilityError)
end
end
@@ -179,7 +179,7 @@ describe "File.expand_path" do
str = FileSpecs::SubString.new "./a/b/../c"
path = File.expand_path(str, @base)
path.should == "#{@base}/a/c"
- path.should be_an_instance_of(String)
+ path.should.instance_of?(String)
end
end
@@ -244,7 +244,7 @@ platform_is_not :windows do
it "raises an ArgumentError when passed '~' if HOME == ''" do
ENV["HOME"] = ""
- -> { File.expand_path("~") }.should raise_error(ArgumentError)
+ -> { File.expand_path("~") }.should.raise(ArgumentError)
end
end
@@ -259,7 +259,7 @@ platform_is_not :windows do
it "raises an ArgumentError" do
ENV["HOME"] = "non-absolute"
- -> { File.expand_path("~") }.should raise_error(ArgumentError, 'non-absolute home')
+ -> { File.expand_path("~") }.should.raise(ArgumentError, 'non-absolute home')
end
end
end
diff --git a/spec/ruby/core/file/extname_spec.rb b/spec/ruby/core/file/extname_spec.rb
index e9b53bc24d..995d0ea31a 100644
--- a/spec/ruby/core/file/extname_spec.rb
+++ b/spec/ruby/core/file/extname_spec.rb
@@ -12,6 +12,16 @@ describe "File.extname" do
File.extname(".app.conf").should == ".conf"
end
+ it "returns unfrozen strings" do
+ File.extname("foo.rb").frozen?.should == false
+ File.extname("/foo/bar.rb").frozen?.should == false
+ File.extname("/foo.rb/bar.c").frozen?.should == false
+ File.extname("bar").frozen?.should == false
+ File.extname(".bashrc").frozen?.should == false
+ File.extname("/foo.bar/baz").frozen?.should == false
+ File.extname(".app.conf").frozen?.should == false
+ end
+
it "returns the extension for edge cases" do
File.extname("").should == ""
File.extname(".").should == ""
@@ -23,14 +33,14 @@ describe "File.extname" do
end
describe "for a filename ending with a dot" do
- guard -> { platform_is :windows or ruby_version_is ""..."2.7" } do
+ platform_is :windows do
it "returns ''" do
File.extname(".foo.").should == ""
File.extname("foo.").should == ""
end
end
- guard -> { platform_is_not :windows and ruby_version_is "2.7" } do
+ platform_is_not :windows do
it "returns '.'" do
File.extname(".foo.").should == "."
File.extname("foo.").should == "."
@@ -47,15 +57,15 @@ describe "File.extname" do
end
it "raises a TypeError if not passed a String type" do
- -> { File.extname(nil) }.should raise_error(TypeError)
- -> { File.extname(0) }.should raise_error(TypeError)
- -> { File.extname(true) }.should raise_error(TypeError)
- -> { File.extname(false) }.should raise_error(TypeError)
+ -> { File.extname(nil) }.should.raise(TypeError)
+ -> { File.extname(0) }.should.raise(TypeError)
+ -> { File.extname(true) }.should.raise(TypeError)
+ -> { File.extname(false) }.should.raise(TypeError)
end
it "raises an ArgumentError if not passed one argument" do
- -> { File.extname }.should raise_error(ArgumentError)
- -> { File.extname("foo.bar", "foo.baz") }.should raise_error(ArgumentError)
+ -> { File.extname }.should.raise(ArgumentError)
+ -> { File.extname("foo.bar", "foo.baz") }.should.raise(ArgumentError)
end
diff --git a/spec/ruby/core/file/flock_spec.rb b/spec/ruby/core/file/flock_spec.rb
index 751e99d994..23ddf89ed8 100644
--- a/spec/ruby/core/file/flock_spec.rb
+++ b/spec/ruby/core/file/flock_spec.rb
@@ -30,7 +30,7 @@ describe "File#flock" do
it "returns false if trying to lock an exclusively locked file" do
@file.flock File::LOCK_EX
- ruby_exe(<<-END_OF_CODE, escape: true).should == "false"
+ ruby_exe(<<-END_OF_CODE).should == "false"
File.open('#{@name}', "w") do |f2|
print f2.flock(File::LOCK_EX | File::LOCK_NB).to_s
end
@@ -40,7 +40,7 @@ describe "File#flock" do
it "blocks if trying to lock an exclusively locked file" do
@file.flock File::LOCK_EX
- out = ruby_exe(<<-END_OF_CODE, escape: true)
+ out = ruby_exe(<<-END_OF_CODE)
running = false
t = Thread.new do
@@ -72,35 +72,3 @@ describe "File#flock" do
end
end
end
-
-platform_is :solaris do
- describe "File#flock on Solaris" do
- before :each do
- @name = tmp("flock_test")
- touch(@name)
-
- @read_file = File.open @name, "r"
- @write_file = File.open @name, "w"
- end
-
- after :each do
- @read_file.flock File::LOCK_UN
- @read_file.close
- @write_file.flock File::LOCK_UN
- @write_file.close
- rm_r @name
- end
-
- it "fails with EBADF acquiring exclusive lock on read-only File" do
- -> do
- @read_file.flock File::LOCK_EX
- end.should raise_error(Errno::EBADF)
- end
-
- it "fails with EBADF acquiring shared lock on read-only File" do
- -> do
- @write_file.flock File::LOCK_SH
- end.should raise_error(Errno::EBADF)
- end
- end
-end
diff --git a/spec/ruby/core/file/ftype_spec.rb b/spec/ruby/core/file/ftype_spec.rb
index cdddc404dc..ab9f76b79b 100644
--- a/spec/ruby/core/file/ftype_spec.rb
+++ b/spec/ruby/core/file/ftype_spec.rb
@@ -7,19 +7,19 @@ describe "File.ftype" do
end
it "raises ArgumentError if not given exactly one filename" do
- -> { File.ftype }.should raise_error(ArgumentError)
- -> { File.ftype('blah', 'bleh') }.should raise_error(ArgumentError)
+ -> { File.ftype }.should.raise(ArgumentError)
+ -> { File.ftype('blah', 'bleh') }.should.raise(ArgumentError)
end
it "raises Errno::ENOENT if the file is not valid" do
-> {
File.ftype("/#{$$}#{Time.now.to_f}")
- }.should raise_error(Errno::ENOENT)
+ }.should.raise(Errno::ENOENT)
end
it "returns a String" do
FileSpecs.normal_file do |file|
- File.ftype(file).should be_kind_of(String)
+ File.ftype(file).should.is_a?(String)
end
end
diff --git a/spec/ruby/core/file/inspect_spec.rb b/spec/ruby/core/file/inspect_spec.rb
index 148e789c62..fe87429e8d 100644
--- a/spec/ruby/core/file/inspect_spec.rb
+++ b/spec/ruby/core/file/inspect_spec.rb
@@ -12,6 +12,6 @@ describe "File#inspect" do
end
it "returns a String" do
- @file.inspect.should be_an_instance_of(String)
+ @file.inspect.should.instance_of?(String)
end
end
diff --git a/spec/ruby/core/file/join_spec.rb b/spec/ruby/core/file/join_spec.rb
index 0feedbae93..0f0911ea31 100644
--- a/spec/ruby/core/file/join_spec.rb
+++ b/spec/ruby/core/file/join_spec.rb
@@ -52,7 +52,7 @@ describe "File.join" do
it "returns a duplicate string when given a single argument" do
str = "usr"
File.join(str).should == str
- File.join(str).should_not equal(str)
+ File.join(str).should_not.equal?(str)
end
it "supports any number of arguments" do
@@ -104,15 +104,15 @@ describe "File.join" do
it "raises an ArgumentError if passed a recursive array" do
a = ["a"]
a << a
- -> { File.join a }.should raise_error(ArgumentError)
+ -> { File.join a }.should.raise(ArgumentError)
end
it "raises a TypeError exception when args are nil" do
- -> { File.join nil }.should raise_error(TypeError)
+ -> { File.join nil }.should.raise(TypeError)
end
it "calls #to_str" do
- -> { File.join(mock('x')) }.should raise_error(TypeError)
+ -> { File.join(mock('x')) }.should.raise(TypeError)
bin = mock("bin")
bin.should_receive(:to_str).exactly(:twice).and_return("bin")
@@ -129,7 +129,7 @@ describe "File.join" do
end
it "calls #to_path" do
- -> { File.join(mock('x')) }.should raise_error(TypeError)
+ -> { File.join(mock('x')) }.should.raise(TypeError)
bin = mock("bin")
bin.should_receive(:to_path).exactly(:twice).and_return("bin")
@@ -138,10 +138,10 @@ describe "File.join" do
end
it "raises errors for null bytes" do
- -> { File.join("\x00x", "metadata.gz") }.should raise_error(ArgumentError) { |e|
+ -> { File.join("\x00x", "metadata.gz") }.should.raise(ArgumentError) { |e|
e.message.should == 'string contains null byte'
}
- -> { File.join("metadata.gz", "\x00x") }.should raise_error(ArgumentError) { |e|
+ -> { File.join("metadata.gz", "\x00x") }.should.raise(ArgumentError) { |e|
e.message.should == 'string contains null byte'
}
end
diff --git a/spec/ruby/core/file/lchmod_spec.rb b/spec/ruby/core/file/lchmod_spec.rb
index 4abe42526d..3c44374983 100644
--- a/spec/ruby/core/file/lchmod_spec.rb
+++ b/spec/ruby/core/file/lchmod_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "File.lchmod" do
- platform_is_not :linux, :windows, :openbsd, :solaris, :aix do
+ platform_is_not :linux, :windows, :openbsd, :aix do
before :each do
@fname = tmp('file_chmod_test')
@lname = @fname + '.lnk'
@@ -29,22 +29,4 @@ describe "File.lchmod" do
File.stat(@lname).should.writable?
end
end
-
- platform_is :openbsd, :aix do
- it "returns false from #respond_to?" do
- File.respond_to?(:lchmod).should be_false
- end
-
- it "raises a NotImplementedError when called" do
- -> { File.lchmod 0, "foo" }.should raise_error(NotImplementedError)
- end
- end
-
- platform_is :linux do
- it "raises a NotImplementedError or Errno::ENOTSUP when called" do
- -> { File.lchmod 0, "foo" }.should raise_error(Exception) { |e|
- [NotImplementedError, Errno::ENOTSUP].should include(e.class)
- }
- end
- end
end
diff --git a/spec/ruby/core/file/link_spec.rb b/spec/ruby/core/file/link_spec.rb
index cc63c76aa3..768ee4b0fa 100644
--- a/spec/ruby/core/file/link_spec.rb
+++ b/spec/ruby/core/file/link_spec.rb
@@ -13,7 +13,7 @@ describe "File.link" do
rm_r @link, @file
end
- platform_is_not :windows do
+ platform_is_not :windows, :android do
it "link a file with another" do
File.link(@file, @link).should == 0
File.should.exist?(@link)
@@ -22,18 +22,18 @@ describe "File.link" do
it "raises an Errno::EEXIST if the target already exists" do
File.link(@file, @link)
- -> { File.link(@file, @link) }.should raise_error(Errno::EEXIST)
+ -> { File.link(@file, @link) }.should.raise(Errno::EEXIST)
end
it "raises an ArgumentError if not passed two arguments" do
- -> { File.link }.should raise_error(ArgumentError)
- -> { File.link(@file) }.should raise_error(ArgumentError)
- -> { File.link(@file, @link, @file) }.should raise_error(ArgumentError)
+ -> { File.link }.should.raise(ArgumentError)
+ -> { File.link(@file) }.should.raise(ArgumentError)
+ -> { File.link(@file, @link, @file) }.should.raise(ArgumentError)
end
it "raises a TypeError if not passed String types" do
- -> { File.link(@file, nil) }.should raise_error(TypeError)
- -> { File.link(@file, 1) }.should raise_error(TypeError)
+ -> { File.link(@file, nil) }.should.raise(TypeError)
+ -> { File.link(@file, 1) }.should.raise(TypeError)
end
end
end
diff --git a/spec/ruby/core/file/lutime_spec.rb b/spec/ruby/core/file/lutime_spec.rb
index 1f0625f61e..0f6df42ea3 100644
--- a/spec/ruby/core/file/lutime_spec.rb
+++ b/spec/ruby/core/file/lutime_spec.rb
@@ -1,7 +1,12 @@
require_relative '../../spec_helper'
+require_relative 'shared/update_time'
-describe "File.lutime" do
- platform_is_not :windows do
+platform_is_not :windows do
+ describe "File.lutime" do
+ it_behaves_like :update_time, :lutime
+ end
+
+ describe "File.lutime" do
before :each do
@atime = Time.utc(2000)
@mtime = Time.utc(2001)
diff --git a/spec/ruby/core/file/mkfifo_spec.rb b/spec/ruby/core/file/mkfifo_spec.rb
index 19298c967c..ce4a67fe31 100644
--- a/spec/ruby/core/file/mkfifo_spec.rb
+++ b/spec/ruby/core/file/mkfifo_spec.rb
@@ -19,13 +19,13 @@ describe "File.mkfifo" do
context "when path passed is not a String value" do
it "raises a TypeError" do
- -> { File.mkfifo(:"/tmp/fifo") }.should raise_error(TypeError)
+ -> { File.mkfifo(:"/tmp/fifo") }.should.raise(TypeError)
end
end
context "when path does not exist" do
it "raises an Errno::ENOENT exception" do
- -> { File.mkfifo("/bogus/path") }.should raise_error(Errno::ENOENT)
+ -> { File.mkfifo("/bogus/path") }.should.raise(Errno::ENOENT)
end
end
diff --git a/spec/ruby/core/file/mtime_spec.rb b/spec/ruby/core/file/mtime_spec.rb
index 8d47d3021a..d83725e25d 100644
--- a/spec/ruby/core/file/mtime_spec.rb
+++ b/spec/ruby/core/file/mtime_spec.rb
@@ -11,25 +11,30 @@ describe "File.mtime" do
end
it "returns the modification Time of the file" do
- File.mtime(@filename).should be_kind_of(Time)
+ File.mtime(@filename).should.is_a?(Time)
File.mtime(@filename).should be_close(@mtime, TIME_TOLERANCE)
end
platform_is :linux, :windows do
- it "returns the modification Time of the file with microseconds" do
- supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d+)/, 1], 10)
- if supports_subseconds != 0
- expected_time = Time.at(Time.now.to_i + 0.123456)
- File.utime 0, expected_time, @filename
- File.mtime(@filename).usec.should == expected_time.usec
- else
- File.mtime(__FILE__).usec.should == 0
+ unless ENV.key?('TRAVIS') # https://bugs.ruby-lang.org/issues/17926
+ it "returns the modification Time of the file with microseconds" do
+ supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d{1,6})/, 1], 10)
+ if supports_subseconds != 0
+ expected_time = Time.at(Time.now.to_i + 0.123456)
+ File.utime 0, expected_time, @filename
+ File.mtime(@filename).usec.should == expected_time.usec
+ else
+ File.mtime(__FILE__).usec.should == 0
+ end
+ rescue Errno::ENOENT => e
+ # Windows don't have stat command.
+ skip e.message
end
end
end
it "raises an Errno::ENOENT exception if the file is not found" do
- -> { File.mtime('bogus') }.should raise_error(Errno::ENOENT)
+ -> { File.mtime('bogus') }.should.raise(Errno::ENOENT)
end
end
@@ -45,7 +50,7 @@ describe "File#mtime" do
end
it "returns the modification Time of the file" do
- @f.mtime.should be_kind_of(Time)
+ @f.mtime.should.is_a?(Time)
end
end
diff --git a/spec/ruby/core/file/new_spec.rb b/spec/ruby/core/file/new_spec.rb
index 004f78503a..4cd2cb5dcb 100644
--- a/spec/ruby/core/file/new_spec.rb
+++ b/spec/ruby/core/file/new_spec.rb
@@ -16,13 +16,13 @@ describe "File.new" do
it "returns a new File with mode string" do
@fh = File.new(@file, 'w')
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
it "returns a new File with mode num" do
@fh = File.new(@file, @flags)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
@@ -30,7 +30,7 @@ describe "File.new" do
rm_r @file
File.umask(0011)
@fh = File.new(@file, @flags, 0755)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
platform_is_not :windows do
File.stat(@file).mode.to_s(8).should == "100744"
end
@@ -44,7 +44,7 @@ describe "File.new" do
rm_r @file
begin
f = File.new(@file, "w", 0444)
- -> { f.puts("test") }.should_not raise_error(IOError)
+ -> { f.puts("test") }.should_not.raise(IOError)
ensure
f.close
end
@@ -74,49 +74,84 @@ describe "File.new" do
@fh = File.new(@file)
fh_copy = File.new(@fh.fileno)
fh_copy.autoclose = false
- fh_copy.should be_kind_of(File)
+ fh_copy.should.is_a?(File)
+ File.should.exist?(@file)
+ end
+
+ it "returns a new read-only File when mode is not specified" do
+ @fh = File.new(@file)
+
+ -> { @fh.puts("test") }.should.raise(IOError)
+ @fh.read.should == ""
+ File.should.exist?(@file)
+ end
+
+ it "returns a new read-only File when mode is not specified but flags option is present" do
+ @fh = File.new(@file, flags: File::CREAT)
+
+ -> { @fh.puts("test") }.should.raise(IOError)
+ @fh.read.should == ""
File.should.exist?(@file)
end
it "creates a new file when use File::EXCL mode" do
@fh = File.new(@file, File::EXCL)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
- it "raises an Errorno::EEXIST if the file exists when create a new file with File::CREAT|File::EXCL" do
- -> { @fh = File.new(@file, File::CREAT|File::EXCL) }.should raise_error(Errno::EEXIST)
+ it "raises an Errno::EEXIST if the file exists when create a new file with File::CREAT|File::EXCL" do
+ -> { @fh = File.new(@file, File::CREAT|File::EXCL) }.should.raise(Errno::EEXIST)
end
it "creates a new file when use File::WRONLY|File::APPEND mode" do
@fh = File.new(@file, File::WRONLY|File::APPEND)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
it "returns a new File when use File::APPEND mode" do
@fh = File.new(@file, File::APPEND)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
it "returns a new File when use File::RDONLY|File::APPEND mode" do
@fh = File.new(@file, File::RDONLY|File::APPEND)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
it "returns a new File when use File::RDONLY|File::WRONLY mode" do
@fh = File.new(@file, File::RDONLY|File::WRONLY)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
-
it "creates a new file when use File::WRONLY|File::TRUNC mode" do
@fh = File.new(@file, File::WRONLY|File::TRUNC)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
+ File.should.exist?(@file)
+ end
+
+ it "returns a new read-only File when use File::RDONLY|File::CREAT mode" do
+ @fh = File.new(@file, File::RDONLY|File::CREAT)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
+
+ # it's read-only
+ -> { @fh.puts("test") }.should.raise(IOError)
+ @fh.read.should == ""
+ end
+
+ it "returns a new read-only File when use File::CREAT mode" do
+ @fh = File.new(@file, File::CREAT)
+ @fh.should.is_a?(File)
+ File.should.exist?(@file)
+
+ # it's read-only
+ -> { @fh.puts("test") }.should.raise(IOError)
+ @fh.read.should == ""
end
it "coerces filename using to_str" do
@@ -133,17 +168,43 @@ describe "File.new" do
File.should.exist?(@file)
end
+ it "accepts options as a keyword argument" do
+ @fh = File.new(@file, 'w', 0755, flags: @flags)
+ @fh.should.is_a?(File)
+ @fh.close
+
+ -> {
+ @fh = File.new(@file, 'w', 0755, {flags: @flags})
+ }.should.raise(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
+ end
+
+ it "bitwise-ORs mode and flags option" do
+ -> {
+ @fh = File.new(@file, 'w', flags: File::EXCL)
+ }.should.raise(Errno::EEXIST, /File exists/)
+
+ -> {
+ @fh = File.new(@file, mode: 'w', flags: File::EXCL)
+ }.should.raise(Errno::EEXIST, /File exists/)
+ end
+
+ it "does not use the given block and warns to use File::open" do
+ -> {
+ @fh = File.new(@file) { raise }
+ }.should complain(/warning: File::new\(\) does not take block; use File::open\(\) instead/)
+ end
+
it "raises a TypeError if the first parameter can't be coerced to a string" do
- -> { File.new(true) }.should raise_error(TypeError)
- -> { File.new(false) }.should raise_error(TypeError)
+ -> { File.new(true) }.should.raise(TypeError)
+ -> { File.new(false) }.should.raise(TypeError)
end
it "raises a TypeError if the first parameter is nil" do
- -> { File.new(nil) }.should raise_error(TypeError)
+ -> { File.new(nil) }.should.raise(TypeError)
end
it "raises an Errno::EBADF if the first parameter is an invalid file descriptor" do
- -> { File.new(-1) }.should raise_error(Errno::EBADF)
+ -> { File.new(-1) }.should.raise(Errno::EBADF)
end
platform_is_not :windows do
@@ -152,7 +213,7 @@ describe "File.new" do
-> {
f = File.new(@fh.fileno, @flags)
f.autoclose = false
- }.should raise_error(Errno::EINVAL)
+ }.should.raise(Errno::EINVAL)
end
end
diff --git a/spec/ruby/core/file/open_spec.rb b/spec/ruby/core/file/open_spec.rb
index 8faa31173e..7318c31636 100644
--- a/spec/ruby/core/file/open_spec.rb
+++ b/spec/ruby/core/file/open_spec.rb
@@ -40,7 +40,7 @@ describe "File.open" do
it "propagates non-StandardErrors produced by close" do
-> {
File.open(@file, 'r') { |f| FileSpecs.make_closer f, Exception }
- }.should raise_error(Exception)
+ }.should.raise(Exception)
ScratchPad.recorded.should == [:file_opened, :file_closed]
end
@@ -48,7 +48,7 @@ describe "File.open" do
it "propagates StandardErrors produced by close" do
-> {
File.open(@file, 'r') { |f| FileSpecs.make_closer f, StandardError }
- }.should raise_error(StandardError)
+ }.should.raise(StandardError)
ScratchPad.recorded.should == [:file_opened, :file_closed]
end
@@ -62,13 +62,13 @@ describe "File.open" do
it "opens the file (basic case)" do
@fh = File.open(@file)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
it "opens the file with unicode characters" do
@fh = File.open(@unicode_path, "w")
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@unicode_path)
end
@@ -79,7 +79,7 @@ describe "File.open" do
it "opens with mode string" do
@fh = File.open(@file, 'w')
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
@@ -90,7 +90,7 @@ describe "File.open" do
it "opens a file with mode num" do
@fh = File.open(@file, @flags)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
@@ -101,7 +101,7 @@ describe "File.open" do
it "opens a file with mode and permission as nil" do
@fh = File.open(@file, nil, nil)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
end
# For this test we delete the file first to reset the perms
@@ -109,7 +109,7 @@ describe "File.open" do
rm_r @file
File.umask(0011)
@fh = File.open(@file, @flags, 0755)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
platform_is_not :windows do
@fh.lstat.mode.to_s(8).should == "100744"
end
@@ -161,69 +161,69 @@ describe "File.open" do
@fh = File.open(@file)
fh_copy = File.open(@fh.fileno)
fh_copy.autoclose = false
- fh_copy.should be_kind_of(File)
+ fh_copy.should.is_a?(File)
File.should.exist?(@file)
end
it "opens a file that no exists when use File::WRONLY mode" do
- -> { File.open(@nonexistent, File::WRONLY) }.should raise_error(Errno::ENOENT)
+ -> { File.open(@nonexistent, File::WRONLY) }.should.raise(Errno::ENOENT)
end
it "opens a file that no exists when use File::RDONLY mode" do
- -> { File.open(@nonexistent, File::RDONLY) }.should raise_error(Errno::ENOENT)
+ -> { File.open(@nonexistent, File::RDONLY) }.should.raise(Errno::ENOENT)
end
it "opens a file that no exists when use 'r' mode" do
- -> { File.open(@nonexistent, 'r') }.should raise_error(Errno::ENOENT)
+ -> { File.open(@nonexistent, 'r') }.should.raise(Errno::ENOENT)
end
it "opens a file that no exists when use File::EXCL mode" do
- -> { File.open(@nonexistent, File::EXCL) }.should raise_error(Errno::ENOENT)
+ -> { File.open(@nonexistent, File::EXCL) }.should.raise(Errno::ENOENT)
end
it "opens a file that no exists when use File::NONBLOCK mode" do
- -> { File.open(@nonexistent, File::NONBLOCK) }.should raise_error(Errno::ENOENT)
+ -> { File.open(@nonexistent, File::NONBLOCK) }.should.raise(Errno::ENOENT)
end
platform_is_not :openbsd, :windows do
it "opens a file that no exists when use File::TRUNC mode" do
- -> { File.open(@nonexistent, File::TRUNC) }.should raise_error(Errno::ENOENT)
+ -> { File.open(@nonexistent, File::TRUNC) }.should.raise(Errno::ENOENT)
end
end
platform_is :openbsd, :windows do
it "does not open a file that does no exists when using File::TRUNC mode" do
- -> { File.open(@nonexistent, File::TRUNC) }.should raise_error(Errno::EINVAL)
+ -> { File.open(@nonexistent, File::TRUNC) }.should.raise(Errno::EINVAL)
end
end
platform_is_not :windows do
it "opens a file that no exists when use File::NOCTTY mode" do
- -> { File.open(@nonexistent, File::NOCTTY) }.should raise_error(Errno::ENOENT)
+ -> { File.open(@nonexistent, File::NOCTTY) }.should.raise(Errno::ENOENT)
end
end
it "opens a file that no exists when use File::CREAT mode" do
@fh = File.open(@nonexistent, File::CREAT) { |f| f }
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
it "opens a file that no exists when use 'a' mode" do
@fh = File.open(@nonexistent, 'a') { |f| f }
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
it "opens a file that no exists when use 'w' mode" do
@fh = File.open(@nonexistent, 'w') { |f| f }
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
# Check the grants associated to the different open modes combinations.
it "raises an ArgumentError exception when call with an unknown mode" do
- -> { File.open(@file, "q") }.should raise_error(ArgumentError)
+ -> { File.open(@file, "q") }.should.raise(ArgumentError)
end
it "can read in a block when call open with RDONLY mode" do
@@ -240,13 +240,13 @@ describe "File.open" do
it "raises an IO exception when write in a block opened with RDONLY mode" do
File.open(@file, File::RDONLY) do |f|
- -> { f.puts "writing ..." }.should raise_error(IOError)
+ -> { f.puts "writing ..." }.should.raise(IOError)
end
end
it "raises an IO exception when write in a block opened with 'r' mode" do
File.open(@file, "r") do |f|
- -> { f.puts "writing ..." }.should raise_error(IOError)
+ -> { f.puts "writing ..." }.should.raise(IOError)
end
end
@@ -261,7 +261,7 @@ describe "File.open" do
File.open(@file, File::WRONLY|File::RDONLY ) do |f|
f.gets.should == nil
end
- }.should raise_error(IOError)
+ }.should.raise(IOError)
end
it "can write in a block when call open with WRONLY mode" do
@@ -278,48 +278,48 @@ describe "File.open" do
it "raises an IOError when read in a block opened with WRONLY mode" do
File.open(@file, File::WRONLY) do |f|
- -> { f.gets }.should raise_error(IOError)
+ -> { f.gets }.should.raise(IOError)
end
end
it "raises an IOError when read in a block opened with 'w' mode" do
File.open(@file, "w") do |f|
- -> { f.gets }.should raise_error(IOError)
+ -> { f.gets }.should.raise(IOError)
end
end
it "raises an IOError when read in a block opened with 'a' mode" do
File.open(@file, "a") do |f|
- -> { f.gets }.should raise_error(IOError)
+ -> { f.gets }.should.raise(IOError)
end
end
it "raises an IOError when read in a block opened with 'a' mode" do
File.open(@file, "a") do |f|
f.puts("writing").should == nil
- -> { f.gets }.should raise_error(IOError)
+ -> { f.gets }.should.raise(IOError)
end
end
it "raises an IOError when read in a block opened with 'a' mode" do
File.open(@file, File::WRONLY|File::APPEND ) do |f|
- -> { f.gets }.should raise_error(IOError)
+ -> { f.gets }.should.raise(IOError)
end
end
it "raises an IOError when read in a block opened with File::WRONLY|File::APPEND mode" do
File.open(@file, File::WRONLY|File::APPEND ) do |f|
f.puts("writing").should == nil
- -> { f.gets }.should raise_error(IOError)
+ -> { f.gets }.should.raise(IOError)
end
end
- it "raises an IOError when read in a block opened with File::RDONLY|File::APPEND mode" do
+ it "raises an IOError when write in a block opened with File::RDONLY|File::APPEND mode" do
-> {
File.open(@file, File::RDONLY|File::APPEND ) do |f|
f.puts("writing")
end
- }.should raise_error(IOError)
+ }.should.raise(IOError)
end
it "can read and write in a block when call open with RDWR mode" do
@@ -336,7 +336,7 @@ describe "File.open" do
File.open(@file, File::EXCL) do |f|
f.puts("writing").should == nil
end
- }.should raise_error(IOError)
+ }.should.raise(IOError)
end
it "can read in a block when call open with File::EXCL mode" do
@@ -354,17 +354,17 @@ describe "File.open" do
end
end
- it "raises an Errorno::EEXIST if the file exists when open with File::CREAT|File::EXCL" do
+ it "raises an Errno::EEXIST if the file exists when open with File::CREAT|File::EXCL" do
-> {
File.open(@file, File::CREAT|File::EXCL) do |f|
f.puts("writing")
end
- }.should raise_error(Errno::EEXIST)
+ }.should.raise(Errno::EEXIST)
end
it "creates a new file when use File::WRONLY|File::APPEND mode" do
@fh = File.open(@file, File::WRONLY|File::APPEND)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
@@ -386,7 +386,7 @@ describe "File.open" do
File.open(@file, File::RDONLY|File::APPEND) do |f|
f.puts("writing").should == nil
end
- }.should raise_error(IOError)
+ }.should.raise(IOError)
end
platform_is_not :openbsd, :windows do
@@ -407,7 +407,7 @@ describe "File.open" do
fh1 = File.open(@file, "w")
begin
@fh = File.open(@file, File::WRONLY|File::TRUNC)
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
ensure
fh1.close
@@ -420,15 +420,15 @@ describe "File.open" do
File.open(@file, File::TRUNC) do |f|
f.puts("writing")
end
- }.should raise_error(IOError)
+ }.should.raise(IOError)
end
- it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
+ it "raises an Errno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
-> {
File.open(@file, File::RDONLY|File::TRUNC) do |f|
f.puts("writing").should == nil
end
- }.should raise_error(IOError)
+ }.should.raise(IOError)
end
end
@@ -438,15 +438,15 @@ describe "File.open" do
File.open(@file, File::TRUNC) do |f|
f.puts("writing")
end
- }.should raise_error(Errno::EINVAL)
+ }.should.raise(Errno::EINVAL)
end
- it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
+ it "raises an Errno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do
-> {
File.open(@file, File::RDONLY|File::TRUNC) do |f|
f.puts("writing").should == nil
end
- }.should raise_error(Errno::EINVAL)
+ }.should.raise(Errno::EINVAL)
end
end
@@ -455,7 +455,7 @@ describe "File.open" do
it "raises an Errno::EACCES when opening non-permitted file" do
@fh = File.open(@file, "w")
@fh.chmod(000)
- -> { fh1 = File.open(@file); fh1.close }.should raise_error(Errno::EACCES)
+ -> { fh1 = File.open(@file); fh1.close }.should.raise(Errno::EACCES)
end
end
end
@@ -464,19 +464,19 @@ describe "File.open" do
it "raises an Errno::EACCES when opening read-only file" do
@fh = File.open(@file, "w")
@fh.chmod(0444)
- -> { File.open(@file, "w") }.should raise_error(Errno::EACCES)
+ -> { File.open(@file, "w") }.should.raise(Errno::EACCES)
end
end
it "opens a file for binary read" do
@fh = File.open(@file, "rb")
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
it "opens a file for binary write" do
@fh = File.open(@file, "wb")
- @fh.should be_kind_of(File)
+ @fh.should.is_a?(File)
File.should.exist?(@file)
end
@@ -494,6 +494,14 @@ describe "File.open" do
File.open(@file, "w") { |f| f.puts "testing" }
File.size(@file).should > 0
File.open(@file, "rb+") do |f|
+ f.binmode?.should == true
+ f.external_encoding.should == Encoding::ASCII_8BIT
+ f.pos.should == 0
+ f.should_not.eof?
+ end
+ File.open(@file, "r+b") do |f|
+ f.binmode?.should == true
+ f.external_encoding.should == Encoding::ASCII_8BIT
f.pos.should == 0
f.should_not.eof?
end
@@ -535,21 +543,21 @@ describe "File.open" do
end
it "raises a TypeError if passed a filename that is not a String or Integer type" do
- -> { File.open(true) }.should raise_error(TypeError)
- -> { File.open(false) }.should raise_error(TypeError)
- -> { File.open(nil) }.should raise_error(TypeError)
+ -> { File.open(true) }.should.raise(TypeError)
+ -> { File.open(false) }.should.raise(TypeError)
+ -> { File.open(nil) }.should.raise(TypeError)
end
it "raises a SystemCallError if passed an invalid Integer type" do
- -> { File.open(-1) }.should raise_error(SystemCallError)
+ -> { File.open(-1) }.should.raise(SystemCallError)
end
it "raises an ArgumentError if passed the wrong number of arguments" do
- -> { File.open(@file, File::CREAT, 0755, 'test') }.should raise_error(ArgumentError)
+ -> { File.open(@file, File::CREAT, 0755, 'test') }.should.raise(ArgumentError)
end
it "raises an ArgumentError if passed an invalid string for mode" do
- -> { File.open(@file, 'fake') }.should raise_error(ArgumentError)
+ -> { File.open(@file, 'fake') }.should.raise(ArgumentError)
end
it "defaults external_encoding to BINARY for binary modes" do
@@ -557,9 +565,18 @@ describe "File.open" do
File.open(@file, 'wb+') {|f| f.external_encoding.should == Encoding::BINARY}
end
+ it "accepts options as a keyword argument" do
+ @fh = File.open(@file, 'w', 0755, flags: File::CREAT)
+ @fh.should.instance_of?(File)
+
+ -> {
+ File.open(@file, 'w', 0755, {flags: File::CREAT})
+ }.should.raise(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
+ end
+
it "uses the second argument as an options Hash" do
@fh = File.open(@file, mode: "r")
- @fh.should be_an_instance_of(File)
+ @fh.should.instance_of?(File)
end
it "calls #to_hash to convert the second argument to a Hash" do
@@ -572,17 +589,17 @@ describe "File.open" do
it "accepts extra flags as a keyword argument and combine with a string mode" do
-> {
File.open(@file, "w", flags: File::EXCL) { }
- }.should raise_error(Errno::EEXIST)
+ }.should.raise(Errno::EEXIST)
-> {
File.open(@file, mode: "w", flags: File::EXCL) { }
- }.should raise_error(Errno::EEXIST)
+ }.should.raise(Errno::EEXIST)
end
it "accepts extra flags as a keyword argument and combine with an integer mode" do
-> {
File.open(@file, File::WRONLY | File::CREAT, flags: File::EXCL) { }
- }.should raise_error(Errno::EEXIST)
+ }.should.raise(Errno::EEXIST)
end
platform_is_not :windows do
@@ -626,34 +643,32 @@ describe "File.open" do
it "raises ArgumentError if mixing :newline and binary mode" do
-> {
File.open(@file, "rb", newline: :universal) {}
- }.should raise_error(ArgumentError, "newline decorator with binary mode")
+ }.should.raise(ArgumentError, "newline decorator with binary mode")
end
- ruby_version_is "2.6" do
- context "'x' flag" do
- before :each do
- @xfile = tmp("x-flag")
- rm_r @xfile
- end
+ context "'x' flag" do
+ before :each do
+ @xfile = tmp("x-flag")
+ rm_r @xfile
+ end
- after :each do
- rm_r @xfile
- end
+ after :each do
+ rm_r @xfile
+ end
- it "does nothing if the file doesn't exist" do
- File.open(@xfile, "wx") { |f| f.write("content") }
- File.read(@xfile).should == "content"
- end
+ it "does nothing if the file doesn't exist" do
+ File.open(@xfile, "wx") { |f| f.write("content") }
+ File.read(@xfile).should == "content"
+ end
- it "throws a Errno::EEXIST error if the file exists" do
- touch @xfile
- -> { File.open(@xfile, "wx") }.should raise_error(Errno::EEXIST)
- end
+ it "throws a Errno::EEXIST error if the file exists" do
+ touch @xfile
+ -> { File.open(@xfile, "wx") }.should.raise(Errno::EEXIST)
+ end
- it "can't be used with 'r' and 'a' flags" do
- -> { File.open(@xfile, "rx") }.should raise_error(ArgumentError, 'invalid access mode rx')
- -> { File.open(@xfile, "ax") }.should raise_error(ArgumentError, 'invalid access mode ax')
- end
+ it "can't be used with 'r' and 'a' flags" do
+ -> { File.open(@xfile, "rx") }.should.raise(ArgumentError, 'invalid access mode rx')
+ -> { File.open(@xfile, "ax") }.should.raise(ArgumentError, 'invalid access mode ax')
end
end
end
@@ -673,8 +688,8 @@ describe "File.open when passed a file descriptor" do
it "opens a file" do
@file = File.open(@fd, "w")
- @file.should be_an_instance_of(File)
- @file.fileno.should equal(@fd)
+ @file.should.instance_of?(File)
+ @file.fileno.should.equal?(@fd)
@file.write @content
@file.flush
File.read(@name).should == @content
@@ -682,8 +697,8 @@ describe "File.open when passed a file descriptor" do
it "opens a file when passed a block" do
@file = File.open(@fd, "w") do |f|
- f.should be_an_instance_of(File)
- f.fileno.should equal(@fd)
+ f.should.instance_of?(File)
+ f.fileno.should.equal?(@fd)
f.write @content
f
end
diff --git a/spec/ruby/core/file/path_spec.rb b/spec/ruby/core/file/path_spec.rb
index dfa0c4ec02..ce78dbeede 100644
--- a/spec/ruby/core/file/path_spec.rb
+++ b/spec/ruby/core/file/path_spec.rb
@@ -37,4 +37,45 @@ describe "File.path" do
path.should_receive(:to_path).and_return("abc")
File.path(path).should == "abc"
end
+
+ it "raises TypeError when #to_path result is not a string" do
+ path = mock("path")
+ path.should_receive(:to_path).and_return(nil)
+ -> { File.path(path) }.should.raise TypeError
+
+ path = mock("path")
+ path.should_receive(:to_path).and_return(42)
+ -> { File.path(path) }.should.raise TypeError
+ end
+
+ it "raises ArgumentError for string argument contains NUL character" do
+ -> { File.path("\0") }.should.raise ArgumentError
+ -> { File.path("a\0") }.should.raise ArgumentError
+ -> { File.path("a\0c") }.should.raise ArgumentError
+ end
+
+ it "raises ArgumentError when #to_path result contains NUL character" do
+ path = mock("path")
+ path.should_receive(:to_path).and_return("\0")
+ -> { File.path(path) }.should.raise ArgumentError
+
+ path = mock("path")
+ path.should_receive(:to_path).and_return("a\0")
+ -> { File.path(path) }.should.raise ArgumentError
+
+ path = mock("path")
+ path.should_receive(:to_path).and_return("a\0c")
+ -> { File.path(path) }.should.raise ArgumentError
+ end
+
+ it "raises Encoding::CompatibilityError for ASCII-incompatible string argument" do
+ path = "abc".encode(Encoding::UTF_32BE)
+ -> { File.path(path) }.should.raise Encoding::CompatibilityError
+ end
+
+ it "raises Encoding::CompatibilityError when #to_path result is ASCII-incompatible" do
+ path = mock("path")
+ path.should_receive(:to_path).and_return("abc".encode(Encoding::UTF_32BE))
+ -> { File.path(path) }.should.raise Encoding::CompatibilityError
+ end
end
diff --git a/spec/ruby/core/file/readlink_spec.rb b/spec/ruby/core/file/readlink_spec.rb
index 20741ba121..568692b9b6 100644
--- a/spec/ruby/core/file/readlink_spec.rb
+++ b/spec/ruby/core/file/readlink_spec.rb
@@ -26,12 +26,12 @@ describe "File.readlink" do
it "raises an Errno::ENOENT if there is no such file" do
# TODO: missing_file
- -> { File.readlink("/this/surely/does/not/exist") }.should raise_error(Errno::ENOENT)
+ -> { File.readlink("/this/surely/does/not/exist") }.should.raise(Errno::ENOENT)
end
it "raises an Errno::EINVAL if called with a normal file" do
touch @file
- -> { File.readlink(@file) }.should raise_error(Errno::EINVAL)
+ -> { File.readlink(@file) }.should.raise(Errno::EINVAL)
end
end
@@ -49,7 +49,7 @@ describe "File.readlink" do
it "returns the name of the file referenced by the given link" do
touch @file
result = File.readlink(@link)
- result.encoding.should equal Encoding.find('filesystem')
+ result.encoding.should.equal? Encoding.find('filesystem')
result.should == @file.dup.force_encoding(Encoding.find('filesystem'))
end
end
diff --git a/spec/ruby/core/file/realdirpath_spec.rb b/spec/ruby/core/file/realdirpath_spec.rb
index 74053afce3..ecf1e0c6d9 100644
--- a/spec/ruby/core/file/realdirpath_spec.rb
+++ b/spec/ruby/core/file/realdirpath_spec.rb
@@ -61,7 +61,7 @@ platform_is_not :windows do
it "raises an Errno::ELOOP if the symlink points to itself" do
File.unlink @link
File.symlink(@link, @link)
- -> { File.realdirpath(@link) }.should raise_error(Errno::ELOOP)
+ -> { File.realdirpath(@link) }.should.raise(Errno::ELOOP)
end
it "returns the real (absolute) pathname if the file is absent" do
@@ -69,7 +69,7 @@ platform_is_not :windows do
end
it "raises Errno::ENOENT if the directory is absent" do
- -> { File.realdirpath(@fake_file_in_fake_dir) }.should raise_error(Errno::ENOENT)
+ -> { File.realdirpath(@fake_file_in_fake_dir) }.should.raise(Errno::ENOENT)
end
it "returns the real (absolute) pathname if the symlink points to an absent file" do
@@ -77,7 +77,7 @@ platform_is_not :windows do
end
it "raises Errno::ENOENT if the symlink points to an absent directory" do
- -> { File.realdirpath(@fake_link_to_fake_dir) }.should raise_error(Errno::ENOENT)
+ -> { File.realdirpath(@fake_link_to_fake_dir) }.should.raise(Errno::ENOENT)
end
end
end
diff --git a/spec/ruby/core/file/realpath_spec.rb b/spec/ruby/core/file/realpath_spec.rb
index bd27e09da6..ccb981eff1 100644
--- a/spec/ruby/core/file/realpath_spec.rb
+++ b/spec/ruby/core/file/realpath_spec.rb
@@ -54,18 +54,22 @@ platform_is_not :windows do
File.realpath(@relative_symlink).should == @file
end
+ it "removes the file element when going one level up" do
+ File.realpath('../', @file).should == @real_dir
+ end
+
it "raises an Errno::ELOOP if the symlink points to itself" do
File.unlink @link
File.symlink(@link, @link)
- -> { File.realpath(@link) }.should raise_error(Errno::ELOOP)
+ -> { File.realpath(@link) }.should.raise(Errno::ELOOP)
end
it "raises Errno::ENOENT if the file is absent" do
- -> { File.realpath(@fake_file) }.should raise_error(Errno::ENOENT)
+ -> { File.realpath(@fake_file) }.should.raise(Errno::ENOENT)
end
it "raises Errno::ENOENT if the symlink points to an absent file" do
- -> { File.realpath(@fake_link) }.should raise_error(Errno::ENOENT)
+ -> { File.realpath(@fake_link) }.should.raise(Errno::ENOENT)
end
it "converts the argument with #to_path" do
diff --git a/spec/ruby/core/file/rename_spec.rb b/spec/ruby/core/file/rename_spec.rb
index f2c18d4905..70ea669a68 100644
--- a/spec/ruby/core/file/rename_spec.rb
+++ b/spec/ruby/core/file/rename_spec.rb
@@ -23,15 +23,15 @@ describe "File.rename" do
it "raises an Errno::ENOENT if the source does not exist" do
rm_r @old
- -> { File.rename(@old, @new) }.should raise_error(Errno::ENOENT)
+ -> { File.rename(@old, @new) }.should.raise(Errno::ENOENT)
end
it "raises an ArgumentError if not passed two arguments" do
- -> { File.rename }.should raise_error(ArgumentError)
- -> { File.rename(@file) }.should raise_error(ArgumentError)
+ -> { File.rename }.should.raise(ArgumentError)
+ -> { File.rename(@file) }.should.raise(ArgumentError)
end
it "raises a TypeError if not passed String types" do
- -> { File.rename(1, 2) }.should raise_error(TypeError)
+ -> { File.rename(1, 2) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/file/setuid_spec.rb b/spec/ruby/core/file/setuid_spec.rb
index 281ef01ab9..9e5e86df61 100644
--- a/spec/ruby/core/file/setuid_spec.rb
+++ b/spec/ruby/core/file/setuid_spec.rb
@@ -26,10 +26,6 @@ describe "File.setuid?" do
platform_is_not :windows do
it "returns true when the gid bit is set" do
- platform_is :solaris do
- # Solaris requires execute bit before setting suid
- system "chmod u+x #{@name}"
- end
system "chmod u+s #{@name}"
File.setuid?(@name).should == true
diff --git a/spec/ruby/core/file/shared/fnmatch.rb b/spec/ruby/core/file/shared/fnmatch.rb
index a8488fd30a..b9140d027d 100644
--- a/spec/ruby/core/file/shared/fnmatch.rb
+++ b/spec/ruby/core/file/shared/fnmatch.rb
@@ -75,6 +75,14 @@ describe :file_fnmatch, shared: true do
File.send(@method, 'c*t', 'c/a/b/t').should == true
end
+ it "does not match unterminated range of characters" do
+ File.send(@method, 'abc[de', 'abcd').should == false
+ end
+
+ it "does not match unterminated range of characters as a literal" do
+ File.send(@method, 'abc[de', 'abc[de').should == false
+ end
+
it "matches ranges of characters using bracket expression (e.g. [a-z])" do
File.send(@method, 'ca[a-z]', 'cat').should == true
end
@@ -94,6 +102,7 @@ describe :file_fnmatch, shared: true do
it "matches ranges of characters using exclusive bracket expression (e.g. [^t] or [!t])" do
File.send(@method, 'ca[^t]', 'cat').should == false
+ File.send(@method, 'ca[^t]', 'cas').should == true
File.send(@method, 'ca[!t]', 'cat').should == false
end
@@ -117,6 +126,13 @@ describe :file_fnmatch, shared: true do
end
end
+ it "matches wildcard with characters when flags includes FNM_PATHNAME" do
+ File.send(@method, '*a', 'aa', File::FNM_PATHNAME).should == true
+ File.send(@method, 'a*', 'aa', File::FNM_PATHNAME).should == true
+ File.send(@method, 'a*', 'aaa', File::FNM_PATHNAME).should == true
+ File.send(@method, '*a', 'aaa', File::FNM_PATHNAME).should == true
+ end
+
it "does not match '/' characters with ? or * when flags includes FNM_PATHNAME" do
File.send(@method, '?', '/', File::FNM_PATHNAME).should == false
File.send(@method, '*', '/', File::FNM_PATHNAME).should == false
@@ -151,15 +167,25 @@ describe :file_fnmatch, shared: true do
end
it "does not match leading periods in filenames with wildcards by default" do
- File.send(@method, '*', '.profile').should == false
- File.send(@method, '*', 'home/.profile').should == true
- File.send(@method, '*/*', 'home/.profile').should == true
- File.send(@method, '*/*', 'dave/.profile', File::FNM_PATHNAME).should == false
+ File.should_not.send(@method, '*', '.profile')
+ File.should.send(@method, '*', 'home/.profile')
+ File.should.send(@method, '*/*', 'home/.profile')
+ File.should_not.send(@method, '*/*', 'dave/.profile', File::FNM_PATHNAME)
end
- it "matches patterns with leading periods to dotfiles by default" do
+ it "matches patterns with leading periods to dotfiles" do
File.send(@method, '.*', '.profile').should == true
+ File.send(@method, '.*', '.profile', File::FNM_PATHNAME).should == true
File.send(@method, ".*file", "nondotfile").should == false
+ File.send(@method, ".*file", "nondotfile", File::FNM_PATHNAME).should == false
+ end
+
+ it "does not match directories with leading periods by default with FNM_PATHNAME" do
+ File.send(@method, '.*', '.directory/nondotfile', File::FNM_PATHNAME).should == false
+ File.send(@method, '.*', '.directory/.profile', File::FNM_PATHNAME).should == false
+ File.send(@method, '.*', 'foo/.directory/nondotfile', File::FNM_PATHNAME).should == false
+ File.send(@method, '.*', 'foo/.directory/.profile', File::FNM_PATHNAME).should == false
+ File.send(@method, '**/.dotfile', '.dotsubdir/.dotfile', File::FNM_PATHNAME).should == false
end
it "matches leading periods in filenames when flags includes FNM_DOTMATCH" do
@@ -196,21 +222,48 @@ describe :file_fnmatch, shared: true do
it "returns false if '/' in pattern do not match '/' in path when flags includes FNM_PATHNAME" do
pattern = '*/*'
- File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME).should be_false
+ File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME).should == false
pattern = '**/foo'
- File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME).should be_false
+ File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME).should == false
end
it "returns true if '/' in pattern match '/' in path when flags includes FNM_PATHNAME" do
pattern = '*/*'
- File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
pattern = '**/foo'
- File.send(@method, pattern, 'a/b/c/foo', File::FNM_PATHNAME).should be_true
- File.send(@method, pattern, '/a/b/c/foo', File::FNM_PATHNAME).should be_true
- File.send(@method, pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME).should be_true
- File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true
+ File.send(@method, pattern, 'a/b/c/foo', File::FNM_PATHNAME).should == true
+ File.send(@method, pattern, '/a/b/c/foo', File::FNM_PATHNAME).should == true
+ File.send(@method, pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME).should == true
+ File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ end
+
+ it "has special handling for ./ when using * and FNM_PATHNAME" do
+ File.send(@method, './*', '.', File::FNM_PATHNAME).should == false
+ File.send(@method, './*', './', File::FNM_PATHNAME).should == true
+ File.send(@method, './*/', './', File::FNM_PATHNAME).should == false
+ File.send(@method, './**', './', File::FNM_PATHNAME).should == true
+ File.send(@method, './**/', './', File::FNM_PATHNAME).should == true
+ File.send(@method, './*', '.', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false
+ File.send(@method, './*', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ File.send(@method, './*/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false
+ File.send(@method, './**', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ File.send(@method, './**/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ end
+
+ it "matches **/* with FNM_PATHNAME to recurse directories" do
+ File.send(@method, 'nested/**/*', 'nested/subdir', File::FNM_PATHNAME).should == true
+ File.send(@method, 'nested/**/*', 'nested/subdir/file', File::FNM_PATHNAME).should == true
+ File.send(@method, 'nested/**/*', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ File.send(@method, 'nested/**/*', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ end
+
+ it "matches ** with FNM_PATHNAME only in current directory" do
+ File.send(@method, 'nested/**', 'nested/subdir', File::FNM_PATHNAME).should == true
+ File.send(@method, 'nested/**', 'nested/subdir/file', File::FNM_PATHNAME).should == false
+ File.send(@method, 'nested/**', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true
+ File.send(@method, 'nested/**', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false
end
it "accepts an object that has a #to_path method" do
@@ -218,21 +271,21 @@ describe :file_fnmatch, shared: true do
end
it "raises a TypeError if the first and second arguments are not string-like" do
- -> { File.send(@method, nil, nil, 0, 0) }.should raise_error(ArgumentError)
- -> { File.send(@method, 1, 'some/thing') }.should raise_error(TypeError)
- -> { File.send(@method, 'some/thing', 1) }.should raise_error(TypeError)
- -> { File.send(@method, 1, 1) }.should raise_error(TypeError)
+ -> { File.send(@method, nil, nil, 0, 0) }.should.raise(ArgumentError)
+ -> { File.send(@method, 1, 'some/thing') }.should.raise(TypeError)
+ -> { File.send(@method, 'some/thing', 1) }.should.raise(TypeError)
+ -> { File.send(@method, 1, 1) }.should.raise(TypeError)
end
it "raises a TypeError if the third argument is not an Integer" do
- -> { File.send(@method, "*/place", "path/to/file", "flags") }.should raise_error(TypeError)
- -> { File.send(@method, "*/place", "path/to/file", nil) }.should raise_error(TypeError)
+ -> { File.send(@method, "*/place", "path/to/file", "flags") }.should.raise(TypeError)
+ -> { File.send(@method, "*/place", "path/to/file", nil) }.should.raise(TypeError)
end
it "does not raise a TypeError if the third argument can be coerced to an Integer" do
flags = mock("flags")
flags.should_receive(:to_int).and_return(10)
- -> { File.send(@method, "*/place", "path/to/file", flags) }.should_not raise_error
+ -> { File.send(@method, "*/place", "path/to/file", flags) }.should_not.raise
end
it "matches multibyte characters" do
diff --git a/spec/ruby/core/file/shared/open.rb b/spec/ruby/core/file/shared/open.rb
index 677a82a351..67149235ca 100644
--- a/spec/ruby/core/file/shared/open.rb
+++ b/spec/ruby/core/file/shared/open.rb
@@ -4,7 +4,7 @@ describe :open_directory, shared: true do
it "opens directories" do
file = File.send(@method, tmp(""))
begin
- file.should be_kind_of(File)
+ file.should.is_a?(File)
ensure
file.close
end
diff --git a/spec/ruby/core/file/shared/path.rb b/spec/ruby/core/file/shared/path.rb
index d964acc855..6c6f7d4234 100644
--- a/spec/ruby/core/file/shared/path.rb
+++ b/spec/ruby/core/file/shared/path.rb
@@ -1,7 +1,7 @@
describe :file_path, shared: true do
before :each do
- @name = "file_to_path"
- @path = tmp(@name)
+ @path = tmp("file_to_path")
+ @name = File.basename(@path)
touch @path
end
@@ -12,7 +12,24 @@ describe :file_path, shared: true do
it "returns a String" do
@file = File.new @path
- @file.send(@method).should be_an_instance_of(String)
+ @file.send(@method).should.instance_of?(String)
+ end
+
+ it "returns a different String on every call" do
+ @file = File.new @path
+ path1 = @file.send(@method)
+ path2 = @file.send(@method)
+ path1.should == path2
+ path1.should_not.equal?(path2)
+ end
+
+ it "returns a mutable String" do
+ @file = File.new @path.dup.freeze
+ path = @file.send(@method)
+ path.should == @path
+ path.should_not.frozen?
+ path << "test"
+ @file.send(@method).should == @path
end
it "calls to_str on argument and returns exact value" do
@@ -60,16 +77,6 @@ describe :file_path, shared: true do
after :each do
rm_r @dir
end
-
- it "raises IOError if file was opened with File::TMPFILE" do
- begin
- File.open(@dir, File::RDWR | File::TMPFILE) do |f|
- -> { f.send(@method) }.should raise_error(IOError)
- end
- rescue Errno::EOPNOTSUPP, Errno::EINVAL, Errno::EISDIR
- skip "no support from the filesystem"
- end
- end
end
end
end
diff --git a/spec/ruby/core/file/shared/read.rb b/spec/ruby/core/file/shared/read.rb
index a2d479966d..f60800bb2f 100644
--- a/spec/ruby/core/file/shared/read.rb
+++ b/spec/ruby/core/file/shared/read.rb
@@ -1,15 +1,15 @@
require_relative '../../dir/fixtures/common'
describe :file_read_directory, shared: true do
- platform_is :darwin, :linux, :openbsd, :windows do
+ platform_is :darwin, :linux, :freebsd, :openbsd, :windows do
it "raises an Errno::EISDIR when passed a path that is a directory" do
- -> { @object.send(@method, ".") }.should raise_error(Errno::EISDIR)
+ -> { @object.send(@method, ".") }.should.raise(Errno::EISDIR)
end
end
- platform_is :freebsd, :netbsd do
+ platform_is :netbsd do
it "does not raises any exception when passed a path that is a directory" do
- -> { @object.send(@method, ".") }.should_not raise_error
+ -> { @object.send(@method, ".") }.should_not.raise
end
end
end
diff --git a/spec/ruby/core/file/shared/stat.rb b/spec/ruby/core/file/shared/stat.rb
index fdaf97ea61..879a7f11ff 100644
--- a/spec/ruby/core/file/shared/stat.rb
+++ b/spec/ruby/core/file/shared/stat.rb
@@ -10,13 +10,13 @@ describe :file_stat, shared: true do
it "returns a File::Stat object if the given file exists" do
st = File.send(@method, @file)
- st.should be_an_instance_of(File::Stat)
+ st.should.instance_of?(File::Stat)
end
it "returns a File::Stat object when called on an instance of File" do
File.open(@file) do |f|
st = f.send(@method)
- st.should be_an_instance_of(File::Stat)
+ st.should.instance_of?(File::Stat)
end
end
@@ -27,6 +27,6 @@ describe :file_stat, shared: true do
it "raises an Errno::ENOENT if the file does not exist" do
-> {
File.send(@method, "fake_file")
- }.should raise_error(Errno::ENOENT)
+ }.should.raise(Errno::ENOENT)
end
end
diff --git a/spec/ruby/core/file/shared/unlink.rb b/spec/ruby/core/file/shared/unlink.rb
index e339e93271..0032907ba2 100644
--- a/spec/ruby/core/file/shared/unlink.rb
+++ b/spec/ruby/core/file/shared/unlink.rb
@@ -31,11 +31,11 @@ describe :file_unlink, shared: true do
end
it "raises a TypeError if not passed a String type" do
- -> { File.send(@method, 1) }.should raise_error(TypeError)
+ -> { File.send(@method, 1) }.should.raise(TypeError)
end
it "raises an Errno::ENOENT when the given file doesn't exist" do
- -> { File.send(@method, 'bogus') }.should raise_error(Errno::ENOENT)
+ -> { File.send(@method, 'bogus') }.should.raise(Errno::ENOENT)
end
it "coerces a given parameter into a string if possible" do
diff --git a/spec/ruby/core/file/shared/update_time.rb b/spec/ruby/core/file/shared/update_time.rb
new file mode 100644
index 0000000000..3fe7266a00
--- /dev/null
+++ b/spec/ruby/core/file/shared/update_time.rb
@@ -0,0 +1,105 @@
+describe :update_time, shared: true do
+ before :all do
+ @time_is_float = platform_is :windows
+ end
+
+ before :each do
+ @atime = Time.now
+ @mtime = Time.now
+ @file1 = tmp("specs_file_utime1")
+ @file2 = tmp("specs_file_utime2")
+ touch @file1
+ touch @file2
+ end
+
+ after :each do
+ rm_r @file1, @file2
+ end
+
+ it "sets the access and modification time of each file" do
+ File.send(@method, @atime, @mtime, @file1, @file2)
+
+ if @time_is_float
+ File.atime(@file1).should be_close(@atime, 0.0001)
+ File.mtime(@file1).should be_close(@mtime, 0.0001)
+ File.atime(@file2).should be_close(@atime, 0.0001)
+ File.mtime(@file2).should be_close(@mtime, 0.0001)
+ else
+ File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ end
+ end
+
+ it "uses the current times if two nil values are passed" do
+ tn = Time.now
+ File.send(@method, nil, nil, @file1, @file2)
+
+ if @time_is_float
+ File.atime(@file1).should be_close(tn, 0.050)
+ File.mtime(@file1).should be_close(tn, 0.050)
+ File.atime(@file2).should be_close(tn, 0.050)
+ File.mtime(@file2).should be_close(tn, 0.050)
+ else
+ File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
+ File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
+ File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
+ File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
+ end
+ end
+
+ it "accepts an object that has a #to_path method" do
+ File.send(@method, @atime, @mtime, mock_to_path(@file1), mock_to_path(@file2))
+ end
+
+ it "accepts numeric atime and mtime arguments" do
+ if @time_is_float
+ File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2)
+
+ File.atime(@file1).should be_close(@atime, 0.0001)
+ File.mtime(@file1).should be_close(@mtime, 0.0001)
+ File.atime(@file2).should be_close(@atime, 0.0001)
+ File.mtime(@file2).should be_close(@mtime, 0.0001)
+ else
+ File.send(@method, @atime.to_i, @mtime.to_i, @file1, @file2)
+
+ File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
+ File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
+ end
+ end
+
+ it "may set nanosecond precision" do
+ t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
+ File.send(@method, t, t, @file1)
+
+ File.atime(@file1).nsec.should.between?(0, 123500000)
+ File.mtime(@file1).nsec.should.between?(0, 123500000)
+ end
+
+ it "returns the number of filenames in the arguments" do
+ File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2).should == 2
+ end
+
+ platform_is :linux do
+ platform_is pointer_size: 64 do
+ it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do
+ # https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps
+ # "Therefore, timestamps should not overflow until May 2446."
+ # https://lwn.net/Articles/804382/
+ # "On-disk timestamps hitting the y2038 limit..."
+ # The problem seems to be being improved, but currently it actually fails on XFS on RHEL8
+ # https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz
+ # Amazon Linux 2023 returns 2486-07-02 in this example
+ # http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz
+ time = Time.at(1<<44)
+ File.send(@method, time, time, @file1)
+
+ [559444, 2486, 2446, 2038].should.include? File.atime(@file1).year
+ [559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/file/size_spec.rb b/spec/ruby/core/file/size_spec.rb
index a2bf408da1..784c18c26e 100644
--- a/spec/ruby/core/file/size_spec.rb
+++ b/spec/ruby/core/file/size_spec.rb
@@ -56,11 +56,11 @@ describe "File#size" do
end
it "is an instance method" do
- @file.respond_to?(:size).should be_true
+ @file.respond_to?(:size).should == true
end
- it "returns the file's size as a Fixnum" do
- @file.size.should be_an_instance_of(Fixnum)
+ it "returns the file's size as an Integer" do
+ @file.size.should.instance_of?(Integer)
end
it "returns the file's size in bytes" do
@@ -81,7 +81,7 @@ describe "File#size" do
it "raises an IOError on a closed file" do
@file.close
- -> { @file.size }.should raise_error(IOError)
+ -> { @file.size }.should.raise(IOError)
end
platform_is_not :windows do
diff --git a/spec/ruby/core/file/socket_spec.rb b/spec/ruby/core/file/socket_spec.rb
index 5d12e21f55..d3f4eb013a 100644
--- a/spec/ruby/core/file/socket_spec.rb
+++ b/spec/ruby/core/file/socket_spec.rb
@@ -1,42 +1,10 @@
require_relative '../../spec_helper'
require_relative '../../shared/file/socket'
-require 'socket'
describe "File.socket?" do
it_behaves_like :file_socket, :socket?, File
-end
-describe "File.socket?" do
it "returns false if file does not exist" do
File.socket?("I_am_a_bogus_file").should == false
end
-
- it "returns false if the file is not a socket" do
- filename = tmp("i_exist")
- touch(filename)
-
- File.socket?(filename).should == false
-
- rm_r filename
- end
-end
-
-platform_is_not :windows do
- describe "File.socket?" do
- before :each do
- # We need a really short name here.
- # On Linux the path length is limited to 107, see unix(7).
- @name = tmp("s")
- @server = UNIXServer.new @name
- end
-
- after :each do
- @server.close
- rm_r @name
- end
-
- it "returns true if the file is a socket" do
- File.socket?(@name).should == true
- end
- end
end
diff --git a/spec/ruby/core/file/split_spec.rb b/spec/ruby/core/file/split_spec.rb
index 7b958621b9..e989a6b86e 100644
--- a/spec/ruby/core/file/split_spec.rb
+++ b/spec/ruby/core/file/split_spec.rb
@@ -44,12 +44,12 @@ describe "File.split" do
end
it "raises an ArgumentError when not passed a single argument" do
- -> { File.split }.should raise_error(ArgumentError)
- -> { File.split('string', 'another string') }.should raise_error(ArgumentError)
+ -> { File.split }.should.raise(ArgumentError)
+ -> { File.split('string', 'another string') }.should.raise(ArgumentError)
end
it "raises a TypeError if the argument is not a String type" do
- -> { File.split(1) }.should raise_error(TypeError)
+ -> { File.split(1) }.should.raise(TypeError)
end
it "coerces the argument with to_str if it is not a String type" do
diff --git a/spec/ruby/core/file/stat/atime_spec.rb b/spec/ruby/core/file/stat/atime_spec.rb
index 9f1111ced1..2fecaed300 100644
--- a/spec/ruby/core/file/stat/atime_spec.rb
+++ b/spec/ruby/core/file/stat/atime_spec.rb
@@ -12,7 +12,7 @@ describe "File::Stat#atime" do
it "returns the atime of a File::Stat object" do
st = File.stat(@file)
- st.atime.should be_kind_of(Time)
+ st.atime.should.is_a?(Time)
st.atime.should <= Time.now
end
end
diff --git a/spec/ruby/core/file/stat/birthtime_spec.rb b/spec/ruby/core/file/stat/birthtime_spec.rb
index a727bbe566..728f635397 100644
--- a/spec/ruby/core/file/stat/birthtime_spec.rb
+++ b/spec/ruby/core/file/stat/birthtime_spec.rb
@@ -1,27 +1,29 @@
require_relative '../../../spec_helper'
-describe "File::Stat#birthtime" do
- before :each do
- @file = tmp('i_exist')
- touch(@file) { |f| f.write "rubinius" }
- end
+platform_is(:windows, :darwin, :freebsd, :netbsd,
+ *ruby_version_is("4.0") { :linux },
+ ) do
+ not_implemented_messages = [
+ "birthtime() function is unimplemented", # unsupported OS/version
+ "birthtime is unimplemented", # unsupported filesystem
+ ]
- after :each do
- rm_r @file
- end
+ describe "File::Stat#birthtime" do
+ before :each do
+ @file = tmp('i_exist')
+ touch(@file) { |f| f.write "rubinius" }
+ end
- platform_is :windows, :darwin, :freebsd, :netbsd do
- it "returns the birthtime of a File::Stat object" do
- st = File.stat(@file)
- st.birthtime.should be_kind_of(Time)
- st.birthtime.should <= Time.now
+ after :each do
+ rm_r @file
end
- end
- platform_is :linux, :openbsd do
- it "raises an NotImplementedError" do
+ it "returns the birthtime of a File::Stat object" do
st = File.stat(@file)
- -> { st.birthtime }.should raise_error(NotImplementedError)
+ st.birthtime.should.is_a?(Time)
+ st.birthtime.should <= Time.now
+ rescue NotImplementedError => e
+ e.message.should.start_with?(*not_implemented_messages)
end
end
end
diff --git a/spec/ruby/core/file/stat/blocks_spec.rb b/spec/ruby/core/file/stat/blocks_spec.rb
index f3f903d0f7..5e0efc8bc2 100644
--- a/spec/ruby/core/file/stat/blocks_spec.rb
+++ b/spec/ruby/core/file/stat/blocks_spec.rb
@@ -21,7 +21,7 @@ describe "File::Stat#blocks" do
platform_is :windows do
it "returns nil" do
st = File.stat(@file)
- st.blocks.should be_nil
+ st.blocks.should == nil
end
end
end
diff --git a/spec/ruby/core/file/stat/ctime_spec.rb b/spec/ruby/core/file/stat/ctime_spec.rb
index fd50487a0a..dbf43a7453 100644
--- a/spec/ruby/core/file/stat/ctime_spec.rb
+++ b/spec/ruby/core/file/stat/ctime_spec.rb
@@ -12,7 +12,7 @@ describe "File::Stat#ctime" do
it "returns the ctime of a File::Stat object" do
st = File.stat(@file)
- st.ctime.should be_kind_of(Time)
+ st.ctime.should.is_a?(Time)
st.ctime.should <= Time.now
end
end
diff --git a/spec/ruby/core/file/stat/dev_major_spec.rb b/spec/ruby/core/file/stat/dev_major_spec.rb
index 4966d609e2..a199eaaa11 100644
--- a/spec/ruby/core/file/stat/dev_major_spec.rb
+++ b/spec/ruby/core/file/stat/dev_major_spec.rb
@@ -11,13 +11,13 @@ describe "File::Stat#dev_major" do
platform_is_not :windows do
it "returns the major part of File::Stat#dev" do
- File.stat(@name).dev_major.should be_kind_of(Integer)
+ File.stat(@name).dev_major.should.is_a?(Integer)
end
end
platform_is :windows do
it "returns nil" do
- File.stat(@name).dev_major.should be_nil
+ File.stat(@name).dev_major.should == nil
end
end
end
diff --git a/spec/ruby/core/file/stat/dev_minor_spec.rb b/spec/ruby/core/file/stat/dev_minor_spec.rb
index ea79c12b99..8ce94778ca 100644
--- a/spec/ruby/core/file/stat/dev_minor_spec.rb
+++ b/spec/ruby/core/file/stat/dev_minor_spec.rb
@@ -11,13 +11,13 @@ describe "File::Stat#dev_minor" do
platform_is_not :windows do
it "returns the minor part of File::Stat#dev" do
- File.stat(@name).dev_minor.should be_kind_of(Integer)
+ File.stat(@name).dev_minor.should.is_a?(Integer)
end
end
platform_is :windows do
it "returns nil" do
- File.stat(@name).dev_minor.should be_nil
+ File.stat(@name).dev_minor.should == nil
end
end
end
diff --git a/spec/ruby/core/file/stat/dev_spec.rb b/spec/ruby/core/file/stat/dev_spec.rb
index e953fcaa58..cd5e3d175e 100644
--- a/spec/ruby/core/file/stat/dev_spec.rb
+++ b/spec/ruby/core/file/stat/dev_spec.rb
@@ -10,6 +10,6 @@ describe "File::Stat#dev" do
end
it "returns the number of the device on which the file exists" do
- File.stat(@name).dev.should be_kind_of(Integer)
+ File.stat(@name).dev.should.is_a?(Integer)
end
end
diff --git a/spec/ruby/core/file/stat/ftype_spec.rb b/spec/ruby/core/file/stat/ftype_spec.rb
index eb892eae5f..df2e3ada1e 100644
--- a/spec/ruby/core/file/stat/ftype_spec.rb
+++ b/spec/ruby/core/file/stat/ftype_spec.rb
@@ -8,7 +8,7 @@ describe "File::Stat#ftype" do
it "returns a String" do
FileSpecs.normal_file do |file|
- File.lstat(file).ftype.should be_kind_of(String)
+ File.lstat(file).ftype.should.is_a?(String)
end
end
diff --git a/spec/ruby/core/file/stat/ino_spec.rb b/spec/ruby/core/file/stat/ino_spec.rb
index 42370aecb7..c09b6448c7 100644
--- a/spec/ruby/core/file/stat/ino_spec.rb
+++ b/spec/ruby/core/file/stat/ino_spec.rb
@@ -13,7 +13,7 @@ describe "File::Stat#ino" do
platform_is_not :windows do
it "returns the ino of a File::Stat object" do
st = File.stat(@file)
- st.ino.should be_kind_of(Integer)
+ st.ino.should.is_a?(Integer)
st.ino.should > 0
end
end
@@ -21,7 +21,7 @@ describe "File::Stat#ino" do
platform_is :windows do
it "returns BY_HANDLE_FILE_INFORMATION.nFileIndexHigh/Low of a File::Stat object" do
st = File.stat(@file)
- st.ino.should be_kind_of(Integer)
+ st.ino.should.is_a?(Integer)
st.ino.should > 0
end
end
diff --git a/spec/ruby/core/file/stat/mtime_spec.rb b/spec/ruby/core/file/stat/mtime_spec.rb
index 08a2b83463..7844491212 100644
--- a/spec/ruby/core/file/stat/mtime_spec.rb
+++ b/spec/ruby/core/file/stat/mtime_spec.rb
@@ -12,7 +12,7 @@ describe "File::Stat#mtime" do
it "returns the mtime of a File::Stat object" do
st = File.stat(@file)
- st.mtime.should be_kind_of(Time)
+ st.mtime.should.is_a?(Time)
st.mtime.should <= Time.now
end
end
diff --git a/spec/ruby/core/file/stat/new_spec.rb b/spec/ruby/core/file/stat/new_spec.rb
index c0d9432ac8..b8c3600028 100644
--- a/spec/ruby/core/file/stat/new_spec.rb
+++ b/spec/ruby/core/file/stat/new_spec.rb
@@ -15,12 +15,12 @@ describe "File::Stat#initialize" do
it "raises an exception if the file doesn't exist" do
-> {
File::Stat.new(tmp("i_am_a_dummy_file_that_doesnt_exist"))
- }.should raise_error(Errno::ENOENT)
+ }.should.raise(Errno::ENOENT)
end
it "creates a File::Stat object for the given file" do
st = File::Stat.new(@file)
- st.should be_kind_of(File::Stat)
+ st.should.is_a?(File::Stat)
st.ftype.should == 'file'
end
diff --git a/spec/ruby/core/file/stat/nlink_spec.rb b/spec/ruby/core/file/stat/nlink_spec.rb
index 2dd0bff124..7143923cfc 100644
--- a/spec/ruby/core/file/stat/nlink_spec.rb
+++ b/spec/ruby/core/file/stat/nlink_spec.rb
@@ -11,7 +11,7 @@ describe "File::Stat#nlink" do
rm_r @link, @file
end
- platform_is_not :windows do
+ platform_is_not :windows, :android do
it "returns the number of links to a file" do
File::Stat.new(@file).nlink.should == 1
File.link(@file, @link)
diff --git a/spec/ruby/core/file/stat/rdev_major_spec.rb b/spec/ruby/core/file/stat/rdev_major_spec.rb
index f8a8d1b107..e1b44fc2d0 100644
--- a/spec/ruby/core/file/stat/rdev_major_spec.rb
+++ b/spec/ruby/core/file/stat/rdev_major_spec.rb
@@ -2,30 +2,23 @@ require_relative '../../../spec_helper'
describe "File::Stat#rdev_major" do
before :each do
- platform_is :solaris do
- @name = "/dev/zfs"
- end
- platform_is_not :solaris do
- @name = tmp("file.txt")
- touch(@name)
- end
+ @name = tmp("file.txt")
+ touch(@name)
end
after :each do
- platform_is_not :solaris do
- rm_r @name
- end
+ rm_r @name
end
platform_is_not :windows do
it "returns the major part of File::Stat#rdev" do
- File.stat(@name).rdev_major.should be_kind_of(Integer)
+ File.stat(@name).rdev_major.should.is_a?(Integer)
end
end
platform_is :windows do
it "returns nil" do
- File.stat(@name).rdev_major.should be_nil
+ File.stat(@name).rdev_major.should == nil
end
end
end
diff --git a/spec/ruby/core/file/stat/rdev_minor_spec.rb b/spec/ruby/core/file/stat/rdev_minor_spec.rb
index dc30c1f56c..8af3b9f587 100644
--- a/spec/ruby/core/file/stat/rdev_minor_spec.rb
+++ b/spec/ruby/core/file/stat/rdev_minor_spec.rb
@@ -2,30 +2,23 @@ require_relative '../../../spec_helper'
describe "File::Stat#rdev_minor" do
before :each do
- platform_is :solaris do
- @name = "/dev/zfs"
- end
- platform_is_not :solaris do
- @name = tmp("file.txt")
- touch(@name)
- end
+ @name = tmp("file.txt")
+ touch(@name)
end
after :each do
- platform_is_not :solaris do
- rm_r @name
- end
+ rm_r @name
end
platform_is_not :windows do
it "returns the minor part of File::Stat#rdev" do
- File.stat(@name).rdev_minor.should be_kind_of(Integer)
+ File.stat(@name).rdev_minor.should.is_a?(Integer)
end
end
platform_is :windows do
it "returns nil" do
- File.stat(@name).rdev_minor.should be_nil
+ File.stat(@name).rdev_minor.should == nil
end
end
end
diff --git a/spec/ruby/core/file/stat/rdev_spec.rb b/spec/ruby/core/file/stat/rdev_spec.rb
index 9e1aee692d..7e4252fcc6 100644
--- a/spec/ruby/core/file/stat/rdev_spec.rb
+++ b/spec/ruby/core/file/stat/rdev_spec.rb
@@ -10,6 +10,6 @@ describe "File::Stat#rdev" do
end
it "returns the number of the device this file represents which the file exists" do
- File.stat(@name).rdev.should be_kind_of(Integer)
+ File.stat(@name).rdev.should.is_a?(Integer)
end
end
diff --git a/spec/ruby/core/file/stat_spec.rb b/spec/ruby/core/file/stat_spec.rb
index 6365500057..d5238b6331 100644
--- a/spec/ruby/core/file/stat_spec.rb
+++ b/spec/ruby/core/file/stat_spec.rb
@@ -28,9 +28,9 @@ platform_is_not :windows do
st.size.should == 8
st.size?.should == 8
st.blksize.should >= 0
- st.atime.should be_kind_of(Time)
- st.ctime.should be_kind_of(Time)
- st.mtime.should be_kind_of(Time)
+ st.atime.should.is_a?(Time)
+ st.ctime.should.is_a?(Time)
+ st.mtime.should.is_a?(Time)
end
end
@@ -46,9 +46,9 @@ platform_is_not :windows do
missing_path = "/missingfilepath\xE3E4".b
-> {
File.stat(missing_path)
- }.should raise_error(SystemCallError) { |e|
- [Errno::ENOENT, Errno::EILSEQ].should include(e.class)
- e.message.should include(missing_path)
+ }.should.raise(SystemCallError) { |e|
+ [Errno::ENOENT, Errno::EILSEQ].should.include?(e.class)
+ e.message.should.include?(missing_path)
}
end
end
diff --git a/spec/ruby/core/file/symlink_spec.rb b/spec/ruby/core/file/symlink_spec.rb
index 0e8b0a5a20..4ceeb28c84 100644
--- a/spec/ruby/core/file/symlink_spec.rb
+++ b/spec/ruby/core/file/symlink_spec.rb
@@ -32,18 +32,18 @@ describe "File.symlink" do
it "raises an Errno::EEXIST if the target already exists" do
File.symlink(@file, @link)
- -> { File.symlink(@file, @link) }.should raise_error(Errno::EEXIST)
+ -> { File.symlink(@file, @link) }.should.raise(Errno::EEXIST)
end
it "raises an ArgumentError if not called with two arguments" do
- -> { File.symlink }.should raise_error(ArgumentError)
- -> { File.symlink(@file) }.should raise_error(ArgumentError)
+ -> { File.symlink }.should.raise(ArgumentError)
+ -> { File.symlink(@file) }.should.raise(ArgumentError)
end
it "raises a TypeError if not called with String types" do
- -> { File.symlink(@file, nil) }.should raise_error(TypeError)
- -> { File.symlink(@file, 1) }.should raise_error(TypeError)
- -> { File.symlink(1, 1) }.should raise_error(TypeError)
+ -> { File.symlink(@file, nil) }.should.raise(TypeError)
+ -> { File.symlink(@file, 1) }.should.raise(TypeError)
+ -> { File.symlink(1, 1) }.should.raise(TypeError)
end
end
end
diff --git a/spec/ruby/core/file/truncate_spec.rb b/spec/ruby/core/file/truncate_spec.rb
index b4a2e3e577..5f37f34155 100644
--- a/spec/ruby/core/file/truncate_spec.rb
+++ b/spec/ruby/core/file/truncate_spec.rb
@@ -54,29 +54,29 @@ describe "File.truncate" do
rm_r not_existing_file
begin
- -> { File.truncate(not_existing_file, 5) }.should raise_error(Errno::ENOENT)
+ -> { File.truncate(not_existing_file, 5) }.should.raise(Errno::ENOENT)
ensure
rm_r not_existing_file
end
end
it "raises an ArgumentError if not passed two arguments" do
- -> { File.truncate }.should raise_error(ArgumentError)
- -> { File.truncate(@name) }.should raise_error(ArgumentError)
+ -> { File.truncate }.should.raise(ArgumentError)
+ -> { File.truncate(@name) }.should.raise(ArgumentError)
end
platform_is_not :netbsd, :openbsd do
it "raises an Errno::EINVAL if the length argument is not valid" do
- -> { File.truncate(@name, -1) }.should raise_error(Errno::EINVAL) # May fail
+ -> { File.truncate(@name, -1) }.should.raise(Errno::EINVAL) # May fail
end
end
it "raises a TypeError if not passed a String type for the first argument" do
- -> { File.truncate(1, 1) }.should raise_error(TypeError)
+ -> { File.truncate(1, 1) }.should.raise(TypeError)
end
it "raises a TypeError if not passed an Integer type for the second argument" do
- -> { File.truncate(@name, nil) }.should raise_error(TypeError)
+ -> { File.truncate(@name, nil) }.should.raise(TypeError)
end
it "accepts an object that has a #to_path method" do
@@ -149,29 +149,29 @@ describe "File#truncate" do
end
it "raises an ArgumentError if not passed one argument" do
- -> { @file.truncate }.should raise_error(ArgumentError)
- -> { @file.truncate(1) }.should_not raise_error(ArgumentError)
+ -> { @file.truncate }.should.raise(ArgumentError)
+ -> { @file.truncate(1) }.should_not.raise(ArgumentError)
end
platform_is_not :netbsd do
it "raises an Errno::EINVAL if the length argument is not valid" do
- -> { @file.truncate(-1) }.should raise_error(Errno::EINVAL) # May fail
+ -> { @file.truncate(-1) }.should.raise(Errno::EINVAL) # May fail
end
end
it "raises an IOError if file is closed" do
@file.close
@file.should.closed?
- -> { @file.truncate(42) }.should raise_error(IOError)
+ -> { @file.truncate(42) }.should.raise(IOError)
end
it "raises an IOError if file is not opened for writing" do
File.open(@name, 'r') do |file|
- -> { file.truncate(42) }.should raise_error(IOError)
+ -> { file.truncate(42) }.should.raise(IOError)
end
end
it "raises a TypeError if not passed an Integer type for the for the argument" do
- -> { @file.truncate(nil) }.should raise_error(TypeError)
+ -> { @file.truncate(nil) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/file/umask_spec.rb b/spec/ruby/core/file/umask_spec.rb
index 2640e3c316..fea8cf7633 100644
--- a/spec/ruby/core/file/umask_spec.rb
+++ b/spec/ruby/core/file/umask_spec.rb
@@ -12,8 +12,8 @@ describe "File.umask" do
File.umask(@orig_umask)
end
- it "returns a Fixnum" do
- File.umask.should be_kind_of(Fixnum)
+ it "returns an Integer" do
+ File.umask.should.is_a?(Integer)
end
platform_is_not :windows do
@@ -47,11 +47,11 @@ describe "File.umask" do
end
it "raises RangeError with too large values" do
- -> { File.umask(2**64) }.should raise_error(RangeError)
- -> { File.umask(-2**63 - 1) }.should raise_error(RangeError)
+ -> { File.umask(2**64) }.should.raise(RangeError)
+ -> { File.umask(-2**63 - 1) }.should.raise(RangeError)
end
it "raises ArgumentError when more than one argument is provided" do
- -> { File.umask(022, 022) }.should raise_error(ArgumentError)
+ -> { File.umask(022, 022) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/file/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb
index 96627b1fc6..d87626be50 100644
--- a/spec/ruby/core/file/utime_spec.rb
+++ b/spec/ruby/core/file/utime_spec.rb
@@ -1,85 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/update_time'
describe "File.utime" do
-
- before :all do
- @time_is_float = platform_is :windows
- end
-
- before :each do
- @atime = Time.now
- @mtime = Time.now
- @file1 = tmp("specs_file_utime1")
- @file2 = tmp("specs_file_utime2")
- touch @file1
- touch @file2
- end
-
- after :each do
- rm_r @file1, @file2
- end
-
- it "sets the access and modification time of each file" do
- File.utime(@atime, @mtime, @file1, @file2)
- if @time_is_float
- File.atime(@file1).should be_close(@atime, 0.0001)
- File.mtime(@file1).should be_close(@mtime, 0.0001)
- File.atime(@file2).should be_close(@atime, 0.0001)
- File.mtime(@file2).should be_close(@mtime, 0.0001)
- else
- File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
- File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
- end
- end
-
- it "uses the current times if two nil values are passed" do
- tn = Time.now
- File.utime(nil, nil, @file1, @file2)
- if @time_is_float
- File.atime(@file1).should be_close(tn, 0.050)
- File.mtime(@file1).should be_close(tn, 0.050)
- File.atime(@file2).should be_close(tn, 0.050)
- File.mtime(@file2).should be_close(tn, 0.050)
- else
- File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
- File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
- File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
- File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE)
- end
- end
-
- it "accepts an object that has a #to_path method" do
- File.utime(@atime, @mtime, mock_to_path(@file1), mock_to_path(@file2))
- end
-
- it "accepts numeric atime and mtime arguments" do
- if @time_is_float
- File.utime(@atime.to_f, @mtime.to_f, @file1, @file2)
- File.atime(@file1).should be_close(@atime, 0.0001)
- File.mtime(@file1).should be_close(@mtime, 0.0001)
- File.atime(@file2).should be_close(@atime, 0.0001)
- File.mtime(@file2).should be_close(@mtime, 0.0001)
- else
- File.utime(@atime.to_i, @mtime.to_i, @file1, @file2)
- File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
- File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE)
- File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE)
- end
- end
-
- platform_is :linux do
- platform_is wordsize: 64 do
- it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10)" do
- # https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps
- # "Therefore, timestamps should not overflow until May 2446."
- time = Time.at(1<<44)
- File.utime(time, time, @file1)
- [559444, 2446].should.include? File.atime(@file1).year
- [559444, 2446].should.include? File.mtime(@file1).year
- end
- end
- end
+ it_behaves_like :update_time, :utime
end
diff --git a/spec/ruby/core/file/world_readable_spec.rb b/spec/ruby/core/file/world_readable_spec.rb
index 11b8e67d0b..0f5e0b13d7 100644
--- a/spec/ruby/core/file/world_readable_spec.rb
+++ b/spec/ruby/core/file/world_readable_spec.rb
@@ -7,6 +7,6 @@ describe "File.world_readable?" do
it "returns nil if the file does not exist" do
file = rand.to_s + $$.to_s
File.should_not.exist?(file)
- File.world_readable?(file).should be_nil
+ File.world_readable?(file).should == nil
end
end
diff --git a/spec/ruby/core/file/world_writable_spec.rb b/spec/ruby/core/file/world_writable_spec.rb
index d378cf2eb9..46ba6832f9 100644
--- a/spec/ruby/core/file/world_writable_spec.rb
+++ b/spec/ruby/core/file/world_writable_spec.rb
@@ -7,6 +7,6 @@ describe "File.world_writable?" do
it "returns nil if the file does not exist" do
file = rand.to_s + $$.to_s
File.should_not.exist?(file)
- File.world_writable?(file).should be_nil
+ File.world_writable?(file).should == nil
end
end
diff --git a/spec/ruby/core/file/zero_spec.rb b/spec/ruby/core/file/zero_spec.rb
index 63dd85ee46..01c7505ef2 100644
--- a/spec/ruby/core/file/zero_spec.rb
+++ b/spec/ruby/core/file/zero_spec.rb
@@ -4,10 +4,4 @@ require_relative '../../shared/file/zero'
describe "File.zero?" do
it_behaves_like :file_zero, :zero?, File
it_behaves_like :file_zero_missing, :zero?, File
-
- platform_is :solaris do
- it "returns false for /dev/null" do
- File.zero?('/dev/null').should == true
- end
- end
end
diff --git a/spec/ruby/core/filetest/exist_spec.rb b/spec/ruby/core/filetest/exist_spec.rb
index 4d14bea231..612ffa9fcb 100644
--- a/spec/ruby/core/filetest/exist_spec.rb
+++ b/spec/ruby/core/filetest/exist_spec.rb
@@ -4,3 +4,9 @@ require_relative '../../shared/file/exist'
describe "FileTest.exist?" do
it_behaves_like :file_exist, :exist?, FileTest
end
+
+describe "FileTest.exists?" do
+ it "has been removed" do
+ FileTest.should_not.respond_to?(:exists?)
+ end
+end
diff --git a/spec/ruby/core/filetest/grpowned_spec.rb b/spec/ruby/core/filetest/grpowned_spec.rb
index d073cb9770..d6bd6abd71 100644
--- a/spec/ruby/core/filetest/grpowned_spec.rb
+++ b/spec/ruby/core/filetest/grpowned_spec.rb
@@ -5,6 +5,6 @@ describe "FileTest.grpowned?" do
it_behaves_like :file_grpowned, :grpowned?, FileTest
it "returns false if the file doesn't exist" do
- FileTest.grpowned?("xxx-tmp-doesnt_exist-blah").should be_false
+ FileTest.grpowned?("xxx-tmp-doesnt_exist-blah").should == false
end
end
diff --git a/spec/ruby/core/filetest/socket_spec.rb b/spec/ruby/core/filetest/socket_spec.rb
index 63a6a31ecb..f274be6318 100644
--- a/spec/ruby/core/filetest/socket_spec.rb
+++ b/spec/ruby/core/filetest/socket_spec.rb
@@ -3,4 +3,8 @@ require_relative '../../shared/file/socket'
describe "FileTest.socket?" do
it_behaves_like :file_socket, :socket?, FileTest
+
+ it "returns false if file does not exist" do
+ FileTest.socket?("I_am_a_bogus_file").should == false
+ end
end
diff --git a/spec/ruby/core/filetest/zero_spec.rb b/spec/ruby/core/filetest/zero_spec.rb
index dd6a164ec9..92cab67f1b 100644
--- a/spec/ruby/core/filetest/zero_spec.rb
+++ b/spec/ruby/core/filetest/zero_spec.rb
@@ -4,10 +4,4 @@ require_relative '../../shared/file/zero'
describe "FileTest.zero?" do
it_behaves_like :file_zero, :zero?, FileTest
it_behaves_like :file_zero_missing, :zero?, FileTest
-
- platform_is :solaris do
- it "returns false for /dev/null" do
- File.zero?('/dev/null').should == true
- end
- end
end
diff --git a/spec/ruby/core/float/ceil_spec.rb b/spec/ruby/core/float/ceil_spec.rb
index 7fc18d304c..efd1e6feb2 100644
--- a/spec/ruby/core/float/ceil_spec.rb
+++ b/spec/ruby/core/float/ceil_spec.rb
@@ -1,21 +1,28 @@
require_relative '../../spec_helper'
+require_relative '../integer/shared/integer_ceil_precision'
describe "Float#ceil" do
+ context "with values equal to integers" do
+ it_behaves_like :integer_ceil_precision, :Float
+ end
+
it "returns the smallest Integer greater than or equal to self" do
- -1.2.ceil.should eql( -1)
- -1.0.ceil.should eql( -1)
- 0.0.ceil.should eql( 0 )
- 1.3.ceil.should eql( 2 )
- 3.0.ceil.should eql( 3 )
- -9223372036854775808.1.ceil.should eql(-9223372036854775808)
- +9223372036854775808.1.ceil.should eql(+9223372036854775808)
+ -1.2.ceil.should.eql?( -1)
+ -1.0.ceil.should.eql?( -1)
+ 0.0.ceil.should.eql?( 0 )
+ 1.3.ceil.should.eql?( 2 )
+ 3.0.ceil.should.eql?( 3 )
+ -9223372036854775808.1.ceil.should.eql?(-9223372036854775808)
+ +9223372036854775808.1.ceil.should.eql?(+9223372036854775808)
end
it "returns the smallest number greater than or equal to self with an optionally given precision" do
- 2.1679.ceil(0).should eql(3)
- 214.94.ceil(-1).should eql(220)
- 7.0.ceil(1).should eql(7.0)
- -1.234.ceil(2).should eql(-1.23)
- 5.123812.ceil(4).should eql(5.1239)
+ 2.1679.ceil(0).should.eql?(3)
+ 214.94.ceil(-1).should.eql?(220)
+ 7.0.ceil(1).should.eql?(7.0)
+ 200.0.ceil(-2).should.eql?(200)
+ -1.234.ceil(2).should.eql?(-1.23)
+ 5.123812.ceil(4).should.eql?(5.1239)
+ 10.00001.ceil(5).should.eql?(10.00001)
end
end
diff --git a/spec/ruby/core/float/coerce_spec.rb b/spec/ruby/core/float/coerce_spec.rb
index ea108f3303..baa831dcf6 100644
--- a/spec/ruby/core/float/coerce_spec.rb
+++ b/spec/ruby/core/float/coerce_spec.rb
@@ -9,10 +9,10 @@ describe "Float#coerce" do
1.0.coerce(3.14).should == [3.14, 1.0]
a, b = -0.0.coerce(bignum_value)
- a.should be_close(9223372036854775808.0, TOLERANCE)
+ a.should be_close(18446744073709551616.0, TOLERANCE)
b.should be_close(-0.0, TOLERANCE)
a, b = 1.0.coerce(bignum_value)
- a.should be_close(9223372036854775808.0, TOLERANCE)
+ a.should be_close(18446744073709551616.0, TOLERANCE)
b.should be_close(1.0, TOLERANCE)
end
end
diff --git a/spec/ruby/core/float/comparison_spec.rb b/spec/ruby/core/float/comparison_spec.rb
index 51bb5a52db..e9adf2fd6a 100644
--- a/spec/ruby/core/float/comparison_spec.rb
+++ b/spec/ruby/core/float/comparison_spec.rb
@@ -7,13 +7,32 @@ describe "Float#<=>" do
((bignum_value*1.1) <=> bignum_value).should == 1
end
- it "returns nil when either argument is NaN" do
- (nan_value <=> 71.2).should be_nil
- (1771.176 <=> nan_value).should be_nil
+ it "returns nil if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value <=> n).should == nil
+ (n <=> nan_value).should == nil
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value <=> n).should == 1
+ (n <=> infinity_value).should == -1
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value <=> n).should == -1
+ (n <=> -infinity_value).should == 1
+ }
end
it "returns nil when the given argument is not a Float" do
- (1.0 <=> "1").should be_nil
+ (1.0 <=> "1").should == nil
+ (1.0 <=> "1".freeze).should == nil
+ (1.0 <=> :one).should == nil
+ (1.0 <=> true).should == nil
end
it "compares using #coerce when argument is not a Float" do
@@ -43,24 +62,52 @@ describe "Float#<=>" do
bad_coercible = klass.new
-> {
4.2 <=> bad_coercible
- }.should raise_error(TypeError, "coerce must return [x, y]")
+ }.should.raise(TypeError, "coerce must return [x, y]")
end
- # The 4 tests below are taken from matz's revision 23730 for Ruby trunk
- #
- it "returns 1 when self is Infinity and other is a Bignum" do
+ it "returns the correct result when one side is infinite" do
(infinity_value <=> Float::MAX.to_i*2).should == 1
+ (-Float::MAX.to_i*2 <=> infinity_value).should == -1
+ (-infinity_value <=> -Float::MAX.to_i*2).should == -1
+ (-Float::MAX.to_i*2 <=> -infinity_value).should == 1
end
- it "returns -1 when self is negative and other is Infinity" do
- (-Float::MAX.to_i*2 <=> infinity_value).should == -1
+ it "returns 0 when self is Infinity and other is infinite?=1" do
+ obj = Object.new
+ def obj.infinite?
+ 1
+ end
+ (infinity_value <=> obj).should == 0
end
- it "returns -1 when self is -Infinity and other is negative" do
- (-infinity_value <=> -Float::MAX.to_i*2).should == -1
+ it "returns 1 when self is Infinity and other is infinite?=-1" do
+ obj = Object.new
+ def obj.infinite?
+ -1
+ end
+ (infinity_value <=> obj).should == 1
end
- it "returns 1 when self is negative and other is -Infinity" do
- (-Float::MAX.to_i*2 <=> -infinity_value).should == 1
+ it "returns 1 when self is Infinity and other is infinite?=nil (which means finite)" do
+ obj = Object.new
+ def obj.infinite?
+ nil
+ end
+ (infinity_value <=> obj).should == 1
+ end
+
+ it "returns 0 for -0.0 and 0.0" do
+ (-0.0 <=> 0.0).should == 0
+ (0.0 <=> -0.0).should == 0
+ end
+
+ it "returns 0 for -0.0 and 0" do
+ (-0.0 <=> 0).should == 0
+ (0 <=> -0.0).should == 0
+ end
+
+ it "returns 0 for 0.0 and 0" do
+ (0.0 <=> 0).should == 0
+ (0 <=> 0.0).should == 0
end
end
diff --git a/spec/ruby/core/float/constants_spec.rb b/spec/ruby/core/float/constants_spec.rb
index 497e0ae188..1b71ee8adf 100644
--- a/spec/ruby/core/float/constants_spec.rb
+++ b/spec/ruby/core/float/constants_spec.rb
@@ -50,6 +50,6 @@ describe "Float constant" do
end
it "NAN is 'not a number'" do
- Float::NAN.nan?.should be_true
+ Float::NAN.nan?.should == true
end
end
diff --git a/spec/ruby/core/float/denominator_spec.rb b/spec/ruby/core/float/denominator_spec.rb
index 6f4fcfcf23..85beaa98cd 100644
--- a/spec/ruby/core/float/denominator_spec.rb
+++ b/spec/ruby/core/float/denominator_spec.rb
@@ -12,7 +12,7 @@ describe "Float#denominator" do
it "returns an Integer" do
@numbers.each do |number|
- number.denominator.should be_kind_of(Integer)
+ number.denominator.should.is_a?(Integer)
end
end
diff --git a/spec/ruby/core/float/divide_spec.rb b/spec/ruby/core/float/divide_spec.rb
index d8f71a6b98..68a2c550a7 100644
--- a/spec/ruby/core/float/divide_spec.rb
+++ b/spec/ruby/core/float/divide_spec.rb
@@ -16,24 +16,28 @@ describe "Float#/" do
end
it "returns +Infinity when dividing non-zero by zero of the same sign" do
- (1.0 / 0.0).should be_positive_infinity
- (-1.0 / -0.0).should be_positive_infinity
+ (1.0 / 0.0).should.infinite? == 1
+ (-1.0 / -0.0).should.infinite? == 1
end
it "returns -Infinity when dividing non-zero by zero of opposite sign" do
- (-1.0 / 0.0).should be_negative_infinity
- (1.0 / -0.0).should be_negative_infinity
+ (-1.0 / 0.0).should.infinite? == -1
+ (1.0 / -0.0).should.infinite? == -1
end
it "returns NaN when dividing zero by zero" do
- (0.0 / 0.0).should be_nan
- (-0.0 / 0.0).should be_nan
- (0.0 / -0.0).should be_nan
- (-0.0 / -0.0).should be_nan
+ (0.0 / 0.0).should.nan?
+ (-0.0 / 0.0).should.nan?
+ (0.0 / -0.0).should.nan?
+ (-0.0 / -0.0).should.nan?
end
it "raises a TypeError when given a non-Numeric" do
- -> { 13.0 / "10" }.should raise_error(TypeError)
- -> { 13.0 / :symbol }.should raise_error(TypeError)
+ -> { 13.0 / "10" }.should.raise(TypeError)
+ -> { 13.0 / :symbol }.should.raise(TypeError)
+ end
+
+ it "divides correctly by Rational numbers" do
+ (1.2345678901234567 / Rational(1, 10000000000000000000)).should == 1.2345678901234567e+19
end
end
diff --git a/spec/ruby/core/float/divmod_spec.rb b/spec/ruby/core/float/divmod_spec.rb
index ec55dd3681..7ed6cd3487 100644
--- a/spec/ruby/core/float/divmod_spec.rb
+++ b/spec/ruby/core/float/divmod_spec.rb
@@ -3,41 +3,41 @@ require_relative '../../spec_helper'
describe "Float#divmod" do
it "returns an [quotient, modulus] from dividing self by other" do
values = 3.14.divmod(2)
- values[0].should eql(1)
+ values[0].should.eql?(1)
values[1].should be_close(1.14, TOLERANCE)
values = 2.8284.divmod(3.1415)
- values[0].should eql(0)
+ values[0].should.eql?(0)
values[1].should be_close(2.8284, TOLERANCE)
values = -1.0.divmod(bignum_value)
- values[0].should eql(-1)
- values[1].should be_close(9223372036854775808.000, TOLERANCE)
+ values[0].should.eql?(-1)
+ values[1].should be_close(18446744073709551616.0, TOLERANCE)
values = -1.0.divmod(1)
- values[0].should eql(-1)
- values[1].should eql(0.0)
+ values[0].should.eql?(-1)
+ values[1].should.eql?(0.0)
end
# Behaviour established as correct in r23953
it "raises a FloatDomainError if self is NaN" do
- -> { nan_value.divmod(1) }.should raise_error(FloatDomainError)
+ -> { nan_value.divmod(1) }.should.raise(FloatDomainError)
end
# Behaviour established as correct in r23953
it "raises a FloatDomainError if other is NaN" do
- -> { 1.divmod(nan_value) }.should raise_error(FloatDomainError)
+ -> { 1.0.divmod(nan_value) }.should.raise(FloatDomainError)
end
# Behaviour established as correct in r23953
it "raises a FloatDomainError if self is Infinity" do
- -> { infinity_value.divmod(1) }.should raise_error(FloatDomainError)
+ -> { infinity_value.divmod(1) }.should.raise(FloatDomainError)
end
it "raises a ZeroDivisionError if other is zero" do
- -> { 1.0.divmod(0) }.should raise_error(ZeroDivisionError)
- -> { 1.0.divmod(0.0) }.should raise_error(ZeroDivisionError)
+ -> { 1.0.divmod(0) }.should.raise(ZeroDivisionError)
+ -> { 1.0.divmod(0.0) }.should.raise(ZeroDivisionError)
end
# redmine #5276"
it "returns the correct [quotient, modulus] even for large quotient" do
- 0.59.divmod(7.761021455128987e-11).first.should eql(7602092113)
+ 0.59.divmod(7.761021455128987e-11).first.should.eql?(7602092113)
end
end
diff --git a/spec/ruby/core/float/dup_spec.rb b/spec/ruby/core/float/dup_spec.rb
index 294da8e2bc..b474e21325 100644
--- a/spec/ruby/core/float/dup_spec.rb
+++ b/spec/ruby/core/float/dup_spec.rb
@@ -3,6 +3,6 @@ require_relative '../../spec_helper'
describe "Float#dup" do
it "returns self" do
float = 2.4
- float.dup.should equal(float)
+ float.dup.should.equal?(float)
end
end
diff --git a/spec/ruby/core/float/eql_spec.rb b/spec/ruby/core/float/eql_spec.rb
index 6b5f91db33..cf1ad8416f 100644
--- a/spec/ruby/core/float/eql_spec.rb
+++ b/spec/ruby/core/float/eql_spec.rb
@@ -2,15 +2,15 @@ require_relative '../../spec_helper'
describe "Float#eql?" do
it "returns true if other is a Float equal to self" do
- 0.0.eql?(0.0).should be_true
+ 0.0.eql?(0.0).should == true
end
it "returns false if other is a Float not equal to self" do
- 1.0.eql?(1.1).should be_false
+ 1.0.eql?(1.1).should == false
end
it "returns false if other is not a Float" do
- 1.0.eql?(1).should be_false
- 1.0.eql?(:one).should be_false
+ 1.0.eql?(1).should == false
+ 1.0.eql?(:one).should == false
end
end
diff --git a/spec/ruby/core/float/float_spec.rb b/spec/ruby/core/float/float_spec.rb
index 263ae82079..46b2eff372 100644
--- a/spec/ruby/core/float/float_spec.rb
+++ b/spec/ruby/core/float/float_spec.rb
@@ -8,12 +8,12 @@ describe "Float" do
it ".allocate raises a TypeError" do
-> do
Float.allocate
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it ".new is undefined" do
-> do
Float.new
- end.should raise_error(NoMethodError)
+ end.should.raise(NoMethodError)
end
end
diff --git a/spec/ruby/core/float/floor_spec.rb b/spec/ruby/core/float/floor_spec.rb
index 046216d36d..f77300eb04 100644
--- a/spec/ruby/core/float/floor_spec.rb
+++ b/spec/ruby/core/float/floor_spec.rb
@@ -1,21 +1,28 @@
require_relative '../../spec_helper'
+require_relative '../integer/shared/integer_floor_precision'
describe "Float#floor" do
+ context "with values equal to integers" do
+ it_behaves_like :integer_floor_precision, :Float
+ end
+
it "returns the largest Integer less than or equal to self" do
- -1.2.floor.should eql( -2)
- -1.0.floor.should eql( -1)
- 0.0.floor.should eql( 0 )
- 1.0.floor.should eql( 1 )
- 5.9.floor.should eql( 5 )
- -9223372036854775808.1.floor.should eql(-9223372036854775808)
- +9223372036854775808.1.floor.should eql(+9223372036854775808)
+ -1.2.floor.should.eql?( -2)
+ -1.0.floor.should.eql?( -1)
+ 0.0.floor.should.eql?( 0 )
+ 1.0.floor.should.eql?( 1 )
+ 5.9.floor.should.eql?( 5 )
+ -9223372036854775808.1.floor.should.eql?(-9223372036854775808)
+ +9223372036854775808.1.floor.should.eql?(+9223372036854775808)
end
it "returns the largest number less than or equal to self with an optionally given precision" do
- 2.1679.floor(0).should eql(2)
- 214.94.floor(-1).should eql(210)
- 7.0.floor(1).should eql(7.0)
- -1.234.floor(2).should eql(-1.24)
- 5.123812.floor(4).should eql(5.1238)
+ 2.1679.floor(0).should.eql?(2)
+ 214.94.floor(-1).should.eql?(210)
+ 7.0.floor(1).should.eql?(7.0)
+ 200.0.floor(-2).should.eql?(200)
+ -1.234.floor(2).should.eql?(-1.24)
+ 5.123812.floor(4).should.eql?(5.1238)
+ 10.00001.floor(5).should.eql?(10.00001)
end
end
diff --git a/spec/ruby/core/float/gt_spec.rb b/spec/ruby/core/float/gt_spec.rb
index 0d73f1c3df..5194796b46 100644
--- a/spec/ruby/core/float/gt_spec.rb
+++ b/spec/ruby/core/float/gt_spec.rb
@@ -11,7 +11,28 @@ describe "Float#>" do
end
it "raises an ArgumentError when given a non-Numeric" do
- -> { 5.0 > "4" }.should raise_error(ArgumentError)
- -> { 5.0 > mock('x') }.should raise_error(ArgumentError)
+ -> { 5.0 > "4" }.should.raise(ArgumentError)
+ -> { 5.0 > mock('x') }.should.raise(ArgumentError)
+ end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value > n).should == false
+ (n > nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value > n).should == true
+ (n > infinity_value).should == false
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value > n).should == false
+ (n > -infinity_value).should == true
+ }
end
end
diff --git a/spec/ruby/core/float/gte_spec.rb b/spec/ruby/core/float/gte_spec.rb
index 98ec60b70b..4a62725d53 100644
--- a/spec/ruby/core/float/gte_spec.rb
+++ b/spec/ruby/core/float/gte_spec.rb
@@ -11,7 +11,28 @@ describe "Float#>=" do
end
it "raises an ArgumentError when given a non-Numeric" do
- -> { 5.0 >= "4" }.should raise_error(ArgumentError)
- -> { 5.0 >= mock('x') }.should raise_error(ArgumentError)
+ -> { 5.0 >= "4" }.should.raise(ArgumentError)
+ -> { 5.0 >= mock('x') }.should.raise(ArgumentError)
+ end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value >= n).should == false
+ (n >= nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value >= n).should == true
+ (n >= infinity_value).should == false
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value >= n).should == false
+ (n >= -infinity_value).should == true
+ }
end
end
diff --git a/spec/ruby/core/float/inspect_spec.rb b/spec/ruby/core/float/inspect_spec.rb
new file mode 100644
index 0000000000..4be1927d84
--- /dev/null
+++ b/spec/ruby/core/float/inspect_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_s'
+
+describe "Float#inspect" do
+ it_behaves_like :float_to_s, :inspect
+end
diff --git a/spec/ruby/core/float/lt_spec.rb b/spec/ruby/core/float/lt_spec.rb
index c01b6e0e02..0f0e1752bd 100644
--- a/spec/ruby/core/float/lt_spec.rb
+++ b/spec/ruby/core/float/lt_spec.rb
@@ -11,7 +11,28 @@ describe "Float#<" do
end
it "raises an ArgumentError when given a non-Numeric" do
- -> { 5.0 < "4" }.should raise_error(ArgumentError)
- -> { 5.0 < mock('x') }.should raise_error(ArgumentError)
+ -> { 5.0 < "4" }.should.raise(ArgumentError)
+ -> { 5.0 < mock('x') }.should.raise(ArgumentError)
+ end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value < n).should == false
+ (n < nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value < n).should == false
+ (n < infinity_value).should == true
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value < n).should == true
+ (n < -infinity_value).should == false
+ }
end
end
diff --git a/spec/ruby/core/float/lte_spec.rb b/spec/ruby/core/float/lte_spec.rb
index 66f2ddc2c7..afb64ade09 100644
--- a/spec/ruby/core/float/lte_spec.rb
+++ b/spec/ruby/core/float/lte_spec.rb
@@ -12,7 +12,28 @@ describe "Float#<=" do
end
it "raises an ArgumentError when given a non-Numeric" do
- -> { 5.0 <= "4" }.should raise_error(ArgumentError)
- -> { 5.0 <= mock('x') }.should raise_error(ArgumentError)
+ -> { 5.0 <= "4" }.should.raise(ArgumentError)
+ -> { 5.0 <= mock('x') }.should.raise(ArgumentError)
+ end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value <= n).should == false
+ (n <= nan_value).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value <= n).should == false
+ (n <= infinity_value).should == true
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (-infinity_value <= n).should == true
+ (n <= -infinity_value).should == false
+ }
end
end
diff --git a/spec/ruby/core/float/magnitude_spec.rb b/spec/ruby/core/float/magnitude_spec.rb
index db577c15c5..7cdd8ef28a 100644
--- a/spec/ruby/core/float/magnitude_spec.rb
+++ b/spec/ruby/core/float/magnitude_spec.rb
@@ -1,3 +1,4 @@
+require_relative "../../spec_helper"
require_relative 'shared/abs'
describe "Float#magnitude" do
diff --git a/spec/ruby/core/float/minus_spec.rb b/spec/ruby/core/float/minus_spec.rb
index 5626cbdac2..a4281a397b 100644
--- a/spec/ruby/core/float/minus_spec.rb
+++ b/spec/ruby/core/float/minus_spec.rb
@@ -6,7 +6,7 @@ describe "Float#-" do
it "returns self minus other" do
(9_237_212.5280 - 5_280).should be_close(9231932.528, TOLERANCE)
- (2_560_496.1691 - bignum_value).should be_close(-9223372036852215808.000, TOLERANCE)
+ (2_560_496.1691 - bignum_value).should be_close(-18446744073706991616.0, TOLERANCE)
(5.5 - 5.5).should be_close(0.0,TOLERANCE)
end
end
diff --git a/spec/ruby/core/float/multiply_spec.rb b/spec/ruby/core/float/multiply_spec.rb
index eca0b52c4f..edaaba7e61 100644
--- a/spec/ruby/core/float/multiply_spec.rb
+++ b/spec/ruby/core/float/multiply_spec.rb
@@ -7,11 +7,11 @@ describe "Float#*" do
it "returns self multiplied by other" do
(4923.98221 * 2).should be_close(9847.96442, TOLERANCE)
(6712.5 * 0.25).should be_close(1678.125, TOLERANCE)
- (256.4096 * bignum_value).should be_close(2364961134621118431232.000, TOLERANCE)
+ (256.4096 * bignum_value).should be_close(4729922269242236862464.0, TOLERANCE)
end
it "raises a TypeError when given a non-Numeric" do
- -> { 13.0 * "10" }.should raise_error(TypeError)
- -> { 13.0 * :symbol }.should raise_error(TypeError)
+ -> { 13.0 * "10" }.should.raise(TypeError)
+ -> { 13.0 * :symbol }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/float/negative_spec.rb b/spec/ruby/core/float/negative_spec.rb
new file mode 100644
index 0000000000..484e636adb
--- /dev/null
+++ b/spec/ruby/core/float/negative_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+
+describe "Float#negative?" do
+ describe "on positive numbers" do
+ it "returns false" do
+ 0.1.negative?.should == false
+ end
+ end
+
+ describe "on zero" do
+ it "returns false" do
+ 0.0.negative?.should == false
+ end
+ end
+
+ describe "on negative zero" do
+ it "returns false" do
+ -0.0.negative?.should == false
+ end
+ end
+
+ describe "on negative numbers" do
+ it "returns true" do
+ -0.1.negative?.should == true
+ end
+ end
+
+ describe "on NaN" do
+ it "returns false" do
+ nan_value.negative?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/float/next_float_spec.rb b/spec/ruby/core/float/next_float_spec.rb
index 29e2d31146..59892be343 100644
--- a/spec/ruby/core/float/next_float_spec.rb
+++ b/spec/ruby/core/float/next_float_spec.rb
@@ -9,7 +9,7 @@ describe "Float#next_float" do
barely_positive.should < barely_positive.next_float
midpoint = barely_positive / 2
- [0.0, barely_positive].should include midpoint
+ [0.0, barely_positive].should.include? midpoint
end
it "returns Float::INFINITY for Float::INFINITY" do
diff --git a/spec/ruby/core/float/numerator_spec.rb b/spec/ruby/core/float/numerator_spec.rb
index 7832e8f056..53b32fdd3d 100644
--- a/spec/ruby/core/float/numerator_spec.rb
+++ b/spec/ruby/core/float/numerator_spec.rb
@@ -15,7 +15,7 @@ describe "Float#numerator" do
it "converts self to a Rational object then returns its numerator" do
@numbers.each do |number|
- number.infinite?.should be_nil
+ number.infinite?.should == nil
number.numerator.should == Rational(number).numerator
end
end
@@ -25,7 +25,7 @@ describe "Float#numerator" do
end
it "returns NaN for NaN" do
- nan_value.numerator.nan?.should be_true
+ nan_value.numerator.nan?.should == true
end
it "returns Infinity for Infinity" do
diff --git a/spec/ruby/core/float/plus_spec.rb b/spec/ruby/core/float/plus_spec.rb
index 06b136a06b..e3e19d7f39 100644
--- a/spec/ruby/core/float/plus_spec.rb
+++ b/spec/ruby/core/float/plus_spec.rb
@@ -6,7 +6,7 @@ describe "Float#+" do
it "returns self plus other" do
(491.213 + 2).should be_close(493.213, TOLERANCE)
- (9.99 + bignum_value).should be_close(9223372036854775808.000, TOLERANCE)
+ (9.99 + bignum_value).should be_close(18446744073709551616.0, TOLERANCE)
(1001.99 + 5.219).should be_close(1007.209, TOLERANCE)
end
end
diff --git a/spec/ruby/core/float/positive_spec.rb b/spec/ruby/core/float/positive_spec.rb
new file mode 100644
index 0000000000..aa87c03151
--- /dev/null
+++ b/spec/ruby/core/float/positive_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+
+describe "Float#positive?" do
+ describe "on positive numbers" do
+ it "returns true" do
+ 0.1.positive?.should == true
+ end
+ end
+
+ describe "on zero" do
+ it "returns false" do
+ 0.0.positive?.should == false
+ end
+ end
+
+ describe "on negative zero" do
+ it "returns false" do
+ -0.0.positive?.should == false
+ end
+ end
+
+ describe "on negative numbers" do
+ it "returns false" do
+ -0.1.positive?.should == false
+ end
+ end
+
+ describe "on NaN" do
+ it "returns false" do
+ nan_value.positive?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/float/prev_float_spec.rb b/spec/ruby/core/float/prev_float_spec.rb
index 5e50269da7..2d1f136254 100644
--- a/spec/ruby/core/float/prev_float_spec.rb
+++ b/spec/ruby/core/float/prev_float_spec.rb
@@ -9,7 +9,7 @@ describe "Float#prev_float" do
barely_negative.should > barely_negative.prev_float
midpoint = barely_negative / 2
- [0.0, barely_negative].should include midpoint
+ [0.0, barely_negative].should.include? midpoint
end
it "returns -Float::INFINITY for -Float::INFINITY" do
diff --git a/spec/ruby/core/float/rationalize_spec.rb b/spec/ruby/core/float/rationalize_spec.rb
index 0c5bef7ac4..fa8fb5c841 100644
--- a/spec/ruby/core/float/rationalize_spec.rb
+++ b/spec/ruby/core/float/rationalize_spec.rb
@@ -29,15 +29,15 @@ describe "Float#rationalize" do
end
it "raises a FloatDomainError for Infinity" do
- -> {infinity_value.rationalize}.should raise_error(FloatDomainError)
+ -> {infinity_value.rationalize}.should.raise(FloatDomainError)
end
it "raises a FloatDomainError for NaN" do
- -> { nan_value.rationalize }.should raise_error(FloatDomainError)
+ -> { nan_value.rationalize }.should.raise(FloatDomainError)
end
it "raises ArgumentError when passed more than one argument" do
- -> { 0.3.rationalize(0.1, 0.1) }.should raise_error(ArgumentError)
- -> { 0.3.rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError)
+ -> { 0.3.rationalize(0.1, 0.1) }.should.raise(ArgumentError)
+ -> { 0.3.rationalize(0.1, 0.1, 2) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/float/round_spec.rb b/spec/ruby/core/float/round_spec.rb
index e143682362..63c1d5689c 100644
--- a/spec/ruby/core/float/round_spec.rb
+++ b/spec/ruby/core/float/round_spec.rb
@@ -16,51 +16,57 @@ describe "Float#round" do
end
it "raises FloatDomainError for exceptional values" do
- -> { (+infinity_value).round }.should raise_error(FloatDomainError)
- -> { (-infinity_value).round }.should raise_error(FloatDomainError)
- -> { nan_value.round }.should raise_error(FloatDomainError)
+ -> { (+infinity_value).round }.should.raise(FloatDomainError)
+ -> { (-infinity_value).round }.should.raise(FloatDomainError)
+ -> { nan_value.round }.should.raise(FloatDomainError)
end
it "rounds self to an optionally given precision" do
- 5.5.round(0).should eql(6)
- 5.7.round(1).should eql(5.7)
+ 5.5.round(0).should.eql?(6)
+ 5.7.round(1).should.eql?(5.7)
1.2345678.round(2).should == 1.23
- 123456.78.round(-2).should eql(123500) # rounded up
- -123456.78.round(-2).should eql(-123500)
+ 123456.78.round(-2).should.eql?(123500) # rounded up
+ -123456.78.round(-2).should.eql?(-123500)
12.345678.round(3.999).should == 12.346
end
+ it "correctly rounds exact floats with a numerous digits in a fraction part" do
+ 0.8241000000000004.round(10).should == 0.8241
+ 0.8241000000000002.round(10).should == 0.8241
+ end
+
it "returns zero when passed a negative argument with magnitude greater than magnitude of the whole number portion of the Float" do
- 0.8346268.round(-1).should eql(0)
+ 0.8346268.round(-1).should.eql?(0)
end
it "raises a TypeError when its argument can not be converted to an Integer" do
- -> { 1.0.round("4") }.should raise_error(TypeError)
- -> { 1.0.round(nil) }.should raise_error(TypeError)
+ -> { 1.0.round("4") }.should.raise(TypeError)
+ -> { 1.0.round(nil) }.should.raise(TypeError)
end
it "raises FloatDomainError for exceptional values when passed a non-positive precision" do
- -> { Float::INFINITY.round( 0) }.should raise_error(FloatDomainError)
- -> { Float::INFINITY.round(-2) }.should raise_error(FloatDomainError)
- -> { (-Float::INFINITY).round( 0) }.should raise_error(FloatDomainError)
- -> { (-Float::INFINITY).round(-2) }.should raise_error(FloatDomainError)
+ -> { Float::INFINITY.round( 0) }.should.raise(FloatDomainError)
+ -> { Float::INFINITY.round(-2) }.should.raise(FloatDomainError)
+ -> { (-Float::INFINITY).round( 0) }.should.raise(FloatDomainError)
+ -> { (-Float::INFINITY).round(-2) }.should.raise(FloatDomainError)
end
it "raises RangeError for NAN when passed a non-positive precision" do
- -> { Float::NAN.round(0) }.should raise_error(RangeError)
- -> { Float::NAN.round(-2) }.should raise_error(RangeError)
+ -> { Float::NAN.round(0) }.should.raise(RangeError)
+ -> { Float::NAN.round(-2) }.should.raise(RangeError)
end
it "returns self for exceptional values when passed a non-negative precision" do
Float::INFINITY.round(2).should == Float::INFINITY
(-Float::INFINITY).round(2).should == -Float::INFINITY
- Float::NAN.round(2).should be_nan
+ Float::NAN.round(2).should.nan?
end
# redmine:5227
it "works for corner cases" do
- 42.0.round(308).should eql(42.0)
- 1.0e307.round(2).should eql(1.0e307)
+ 42.0.round(308).should.eql?(42.0)
+ 1.0e307.round(2).should.eql?(1.0e307)
+ 120.0.round(-1).should.eql?(120)
end
# redmine:5271
@@ -68,50 +74,135 @@ describe "Float#round" do
0.42.round(2.0**30).should == 0.42
end
+ it "returns rounded values for not so big argument" do
+ 0.42.round(2.0**23).should == 0.42
+ end
+
it "returns big values rounded to nearest" do
- +2.5e20.round(-20).should eql( +3 * 10 ** 20 )
- -2.5e20.round(-20).should eql( -3 * 10 ** 20 )
+ +2.5e20.round(-20).should.eql?( +3 * 10 ** 20 )
+ -2.5e20.round(-20).should.eql?( -3 * 10 ** 20 )
end
# redmine #5272
it "returns rounded values for big values" do
- +2.4e20.round(-20).should eql( +2 * 10 ** 20 )
- -2.4e20.round(-20).should eql( -2 * 10 ** 20 )
- +2.5e200.round(-200).should eql( +3 * 10 ** 200 )
- +2.4e200.round(-200).should eql( +2 * 10 ** 200 )
- -2.5e200.round(-200).should eql( -3 * 10 ** 200 )
- -2.4e200.round(-200).should eql( -2 * 10 ** 200 )
+ +2.4e20.round(-20).should.eql?( +2 * 10 ** 20 )
+ -2.4e20.round(-20).should.eql?( -2 * 10 ** 20 )
+ +2.5e200.round(-200).should.eql?( +3 * 10 ** 200 )
+ +2.4e200.round(-200).should.eql?( +2 * 10 ** 200 )
+ -2.5e200.round(-200).should.eql?( -3 * 10 ** 200 )
+ -2.4e200.round(-200).should.eql?( -2 * 10 ** 200 )
end
it "returns different rounded values depending on the half option" do
- 2.5.round(half: nil).should eql(3)
- 2.5.round(half: :up).should eql(3)
- 2.5.round(half: :down).should eql(2)
- 2.5.round(half: :even).should eql(2)
- 3.5.round(half: nil).should eql(4)
- 3.5.round(half: :up).should eql(4)
- 3.5.round(half: :down).should eql(3)
- 3.5.round(half: :even).should eql(4)
- (-2.5).round(half: nil).should eql(-3)
- (-2.5).round(half: :up).should eql(-3)
- (-2.5).round(half: :down).should eql(-2)
- (-2.5).round(half: :even).should eql(-2)
+ 2.5.round(half: nil).should.eql?(3)
+ 2.5.round(half: :up).should.eql?(3)
+ 2.5.round(half: :down).should.eql?(2)
+ 2.5.round(half: :even).should.eql?(2)
+ 3.5.round(half: nil).should.eql?(4)
+ 3.5.round(half: :up).should.eql?(4)
+ 3.5.round(half: :down).should.eql?(3)
+ 3.5.round(half: :even).should.eql?(4)
+ (-2.5).round(half: nil).should.eql?(-3)
+ (-2.5).round(half: :up).should.eql?(-3)
+ (-2.5).round(half: :down).should.eql?(-2)
+ (-2.5).round(half: :even).should.eql?(-2)
end
it "rounds self to an optionally given precision with a half option" do
- 5.55.round(1, half: nil).should eql(5.6)
- 5.55.round(1, half: :up).should eql(5.6)
- 5.55.round(1, half: :down).should eql(5.5)
- 5.55.round(1, half: :even).should eql(5.6)
+ 5.55.round(1, half: nil).should.eql?(5.6)
+ 5.55.round(1, half: :up).should.eql?(5.6)
+ 5.55.round(1, half: :down).should.eql?(5.5)
+ 5.55.round(1, half: :even).should.eql?(5.6)
+ -5.55.round(1, half: nil).should.eql?(-5.6)
+ -5.55.round(1, half: :up).should.eql?(-5.6)
+ -5.55.round(1, half: :down).should.eql?(-5.5)
+ -5.55.round(1, half: :even).should.eql?(-5.6)
+ end
+
+ it "preserves cases where neighbouring floating pointer number increase the decimal places" do
+ 4.8100000000000005.round(5, half: nil).should.eql?(4.81)
+ 4.8100000000000005.round(5, half: :up).should.eql?(4.81)
+ 4.8100000000000005.round(5, half: :down).should.eql?(4.81)
+ 4.8100000000000005.round(5, half: :even).should.eql?(4.81)
+ -4.8100000000000005.round(5, half: nil).should.eql?(-4.81)
+ -4.8100000000000005.round(5, half: :up).should.eql?(-4.81)
+ -4.8100000000000005.round(5, half: :down).should.eql?(-4.81)
+ -4.8100000000000005.round(5, half: :even).should.eql?(-4.81)
+ 4.81.round(5, half: nil).should.eql?(4.81)
+ 4.81.round(5, half: :up).should.eql?(4.81)
+ 4.81.round(5, half: :down).should.eql?(4.81)
+ 4.81.round(5, half: :even).should.eql?(4.81)
+ -4.81.round(5, half: nil).should.eql?(-4.81)
+ -4.81.round(5, half: :up).should.eql?(-4.81)
+ -4.81.round(5, half: :down).should.eql?(-4.81)
+ -4.81.round(5, half: :even).should.eql?(-4.81)
+ 4.809999999999999.round(5, half: nil).should.eql?(4.81)
+ 4.809999999999999.round(5, half: :up).should.eql?(4.81)
+ 4.809999999999999.round(5, half: :down).should.eql?(4.81)
+ 4.809999999999999.round(5, half: :even).should.eql?(4.81)
+ -4.809999999999999.round(5, half: nil).should.eql?(-4.81)
+ -4.809999999999999.round(5, half: :up).should.eql?(-4.81)
+ -4.809999999999999.round(5, half: :down).should.eql?(-4.81)
+ -4.809999999999999.round(5, half: :even).should.eql?(-4.81)
+ end
+
+ # These numbers are neighbouring floating point numbers round a
+ # precise value. They test that the rounding modes work correctly
+ # round that value and precision is not lost which might cause
+ # incorrect results.
+ it "does not lose precision during the rounding process" do
+ 767573.1875850001.round(5, half: nil).should.eql?(767573.18759)
+ 767573.1875850001.round(5, half: :up).should.eql?(767573.18759)
+ 767573.1875850001.round(5, half: :down).should.eql?(767573.18759)
+ 767573.1875850001.round(5, half: :even).should.eql?(767573.18759)
+ -767573.1875850001.round(5, half: nil).should.eql?(-767573.18759)
+ -767573.1875850001.round(5, half: :up).should.eql?(-767573.18759)
+ -767573.1875850001.round(5, half: :down).should.eql?(-767573.18759)
+ -767573.1875850001.round(5, half: :even).should.eql?(-767573.18759)
+ 767573.187585.round(5, half: nil).should.eql?(767573.18759)
+ 767573.187585.round(5, half: :up).should.eql?(767573.18759)
+ 767573.187585.round(5, half: :down).should.eql?(767573.18758)
+ 767573.187585.round(5, half: :even).should.eql?(767573.18758)
+ -767573.187585.round(5, half: nil).should.eql?(-767573.18759)
+ -767573.187585.round(5, half: :up).should.eql?(-767573.18759)
+ -767573.187585.round(5, half: :down).should.eql?(-767573.18758)
+ -767573.187585.round(5, half: :even).should.eql?(-767573.18758)
+ 767573.1875849998.round(5, half: nil).should.eql?(767573.18758)
+ 767573.1875849998.round(5, half: :up).should.eql?(767573.18758)
+ 767573.1875849998.round(5, half: :down).should.eql?(767573.18758)
+ 767573.1875849998.round(5, half: :even).should.eql?(767573.18758)
+ -767573.1875849998.round(5, half: nil).should.eql?(-767573.18758)
+ -767573.1875849998.round(5, half: :up).should.eql?(-767573.18758)
+ -767573.1875849998.round(5, half: :down).should.eql?(-767573.18758)
+ -767573.1875849998.round(5, half: :even).should.eql?(-767573.18758)
end
it "raises FloatDomainError for exceptional values with a half option" do
- -> { (+infinity_value).round(half: :up) }.should raise_error(FloatDomainError)
- -> { (-infinity_value).round(half: :down) }.should raise_error(FloatDomainError)
- -> { nan_value.round(half: :even) }.should raise_error(FloatDomainError)
+ -> { (+infinity_value).round(half: :up) }.should.raise(FloatDomainError)
+ -> { (-infinity_value).round(half: :down) }.should.raise(FloatDomainError)
+ -> { nan_value.round(half: :even) }.should.raise(FloatDomainError)
end
it "raise for a non-existent round mode" do
- -> { 14.2.round(half: :nonsense) }.should raise_error(ArgumentError, "invalid rounding mode: nonsense")
+ -> { 14.2.round(half: :nonsense) }.should.raise(ArgumentError, "invalid rounding mode: nonsense")
+ end
+
+ describe "when 0.0 is given" do
+ it "returns self for positive ndigits" do
+ (0.0).round(5).inspect.should == "0.0"
+ (-0.0).round(1).inspect.should == "-0.0"
+ end
+
+ it "returns 0 for 0 or undefined ndigits" do
+ (0.0).round.should == 0
+ (-0.0).round(0).should == 0
+ (0.0).round(half: :up).should == 0
+ end
+
+ it "returns 0 for negative ndigits" do
+ (0.0).round(-1).should == 0
+ (-0.0).round(-1).should == 0
+ (0.0).round(-1, half: :up).should == 0
+ end
end
end
diff --git a/spec/ruby/core/float/shared/abs.rb b/spec/ruby/core/float/shared/abs.rb
index 607983322d..ab21480e24 100644
--- a/spec/ruby/core/float/shared/abs.rb
+++ b/spec/ruby/core/float/shared/abs.rb
@@ -16,6 +16,6 @@ describe :float_abs, shared: true do
end
it "returns NaN if NaN" do
- nan_value.send(@method).nan?.should be_true
+ nan_value.send(@method).nan?.should == true
end
end
diff --git a/spec/ruby/core/float/shared/arg.rb b/spec/ruby/core/float/shared/arg.rb
index 136cf19ec8..de0024313d 100644
--- a/spec/ruby/core/float/shared/arg.rb
+++ b/spec/ruby/core/float/shared/arg.rb
@@ -1,12 +1,12 @@
describe :float_arg, shared: true do
it "returns NaN if NaN" do
f = nan_value
- f.send(@method).nan?.should be_true
+ f.send(@method).nan?.should == true
end
it "returns self if NaN" do
f = nan_value
- f.send(@method).should equal(f)
+ f.send(@method).should.equal?(f)
end
it "returns 0 if positive" do
diff --git a/spec/ruby/core/float/shared/arithmetic_exception_in_coerce.rb b/spec/ruby/core/float/shared/arithmetic_exception_in_coerce.rb
index eec92d858f..bd3bf9019f 100644
--- a/spec/ruby/core/float/shared/arithmetic_exception_in_coerce.rb
+++ b/spec/ruby/core/float/shared/arithmetic_exception_in_coerce.rb
@@ -6,6 +6,6 @@ describe :float_arithmetic_exception_in_coerce, shared: true do
b.should_receive(:coerce).and_raise(FloatSpecs::CoerceError)
# e.g. 1.0 > b
- -> { 1.0.send(@method, b) }.should raise_error(FloatSpecs::CoerceError)
+ -> { 1.0.send(@method, b) }.should.raise(FloatSpecs::CoerceError)
end
end
diff --git a/spec/ruby/core/float/shared/comparison_exception_in_coerce.rb b/spec/ruby/core/float/shared/comparison_exception_in_coerce.rb
index 3e2c1e28dd..eec5d8daf9 100644
--- a/spec/ruby/core/float/shared/comparison_exception_in_coerce.rb
+++ b/spec/ruby/core/float/shared/comparison_exception_in_coerce.rb
@@ -6,6 +6,6 @@ describe :float_comparison_exception_in_coerce, shared: true do
b.should_receive(:coerce).and_raise(FloatSpecs::CoerceError)
# e.g. 1.0 > b
- -> { 1.0.send(@method, b) }.should raise_error(FloatSpecs::CoerceError)
+ -> { 1.0.send(@method, b) }.should.raise(FloatSpecs::CoerceError)
end
end
diff --git a/spec/ruby/core/float/shared/equal.rb b/spec/ruby/core/float/shared/equal.rb
index 668aa069b5..4d524e1cf2 100644
--- a/spec/ruby/core/float/shared/equal.rb
+++ b/spec/ruby/core/float/shared/equal.rb
@@ -14,4 +14,25 @@ describe :float_equal, shared: true do
1.0.send(@method, x).should == false
2.0.send(@method, x).should == true
end
+
+ it "returns false if one side is NaN" do
+ [1.0, 42, bignum_value].each { |n|
+ (nan_value.send(@method, n)).should == false
+ (n.send(@method, nan_value)).should == false
+ }
+ end
+
+ it "handles positive infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ (infinity_value.send(@method, n)).should == false
+ (n.send(@method, infinity_value)).should == false
+ }
+ end
+
+ it "handles negative infinity" do
+ [1.0, 42, bignum_value].each { |n|
+ ((-infinity_value).send(@method, n)).should == false
+ (n.send(@method, -infinity_value)).should == false
+ }
+ end
end
diff --git a/spec/ruby/core/float/shared/modulo.rb b/spec/ruby/core/float/shared/modulo.rb
index 6700bd4f4e..1efee1476d 100644
--- a/spec/ruby/core/float/shared/modulo.rb
+++ b/spec/ruby/core/float/shared/modulo.rb
@@ -16,13 +16,13 @@ describe :float_modulo, shared: true do
end
it "returns NaN when called on NaN or Infinities" do
- Float::NAN.send(@method, 42).should be_nan
- Float::INFINITY.send(@method, 42).should be_nan
- (-Float::INFINITY).send(@method, 42).should be_nan
+ Float::NAN.send(@method, 42).should.nan?
+ Float::INFINITY.send(@method, 42).should.nan?
+ (-Float::INFINITY).send(@method, 42).should.nan?
end
it "returns NaN when modulus is NaN" do
- 4.2.send(@method, Float::NAN).should be_nan
+ 4.2.send(@method, Float::NAN).should.nan?
end
it "returns -0.0 when called on -0.0 with a non zero modulus" do
@@ -42,7 +42,7 @@ describe :float_modulo, shared: true do
end
it "raises a ZeroDivisionError if other is zero" do
- -> { 1.0.send(@method, 0) }.should raise_error(ZeroDivisionError)
- -> { 1.0.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
+ -> { 1.0.send(@method, 0) }.should.raise(ZeroDivisionError)
+ -> { 1.0.send(@method, 0.0) }.should.raise(ZeroDivisionError)
end
end
diff --git a/spec/ruby/core/float/shared/quo.rb b/spec/ruby/core/float/shared/quo.rb
index 3487824f70..930187aaf7 100644
--- a/spec/ruby/core/float/shared/quo.rb
+++ b/spec/ruby/core/float/shared/quo.rb
@@ -1,9 +1,9 @@
describe :float_quo, shared: true do
- it "performs floating-point division between self and a Fixnum" do
+ it "performs floating-point division between self and an Integer" do
8.9.send(@method, 7).should == 1.2714285714285716
end
- it "performs floating-point division between self and a Bignum" do
+ it "performs floating-point division between self and an Integer" do
8.9.send(@method, 9999999999999**9).should == 8.900000000008011e-117
end
@@ -12,8 +12,8 @@ describe :float_quo, shared: true do
end
it "returns NaN when the argument is NaN" do
- -1819.999999.send(@method, nan_value).nan?.should be_true
- 11109.1981271.send(@method, nan_value).nan?.should be_true
+ -1819.999999.send(@method, nan_value).nan?.should == true
+ 11109.1981271.send(@method, nan_value).nan?.should == true
end
it "returns Infinity when the argument is 0.0" do
@@ -50,10 +50,10 @@ describe :float_quo, shared: true do
end
it "raises a TypeError when argument isn't numeric" do
- -> { 27292.2.send(@method, mock('non-numeric')) }.should raise_error(TypeError)
+ -> { 27292.2.send(@method, mock('non-numeric')) }.should.raise(TypeError)
end
it "raises an ArgumentError when passed multiple arguments" do
- -> { 272.221.send(@method, 6,0.2) }.should raise_error(ArgumentError)
+ -> { 272.221.send(@method, 6,0.2) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/float/shared/to_i.rb b/spec/ruby/core/float/shared/to_i.rb
index 960295f095..1e6f941467 100644
--- a/spec/ruby/core/float/shared/to_i.rb
+++ b/spec/ruby/core/float/shared/to_i.rb
@@ -1,10 +1,14 @@
describe :float_to_i, shared: true do
it "returns self truncated to an Integer" do
- 899.2.send(@method).should eql(899)
- -1.122256e-45.send(@method).should eql(0)
- 5_213_451.9201.send(@method).should eql(5213451)
- 1.233450999123389e+12.send(@method).should eql(1233450999123)
- -9223372036854775808.1.send(@method).should eql(-9223372036854775808)
- 9223372036854775808.1.send(@method).should eql(9223372036854775808)
+ 899.2.send(@method).should.eql?(899)
+ -1.122256e-45.send(@method).should.eql?(0)
+ 5_213_451.9201.send(@method).should.eql?(5213451)
+ 1.233450999123389e+12.send(@method).should.eql?(1233450999123)
+ -9223372036854775808.1.send(@method).should.eql?(-9223372036854775808)
+ 9223372036854775808.1.send(@method).should.eql?(9223372036854775808)
+ end
+
+ it "raises a FloatDomainError for NaN" do
+ -> { nan_value.send(@method) }.should.raise(FloatDomainError)
end
end
diff --git a/spec/ruby/core/float/shared/to_s.rb b/spec/ruby/core/float/shared/to_s.rb
new file mode 100644
index 0000000000..81ffdd9e81
--- /dev/null
+++ b/spec/ruby/core/float/shared/to_s.rb
@@ -0,0 +1,308 @@
+describe :float_to_s, shared: true do
+ it "returns 'NaN' for NaN" do
+ nan_value().send(@method).should == 'NaN'
+ end
+
+ it "returns 'Infinity' for positive infinity" do
+ infinity_value().send(@method).should == 'Infinity'
+ end
+
+ it "returns '-Infinity' for negative infinity" do
+ (-infinity_value()).send(@method).should == '-Infinity'
+ end
+
+ it "returns '0.0' for 0.0" do
+ 0.0.send(@method).should == "0.0"
+ end
+
+ platform_is_not :openbsd do
+ it "emits '-' for -0.0" do
+ -0.0.send(@method).should == "-0.0"
+ end
+ end
+
+ it "emits a '-' for negative values" do
+ -3.14.send(@method).should == "-3.14"
+ end
+
+ it "emits a trailing '.0' for a whole number" do
+ 50.0.send(@method).should == "50.0"
+ end
+
+ it "emits a trailing '.0' for the mantissa in e format" do
+ 1.0e20.send(@method).should == "1.0e+20"
+ end
+
+ it "uses non-e format for a positive value with fractional part having 5 significant figures" do
+ 0.0001.send(@method).should == "0.0001"
+ end
+
+ it "uses non-e format for a negative value with fractional part having 5 significant figures" do
+ -0.0001.send(@method).should == "-0.0001"
+ end
+
+ it "uses e format for a positive value with fractional part having 6 significant figures" do
+ 0.00001.send(@method).should == "1.0e-05"
+ end
+
+ it "uses e format for a negative value with fractional part having 6 significant figures" do
+ -0.00001.send(@method).should == "-1.0e-05"
+ end
+
+ it "uses non-e format for a positive value with whole part having 15 significant figures" do
+ 10000000000000.0.send(@method).should == "10000000000000.0"
+ end
+
+ it "uses non-e format for a negative value with whole part having 15 significant figures" do
+ -10000000000000.0.send(@method).should == "-10000000000000.0"
+ end
+
+ it "uses non-e format for a positive value with whole part having 16 significant figures" do
+ 100000000000000.0.send(@method).should == "100000000000000.0"
+ end
+
+ it "uses non-e format for a negative value with whole part having 16 significant figures" do
+ -100000000000000.0.send(@method).should == "-100000000000000.0"
+ end
+
+ it "uses e format for a positive value with whole part having 18 significant figures" do
+ 10000000000000000.0.send(@method).should == "1.0e+16"
+ end
+
+ it "uses e format for a negative value with whole part having 18 significant figures" do
+ -10000000000000000.0.send(@method).should == "-1.0e+16"
+ end
+
+ it "uses e format for a positive value with whole part having 17 significant figures" do
+ 1000000000000000.0.send(@method).should == "1.0e+15"
+ end
+
+ it "uses e format for a negative value with whole part having 17 significant figures" do
+ -1000000000000000.0.send(@method).should == "-1.0e+15"
+ end
+
+ # #3273
+ it "outputs the minimal, unique form necessary to recreate the value" do
+ value = 0.21611564636388508
+ string = "0.21611564636388508"
+
+ value.send(@method).should == string
+ string.to_f.should == value
+ end
+
+ it "outputs the minimal, unique form to represent the value" do
+ 0.56.send(@method).should == "0.56"
+ end
+
+ describe "matches" do
+ it "random examples in all ranges" do
+ # 50.times do
+ # bytes = (0...8).map { rand(256) }
+ # string = bytes.pack('C8')
+ # float = string.unpack('D').first
+ # puts "#{'%.20g' % float}.send(@method).should == #{float.send(@method).inspect}"
+ # end
+
+ 2.5540217314354050325e+163.send(@method).should == "2.554021731435405e+163"
+ 2.5492588360356597544e-172.send(@method).should == "2.5492588360356598e-172"
+ 1.742770260934704852e-82.send(@method).should == "1.7427702609347049e-82"
+ 6.2108093676180883209e-104.send(@method).should == "6.210809367618088e-104"
+ -3.3448803488331067402e-143.send(@method).should == "-3.3448803488331067e-143"
+ -2.2740074343500832557e-168.send(@method).should == "-2.2740074343500833e-168"
+ 7.0587971678048535732e+191.send(@method).should == "7.058797167804854e+191"
+ -284438.88327586348169.send(@method).should == "-284438.8832758635"
+ 3.953272468476091301e+105.send(@method).should == "3.9532724684760913e+105"
+ -3.6361359552959847853e+100.send(@method).should == "-3.636135955295985e+100"
+ -1.3222325865575206185e-31.send(@method).should == "-1.3222325865575206e-31"
+ 1.1440138916932761366e+130.send(@method).should == "1.1440138916932761e+130"
+ 4.8750891560387561157e-286.send(@method).should == "4.875089156038756e-286"
+ 5.6101113356591453525e-257.send(@method).should == "5.610111335659145e-257"
+ -3.829644279545809575e-100.send(@method).should == "-3.8296442795458096e-100"
+ 1.5342839401396406117e-194.send(@method).should == "1.5342839401396406e-194"
+ 2.2284972755169921402e-144.send(@method).should == "2.228497275516992e-144"
+ 2.1825655917065601737e-61.send(@method).should == "2.1825655917065602e-61"
+ -2.6672271363524338322e-62.send(@method).should == "-2.667227136352434e-62"
+ -1.9257995160119059415e+21.send(@method).should == "-1.925799516011906e+21"
+ -8.9096732962887121718e-198.send(@method).should == "-8.909673296288712e-198"
+ 2.0202075376548644959e-90.send(@method).should == "2.0202075376548645e-90"
+ -7.7341602581786258961e-266.send(@method).should == "-7.734160258178626e-266"
+ 3.5134482598733635046e+98.send(@method).should == "3.5134482598733635e+98"
+ -2.124411722371029134e+154.send(@method).should == "-2.124411722371029e+154"
+ -4.573908787355718687e+110.send(@method).should == "-4.573908787355719e+110"
+ -1.9344425934170969879e-232.send(@method).should == "-1.934442593417097e-232"
+ -1.3274227399979271095e+171.send(@method).should == "-1.3274227399979271e+171"
+ 9.3495270482104442383e-283.send(@method).should == "9.349527048210444e-283"
+ -4.2046059371986483233e+307.send(@method).should == "-4.2046059371986483e+307"
+ 3.6133547278583543004e-117.send(@method).should == "3.613354727858354e-117"
+ 4.9247416523566613499e-08.send(@method).should == "4.9247416523566613e-08"
+ 1.6936145488250064007e-71.send(@method).should == "1.6936145488250064e-71"
+ 2.4455483206829433098e+96.send(@method).should == "2.4455483206829433e+96"
+ 7.9797449851436455384e+124.send(@method).should == "7.979744985143646e+124"
+ -1.3873689634457876774e-129.send(@method).should == "-1.3873689634457877e-129"
+ 3.9761102037533483075e+284.send(@method).should == "3.976110203753348e+284"
+ -4.2819791952139402486e-303.send(@method).should == "-4.28197919521394e-303"
+ -5.7981017546689831298e-116.send(@method).should == "-5.798101754668983e-116"
+ -3.953266497860534199e-28.send(@method).should == "-3.953266497860534e-28"
+ -2.0659852720290440959e-243.send(@method).should == "-2.065985272029044e-243"
+ 8.9670488995878688018e-05.send(@method).should == "8.967048899587869e-05"
+ -1.2317943708113061768e-98.send(@method).should == "-1.2317943708113062e-98"
+ -3.8930768307633080463e+248.send(@method).should == "-3.893076830763308e+248"
+ 6.5854032671803925627e-239.send(@method).should == "6.5854032671803926e-239"
+ 4.6257022188980878952e+177.send(@method).should == "4.625702218898088e+177"
+ -1.9397155125507235603e-187.send(@method).should == "-1.9397155125507236e-187"
+ 8.5752156951245705056e+117.send(@method).should == "8.57521569512457e+117"
+ -2.4784875958162501671e-132.send(@method).should == "-2.4784875958162502e-132"
+ -4.4125691841230058457e-203.send(@method).should == "-4.412569184123006e-203"
+ end
+
+ it "random examples in human ranges" do
+ # 50.times do
+ # formatted = ''
+ # rand(1..3).times do
+ # formatted << rand(10).to_s
+ # end
+ # formatted << '.'
+ # rand(1..9).times do
+ # formatted << rand(10).to_s
+ # end
+ # float = formatted.to_f
+ # puts "#{'%.20f' % float}.send(@method).should == #{float.send(@method).inspect}"
+ # end
+
+ 5.17869899999999994122.send(@method).should == "5.178699"
+ 905.62695729999995819526.send(@method).should == "905.6269573"
+ 62.75999999999999801048.send(@method).should == "62.76"
+ 6.93856795800000014651.send(@method).should == "6.938567958"
+ 4.95999999999999996447.send(@method).should == "4.96"
+ 32.77993899999999882766.send(@method).should == "32.779939"
+ 544.12756779999995160324.send(@method).should == "544.1275678"
+ 66.25801119999999855281.send(@method).should == "66.2580112"
+ 7.90000000000000035527.send(@method).should == "7.9"
+ 5.93100000000000004974.send(@method).should == "5.931"
+ 5.21229313600000043749.send(@method).should == "5.212293136"
+ 503.44173809000000119340.send(@method).should == "503.44173809"
+ 79.26000000000000511591.send(@method).should == "79.26"
+ 8.51524999999999998579.send(@method).should == "8.51525"
+ 174.00000000000000000000.send(@method).should == "174.0"
+ 50.39580000000000126192.send(@method).should == "50.3958"
+ 35.28999999999999914735.send(@method).should == "35.29"
+ 5.43136675399999990788.send(@method).should == "5.431366754"
+ 654.07680000000004838512.send(@method).should == "654.0768"
+ 6.07423700000000010846.send(@method).should == "6.074237"
+ 102.25779799999999397642.send(@method).should == "102.257798"
+ 5.08129999999999970584.send(@method).should == "5.0813"
+ 6.00000000000000000000.send(@method).should == "6.0"
+ 8.30000000000000071054.send(@method).should == "8.3"
+ 32.68345999999999662577.send(@method).should == "32.68346"
+ 581.11170000000004165486.send(@method).should == "581.1117"
+ 76.31342999999999676675.send(@method).should == "76.31343"
+ 438.30826000000001840817.send(@method).should == "438.30826"
+ 482.06631994000002805478.send(@method).should == "482.06631994"
+ 55.92721026899999969828.send(@method).should == "55.927210269"
+ 4.00000000000000000000.send(@method).should == "4.0"
+ 55.86693999999999959982.send(@method).should == "55.86694"
+ 787.98299999999994724931.send(@method).should == "787.983"
+ 5.73810511000000023074.send(@method).should == "5.73810511"
+ 74.51926810000000500622.send(@method).should == "74.5192681"
+ 892.89999999999997726263.send(@method).should == "892.9"
+ 68.27299999999999613465.send(@method).should == "68.273"
+ 904.10000000000002273737.send(@method).should == "904.1"
+ 5.23200000000000020606.send(@method).should == "5.232"
+ 4.09628000000000014325.send(@method).should == "4.09628"
+ 46.05152633699999853434.send(@method).should == "46.051526337"
+ 142.12884990599999923688.send(@method).should == "142.128849906"
+ 3.83057023500000015659.send(@method).should == "3.830570235"
+ 11.81684594699999912848.send(@method).should == "11.816845947"
+ 80.50000000000000000000.send(@method).should == "80.5"
+ 382.18215010000000120272.send(@method).should == "382.1821501"
+ 55.38444606899999911320.send(@method).should == "55.384446069"
+ 5.78000000000000024869.send(@method).should == "5.78"
+ 2.88244999999999995666.send(@method).should == "2.88245"
+ 43.27709999999999723741.send(@method).should == "43.2771"
+ end
+
+ it "random values from divisions" do
+ (1.0 / 7).send(@method).should == "0.14285714285714285"
+
+ # 50.times do
+ # a = rand(10)
+ # b = rand(10)
+ # c = rand(10)
+ # d = rand(10)
+ # expression = "#{a}.#{b} / #{c}.#{d}"
+ # puts " (#{expression}).send(@method).should == #{eval(expression).send(@method).inspect}"
+ # end
+
+ (1.1 / 7.1).send(@method).should == "0.15492957746478875"
+ (6.5 / 8.8).send(@method).should == "0.7386363636363635"
+ (4.8 / 4.3).send(@method).should == "1.1162790697674418"
+ (4.0 / 1.9).send(@method).should == "2.1052631578947367"
+ (9.1 / 0.8).send(@method).should == "11.374999999999998"
+ (5.3 / 7.5).send(@method).should == "0.7066666666666667"
+ (2.8 / 1.8).send(@method).should == "1.5555555555555554"
+ (2.1 / 2.5).send(@method).should == "0.8400000000000001"
+ (3.5 / 6.0).send(@method).should == "0.5833333333333334"
+ (4.6 / 0.3).send(@method).should == "15.333333333333332"
+ (0.6 / 2.4).send(@method).should == "0.25"
+ (1.3 / 9.1).send(@method).should == "0.14285714285714288"
+ (0.3 / 5.0).send(@method).should == "0.06"
+ (5.0 / 4.2).send(@method).should == "1.1904761904761905"
+ (3.0 / 2.0).send(@method).should == "1.5"
+ (6.3 / 2.0).send(@method).should == "3.15"
+ (5.4 / 6.0).send(@method).should == "0.9"
+ (9.6 / 8.1).send(@method).should == "1.1851851851851851"
+ (8.7 / 1.6).send(@method).should == "5.437499999999999"
+ (1.9 / 7.8).send(@method).should == "0.24358974358974358"
+ (0.5 / 2.1).send(@method).should == "0.23809523809523808"
+ (9.3 / 5.8).send(@method).should == "1.6034482758620692"
+ (2.7 / 8.0).send(@method).should == "0.3375"
+ (9.7 / 7.8).send(@method).should == "1.2435897435897436"
+ (8.1 / 2.4).send(@method).should == "3.375"
+ (7.7 / 2.7).send(@method).should == "2.8518518518518516"
+ (7.9 / 1.7).send(@method).should == "4.647058823529412"
+ (6.5 / 8.2).send(@method).should == "0.7926829268292683"
+ (7.8 / 9.6).send(@method).should == "0.8125"
+ (2.2 / 4.6).send(@method).should == "0.47826086956521746"
+ (0.0 / 1.0).send(@method).should == "0.0"
+ (8.3 / 2.9).send(@method).should == "2.8620689655172415"
+ (3.1 / 6.1).send(@method).should == "0.5081967213114754"
+ (2.8 / 7.8).send(@method).should == "0.358974358974359"
+ (8.0 / 0.1).send(@method).should == "80.0"
+ (1.7 / 6.4).send(@method).should == "0.265625"
+ (1.8 / 5.4).send(@method).should == "0.3333333333333333"
+ (8.0 / 5.8).send(@method).should == "1.3793103448275863"
+ (5.2 / 4.1).send(@method).should == "1.2682926829268295"
+ (9.8 / 5.8).send(@method).should == "1.6896551724137934"
+ (5.4 / 9.5).send(@method).should == "0.5684210526315789"
+ (8.4 / 4.9).send(@method).should == "1.7142857142857142"
+ (1.7 / 3.5).send(@method).should == "0.4857142857142857"
+ (1.2 / 5.1).send(@method).should == "0.23529411764705882"
+ (1.4 / 2.0).send(@method).should == "0.7"
+ (4.8 / 8.0).send(@method).should == "0.6"
+ (9.0 / 2.5).send(@method).should == "3.6"
+ (0.2 / 0.6).send(@method).should == "0.33333333333333337"
+ (7.8 / 5.2).send(@method).should == "1.5"
+ (9.5 / 5.5).send(@method).should == "1.7272727272727273"
+ end
+ end
+
+ describe 'encoding' do
+ before :each do
+ @internal = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @internal
+ end
+
+ it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do
+ Encoding.default_internal = nil
+ 1.23.send(@method).encoding.should.equal?(Encoding::US_ASCII)
+ end
+
+ it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do
+ Encoding.default_internal = Encoding::IBM437
+ 5.47.send(@method).encoding.should.equal?(Encoding::US_ASCII)
+ end
+ end
+end
diff --git a/spec/ruby/core/float/to_s_spec.rb b/spec/ruby/core/float/to_s_spec.rb
index ad04bc4fb6..6727a883f8 100644
--- a/spec/ruby/core/float/to_s_spec.rb
+++ b/spec/ruby/core/float/to_s_spec.rb
@@ -1,310 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/to_s'
describe "Float#to_s" do
- it "returns 'NaN' for NaN" do
- nan_value().to_s.should == 'NaN'
- end
-
- it "returns 'Infinity' for positive infinity" do
- infinity_value().to_s.should == 'Infinity'
- end
-
- it "returns '-Infinity' for negative infinity" do
- (-infinity_value()).to_s.should == '-Infinity'
- end
-
- it "returns '0.0' for 0.0" do
- 0.0.to_s.should == "0.0"
- end
-
- platform_is_not :openbsd do
- it "emits '-' for -0.0" do
- -0.0.to_s.should == "-0.0"
- end
- end
-
- it "emits a '-' for negative values" do
- -3.14.to_s.should == "-3.14"
- end
-
- it "emits a trailing '.0' for a whole number" do
- 50.0.to_s.should == "50.0"
- end
-
- it "emits a trailing '.0' for the mantissa in e format" do
- 1.0e20.to_s.should == "1.0e+20"
- end
-
- it "uses non-e format for a positive value with fractional part having 5 significant figures" do
- 0.0001.to_s.should == "0.0001"
- end
-
- it "uses non-e format for a negative value with fractional part having 5 significant figures" do
- -0.0001.to_s.should == "-0.0001"
- end
-
- it "uses e format for a positive value with fractional part having 6 significant figures" do
- 0.00001.to_s.should == "1.0e-05"
- end
-
- it "uses e format for a negative value with fractional part having 6 significant figures" do
- -0.00001.to_s.should == "-1.0e-05"
- end
-
- it "uses non-e format for a positive value with whole part having 15 significant figures" do
- 10000000000000.0.to_s.should == "10000000000000.0"
- end
-
- it "uses non-e format for a negative value with whole part having 15 significant figures" do
- -10000000000000.0.to_s.should == "-10000000000000.0"
- end
-
- it "uses non-e format for a positive value with whole part having 16 significant figures" do
- 100000000000000.0.to_s.should == "100000000000000.0"
- end
-
- it "uses non-e format for a negative value with whole part having 16 significant figures" do
- -100000000000000.0.to_s.should == "-100000000000000.0"
- end
-
- it "uses e format for a positive value with whole part having 18 significant figures" do
- 10000000000000000.0.to_s.should == "1.0e+16"
- end
-
- it "uses e format for a negative value with whole part having 18 significant figures" do
- -10000000000000000.0.to_s.should == "-1.0e+16"
- end
-
- it "uses e format for a positive value with whole part having 17 significant figures" do
- 1000000000000000.0.to_s.should == "1.0e+15"
- end
-
- it "uses e format for a negative value with whole part having 17 significant figures" do
- -1000000000000000.0.to_s.should == "-1.0e+15"
- end
-
- # #3273
- it "outputs the minimal, unique form necessary to recreate the value" do
- value = 0.21611564636388508
- string = "0.21611564636388508"
-
- value.to_s.should == string
- string.to_f.should == value
- end
-
- it "outputs the minimal, unique form to represent the value" do
- 0.56.to_s.should == "0.56"
- end
-
- describe "matches" do
- it "random examples in all ranges" do
- # 50.times do
- # bytes = (0...8).map { rand(256) }
- # string = bytes.pack('C8')
- # float = string.unpack('D').first
- # puts "#{'%.20g' % float}.to_s.should == #{float.to_s.inspect}"
- # end
-
- 2.5540217314354050325e+163.to_s.should == "2.554021731435405e+163"
- 2.5492588360356597544e-172.to_s.should == "2.5492588360356598e-172"
- 1.742770260934704852e-82.to_s.should == "1.7427702609347049e-82"
- 6.2108093676180883209e-104.to_s.should == "6.210809367618088e-104"
- -3.3448803488331067402e-143.to_s.should == "-3.3448803488331067e-143"
- -2.2740074343500832557e-168.to_s.should == "-2.2740074343500833e-168"
- 7.0587971678048535732e+191.to_s.should == "7.058797167804854e+191"
- -284438.88327586348169.to_s.should == "-284438.8832758635"
- 3.953272468476091301e+105.to_s.should == "3.9532724684760913e+105"
- -3.6361359552959847853e+100.to_s.should == "-3.636135955295985e+100"
- -1.3222325865575206185e-31.to_s.should == "-1.3222325865575206e-31"
- 1.1440138916932761366e+130.to_s.should == "1.1440138916932761e+130"
- 4.8750891560387561157e-286.to_s.should == "4.875089156038756e-286"
- 5.6101113356591453525e-257.to_s.should == "5.610111335659145e-257"
- -3.829644279545809575e-100.to_s.should == "-3.8296442795458096e-100"
- 1.5342839401396406117e-194.to_s.should == "1.5342839401396406e-194"
- 2.2284972755169921402e-144.to_s.should == "2.228497275516992e-144"
- 2.1825655917065601737e-61.to_s.should == "2.1825655917065602e-61"
- -2.6672271363524338322e-62.to_s.should == "-2.667227136352434e-62"
- -1.9257995160119059415e+21.to_s.should == "-1.925799516011906e+21"
- -8.9096732962887121718e-198.to_s.should == "-8.909673296288712e-198"
- 2.0202075376548644959e-90.to_s.should == "2.0202075376548645e-90"
- -7.7341602581786258961e-266.to_s.should == "-7.734160258178626e-266"
- 3.5134482598733635046e+98.to_s.should == "3.5134482598733635e+98"
- -2.124411722371029134e+154.to_s.should == "-2.124411722371029e+154"
- -4.573908787355718687e+110.to_s.should == "-4.573908787355719e+110"
- -1.9344425934170969879e-232.to_s.should == "-1.934442593417097e-232"
- -1.3274227399979271095e+171.to_s.should == "-1.3274227399979271e+171"
- 9.3495270482104442383e-283.to_s.should == "9.349527048210444e-283"
- -4.2046059371986483233e+307.to_s.should == "-4.2046059371986483e+307"
- 3.6133547278583543004e-117.to_s.should == "3.613354727858354e-117"
- 4.9247416523566613499e-08.to_s.should == "4.9247416523566613e-08"
- 1.6936145488250064007e-71.to_s.should == "1.6936145488250064e-71"
- 2.4455483206829433098e+96.to_s.should == "2.4455483206829433e+96"
- 7.9797449851436455384e+124.to_s.should == "7.979744985143646e+124"
- -1.3873689634457876774e-129.to_s.should == "-1.3873689634457877e-129"
- 3.9761102037533483075e+284.to_s.should == "3.976110203753348e+284"
- -4.2819791952139402486e-303.to_s.should == "-4.28197919521394e-303"
- -5.7981017546689831298e-116.to_s.should == "-5.798101754668983e-116"
- -3.953266497860534199e-28.to_s.should == "-3.953266497860534e-28"
- -2.0659852720290440959e-243.to_s.should == "-2.065985272029044e-243"
- 8.9670488995878688018e-05.to_s.should == "8.967048899587869e-05"
- -1.2317943708113061768e-98.to_s.should == "-1.2317943708113062e-98"
- -3.8930768307633080463e+248.to_s.should == "-3.893076830763308e+248"
- 6.5854032671803925627e-239.to_s.should == "6.5854032671803926e-239"
- 4.6257022188980878952e+177.to_s.should == "4.625702218898088e+177"
- -1.9397155125507235603e-187.to_s.should == "-1.9397155125507236e-187"
- 8.5752156951245705056e+117.to_s.should == "8.57521569512457e+117"
- -2.4784875958162501671e-132.to_s.should == "-2.4784875958162502e-132"
- -4.4125691841230058457e-203.to_s.should == "-4.412569184123006e-203"
- end
-
- it "random examples in human ranges" do
- # 50.times do
- # formatted = ''
- # rand(1..3).times do
- # formatted << rand(10).to_s
- # end
- # formatted << '.'
- # rand(1..9).times do
- # formatted << rand(10).to_s
- # end
- # float = formatted.to_f
- # puts "#{'%.20f' % float}.to_s.should == #{float.to_s.inspect}"
- # end
-
- 5.17869899999999994122.to_s.should == "5.178699"
- 905.62695729999995819526.to_s.should == "905.6269573"
- 62.75999999999999801048.to_s.should == "62.76"
- 6.93856795800000014651.to_s.should == "6.938567958"
- 4.95999999999999996447.to_s.should == "4.96"
- 32.77993899999999882766.to_s.should == "32.779939"
- 544.12756779999995160324.to_s.should == "544.1275678"
- 66.25801119999999855281.to_s.should == "66.2580112"
- 7.90000000000000035527.to_s.should == "7.9"
- 5.93100000000000004974.to_s.should == "5.931"
- 5.21229313600000043749.to_s.should == "5.212293136"
- 503.44173809000000119340.to_s.should == "503.44173809"
- 79.26000000000000511591.to_s.should == "79.26"
- 8.51524999999999998579.to_s.should == "8.51525"
- 174.00000000000000000000.to_s.should == "174.0"
- 50.39580000000000126192.to_s.should == "50.3958"
- 35.28999999999999914735.to_s.should == "35.29"
- 5.43136675399999990788.to_s.should == "5.431366754"
- 654.07680000000004838512.to_s.should == "654.0768"
- 6.07423700000000010846.to_s.should == "6.074237"
- 102.25779799999999397642.to_s.should == "102.257798"
- 5.08129999999999970584.to_s.should == "5.0813"
- 6.00000000000000000000.to_s.should == "6.0"
- 8.30000000000000071054.to_s.should == "8.3"
- 32.68345999999999662577.to_s.should == "32.68346"
- 581.11170000000004165486.to_s.should == "581.1117"
- 76.31342999999999676675.to_s.should == "76.31343"
- 438.30826000000001840817.to_s.should == "438.30826"
- 482.06631994000002805478.to_s.should == "482.06631994"
- 55.92721026899999969828.to_s.should == "55.927210269"
- 4.00000000000000000000.to_s.should == "4.0"
- 55.86693999999999959982.to_s.should == "55.86694"
- 787.98299999999994724931.to_s.should == "787.983"
- 5.73810511000000023074.to_s.should == "5.73810511"
- 74.51926810000000500622.to_s.should == "74.5192681"
- 892.89999999999997726263.to_s.should == "892.9"
- 68.27299999999999613465.to_s.should == "68.273"
- 904.10000000000002273737.to_s.should == "904.1"
- 5.23200000000000020606.to_s.should == "5.232"
- 4.09628000000000014325.to_s.should == "4.09628"
- 46.05152633699999853434.to_s.should == "46.051526337"
- 142.12884990599999923688.to_s.should == "142.128849906"
- 3.83057023500000015659.to_s.should == "3.830570235"
- 11.81684594699999912848.to_s.should == "11.816845947"
- 80.50000000000000000000.to_s.should == "80.5"
- 382.18215010000000120272.to_s.should == "382.1821501"
- 55.38444606899999911320.to_s.should == "55.384446069"
- 5.78000000000000024869.to_s.should == "5.78"
- 2.88244999999999995666.to_s.should == "2.88245"
- 43.27709999999999723741.to_s.should == "43.2771"
- end
-
- it "random values from divisions" do
- (1.0 / 7).to_s.should == "0.14285714285714285"
-
- # 50.times do
- # a = rand(10)
- # b = rand(10)
- # c = rand(10)
- # d = rand(10)
- # expression = "#{a}.#{b} / #{c}.#{d}"
- # puts " (#{expression}).to_s.should == #{eval(expression).to_s.inspect}"
- # end
-
- (1.1 / 7.1).to_s.should == "0.15492957746478875"
- (6.5 / 8.8).to_s.should == "0.7386363636363635"
- (4.8 / 4.3).to_s.should == "1.1162790697674418"
- (4.0 / 1.9).to_s.should == "2.1052631578947367"
- (9.1 / 0.8).to_s.should == "11.374999999999998"
- (5.3 / 7.5).to_s.should == "0.7066666666666667"
- (2.8 / 1.8).to_s.should == "1.5555555555555554"
- (2.1 / 2.5).to_s.should == "0.8400000000000001"
- (3.5 / 6.0).to_s.should == "0.5833333333333334"
- (4.6 / 0.3).to_s.should == "15.333333333333332"
- (0.6 / 2.4).to_s.should == "0.25"
- (1.3 / 9.1).to_s.should == "0.14285714285714288"
- (0.3 / 5.0).to_s.should == "0.06"
- (5.0 / 4.2).to_s.should == "1.1904761904761905"
- (3.0 / 2.0).to_s.should == "1.5"
- (6.3 / 2.0).to_s.should == "3.15"
- (5.4 / 6.0).to_s.should == "0.9"
- (9.6 / 8.1).to_s.should == "1.1851851851851851"
- (8.7 / 1.6).to_s.should == "5.437499999999999"
- (1.9 / 7.8).to_s.should == "0.24358974358974358"
- (0.5 / 2.1).to_s.should == "0.23809523809523808"
- (9.3 / 5.8).to_s.should == "1.6034482758620692"
- (2.7 / 8.0).to_s.should == "0.3375"
- (9.7 / 7.8).to_s.should == "1.2435897435897436"
- (8.1 / 2.4).to_s.should == "3.375"
- (7.7 / 2.7).to_s.should == "2.8518518518518516"
- (7.9 / 1.7).to_s.should == "4.647058823529412"
- (6.5 / 8.2).to_s.should == "0.7926829268292683"
- (7.8 / 9.6).to_s.should == "0.8125"
- (2.2 / 4.6).to_s.should == "0.47826086956521746"
- (0.0 / 1.0).to_s.should == "0.0"
- (8.3 / 2.9).to_s.should == "2.8620689655172415"
- (3.1 / 6.1).to_s.should == "0.5081967213114754"
- (2.8 / 7.8).to_s.should == "0.358974358974359"
- (8.0 / 0.1).to_s.should == "80.0"
- (1.7 / 6.4).to_s.should == "0.265625"
- (1.8 / 5.4).to_s.should == "0.3333333333333333"
- (8.0 / 5.8).to_s.should == "1.3793103448275863"
- (5.2 / 4.1).to_s.should == "1.2682926829268295"
- (9.8 / 5.8).to_s.should == "1.6896551724137934"
- (5.4 / 9.5).to_s.should == "0.5684210526315789"
- (8.4 / 4.9).to_s.should == "1.7142857142857142"
- (1.7 / 3.5).to_s.should == "0.4857142857142857"
- (1.2 / 5.1).to_s.should == "0.23529411764705882"
- (1.4 / 2.0).to_s.should == "0.7"
- (4.8 / 8.0).to_s.should == "0.6"
- (9.0 / 2.5).to_s.should == "3.6"
- (0.2 / 0.6).to_s.should == "0.33333333333333337"
- (7.8 / 5.2).to_s.should == "1.5"
- (9.5 / 5.5).to_s.should == "1.7272727272727273"
- end
- end
-end
-
-describe "Float#to_s" do
- before :each do
- @internal = Encoding.default_internal
- end
-
- after :each do
- Encoding.default_internal = @internal
- end
-
- it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do
- Encoding.default_internal = nil
- 1.23.to_s.encoding.should equal(Encoding::US_ASCII)
- end
-
- it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do
- Encoding.default_internal = Encoding::IBM437
- 5.47.to_s.encoding.should equal(Encoding::US_ASCII)
- end
+ it_behaves_like :float_to_s, :to_s
end
diff --git a/spec/ruby/core/float/truncate_spec.rb b/spec/ruby/core/float/truncate_spec.rb
index 2c80145f9f..1750e3fdbc 100644
--- a/spec/ruby/core/float/truncate_spec.rb
+++ b/spec/ruby/core/float/truncate_spec.rb
@@ -5,10 +5,10 @@ describe "Float#truncate" do
it_behaves_like :float_to_i, :truncate
it "returns self truncated to an optionally given precision" do
- 2.1679.truncate(0).should eql(2)
- 7.1.truncate(1).should eql(7.1)
- 214.94.truncate(-1).should eql(210)
- -1.234.truncate(2).should eql(-1.23)
- 5.123812.truncate(4).should eql(5.1238)
+ 2.1679.truncate(0).should.eql?(2)
+ 7.1.truncate(1).should.eql?(7.1)
+ 214.94.truncate(-1).should.eql?(210)
+ -1.234.truncate(2).should.eql?(-1.23)
+ 5.123812.truncate(4).should.eql?(5.1238)
end
end
diff --git a/spec/ruby/core/float/uplus_spec.rb b/spec/ruby/core/float/uplus_spec.rb
index 936123558c..b979b2717a 100644
--- a/spec/ruby/core/float/uplus_spec.rb
+++ b/spec/ruby/core/float/uplus_spec.rb
@@ -4,6 +4,6 @@ describe "Float#+@" do
it "returns the same value with same sign (twos complement)" do
34.56.send(:+@).should == 34.56
-34.56.send(:+@).should == -34.56
- 0.0.send(:+@).should eql(0.0)
+ 0.0.send(:+@).should.eql?(0.0)
end
end
diff --git a/spec/ruby/core/gc/auto_compact_spec.rb b/spec/ruby/core/gc/auto_compact_spec.rb
new file mode 100644
index 0000000000..33ad1cb56c
--- /dev/null
+++ b/spec/ruby/core/gc/auto_compact_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+
+describe "GC.auto_compact" do
+ it "can set and get a boolean value" do
+ begin
+ GC.auto_compact = GC.auto_compact
+ rescue NotImplementedError # platform does not support autocompact
+ skip
+ end
+
+ original = GC.auto_compact
+ begin
+ GC.auto_compact = !original
+ rescue NotImplementedError # platform does not support autocompact
+ skip
+ end
+
+ begin
+ GC.auto_compact.should == !original
+ ensure
+ GC.auto_compact = original
+ end
+ end
+end
diff --git a/spec/ruby/core/gc/config_spec.rb b/spec/ruby/core/gc/config_spec.rb
new file mode 100644
index 0000000000..57160f122c
--- /dev/null
+++ b/spec/ruby/core/gc/config_spec.rb
@@ -0,0 +1,97 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.4" do
+ describe "GC.config" do
+ context "without arguments" do
+ it "returns a hash of current settings" do
+ GC.config.should.is_a?(Hash)
+ end
+
+ it "includes the name of currently loaded GC implementation as a global key" do
+ GC.config.should.include?(:implementation)
+ GC.config[:implementation].should.is_a?(String)
+ end
+ end
+
+ context "with a hash of options" do
+ it "allows to set GC implementation's options, returning the new config" do
+ config = GC.config({})
+ # Try to find a boolean setting to reliably test changing it.
+ key, _value = config.find { |_k, v| v == true }
+ skip unless key
+
+ GC.config(key => false).should == config.merge(key => false)
+ GC.config[key].should == false
+ GC.config(key => true).should == config
+ GC.config[key].should == true
+ ensure
+ GC.config(config.except(:implementation))
+ end
+
+ it "does not change settings that aren't present in the hash" do
+ previous = GC.config
+ GC.config({})
+ GC.config.should == previous
+ end
+
+ it "ignores unknown keys" do
+ previous = GC.config
+ GC.config(foo: "bar")
+ GC.config.should == previous
+ end
+
+ ruby_version_is ""..."4.0" do
+ it "returns the same as GC.config but without the :implementation key" do
+ previous = GC.config
+ GC.config({}).should == previous.except(:implementation)
+ end
+ end
+
+ ruby_version_is "4.0" do
+ it "returns the same as GC.config, including the :implementation key" do
+ previous = GC.config
+ GC.config({}).should == previous
+ end
+ end
+
+ it "raises an ArgumentError if options include global keys" do
+ -> { GC.config(implementation: "default") }.should.raise(ArgumentError, 'Attempting to set read-only key "Implementation"')
+ end
+ end
+
+ context "with a non-hash argument" do
+ it "returns current settings if argument is nil" do
+ GC.config(nil).should == GC.config
+ end
+
+ it "raises ArgumentError for all other arguments" do
+ -> { GC.config([]) }.should.raise(ArgumentError)
+ -> { GC.config("default") }.should.raise(ArgumentError)
+ -> { GC.config(1) }.should.raise(ArgumentError)
+ end
+ end
+
+ guard -> { PlatformGuard.standard? && GC.config[:implementation] == "default" } do
+ context "with default GC implementation on MRI" do
+ before do
+ @default_config = GC.config({})
+ end
+
+ after do
+ GC.config(@default_config.except(:implementation))
+ end
+
+ it "includes :rgengc_allow_full_mark option, true by default" do
+ GC.config.should.include?(:rgengc_allow_full_mark)
+ GC.config[:rgengc_allow_full_mark].should == true
+ end
+
+ it "allows to set :rgengc_allow_full_mark" do
+ # This key maps truthy and falsey values to true and false.
+ GC.config(rgengc_allow_full_mark: nil).should == @default_config.merge(rgengc_allow_full_mark: false)
+ GC.config(rgengc_allow_full_mark: 1.23).should == @default_config.merge(rgengc_allow_full_mark: true)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/gc/count_spec.rb b/spec/ruby/core/gc/count_spec.rb
index af2fbbe713..fe20c4ed2b 100644
--- a/spec/ruby/core/gc/count_spec.rb
+++ b/spec/ruby/core/gc/count_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "GC.count" do
it "returns an integer" do
- GC.count.should be_kind_of(Integer)
+ GC.count.should.is_a?(Integer)
end
it "increases as collections are run" do
diff --git a/spec/ruby/core/gc/disable_spec.rb b/spec/ruby/core/gc/disable_spec.rb
index b0221d8520..f89a9d2768 100644
--- a/spec/ruby/core/gc/disable_spec.rb
+++ b/spec/ruby/core/gc/disable_spec.rb
@@ -5,7 +5,7 @@ describe "GC.disable" do
GC.enable
end
- it "returns true iff the garbage collection was previously disabled" do
+ it "returns true if and only if the garbage collection was previously disabled" do
GC.enable
GC.disable.should == false
GC.disable.should == true
diff --git a/spec/ruby/core/gc/enable_spec.rb b/spec/ruby/core/gc/enable_spec.rb
index eb8d572f46..ca4488547a 100644
--- a/spec/ruby/core/gc/enable_spec.rb
+++ b/spec/ruby/core/gc/enable_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "GC.enable" do
- it "returns true iff the garbage collection was already disabled" do
+ it "returns true if and only if the garbage collection was already disabled" do
GC.enable
GC.enable.should == false
GC.disable
diff --git a/spec/ruby/core/gc/measure_total_time_spec.rb b/spec/ruby/core/gc/measure_total_time_spec.rb
new file mode 100644
index 0000000000..f5377c18fd
--- /dev/null
+++ b/spec/ruby/core/gc/measure_total_time_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "GC.measure_total_time" do
+ before :each do
+ @default = GC.measure_total_time
+ end
+
+ after :each do
+ GC.measure_total_time = @default
+ end
+
+ it "can set and get a boolean value" do
+ original = GC.measure_total_time
+ GC.measure_total_time = !original
+ GC.measure_total_time.should == !original
+ end
+end
diff --git a/spec/ruby/core/gc/profiler/enabled_spec.rb b/spec/ruby/core/gc/profiler/enabled_spec.rb
index 23677be555..cfcd0951f0 100644
--- a/spec/ruby/core/gc/profiler/enabled_spec.rb
+++ b/spec/ruby/core/gc/profiler/enabled_spec.rb
@@ -11,11 +11,11 @@ describe "GC::Profiler.enabled?" do
it "reports as enabled when enabled" do
GC::Profiler.enable
- GC::Profiler.enabled?.should be_true
+ GC::Profiler.enabled?.should == true
end
it "reports as disabled when disabled" do
GC::Profiler.disable
- GC::Profiler.enabled?.should be_false
+ GC::Profiler.enabled?.should == false
end
end
diff --git a/spec/ruby/core/gc/profiler/result_spec.rb b/spec/ruby/core/gc/profiler/result_spec.rb
index 2ab46a8371..e888f975dc 100644
--- a/spec/ruby/core/gc/profiler/result_spec.rb
+++ b/spec/ruby/core/gc/profiler/result_spec.rb
@@ -2,6 +2,6 @@ require_relative '../../../spec_helper'
describe "GC::Profiler.result" do
it "returns a string" do
- GC::Profiler.result.should be_kind_of(String)
+ GC::Profiler.result.should.is_a?(String)
end
end
diff --git a/spec/ruby/core/gc/profiler/total_time_spec.rb b/spec/ruby/core/gc/profiler/total_time_spec.rb
index 7709f168dd..a351e922af 100644
--- a/spec/ruby/core/gc/profiler/total_time_spec.rb
+++ b/spec/ruby/core/gc/profiler/total_time_spec.rb
@@ -2,6 +2,6 @@ require_relative '../../../spec_helper'
describe "GC::Profiler.total_time" do
it "returns an float" do
- GC::Profiler.total_time.should be_kind_of(Float)
+ GC::Profiler.total_time.should.is_a?(Float)
end
end
diff --git a/spec/ruby/core/gc/stat_spec.rb b/spec/ruby/core/gc/stat_spec.rb
index 51bd00c7c7..6e4800b3e3 100644
--- a/spec/ruby/core/gc/stat_spec.rb
+++ b/spec/ruby/core/gc/stat_spec.rb
@@ -1,16 +1,62 @@
require_relative '../../spec_helper'
describe "GC.stat" do
- it "supports access by key" do
- keys = [:heap_free_slots, :total_allocated_objects, :count]
- keys.each do |key|
- GC.stat(key).should be_kind_of(Integer)
- end
- end
-
it "returns hash of values" do
stat = GC.stat
- stat.should be_kind_of(Hash)
- stat.keys.should include(:count)
+ stat.should.is_a?(Hash)
+ stat.keys.should.include?(:count)
+ end
+
+ it "updates the given hash values" do
+ hash = { count: "hello", __other__: "world" }
+ stat = GC.stat(hash)
+
+ stat.should.is_a?(Hash)
+ stat.should.equal? hash
+ stat[:count].should.is_a?(Integer)
+ stat[:__other__].should == "world"
+ end
+
+ it "the values are all Integer since rb_gc_stat() returns size_t" do
+ GC.stat.values.each { |value| value.should.is_a?(Integer) }
+ end
+
+ it "can return a single value" do
+ GC.stat(:count).should.is_a?(Integer)
+ end
+
+ it "increases count after GC is run" do
+ count = GC.stat(:count)
+ GC.start
+ GC.stat(:count).should > count
+ end
+
+ it "increases major_gc_count after GC is run" do
+ count = GC.stat(:major_gc_count)
+ GC.start
+ GC.stat(:major_gc_count).should > count
+ end
+
+ it "provides some number for count" do
+ GC.stat(:count).should.is_a?(Integer)
+ GC.stat[:count].should.is_a?(Integer)
+ end
+
+ it "provides some number for heap_free_slots" do
+ GC.stat(:heap_free_slots).should.is_a?(Integer)
+ GC.stat[:heap_free_slots].should.is_a?(Integer)
+ end
+
+ it "provides some number for total_allocated_objects" do
+ GC.stat(:total_allocated_objects).should.is_a?(Integer)
+ GC.stat[:total_allocated_objects].should.is_a?(Integer)
+ end
+
+ it "raises an error if argument is not nil, a symbol, or a hash" do
+ -> { GC.stat(7) }.should.raise(TypeError, "non-hash or symbol given")
+ end
+
+ it "raises an error if an unknown key is given" do
+ -> { GC.stat(:foo) }.should.raise(ArgumentError, "unknown key: foo")
end
end
diff --git a/spec/ruby/core/gc/stress_spec.rb b/spec/ruby/core/gc/stress_spec.rb
index ca62c0cb4e..227a795e7f 100644
--- a/spec/ruby/core/gc/stress_spec.rb
+++ b/spec/ruby/core/gc/stress_spec.rb
@@ -7,11 +7,11 @@ describe "GC.stress" do
end
it "returns current status of GC stress mode" do
- GC.stress.should be_false
+ GC.stress.should == false
GC.stress = true
- GC.stress.should be_true
+ GC.stress.should == true
GC.stress = false
- GC.stress.should be_false
+ GC.stress.should == false
end
end
@@ -22,6 +22,6 @@ describe "GC.stress=" do
it "sets the stress mode" do
GC.stress = true
- GC.stress.should be_true
+ GC.stress.should == true
end
end
diff --git a/spec/ruby/core/gc/total_time_spec.rb b/spec/ruby/core/gc/total_time_spec.rb
new file mode 100644
index 0000000000..f10e0ad742
--- /dev/null
+++ b/spec/ruby/core/gc/total_time_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "GC.total_time" do
+ it "returns an Integer" do
+ GC.total_time.should.is_a?(Integer)
+ end
+
+ it "increases as collections are run" do
+ time_before = GC.total_time
+ GC.start
+ GC.total_time.should >= time_before
+ end
+end
diff --git a/spec/ruby/core/hash/allocate_spec.rb b/spec/ruby/core/hash/allocate_spec.rb
index 93420de866..a92accc161 100644
--- a/spec/ruby/core/hash/allocate_spec.rb
+++ b/spec/ruby/core/hash/allocate_spec.rb
@@ -3,7 +3,7 @@ require_relative '../../spec_helper'
describe "Hash.allocate" do
it "returns an instance of Hash" do
hsh = Hash.allocate
- hsh.should be_an_instance_of(Hash)
+ hsh.should.instance_of?(Hash)
end
it "returns a fully-formed instance of Hash" do
diff --git a/spec/ruby/core/hash/assoc_spec.rb b/spec/ruby/core/hash/assoc_spec.rb
index 64442918d1..51af51a20f 100644
--- a/spec/ruby/core/hash/assoc_spec.rb
+++ b/spec/ruby/core/hash/assoc_spec.rb
@@ -6,7 +6,7 @@ describe "Hash#assoc" do
end
it "returns an Array if the argument is == to a key of the Hash" do
- @h.assoc(:apple).should be_an_instance_of(Array)
+ @h.assoc(:apple).should.instance_of?(Array)
end
it "returns a 2-element Array if the argument is == to a key of the Hash" do
@@ -22,11 +22,11 @@ describe "Hash#assoc" do
end
it "only returns the first matching key-value pair for identity hashes" do
- # Avoid literal String keys in Hash#[]= due to https://bugs.ruby-lang.org/issues/12855
+ # Avoid literal String keys since string literals can be frozen and interned e.g. with --enable-frozen-string-literal
h = {}.compare_by_identity
- k1 = 'pear'
+ k1 = 'pear'.dup
h[k1] = :red
- k2 = 'pear'
+ k2 = 'pear'.dup
h[k2] = :green
h.size.should == 2
h.keys.grep(/pear/).size.should == 2
@@ -40,11 +40,11 @@ describe "Hash#assoc" do
end
it "returns nil if the argument is not a key of the Hash" do
- @h.assoc(:green).should be_nil
+ @h.assoc(:green).should == nil
end
it "returns nil if the argument is not a key of the Hash even when there is a default" do
- Hash.new(42).merge!( foo: :bar ).assoc(42).should be_nil
- Hash.new{|h, k| h[k] = 42}.merge!( foo: :bar ).assoc(42).should be_nil
+ Hash.new(42).merge!( foo: :bar ).assoc(42).should == nil
+ Hash.new{|h, k| h[k] = 42}.merge!( foo: :bar ).assoc(42).should == nil
end
end
diff --git a/spec/ruby/core/hash/clear_spec.rb b/spec/ruby/core/hash/clear_spec.rb
index cf05e36ac9..beff925a63 100644
--- a/spec/ruby/core/hash/clear_spec.rb
+++ b/spec/ruby/core/hash/clear_spec.rb
@@ -4,7 +4,7 @@ require_relative 'fixtures/classes'
describe "Hash#clear" do
it "removes all key, value pairs" do
h = { 1 => 2, 3 => 4 }
- h.clear.should equal(h)
+ h.clear.should.equal?(h)
h.should == {}
end
@@ -26,7 +26,7 @@ describe "Hash#clear" do
end
it "raises a FrozenError if called on a frozen instance" do
- -> { HashSpecs.frozen_hash.clear }.should raise_error(FrozenError)
- -> { HashSpecs.empty_frozen_hash.clear }.should raise_error(FrozenError)
+ -> { HashSpecs.frozen_hash.clear }.should.raise(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.clear }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/clone_spec.rb b/spec/ruby/core/hash/clone_spec.rb
index 6c96fc0c67..d135a84453 100644
--- a/spec/ruby/core/hash/clone_spec.rb
+++ b/spec/ruby/core/hash/clone_spec.rb
@@ -7,6 +7,6 @@ describe "Hash#clone" do
clone = hash.clone
clone.should == hash
- clone.should_not equal hash
+ clone.should_not.equal? hash
end
end
diff --git a/spec/ruby/core/hash/compact_spec.rb b/spec/ruby/core/hash/compact_spec.rb
index 2989afc8b7..de83dabed3 100644
--- a/spec/ruby/core/hash/compact_spec.rb
+++ b/spec/ruby/core/hash/compact_spec.rb
@@ -10,7 +10,7 @@ describe "Hash#compact" do
it "returns new object that rejects pair has nil value" do
ret = @hash.compact
- ret.should_not equal(@hash)
+ ret.should_not.equal?(@hash)
ret.should == @compact
end
@@ -18,6 +18,28 @@ describe "Hash#compact" do
@hash.compact
@hash.should == @initial_pairs
end
+
+ it "retains the default value" do
+ hash = Hash.new(1)
+ hash.compact.default.should == 1
+ hash[:a] = 1
+ hash.compact.default.should == 1
+ end
+
+ it "retains the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ hash = Hash.new(&pr)
+ hash.compact.default_proc.should == pr
+ hash[:a] = 1
+ hash.compact.default_proc.should == pr
+ end
+
+ it "retains compare_by_identity flag" do
+ hash = {}.compare_by_identity
+ hash.compact.compare_by_identity?.should == true
+ hash[:a] = 1
+ hash.compact.compare_by_identity?.should == true
+ end
end
describe "Hash#compact!" do
@@ -28,7 +50,7 @@ describe "Hash#compact!" do
end
it "returns self" do
- @hash.compact!.should equal(@hash)
+ @hash.compact!.should.equal?(@hash)
end
it "rejects own pair has nil value" do
@@ -42,7 +64,7 @@ describe "Hash#compact!" do
end
it "returns nil" do
- @hash.compact!.should be_nil
+ @hash.compact!.should == nil
end
end
@@ -52,7 +74,7 @@ describe "Hash#compact!" do
end
it "keeps pairs and raises a FrozenError" do
- ->{ @hash.compact! }.should raise_error(FrozenError)
+ ->{ @hash.compact! }.should.raise(FrozenError)
@hash.should == @initial_pairs
end
end
diff --git a/spec/ruby/core/hash/compare_by_identity_spec.rb b/spec/ruby/core/hash/compare_by_identity_spec.rb
index 874cd46eb7..1abf9d5666 100644
--- a/spec/ruby/core/hash/compare_by_identity_spec.rb
+++ b/spec/ruby/core/hash/compare_by_identity_spec.rb
@@ -11,7 +11,7 @@ describe "Hash#compare_by_identity" do
@h[[1]] = :a
@h[[1]].should == :a
@h.compare_by_identity
- @h[[1].dup].should be_nil
+ @h[[1].dup].should == nil
end
it "rehashes internally so that old keys can be looked up" do
@@ -27,21 +27,21 @@ describe "Hash#compare_by_identity" do
it "returns self" do
h = {}
h[:foo] = :bar
- h.compare_by_identity.should equal h
+ h.compare_by_identity.should.equal? h
end
it "has no effect on an already compare_by_identity hash" do
@idh[:foo] = :bar
- @idh.compare_by_identity.should equal @idh
+ @idh.compare_by_identity.should.equal? @idh
@idh.should.compare_by_identity?
@idh[:foo].should == :bar
end
it "uses the semantics of BasicObject#equal? to determine key identity" do
- [1].should_not equal([1])
+ [1].should_not.equal?([1])
@idh[[1]] = :c
@idh[[1]] = :d
- :bar.should equal(:bar)
+ :bar.should.equal?(:bar)
@idh[:bar] = :e
@idh[:bar] = :f
@idh.values.should == [:c, :d, :f]
@@ -64,13 +64,13 @@ describe "Hash#compare_by_identity" do
it "regards #dup'd objects as having different identities" do
key = ['foo']
@idh[key.dup] = :str
- @idh[key].should be_nil
+ @idh[key].should == nil
end
it "regards #clone'd objects as having different identities" do
key = ['foo']
@idh[key.clone] = :str
- @idh[key].should be_nil
+ @idh[key].should == nil
end
it "regards references to the same object as having the same identity" do
@@ -82,22 +82,24 @@ describe "Hash#compare_by_identity" do
it "raises a FrozenError on frozen hashes" do
@h = @h.freeze
- -> { @h.compare_by_identity }.should raise_error(FrozenError)
+ -> { @h.compare_by_identity }.should.raise(FrozenError)
end
- # Behaviour confirmed in bug #1871
+ # Behaviour confirmed in https://bugs.ruby-lang.org/issues/1871
it "persists over #dups" do
- @idh['foo'] = :bar
- @idh['foo'] = :glark
+ @idh['foo'.dup] = :bar
+ @idh['foo'.dup] = :glark
@idh.dup.should == @idh
@idh.dup.size.should == @idh.size
+ @idh.dup.should.compare_by_identity?
end
it "persists over #clones" do
- @idh['foo'] = :bar
- @idh['foo'] = :glark
+ @idh['foo'.dup] = :bar
+ @idh['foo'.dup] = :glark
@idh.clone.should == @idh
@idh.clone.size.should == @idh.size
+ @idh.dup.should.compare_by_identity?
end
it "does not copy string keys" do
@@ -105,12 +107,19 @@ describe "Hash#compare_by_identity" do
@idh[foo] = true
@idh[foo] = true
@idh.size.should == 1
- @idh.keys.first.should equal foo
+ @idh.keys.first.should.equal? foo
end
+ # Check `#[]=` call with a String literal.
+ # Don't use `#+` because with `#+` it's no longer a String literal.
+ #
+ # See https://bugs.ruby-lang.org/issues/12855
it "gives different identity for string literals" do
+ eval <<~RUBY
+ # frozen_string_literal: false
@idh['foo'] = 1
@idh['foo'] = 2
+ RUBY
@idh.values.should == [1, 2]
@idh.size.should == 2
end
@@ -119,20 +128,20 @@ end
describe "Hash#compare_by_identity?" do
it "returns false by default" do
h = {}
- h.compare_by_identity?.should be_false
+ h.compare_by_identity?.should == false
end
it "returns true once #compare_by_identity has been invoked on self" do
h = {}
h.compare_by_identity
- h.compare_by_identity?.should be_true
+ h.compare_by_identity?.should == true
end
it "returns true when called multiple times on the same ident hash" do
h = {}
h.compare_by_identity
- h.compare_by_identity?.should be_true
- h.compare_by_identity?.should be_true
- h.compare_by_identity?.should be_true
+ h.compare_by_identity?.should == true
+ h.compare_by_identity?.should == true
+ h.compare_by_identity?.should == true
end
end
diff --git a/spec/ruby/core/hash/constructor_spec.rb b/spec/ruby/core/hash/constructor_spec.rb
index ad67274802..c16f373160 100644
--- a/spec/ruby/core/hash/constructor_spec.rb
+++ b/spec/ruby/core/hash/constructor_spec.rb
@@ -42,38 +42,25 @@ describe "Hash.[]" do
Hash[ary].should == { a: :b }
end
- ruby_version_is "" ... "2.7" do
- it "ignores elements that are not arrays" do
- -> {
- Hash[[:a]].should == {}
- }.should complain(/ignoring wrong elements/)
- -> {
- Hash[[:nil]].should == {}
- }.should complain(/ignoring wrong elements/)
- end
- end
-
- ruby_version_is "2.7" do
- it "raises for elements that are not arrays" do
- -> {
- Hash[[:a]].should == {}
- }.should raise_error(ArgumentError)
- -> {
- Hash[[:nil]].should == {}
- }.should raise_error(ArgumentError)
- end
+ it "raises for elements that are not arrays" do
+ -> {
+ Hash[[:a]]
+ }.should.raise(ArgumentError, "wrong element type Symbol at 0 (expected array)")
+ -> {
+ Hash[[nil]]
+ }.should.raise(ArgumentError, "wrong element type nil at 0 (expected array)")
end
it "raises an ArgumentError for arrays of more than 2 elements" do
- ->{ Hash[[[:a, :b, :c]]].should == {} }.should raise_error(ArgumentError)
+ ->{
+ Hash[[[:a, :b, :c]]]
+ }.should.raise(ArgumentError, "invalid number of elements (3 for 1..2)")
end
it "raises an ArgumentError when passed a list of value-invalid-pairs in an array" do
-> {
- -> {
- Hash[[[:a, 1], [:b], 42, [:d, 2], [:e, 2, 3], []]]
- }.should complain(/ignoring wrong elements/)
- }.should raise_error(ArgumentError)
+ Hash[[[:a, 1], [:b], 42, [:d, 2], [:e, 2, 3], []]]
+ }.should.raise(ArgumentError, "wrong element type Integer at 2 (expected array)")
end
describe "passed a single argument which responds to #to_hash" do
@@ -84,13 +71,13 @@ describe "Hash.[]" do
result = Hash[to_hash]
result.should == h
- result.should_not equal(h)
+ result.should_not.equal?(h)
end
end
it "raises an ArgumentError when passed an odd number of arguments" do
- -> { Hash[1, 2, 3] }.should raise_error(ArgumentError)
- -> { Hash[1, 2, { 3 => 4 }] }.should raise_error(ArgumentError)
+ -> { Hash[1, 2, 3] }.should.raise(ArgumentError)
+ -> { Hash[1, 2, { 3 => 4 }] }.should.raise(ArgumentError)
end
it "calls to_hash" do
@@ -100,24 +87,41 @@ describe "Hash.[]" do
end
it "returns an instance of a subclass when passed an Array" do
- HashSpecs::MyHash[1,2,3,4].should be_an_instance_of(HashSpecs::MyHash)
+ HashSpecs::MyHash[1,2,3,4].should.instance_of?(HashSpecs::MyHash)
end
it "returns instances of subclasses" do
- HashSpecs::MyHash[].should be_an_instance_of(HashSpecs::MyHash)
+ HashSpecs::MyHash[].should.instance_of?(HashSpecs::MyHash)
end
it "returns an instance of the class it's called on" do
Hash[HashSpecs::MyHash[1, 2]].class.should == Hash
- HashSpecs::MyHash[Hash[1, 2]].should be_an_instance_of(HashSpecs::MyHash)
+ HashSpecs::MyHash[Hash[1, 2]].should.instance_of?(HashSpecs::MyHash)
end
it "does not call #initialize on the subclass instance" do
- HashSpecs::MyInitializerHash[Hash[1, 2]].should be_an_instance_of(HashSpecs::MyInitializerHash)
+ HashSpecs::MyInitializerHash[Hash[1, 2]].should.instance_of?(HashSpecs::MyInitializerHash)
end
- it "removes the default_proc" do
+ it "does not retain the default value" do
+ hash = Hash.new(1)
+ Hash[hash].default.should == nil
+ hash[:a] = 1
+ Hash[hash].default.should == nil
+ end
+
+ it "does not retain the default_proc" do
hash = Hash.new { |h, k| h[k] = [] }
- Hash[hash].default_proc.should be_nil
+ Hash[hash].default_proc.should == nil
+ hash[:a] = 1
+ Hash[hash].default_proc.should == nil
+ end
+
+ it "does not retain compare_by_identity flag" do
+ hash = { a: 1 }.compare_by_identity
+ Hash[hash].compare_by_identity?.should == false
+
+ hash = {}.compare_by_identity
+ Hash[hash].compare_by_identity?.should == false
end
end
diff --git a/spec/ruby/core/hash/deconstruct_keys_spec.rb b/spec/ruby/core/hash/deconstruct_keys_spec.rb
index b265732616..f6ff3a2818 100644
--- a/spec/ruby/core/hash/deconstruct_keys_spec.rb
+++ b/spec/ruby/core/hash/deconstruct_keys_spec.rb
@@ -1,25 +1,23 @@
require_relative '../../spec_helper'
-ruby_version_is "2.7" do
- describe "Hash#deconstruct_keys" do
- it "returns self" do
- hash = {a: 1, b: 2}
+describe "Hash#deconstruct_keys" do
+ it "returns self" do
+ hash = {a: 1, b: 2}
- hash.deconstruct_keys([:a, :b]).should equal hash
- end
+ hash.deconstruct_keys([:a, :b]).should.equal? hash
+ end
- it "requires one argument" do
- -> {
- {a: 1}.deconstruct_keys
- }.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
- end
+ it "requires one argument" do
+ -> {
+ {a: 1}.deconstruct_keys
+ }.should.raise(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
+ end
- it "ignores argument" do
- hash = {a: 1, b: 2}
+ it "ignores argument" do
+ hash = {a: 1, b: 2}
- hash.deconstruct_keys([:a]).should == {a: 1, b: 2}
- hash.deconstruct_keys(0 ).should == {a: 1, b: 2}
- hash.deconstruct_keys('' ).should == {a: 1, b: 2}
- end
+ hash.deconstruct_keys([:a]).should == {a: 1, b: 2}
+ hash.deconstruct_keys(0 ).should == {a: 1, b: 2}
+ hash.deconstruct_keys('' ).should == {a: 1, b: 2}
end
end
diff --git a/spec/ruby/core/hash/default_proc_spec.rb b/spec/ruby/core/hash/default_proc_spec.rb
index f4e4803632..4830e02acd 100644
--- a/spec/ruby/core/hash/default_proc_spec.rb
+++ b/spec/ruby/core/hash/default_proc_spec.rb
@@ -25,24 +25,24 @@ describe "Hash#default_proc=" do
h = Hash.new { 'Paris' }
obj = mock('to_proc')
obj.should_receive(:to_proc).and_return(Proc.new { 'Montreal' })
- (h.default_proc = obj).should equal(obj)
+ (h.default_proc = obj).should.equal?(obj)
h[:cool_city].should == 'Montreal'
end
it "overrides the static default" do
h = Hash.new(42)
h.default_proc = Proc.new { 6 }
- h.default.should be_nil
+ h.default.should == nil
h.default_proc.call.should == 6
end
it "raises an error if passed stuff not convertible to procs" do
- ->{{}.default_proc = 42}.should raise_error(TypeError)
+ ->{{}.default_proc = 42}.should.raise(TypeError)
end
it "returns the passed Proc" do
new_proc = Proc.new {}
- ({}.default_proc = new_proc).should equal(new_proc)
+ ({}.default_proc = new_proc).should.equal?(new_proc)
end
it "clears the default proc if passed nil" do
@@ -53,28 +53,28 @@ describe "Hash#default_proc=" do
end
it "returns nil if passed nil" do
- ({}.default_proc = nil).should be_nil
+ ({}.default_proc = nil).should == nil
end
it "accepts a lambda with an arity of 2" do
h = {}
-> do
h.default_proc = -> a, b { }
- end.should_not raise_error(TypeError)
+ end.should_not.raise(TypeError)
end
it "raises a TypeError if passed a lambda with an arity other than 2" do
h = {}
-> do
h.default_proc = -> a { }
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
-> do
h.default_proc = -> a, b, c { }
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it "raises a FrozenError if self is frozen" do
- -> { {}.freeze.default_proc = Proc.new {} }.should raise_error(FrozenError)
- -> { {}.freeze.default_proc = nil }.should raise_error(FrozenError)
+ -> { {}.freeze.default_proc = Proc.new {} }.should.raise(FrozenError)
+ -> { {}.freeze.default_proc = nil }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/default_spec.rb b/spec/ruby/core/hash/default_spec.rb
index d8b62ea196..17ee032e2b 100644
--- a/spec/ruby/core/hash/default_spec.rb
+++ b/spec/ruby/core/hash/default_spec.rb
@@ -40,7 +40,7 @@ describe "Hash#default=" do
end
it "raises a FrozenError if called on a frozen instance" do
- -> { HashSpecs.frozen_hash.default = nil }.should raise_error(FrozenError)
- -> { HashSpecs.empty_frozen_hash.default = nil }.should raise_error(FrozenError)
+ -> { HashSpecs.frozen_hash.default = nil }.should.raise(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.default = nil }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/delete_if_spec.rb b/spec/ruby/core/hash/delete_if_spec.rb
index c9e670ffc3..3090633d88 100644
--- a/spec/ruby/core/hash/delete_if_spec.rb
+++ b/spec/ruby/core/hash/delete_if_spec.rb
@@ -12,13 +12,13 @@ describe "Hash#delete_if" do
it "removes every entry for which block is true and returns self" do
h = { a: 1, b: 2, c: 3, d: 4 }
- h.delete_if { |k,v| v % 2 == 1 }.should equal(h)
+ h.delete_if { |k,v| v % 2 == 1 }.should.equal?(h)
h.should == { b: 2, d: 4 }
end
it "removes all entries if the block is true" do
h = { a: 1, b: 2, c: 3 }
- h.delete_if { |k,v| true }.should equal(h)
+ h.delete_if { |k,v| true }.should.equal?(h)
h.should == {}
end
@@ -35,8 +35,8 @@ describe "Hash#delete_if" do
end
it "raises a FrozenError if called on a frozen instance" do
- -> { HashSpecs.frozen_hash.delete_if { false } }.should raise_error(FrozenError)
- -> { HashSpecs.empty_frozen_hash.delete_if { true } }.should raise_error(FrozenError)
+ -> { HashSpecs.frozen_hash.delete_if { false } }.should.raise(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.delete_if { true } }.should.raise(FrozenError)
end
it_behaves_like :hash_iteration_no_block, :delete_if
diff --git a/spec/ruby/core/hash/delete_spec.rb b/spec/ruby/core/hash/delete_spec.rb
index b262e8846b..33ff01bf36 100644
--- a/spec/ruby/core/hash/delete_spec.rb
+++ b/spec/ruby/core/hash/delete_spec.rb
@@ -24,7 +24,7 @@ describe "Hash#delete" do
it "allows removing a key while iterating" do
h = { a: 1, b: 2 }
visited = []
- h.each_pair { |k,v|
+ h.each_pair { |k, v|
visited << k
h.delete(k)
}
@@ -32,13 +32,27 @@ describe "Hash#delete" do
h.should == {}
end
+ it "allows removing a key while iterating for big hashes" do
+ h = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10,
+ k: 11, l: 12, m: 13, n: 14, o: 15, p: 16, q: 17, r: 18, s: 19, t: 20,
+ u: 21, v: 22, w: 23, x: 24, y: 25, z: 26 }
+ visited = []
+ h.each_pair { |k, v|
+ visited << k
+ h.delete(k)
+ }
+ visited.should == [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m,
+ :n, :o, :p, :q, :r, :s, :t, :u, :v, :w, :x, :y, :z]
+ h.should == {}
+ end
+
it "accepts keys with private #hash method" do
key = HashSpecs::KeyWithPrivateHash.new
{ key => 5 }.delete(key).should == 5
end
it "raises a FrozenError if called on a frozen instance" do
- -> { HashSpecs.frozen_hash.delete("foo") }.should raise_error(FrozenError)
- -> { HashSpecs.empty_frozen_hash.delete("foo") }.should raise_error(FrozenError)
+ -> { HashSpecs.frozen_hash.delete("foo") }.should.raise(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.delete("foo") }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/dig_spec.rb b/spec/ruby/core/hash/dig_spec.rb
index aa0ecadd2f..94ac94e850 100644
--- a/spec/ruby/core/hash/dig_spec.rb
+++ b/spec/ruby/core/hash/dig_spec.rb
@@ -5,16 +5,16 @@ describe "Hash#dig" do
it "returns #[] with one arg" do
h = { 0 => false, a: 1 }
h.dig(:a).should == 1
- h.dig(0).should be_false
- h.dig(1).should be_nil
+ h.dig(0).should == false
+ h.dig(1).should == nil
end
it "returns the nested value specified by the sequence of keys" do
h = { foo: { bar: { baz: 1 } } }
h.dig(:foo, :bar, :baz).should == 1
- h.dig(:foo, :bar, :nope).should be_nil
- h.dig(:foo, :baz).should be_nil
- h.dig(:bar, :baz, :foo).should be_nil
+ h.dig(:foo, :bar, :nope).should == nil
+ h.dig(:foo, :baz).should == nil
+ h.dig(:bar, :baz, :foo).should == nil
end
it "returns the nested value specified if the sequence includes an index" do
@@ -28,7 +28,7 @@ describe "Hash#dig" do
end
it "raises an ArgumentError if no arguments provided" do
- -> { { the: 'borg' }.dig() }.should raise_error(ArgumentError)
+ -> { { the: 'borg' }.dig() }.should.raise(ArgumentError)
end
it "handles type-mixed deep digging" do
@@ -47,8 +47,8 @@ describe "Hash#dig" do
it "raises TypeError if an intermediate element does not respond to #dig" do
h = {}
h[:foo] = [ { bar: [ 1 ] }, [ nil, 'str' ] ]
- -> { h.dig(:foo, 0, :bar, 0, 0) }.should raise_error(TypeError)
- -> { h.dig(:foo, 1, 1, 0) }.should raise_error(TypeError)
+ -> { h.dig(:foo, 0, :bar, 0, 0) }.should.raise(TypeError)
+ -> { h.dig(:foo, 1, 1, 0) }.should.raise(TypeError)
end
it "calls #dig on the result of #[] with the remaining arguments" do
@@ -60,7 +60,7 @@ describe "Hash#dig" do
it "respects Hash's default" do
default = {bar: 42}
h = Hash.new(default)
- h.dig(:foo).should equal default
+ h.dig(:foo).should.equal? default
h.dig(:foo, :bar).should == 42
end
end
diff --git a/spec/ruby/core/hash/each_key_spec.rb b/spec/ruby/core/hash/each_key_spec.rb
index c84dd696d5..5b2e9deb63 100644
--- a/spec/ruby/core/hash/each_key_spec.rb
+++ b/spec/ruby/core/hash/each_key_spec.rb
@@ -7,7 +7,7 @@ describe "Hash#each_key" do
it "calls block once for each key, passing key" do
r = {}
h = { 1 => -1, 2 => -2, 3 => -3, 4 => -4 }
- h.each_key { |k| r[k] = k }.should equal(h)
+ h.each_key { |k| r[k] = k }.should.equal?(h)
r.should == { 1 => 1, 2 => 2, 3 => 3, 4 => 4 }
end
diff --git a/spec/ruby/core/hash/each_pair_spec.rb b/spec/ruby/core/hash/each_pair_spec.rb
index eb6656681d..01810c130c 100644
--- a/spec/ruby/core/hash/each_pair_spec.rb
+++ b/spec/ruby/core/hash/each_pair_spec.rb
@@ -1,11 +1,111 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
require_relative 'shared/iteration'
-require_relative 'shared/each'
require_relative '../enumerable/shared/enumeratorized'
describe "Hash#each_pair" do
- it_behaves_like :hash_each, :each_pair
it_behaves_like :hash_iteration_no_block, :each_pair
it_behaves_like :enumeratorized_with_origin_size, :each_pair, { 1 => 2, 3 => 4, 5 => 6 }
+
+ # This is inconsistent with below, MRI checks the block arity in rb_hash_each_pair()
+ it "yields a [[key, value]] Array for each pair to a block expecting |*args|" do
+ all_args = []
+ { 1 => 2, 3 => 4 }.each_pair { |*args| all_args << args }
+ all_args.sort.should == [[[1, 2]], [[3, 4]]]
+ end
+
+ it "yields the key and value of each pair to a block expecting |key, value|" do
+ r = {}
+ h = { a: 1, b: 2, c: 3, d: 5 }
+ h.each_pair { |k,v| r[k.to_s] = v.to_s }.should.equal?(h)
+ r.should == { "a" => "1", "b" => "2", "c" => "3", "d" => "5" }
+ end
+
+ it "yields the key only to a block expecting |key,|" do
+ ary = []
+ h = { "a" => 1, "b" => 2, "c" => 3 }
+ h.each_pair { |k,| ary << k }
+ ary.sort.should == ["a", "b", "c"]
+ end
+
+ it "always yields an Array of 2 elements, even when given a callable of arity 2" do
+ obj = Object.new
+ def obj.foo(key, value)
+ end
+
+ -> {
+ { "a" => 1 }.each_pair(&obj.method(:foo))
+ }.should.raise(ArgumentError)
+
+ -> {
+ { "a" => 1 }.each_pair(&-> key, value { })
+ }.should.raise(ArgumentError)
+ end
+
+ it "yields an Array of 2 elements when given a callable of arity 1" do
+ obj = Object.new
+ def obj.foo(key_value)
+ ScratchPad << key_value
+ end
+
+ ScratchPad.record([])
+ { "a" => 1 }.each_pair(&obj.method(:foo))
+ ScratchPad.recorded.should == [["a", 1]]
+ end
+
+ it "raises an error for a Hash when an arity enforcing callable of arity >2 is passed in" do
+ obj = Object.new
+ def obj.foo(key, value, extra)
+ end
+
+ -> {
+ { "a" => 1 }.each_pair(&obj.method(:foo))
+ }.should.raise(ArgumentError)
+ end
+
+ it "uses the same order as keys() and values()" do
+ h = { a: 1, b: 2, c: 3, d: 5 }
+ keys = []
+ values = []
+
+ h.each_pair do |k, v|
+ keys << k
+ values << v
+ end
+
+ keys.should == h.keys
+ values.should == h.values
+ end
+
+ # Confirming the argument-splatting works from child class for both k, v and [k, v]
+ it "properly expands (or not) child class's 'each'-yielded args" do
+ cls1 = Class.new(Hash) do
+ attr_accessor :k_v
+ def each
+ super do |k, v|
+ @k_v = [k, v]
+ yield k, v
+ end
+ end
+ end
+
+ cls2 = Class.new(Hash) do
+ attr_accessor :k_v
+ def each
+ super do |k, v|
+ @k_v = [k, v]
+ yield([k, v])
+ end
+ end
+ end
+
+ obj1 = cls1.new
+ obj1['a'] = 'b'
+ obj1.map {|k, v| [k, v]}.should == [['a', 'b']]
+ obj1.k_v.should == ['a', 'b']
+
+ obj2 = cls2.new
+ obj2['a'] = 'b'
+ obj2.map {|k, v| [k, v]}.should == [['a', 'b']]
+ obj2.k_v.should == ['a', 'b']
+ end
end
diff --git a/spec/ruby/core/hash/each_spec.rb b/spec/ruby/core/hash/each_spec.rb
index f0de0bdee5..1644b63216 100644
--- a/spec/ruby/core/hash/each_spec.rb
+++ b/spec/ruby/core/hash/each_spec.rb
@@ -1,11 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/iteration'
-require_relative 'shared/each'
-require_relative '../enumerable/shared/enumeratorized'
describe "Hash#each" do
- it_behaves_like :hash_each, :each
- it_behaves_like :hash_iteration_no_block, :each
- it_behaves_like :enumeratorized_with_origin_size, :each, { 1 => 2, 3 => 4, 5 => 6 }
+ it "is an alias of Hash#each_pair" do
+ Hash.instance_method(:each).should == Hash.instance_method(:each_pair)
+ end
end
diff --git a/spec/ruby/core/hash/each_value_spec.rb b/spec/ruby/core/hash/each_value_spec.rb
index 19b076730d..2df95a8789 100644
--- a/spec/ruby/core/hash/each_value_spec.rb
+++ b/spec/ruby/core/hash/each_value_spec.rb
@@ -7,7 +7,7 @@ describe "Hash#each_value" do
it "calls block once for each key, passing value" do
r = []
h = { a: -5, b: -3, c: -2, d: -1, e: -1 }
- h.each_value { |v| r << v }.should equal(h)
+ h.each_value { |v| r << v }.should.equal?(h)
r.sort.should == [-5, -3, -2, -1, -1]
end
diff --git a/spec/ruby/core/hash/element_reference_spec.rb b/spec/ruby/core/hash/element_reference_spec.rb
index 2eb65d3789..3d074b346d 100644
--- a/spec/ruby/core/hash/element_reference_spec.rb
+++ b/spec/ruby/core/hash/element_reference_spec.rb
@@ -12,7 +12,7 @@ describe "Hash#[]" do
h[[]].should == "baz"
end
- it "returns nil as default default value" do
+ it "returns nil as default value" do
{ 0 => 0 }[5].should == nil
end
@@ -30,13 +30,13 @@ describe "Hash#[]" do
end
it "does not create copies of the immediate default value" do
- str = "foo"
+ str = +"foo"
h = Hash.new(str)
a = h[:a]
b = h[:b]
a << "bar"
- a.should equal(b)
+ a.should.equal?(b)
a.should == "foobar"
b.should == "foobar"
end
@@ -117,4 +117,18 @@ describe "Hash#[]" do
key = HashSpecs::KeyWithPrivateHash.new
{ key => 42 }[key].should == 42
end
+
+ it "does not dispatch to hash for Boolean, Integer, Float, String, or Symbol" do
+ code = <<-EOC
+ load '#{fixture __FILE__, "name.rb"}'
+ hash = { true => 42, false => 42, 1 => 42, 2.0 => 42, "hello" => 42, :ok => 42 }
+ [true, false, 1, 2.0, "hello", :ok].each do |value|
+ raise "incorrect value" unless hash[value] == 42
+ end
+ puts "Ok."
+ EOC
+ result = ruby_exe(code, args: "2>&1")
+ result.should == "Ok.\n"
+ end
+
end
diff --git a/spec/ruby/core/hash/element_set_spec.rb b/spec/ruby/core/hash/element_set_spec.rb
index 67c5a04d73..bb805477ca 100644
--- a/spec/ruby/core/hash/element_set_spec.rb
+++ b/spec/ruby/core/hash/element_set_spec.rb
@@ -1,7 +1,121 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/store'
describe "Hash#[]=" do
- it_behaves_like :hash_store, :[]=
+ it "associates the key with the value" do
+ h = { a: 1 }
+ h[:b] = 2
+ h.should == { b:2, a:1 }
+ end
+
+ it "returns the assigned value" do
+ h = {}
+ h.send(:[]=, 1, 2).should == 2
+ end
+
+ it "duplicates string keys using dup semantics" do
+ # dup doesn't copy singleton methods
+ key = +"foo"
+ def key.reverse() "bar" end
+ h = {}
+ h[key] = 0
+ h.keys[0].reverse.should == "oof"
+ end
+
+ it "stores unequal keys that hash to the same value" do
+ h = {}
+ k1 = ["x"]
+ k2 = ["y"]
+ # So they end up in the same bucket
+ k1.should_receive(:hash).and_return(0)
+ k2.should_receive(:hash).and_return(0)
+
+ h[k1] = 1
+ h[k2] = 2
+ h.size.should == 2
+ end
+
+ it "accepts keys with private #hash method" do
+ key = HashSpecs::KeyWithPrivateHash.new
+ h = {}
+ h[key] = "foo"
+ h[key].should == "foo"
+ end
+
+ it " accepts keys with an Integer hash" do
+ o = mock(hash: 1 << 100)
+ h = {}
+ h[o] = 1
+ h[o].should == 1
+ end
+
+ it "duplicates and freezes string keys" do
+ key = +"foo"
+ h = {}
+ h[key] = 0
+ key << "bar"
+
+ h.should == { "foo" => 0 }
+ h.keys[0].should.frozen?
+ end
+
+ it "doesn't duplicate and freeze already frozen string keys" do
+ key = "foo".freeze
+ h = {}
+ h[key] = 0
+ h.keys[0].should.equal?(key)
+ end
+
+ it "keeps the existing key in the hash if there is a matching one" do
+ h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 }
+ key1 = HashSpecs::ByValueKey.new(13)
+ key2 = HashSpecs::ByValueKey.new(13)
+ h[key1] = 41
+ key_in_hash = h.keys.last
+ key_in_hash.should.equal?(key1)
+ h[key2] = 42
+ last_key = h.keys.last
+ last_key.should.equal?(key_in_hash)
+ last_key.should_not.equal?(key2)
+ end
+
+ it "keeps the existing String key in the hash if there is a matching one" do
+ h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 }
+ key1 = "foo".dup
+ key2 = "foo".dup
+ key1.should_not.equal?(key2)
+ h[key1] = 41
+ frozen_key = h.keys.last
+ frozen_key.should_not.equal?(key1)
+ h[key2] = 42
+ h.keys.last.should.equal?(frozen_key)
+ h.keys.last.should_not.equal?(key2)
+ end
+
+ it "raises a FrozenError if called on a frozen instance" do
+ -> { HashSpecs.frozen_hash[1] = 2 }.should.raise(FrozenError)
+ end
+
+ it "does not raise an exception if changing the value of an existing key during iteration" do
+ hash = {1 => 2, 3 => 4, 5 => 6}
+ hash.each { hash[1] = :foo }
+ hash.should == {1 => :foo, 3 => 4, 5 => 6}
+ end
+
+ it "does not dispatch to hash for Boolean, Integer, Float, String, or Symbol" do
+ code = <<-EOC
+ load '#{fixture __FILE__, "name.rb"}'
+ hash = {}
+ [true, false, 1, 2.0, "hello", :ok].each do |value|
+ hash[value] = 42
+ raise "incorrect value" unless hash[value] == 42
+ hash[value] = 43
+ raise "incorrect value" unless hash[value] == 43
+ end
+ puts "OK"
+ puts hash.size
+ EOC
+ result = ruby_exe(code, args: "2>&1")
+ result.should == "OK\n6\n"
+ end
end
diff --git a/spec/ruby/core/hash/equal_value_spec.rb b/spec/ruby/core/hash/equal_value_spec.rb
index ae13a42679..ab14f81489 100644
--- a/spec/ruby/core/hash/equal_value_spec.rb
+++ b/spec/ruby/core/hash/equal_value_spec.rb
@@ -13,6 +13,6 @@ describe "Hash#==" do
l_val.should_receive(:==).with(r_val).and_return(true)
- ({ 1 => l_val } == { 1 => r_val }).should be_true
+ ({ 1 => l_val } == { 1 => r_val }).should == true
end
end
diff --git a/spec/ruby/core/hash/except_spec.rb b/spec/ruby/core/hash/except_spec.rb
new file mode 100644
index 0000000000..77a020e9b7
--- /dev/null
+++ b/spec/ruby/core/hash/except_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+
+describe "Hash#except" do
+ before :each do
+ @hash = { a: 1, b: 2, c: 3 }
+ end
+
+ it "returns a new duplicate hash without arguments" do
+ ret = @hash.except
+ ret.should_not.equal?(@hash)
+ ret.should == @hash
+ end
+
+ it "returns a hash without the requested subset" do
+ @hash.except(:c, :a).should == { b: 2 }
+ end
+
+ it "ignores keys not present in the original hash" do
+ @hash.except(:a, :chunky_bacon).should == { b: 2, c: 3 }
+ end
+
+ it "does not retain the default value" do
+ h = Hash.new(1)
+ h.except(:a).default.should == nil
+ h[:a] = 1
+ h.except(:a).default.should == nil
+ end
+
+ it "does not retain the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ h = Hash.new(&pr)
+ h.except(:a).default_proc.should == nil
+ h[:a] = 1
+ h.except(:a).default_proc.should == nil
+ end
+
+ it "retains compare_by_identity flag" do
+ h = { a: 9, c: 4 }.compare_by_identity
+ h2 = h.except(:a)
+ h2.compare_by_identity?.should == true
+ end
+end
diff --git a/spec/ruby/core/hash/fetch_spec.rb b/spec/ruby/core/hash/fetch_spec.rb
index 753167f709..f9b80b6c49 100644
--- a/spec/ruby/core/hash/fetch_spec.rb
+++ b/spec/ruby/core/hash/fetch_spec.rb
@@ -4,7 +4,7 @@ require_relative '../../shared/hash/key_error'
describe "Hash#fetch" do
context "when the key is not found" do
- it_behaves_like :key_error, -> obj, key { obj.fetch(key) }, Hash.new(a: 5)
+ it_behaves_like :key_error, -> obj, key { obj.fetch(key) }, Hash.new({ a: 5 })
it_behaves_like :key_error, -> obj, key { obj.fetch(key) }, {}
it_behaves_like :key_error, -> obj, key { obj.fetch(key) }, Hash.new { 5 }
it_behaves_like :key_error, -> obj, key { obj.fetch(key) }, Hash.new(5)
@@ -12,7 +12,7 @@ describe "Hash#fetch" do
it "formats the object with #inspect in the KeyError message" do
-> {
{}.fetch('foo')
- }.should raise_error(KeyError, 'key not found: "foo"')
+ }.should.raise(KeyError, 'key not found: "foo"')
end
end
@@ -38,7 +38,7 @@ describe "Hash#fetch" do
end
it "raises an ArgumentError when not passed one or two arguments" do
- -> { {}.fetch() }.should raise_error(ArgumentError)
- -> { {}.fetch(1, 2, 3) }.should raise_error(ArgumentError)
+ -> { {}.fetch() }.should.raise(ArgumentError)
+ -> { {}.fetch(1, 2, 3) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/hash/fetch_values_spec.rb b/spec/ruby/core/hash/fetch_values_spec.rb
index af3673f6ef..0cd48565af 100644
--- a/spec/ruby/core/hash/fetch_values_spec.rb
+++ b/spec/ruby/core/hash/fetch_values_spec.rb
@@ -19,7 +19,7 @@ describe "Hash#fetch_values" do
end
describe "with unmatched keys" do
- it_behaves_like :key_error, -> obj, key { obj.fetch_values(key) }, Hash.new(a: 5)
+ it_behaves_like :key_error, -> obj, key { obj.fetch_values(key) }, Hash.new({ a: 5 })
it "returns the default value from block" do
@hash.fetch_values(:z) { |key| "`#{key}' is not found" }.should == ["`z' is not found"]
diff --git a/spec/ruby/core/hash/filter_spec.rb b/spec/ruby/core/hash/filter_spec.rb
index 4382c94e62..625a7feb90 100644
--- a/spec/ruby/core/hash/filter_spec.rb
+++ b/spec/ruby/core/hash/filter_spec.rb
@@ -1,12 +1,13 @@
require_relative '../../spec_helper'
-require_relative 'shared/select'
-ruby_version_is "2.6" do
- describe "Hash#filter" do
- it_behaves_like :hash_select, :filter
+describe "Hash#filter" do
+ it "is an alias of Hash#select" do
+ Hash.instance_method(:filter).should == Hash.instance_method(:select)
end
+end
- describe "Hash#filter!" do
- it_behaves_like :hash_select!, :filter!
+describe "Hash#filter!" do
+ it "is an alias of Hash#select!" do
+ Hash.instance_method(:filter!).should == Hash.instance_method(:select!)
end
end
diff --git a/spec/ruby/core/hash/fixtures/name.rb b/spec/ruby/core/hash/fixtures/name.rb
new file mode 100644
index 0000000000..b203bf6ae4
--- /dev/null
+++ b/spec/ruby/core/hash/fixtures/name.rb
@@ -0,0 +1,30 @@
+class TrueClass
+ def hash
+ raise "TrueClass#hash should not be called"
+ end
+end
+class FalseClass
+ def hash
+ raise "FalseClass#hash should not be called"
+ end
+end
+class Integer
+ def hash
+ raise "Integer#hash should not be called"
+ end
+end
+class Float
+ def hash
+ raise "Float#hash should not be called"
+ end
+end
+class String
+ def hash
+ raise "String#hash should not be called"
+ end
+end
+class Symbol
+ def hash
+ raise "Symbol#hash should not be called"
+ end
+end
diff --git a/spec/ruby/core/hash/flatten_spec.rb b/spec/ruby/core/hash/flatten_spec.rb
index 825da15bfc..18e9a7b56a 100644
--- a/spec/ruby/core/hash/flatten_spec.rb
+++ b/spec/ruby/core/hash/flatten_spec.rb
@@ -9,7 +9,7 @@ describe "Hash#flatten" do
end
it "returns an Array" do
- {}.flatten.should be_an_instance_of(Array)
+ {}.flatten.should.instance_of?(Array)
end
it "returns an empty Array for an empty Hash" do
@@ -57,6 +57,6 @@ describe "Hash#flatten" do
it "raises a TypeError if given a non-Integer argument" do
-> do
@h.flatten(Object.new)
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/hash/gt_spec.rb b/spec/ruby/core/hash/gt_spec.rb
index cd541d4d83..69d23b7d29 100644
--- a/spec/ruby/core/hash/gt_spec.rb
+++ b/spec/ruby/core/hash/gt_spec.rb
@@ -8,7 +8,7 @@ describe "Hash#>" do
it "returns false if both hashes are identical" do
h = { a: 1, b: 2 }
- (h > h).should be_false
+ (h > h).should == false
end
end
diff --git a/spec/ruby/core/hash/gte_spec.rb b/spec/ruby/core/hash/gte_spec.rb
index 99b89e7217..be5060ea4f 100644
--- a/spec/ruby/core/hash/gte_spec.rb
+++ b/spec/ruby/core/hash/gte_spec.rb
@@ -8,7 +8,7 @@ describe "Hash#>=" do
it "returns true if both hashes are identical" do
h = { a: 1, b: 2 }
- (h >= h).should be_true
+ (h >= h).should == true
end
end
diff --git a/spec/ruby/core/hash/has_key_spec.rb b/spec/ruby/core/hash/has_key_spec.rb
index 4af53579e5..9a6244c6f6 100644
--- a/spec/ruby/core/hash/has_key_spec.rb
+++ b/spec/ruby/core/hash/has_key_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/key'
describe "Hash#has_key?" do
- it_behaves_like :hash_key_p, :has_key?
+ it "is an alias of Hash#include?" do
+ Hash.instance_method(:has_key?).should == Hash.instance_method(:include?)
+ end
end
diff --git a/spec/ruby/core/hash/has_value_spec.rb b/spec/ruby/core/hash/has_value_spec.rb
index 39f1627fd3..d40e52ebfd 100644
--- a/spec/ruby/core/hash/has_value_spec.rb
+++ b/spec/ruby/core/hash/has_value_spec.rb
@@ -1,7 +1,16 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/value'
describe "Hash#has_value?" do
- it_behaves_like :hash_value_p, :has_value?
+ it "returns true if the value exists in the hash" do
+ { a: :b }.has_value?(:a).should == false
+ { 1 => 2 }.has_value?(2).should == true
+ h = Hash.new(5)
+ h.has_value?(5).should == false
+ h = Hash.new { 5 }
+ h.has_value?(5).should == false
+ end
+
+ it "uses == semantics for comparing values" do
+ { 5 => 2.0 }.has_value?(2).should == true
+ end
end
diff --git a/spec/ruby/core/hash/hash_spec.rb b/spec/ruby/core/hash/hash_spec.rb
index 3649d4d8de..cd67f7a652 100644
--- a/spec/ruby/core/hash/hash_spec.rb
+++ b/spec/ruby/core/hash/hash_spec.rb
@@ -41,4 +41,11 @@ describe "Hash#hash" do
h.hash.should == {x: [h]}.hash
# Like above, because h.eql?(x: [h])
end
+
+ it "allows omitting values" do
+ a = 1
+ b = 2
+
+ {a:, b:}.should == { a: 1, b: 2 }
+ end
end
diff --git a/spec/ruby/core/hash/include_spec.rb b/spec/ruby/core/hash/include_spec.rb
index f3959dc589..1c32e9fb23 100644
--- a/spec/ruby/core/hash/include_spec.rb
+++ b/spec/ruby/core/hash/include_spec.rb
@@ -1,7 +1,40 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/key'
describe "Hash#include?" do
- it_behaves_like :hash_key_p, :include?
+ it "returns true if argument is a key" do
+ h = { a: 1, b: 2, c: 3, 4 => 0 }
+ h.include?(:a).should == true
+ h.include?(:b).should == true
+ h.include?(2).should == false
+ h.include?(4).should == true
+
+ not_supported_on :opal do
+ h.include?('b').should == false
+ h.include?(4.0).should == false
+ end
+ end
+
+ it "returns true if the key's matching value was nil" do
+ { xyz: nil }.include?(:xyz).should == true
+ end
+
+ it "returns true if the key's matching value was false" do
+ { xyz: false }.include?(:xyz).should == true
+ end
+
+ it "returns true if the key is nil" do
+ { nil => 'b' }.include?(nil).should == true
+ { nil => nil }.include?(nil).should == true
+ end
+
+ it "compares keys with the same #hash value via #eql?" do
+ x = mock('x')
+ x.stub!(:hash).and_return(42)
+
+ y = mock('y')
+ y.stub!(:hash).and_return(42)
+ y.should_receive(:eql?).and_return(false)
+
+ { x => nil }.include?(y).should == false
+ end
end
diff --git a/spec/ruby/core/hash/index_spec.rb b/spec/ruby/core/hash/index_spec.rb
deleted file mode 100644
index 2b52c69949..0000000000
--- a/spec/ruby/core/hash/index_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/index'
-
-describe "Hash#index" do
- it_behaves_like :hash_index, :index
-end
diff --git a/spec/ruby/core/hash/initialize_spec.rb b/spec/ruby/core/hash/initialize_spec.rb
index d13496ba3b..cdba598813 100644
--- a/spec/ruby/core/hash/initialize_spec.rb
+++ b/spec/ruby/core/hash/initialize_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "Hash#initialize" do
it "is private" do
- Hash.should have_private_instance_method("initialize")
+ Hash.private_instance_methods(false).should.include?(:initialize)
end
it "can be used to reset default_proc" do
@@ -42,20 +42,20 @@ describe "Hash#initialize" do
it "returns self" do
h = Hash.new
- h.send(:initialize).should equal(h)
+ h.send(:initialize).should.equal?(h)
end
it "raises a FrozenError if called on a frozen instance" do
block = -> { HashSpecs.frozen_hash.instance_eval { initialize() }}
- block.should raise_error(FrozenError)
+ block.should.raise(FrozenError)
block = -> { HashSpecs.frozen_hash.instance_eval { initialize(nil) } }
- block.should raise_error(FrozenError)
+ block.should.raise(FrozenError)
block = -> { HashSpecs.frozen_hash.instance_eval { initialize(5) } }
- block.should raise_error(FrozenError)
+ block.should.raise(FrozenError)
block = -> { HashSpecs.frozen_hash.instance_eval { initialize { 5 } } }
- block.should raise_error(FrozenError)
+ block.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/inspect_spec.rb b/spec/ruby/core/hash/inspect_spec.rb
index f41ebb70a6..359b13360f 100644
--- a/spec/ruby/core/hash/inspect_spec.rb
+++ b/spec/ruby/core/hash/inspect_spec.rb
@@ -1,7 +1,123 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/to_s'
describe "Hash#inspect" do
- it_behaves_like :hash_to_s, :inspect
+ it "returns a string representation with same order as each()" do
+ h = { a: [1, 2], b: -2, d: -6, nil => nil }
+ expected = ruby_version_is("3.4") ? "{a: [1, 2], b: -2, d: -6, nil => nil}" : "{:a=>[1, 2], :b=>-2, :d=>-6, nil=>nil}"
+ h.inspect.should == expected
+ end
+
+ it "calls #inspect on keys and values" do
+ key = mock('key')
+ val = mock('val')
+ key.should_receive(:inspect).and_return('key')
+ val.should_receive(:inspect).and_return('val')
+ expected = ruby_version_is("3.4") ? "{key => val}" : "{key=>val}"
+ { key => val }.inspect.should == expected
+ end
+
+ it "does not call #to_s on a String returned from #inspect" do
+ str = +"abc"
+ str.should_not_receive(:to_s)
+ expected = ruby_version_is("3.4") ? '{a: "abc"}' : '{:a=>"abc"}'
+ { a: str }.inspect.should == expected
+ end
+
+ it "calls #to_s on the object returned from #inspect if the Object isn't a String" do
+ obj = mock("Hash#inspect/to_s calls #to_s")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_return("abc")
+ expected = ruby_version_is("3.4") ? "{a: abc}" : "{:a=>abc}"
+ { a: obj }.inspect.should == expected
+ end
+
+ it "does not call #to_str on the object returned from #inspect when it is not a String" do
+ obj = mock("Hash#inspect/to_s does not call #to_str")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_not_receive(:to_str)
+ expected_pattern = ruby_version_is("3.4") ? /^\{a: #<MockObject:0x[0-9a-f]+>\}$/ : /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/
+ { a: obj }.inspect.should =~ expected_pattern
+ end
+
+ it "does not call #to_str on the object returned from #to_s when it is not a String" do
+ obj = mock("Hash#inspect/to_s does not call #to_str on #to_s result")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_return(obj)
+ obj.should_not_receive(:to_str)
+ expected_pattern = ruby_version_is("3.4") ? /^\{a: #<MockObject:0x[0-9a-f]+>\}$/ : /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/
+ { a: obj }.inspect.should =~ expected_pattern
+ end
+
+ it "does not swallow exceptions raised by #to_s" do
+ obj = mock("Hash#inspect/to_s does not swallow #to_s exceptions")
+ obj.should_receive(:inspect).and_return(obj)
+ obj.should_receive(:to_s).and_raise(Exception)
+
+ -> { { a: obj }.inspect }.should.raise(Exception)
+ end
+
+ it "handles hashes with recursive values" do
+ x = {}
+ x[0] = x
+ expected = ruby_version_is("3.4") ? '{0 => {...}}' : '{0=>{...}}'
+ x.inspect.should == expected
+
+ x = {}
+ y = {}
+ x[0] = y
+ y[1] = x
+ expected_x = ruby_version_is("3.4") ? '{0 => {1 => {...}}}' : '{0=>{1=>{...}}}'
+ expected_y = ruby_version_is("3.4") ? '{1 => {0 => {...}}}' : '{1=>{0=>{...}}}'
+ x.inspect.should == expected_x
+ y.inspect.should == expected_y
+ end
+
+ it "does not raise if inspected result is not default external encoding" do
+ utf_16be = mock("utf_16be")
+ utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode(Encoding::UTF_16BE))
+ expected = ruby_version_is("3.4") ? '{a: "utf_16be \u3042"}' : '{:a=>"utf_16be \u3042"}'
+ {a: utf_16be}.inspect.should == expected
+ end
+
+ it "works for keys and values whose #inspect return a frozen String" do
+ expected = ruby_version_is("3.4") ? "{true => false}" : "{true=>false}"
+ { true => false }.inspect.should == expected
+ end
+
+ ruby_version_is "3.4" do
+ it "adds quotes to symbol keys that are not valid symbol literals" do
+ { "needs-quotes": 1 }.inspect.should == '{"needs-quotes": 1}'
+ end
+
+ it "can be evaled" do
+ no_quote = '{a: 1, a!: 1, a?: 1}'
+ eval(no_quote).inspect.should == no_quote
+ [
+ '{"": 1}',
+ '{"0": 1, "!": 1, "%": 1, "&": 1, "*": 1, "+": 1, "-": 1, "/": 1, "<": 1, ">": 1, "^": 1, "`": 1, "|": 1, "~": 1}',
+ '{"@a": 1, "$a": 1, "+@": 1, "a=": 1, "[]": 1}',
+ '{"a\"b": 1, "@@a": 1, "<=>": 1, "===": 1, "[]=": 1}',
+ ].each do |quote|
+ eval(quote).inspect.should == quote
+ end
+ end
+
+ it "can be evaled when Encoding.default_external is changed" do
+ external = Encoding.default_external
+
+ Encoding.default_external = Encoding::ASCII
+ utf8_ascii_hash = '{"\\u3042": 1}'
+ eval(utf8_ascii_hash).inspect.should == utf8_ascii_hash
+
+ Encoding.default_external = Encoding::UTF_8
+ utf8_hash = "{\u3042: 1}"
+ eval(utf8_hash).inspect.should == utf8_hash
+
+ Encoding.default_external = Encoding::Windows_31J
+ sjis_hash = "{\x87]: 1}".dup.force_encoding('sjis')
+ eval(sjis_hash).inspect.should == sjis_hash
+ ensure
+ Encoding.default_external = external
+ end
+ end
end
diff --git a/spec/ruby/core/hash/invert_spec.rb b/spec/ruby/core/hash/invert_spec.rb
index 73377a9e97..ea441751f2 100644
--- a/spec/ruby/core/hash/invert_spec.rb
+++ b/spec/ruby/core/hash/invert_spec.rb
@@ -24,4 +24,25 @@ describe "Hash#invert" do
HashSpecs::MyHash[1 => 2, 3 => 4].invert.class.should == Hash
HashSpecs::MyHash[].invert.class.should == Hash
end
+
+ it "does not retain the default value" do
+ h = Hash.new(1)
+ h.invert.default.should == nil
+ h[:a] = 1
+ h.invert.default.should == nil
+ end
+
+ it "does not retain the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ h = Hash.new(&pr)
+ h.invert.default_proc.should == nil
+ h[:a] = 1
+ h.invert.default_proc.should == nil
+ end
+
+ it "does not retain compare_by_identity flag" do
+ h = { a: 9, c: 4 }.compare_by_identity
+ h2 = h.invert
+ h2.compare_by_identity?.should == false
+ end
end
diff --git a/spec/ruby/core/hash/keep_if_spec.rb b/spec/ruby/core/hash/keep_if_spec.rb
index d50d969467..c975efc5f8 100644
--- a/spec/ruby/core/hash/keep_if_spec.rb
+++ b/spec/ruby/core/hash/keep_if_spec.rb
@@ -12,24 +12,24 @@ describe "Hash#keep_if" do
it "keeps every entry for which block is true and returns self" do
h = { a: 1, b: 2, c: 3, d: 4 }
- h.keep_if { |k,v| v % 2 == 0 }.should equal(h)
+ h.keep_if { |k,v| v % 2 == 0 }.should.equal?(h)
h.should == { b: 2, d: 4 }
end
it "removes all entries if the block is false" do
h = { a: 1, b: 2, c: 3 }
- h.keep_if { |k,v| false }.should equal(h)
+ h.keep_if { |k,v| false }.should.equal?(h)
h.should == {}
end
it "returns self even if unmodified" do
h = { 1 => 2, 3 => 4 }
- h.keep_if { true }.should equal(h)
+ h.keep_if { true }.should.equal?(h)
end
it "raises a FrozenError if called on a frozen instance" do
- -> { HashSpecs.frozen_hash.keep_if { true } }.should raise_error(FrozenError)
- -> { HashSpecs.empty_frozen_hash.keep_if { false } }.should raise_error(FrozenError)
+ -> { HashSpecs.frozen_hash.keep_if { true } }.should.raise(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.keep_if { false } }.should.raise(FrozenError)
end
it_behaves_like :hash_iteration_no_block, :keep_if
diff --git a/spec/ruby/core/hash/key_spec.rb b/spec/ruby/core/hash/key_spec.rb
index 73eecbc98e..c2d7049aed 100644
--- a/spec/ruby/core/hash/key_spec.rb
+++ b/spec/ruby/core/hash/key_spec.rb
@@ -1,12 +1,32 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/key'
-require_relative 'shared/index'
describe "Hash#key?" do
- it_behaves_like :hash_key_p, :key?
+ it "is an alias of Hash#include?" do
+ Hash.instance_method(:key?).should == Hash.instance_method(:include?)
+ end
end
describe "Hash#key" do
- it_behaves_like :hash_index, :key
+ it "returns the corresponding key for value" do
+ { 2 => 'a', 1 => 'b' }.key('b').should == 1
+ end
+
+ it "returns nil if the value is not found" do
+ { a: -1, b: 3.14, c: 2.718 }.key(1).should == nil
+ end
+
+ it "doesn't return default value if the value is not found" do
+ Hash.new(5).key(5).should == nil
+ end
+
+ it "compares values using ==" do
+ { 1 => 0 }.key(0.0).should == 1
+ { 1 => 0.0 }.key(0).should == 1
+
+ needle = mock('needle')
+ inhash = mock('inhash')
+ inhash.should_receive(:==).with(needle).and_return(true)
+
+ { 1 => inhash }.key(needle).should == 1
+ end
end
diff --git a/spec/ruby/core/hash/keys_spec.rb b/spec/ruby/core/hash/keys_spec.rb
index 9a067085e5..789c413bee 100644
--- a/spec/ruby/core/hash/keys_spec.rb
+++ b/spec/ruby/core/hash/keys_spec.rb
@@ -5,11 +5,11 @@ describe "Hash#keys" do
it "returns an array with the keys in the order they were inserted" do
{}.keys.should == []
- {}.keys.should be_kind_of(Array)
+ {}.keys.should.is_a?(Array)
Hash.new(5).keys.should == []
Hash.new { 5 }.keys.should == []
{ 1 => 2, 4 => 8, 2 => 4 }.keys.should == [1, 4, 2]
- { 1 => 2, 2 => 4, 4 => 8 }.keys.should be_kind_of(Array)
+ { 1 => 2, 2 => 4, 4 => 8 }.keys.should.is_a?(Array)
{ nil => nil }.keys.should == [nil]
end
diff --git a/spec/ruby/core/hash/length_spec.rb b/spec/ruby/core/hash/length_spec.rb
index d0af0945df..325973306f 100644
--- a/spec/ruby/core/hash/length_spec.rb
+++ b/spec/ruby/core/hash/length_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/length'
describe "Hash#length" do
- it_behaves_like :hash_length, :length
+ it "is an alias of Hash#size" do
+ Hash.instance_method(:size).should == Hash.instance_method(:length)
+ end
end
diff --git a/spec/ruby/core/hash/lt_spec.rb b/spec/ruby/core/hash/lt_spec.rb
index 2219615880..4ca326d077 100644
--- a/spec/ruby/core/hash/lt_spec.rb
+++ b/spec/ruby/core/hash/lt_spec.rb
@@ -8,7 +8,7 @@ describe "Hash#<" do
it "returns false if both hashes are identical" do
h = { a: 1, b: 2 }
- (h < h).should be_false
+ (h < h).should == false
end
end
diff --git a/spec/ruby/core/hash/lte_spec.rb b/spec/ruby/core/hash/lte_spec.rb
index a166e5bca4..29e60273d1 100644
--- a/spec/ruby/core/hash/lte_spec.rb
+++ b/spec/ruby/core/hash/lte_spec.rb
@@ -8,7 +8,7 @@ describe "Hash#<=" do
it "returns true if both hashes are identical" do
h = { a: 1, b: 2 }
- (h <= h).should be_true
+ (h <= h).should == true
end
end
diff --git a/spec/ruby/core/hash/member_spec.rb b/spec/ruby/core/hash/member_spec.rb
index 37c0414559..e7309c3f7d 100644
--- a/spec/ruby/core/hash/member_spec.rb
+++ b/spec/ruby/core/hash/member_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/key'
describe "Hash#member?" do
- it_behaves_like :hash_key_p, :member?
+ it "is an alias of Hash#include?" do
+ Hash.instance_method(:member?).should == Hash.instance_method(:include?)
+ end
end
diff --git a/spec/ruby/core/hash/merge_spec.rb b/spec/ruby/core/hash/merge_spec.rb
index 91ded30eb7..9e566fcee9 100644
--- a/spec/ruby/core/hash/merge_spec.rb
+++ b/spec/ruby/core/hash/merge_spec.rb
@@ -1,7 +1,5 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/iteration'
-require_relative 'shared/update'
describe "Hash#merge" do
it "returns a new hash by combining self with the contents of other" do
@@ -30,7 +28,7 @@ describe "Hash#merge" do
-> {
h1.merge(h2) { |k, x, y| raise(IndexError) }
- }.should raise_error(IndexError)
+ }.should.raise(IndexError)
r = h1.merge(h1) { |k,x,y| :x }
r.should == { a: :x, b: :x, d: :x }
@@ -47,8 +45,8 @@ describe "Hash#merge" do
end
it "returns subclass instance for subclasses" do
- HashSpecs::MyHash[1 => 2, 3 => 4].merge({ 1 => 2 }).should be_an_instance_of(HashSpecs::MyHash)
- HashSpecs::MyHash[].merge({ 1 => 2 }).should be_an_instance_of(HashSpecs::MyHash)
+ HashSpecs::MyHash[1 => 2, 3 => 4].merge({ 1 => 2 }).should.instance_of?(HashSpecs::MyHash)
+ HashSpecs::MyHash[].merge({ 1 => 2 }).should.instance_of?(HashSpecs::MyHash)
{ 1 => 2, 3 => 4 }.merge(HashSpecs::MyHash[1 => 2]).class.should == Hash
{}.merge(HashSpecs::MyHash[1 => 2]).class.should == Hash
@@ -81,22 +79,45 @@ describe "Hash#merge" do
merge_pairs.should == expected_pairs
end
- ruby_version_is "2.6" do
- it "accepts multiple hashes" do
- result = { a: 1 }.merge({ b: 2 }, { c: 3 }, { d: 4 })
- result.should == { a: 1, b: 2, c: 3, d: 4 }
- end
+ it "accepts multiple hashes" do
+ result = { a: 1 }.merge({ b: 2 }, { c: 3 }, { d: 4 })
+ result.should == { a: 1, b: 2, c: 3, d: 4 }
+ end
+
+ it "accepts zero arguments and returns a copy of self" do
+ hash = { a: 1 }
+ merged = hash.merge
+
+ merged.should.eql?(hash)
+ merged.should_not.equal?(hash)
+ end
+
+ it "retains the default value" do
+ h = Hash.new(1)
+ h.merge(b: 1, d: 2).default.should == 1
+ end
- it "accepts zero arguments and returns a copy of self" do
- hash = { a: 1 }
- merged = hash.merge
+ it "retains the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ h = Hash.new(&pr)
+ h.merge(b: 1, d: 2).default_proc.should == pr
+ end
- merged.should eql(hash)
- merged.should_not equal(hash)
- end
+ it "retains compare_by_identity flag" do
+ h = { a: 9, c: 4 }.compare_by_identity
+ h2 = h.merge(b: 1, d: 2)
+ h2.compare_by_identity?.should == true
+ end
+
+ it "ignores compare_by_identity flag of an argument" do
+ h = { a: 9, c: 4 }.compare_by_identity
+ h2 = { b: 1, d: 2 }.merge(h)
+ h2.compare_by_identity?.should == false
end
end
describe "Hash#merge!" do
- it_behaves_like :hash_update, :merge!
+ it "is an alias of Hash#update" do
+ Hash.instance_method(:merge!).should == Hash.instance_method(:update)
+ end
end
diff --git a/spec/ruby/core/hash/new_spec.rb b/spec/ruby/core/hash/new_spec.rb
index 6054b69bdd..207fc2931f 100644
--- a/spec/ruby/core/hash/new_spec.rb
+++ b/spec/ruby/core/hash/new_spec.rb
@@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+# Actually these are specs of Hash#initialize, there is no Hash.new it's just Class.new.
describe "Hash.new" do
it "creates an empty Hash if passed no arguments" do
Hash.new.should == {}
@@ -14,7 +15,7 @@ describe "Hash.new" do
it "does not create a copy of the default argument" do
str = "foo"
- Hash.new(str).default.should equal(str)
+ Hash.new(str).default.should.equal?(str)
end
it "creates a Hash with a default_proc if passed a block" do
@@ -26,11 +27,42 @@ describe "Hash.new" do
end
it "raises an ArgumentError if more than one argument is passed" do
- -> { Hash.new(5,6) }.should raise_error(ArgumentError)
+ -> { Hash.new(5,6) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if passed both default argument and default block" do
- -> { Hash.new(5) { 0 } }.should raise_error(ArgumentError)
- -> { Hash.new(nil) { 0 } }.should raise_error(ArgumentError)
+ -> { Hash.new(5) { 0 } }.should.raise(ArgumentError)
+ -> { Hash.new(nil) { 0 } }.should.raise(ArgumentError)
+ end
+
+ ruby_version_is ""..."3.4" do
+ it "emits a deprecation warning if keyword arguments are passed" do
+ -> { Hash.new(unknown: true) }.should complain(
+ Regexp.new(Regexp.escape("Calling Hash.new with keyword arguments is deprecated and will be removed in Ruby 3.4; use Hash.new({ key: value }) instead"))
+ )
+
+ -> { Hash.new(1, unknown: true) }.should.raise(ArgumentError)
+ -> { Hash.new(unknown: true) { 0 } }.should.raise(ArgumentError)
+
+ Hash.new({ unknown: true }).default.should == { unknown: true }
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "accepts a capacity: argument" do
+ Hash.new(5, capacity: 42).default.should == 5
+ Hash.new(capacity: 42).default.should == nil
+ (Hash.new(capacity: 42) { 1 }).default_proc.should_not == nil
+ end
+
+ it "ignores negative capacity" do
+ -> { Hash.new(capacity: -42) }.should_not.raise
+ end
+
+ it "raises an error if unknown keyword arguments are passed" do
+ -> { Hash.new(unknown: true) }.should.raise(ArgumentError)
+ -> { Hash.new(1, unknown: true) }.should.raise(ArgumentError)
+ -> { Hash.new(unknown: true) { 0 } }.should.raise(ArgumentError)
+ end
end
end
diff --git a/spec/ruby/core/hash/rassoc_spec.rb b/spec/ruby/core/hash/rassoc_spec.rb
index f3b2a6de20..73379f6867 100644
--- a/spec/ruby/core/hash/rassoc_spec.rb
+++ b/spec/ruby/core/hash/rassoc_spec.rb
@@ -6,7 +6,7 @@ describe "Hash#rassoc" do
end
it "returns an Array if the argument is a value of the Hash" do
- @h.rassoc(:green).should be_an_instance_of(Array)
+ @h.rassoc(:green).should.instance_of?(Array)
end
it "returns a 2-element Array if the argument is a value of the Hash" do
@@ -28,15 +28,15 @@ describe "Hash#rassoc" do
it "uses #== to compare the argument to the values" do
@h[:key] = 1.0
1.should == 1.0
- @h.rassoc(1).should eql [:key, 1.0]
+ @h.rassoc(1).should.eql? [:key, 1.0]
end
it "returns nil if the argument is not a value of the Hash" do
- @h.rassoc(:banana).should be_nil
+ @h.rassoc(:banana).should == nil
end
it "returns nil if the argument is not a value of the Hash even when there is a default" do
- Hash.new(42).merge!( foo: :bar ).rassoc(42).should be_nil
- Hash.new{|h, k| h[k] = 42}.merge!( foo: :bar ).rassoc(42).should be_nil
+ Hash.new(42).merge!( foo: :bar ).rassoc(42).should == nil
+ Hash.new{|h, k| h[k] = 42}.merge!( foo: :bar ).rassoc(42).should == nil
end
end
diff --git a/spec/ruby/core/hash/rehash_spec.rb b/spec/ruby/core/hash/rehash_spec.rb
index ff7b644786..fcd5a037bd 100644
--- a/spec/ruby/core/hash/rehash_spec.rb
+++ b/spec/ruby/core/hash/rehash_spec.rb
@@ -20,7 +20,7 @@ describe "Hash#rehash" do
h.keys.include?(k1).should == true
- h.rehash.should equal(h)
+ h.rehash.should.equal?(h)
h.key?(k1).should == true
h[k1].should == :v1
end
@@ -59,8 +59,56 @@ describe "Hash#rehash" do
h.keys.should == [a]
end
+ it "removes duplicate keys for large hashes" do
+ a = [1,2]
+ b = [1]
+
+ h = {}
+ h[a] = true
+ h[b] = true
+ 100.times { |n| h[n] = true }
+ b << 2
+ h.size.should == 102
+ h.keys.should.include? a
+ h.keys.should.include? b
+ h.rehash
+ h.size.should == 101
+ h.keys.should.include? a
+ h.keys.should_not.include? [1]
+ end
+
+ it "iterates keys in insertion order" do
+ key = Class.new do
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ def hash
+ 123
+ end
+ end
+
+ a, b, c, d = key.new('a'), key.new('b'), key.new('c'), key.new('d')
+ h = { a => 1, b => 2, c => 3, d => 4 }
+ h.size.should == 4
+
+ key.class_exec do
+ def eql?(other)
+ true
+ end
+ end
+
+ h.rehash
+ h.size.should == 1
+ k, v = h.first
+ k.name.should == 'a'
+ v.should == 4
+ end
+
it "raises a FrozenError if called on a frozen instance" do
- -> { HashSpecs.frozen_hash.rehash }.should raise_error(FrozenError)
- -> { HashSpecs.empty_frozen_hash.rehash }.should raise_error(FrozenError)
+ -> { HashSpecs.frozen_hash.rehash }.should.raise(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.rehash }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/hash/reject_spec.rb b/spec/ruby/core/hash/reject_spec.rb
index 397000ab67..aa1a074897 100644
--- a/spec/ruby/core/hash/reject_spec.rb
+++ b/spec/ruby/core/hash/reject_spec.rb
@@ -28,15 +28,8 @@ describe "Hash#reject" do
context "with extra state" do
it "returns Hash instance for subclasses" do
- HashSpecs::MyHash[1 => 2, 3 => 4].reject { false }.should be_kind_of(Hash)
- HashSpecs::MyHash[1 => 2, 3 => 4].reject { true }.should be_kind_of(Hash)
- end
-
- ruby_version_is ''...'2.7' do
- it "does not taint the resulting hash" do
- h = { a: 1 }.taint
- h.reject {false}.should_not.tainted?
- end
+ HashSpecs::MyHash[1 => 2, 3 => 4].reject { false }.should.is_a?(Hash)
+ HashSpecs::MyHash[1 => 2, 3 => 4].reject { true }.should.is_a?(Hash)
end
end
@@ -51,6 +44,27 @@ describe "Hash#reject" do
reject_pairs.should == reject_bang_pairs
end
+ it "does not retain the default value" do
+ h = Hash.new(1)
+ h.reject { false }.default.should == nil
+ h[:a] = 1
+ h.reject { false }.default.should == nil
+ end
+
+ it "does not retain the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ h = Hash.new(&pr)
+ h.reject { false }.default_proc.should == nil
+ h[:a] = 1
+ h.reject { false }.default_proc.should == nil
+ end
+
+ it "retains compare_by_identity flag" do
+ h = { a: 9, c: 4 }.compare_by_identity
+ h2 = h.reject { |k, _| k == :a }
+ h2.compare_by_identity?.should == true
+ end
+
it_behaves_like :hash_iteration_no_block, :reject
it_behaves_like :enumeratorized_with_origin_size, :reject, { 1 => 2, 3 => 4, 5 => 6 }
end
@@ -65,7 +79,7 @@ describe "Hash#reject!" do
it "removes all entries if the block is true" do
h = { a: 1, b: 2, c: 3 }
- h.reject! { |k,v| true }.should equal(h)
+ h.reject! { |k,v| true }.should.equal?(h)
h.should == {}
end
@@ -90,11 +104,11 @@ describe "Hash#reject!" do
end
it "raises a FrozenError if called on a frozen instance that is modified" do
- -> { HashSpecs.empty_frozen_hash.reject! { true } }.should raise_error(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.reject! { true } }.should.raise(FrozenError)
end
it "raises a FrozenError if called on a frozen instance that would not be modified" do
- -> { HashSpecs.frozen_hash.reject! { false } }.should raise_error(FrozenError)
+ -> { HashSpecs.frozen_hash.reject! { false } }.should.raise(FrozenError)
end
it_behaves_like :hash_iteration_no_block, :reject!
diff --git a/spec/ruby/core/hash/replace_spec.rb b/spec/ruby/core/hash/replace_spec.rb
index 92b2118fd3..4dacbf9779 100644
--- a/spec/ruby/core/hash/replace_spec.rb
+++ b/spec/ruby/core/hash/replace_spec.rb
@@ -1,7 +1,79 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/replace'
describe "Hash#replace" do
- it_behaves_like :hash_replace, :replace
+ it "replaces the contents of self with other" do
+ h = { a: 1, b: 2 }
+ h.replace(c: -1, d: -2).should.equal?(h)
+ h.should == { c: -1, d: -2 }
+ end
+
+ it "tries to convert the passed argument to a hash using #to_hash" do
+ obj = mock('{1=>2,3=>4}')
+ obj.should_receive(:to_hash).and_return({ 1 => 2, 3 => 4 })
+
+ h = {}
+ h.replace(obj)
+ h.should == { 1 => 2, 3 => 4 }
+ end
+
+ it "calls to_hash on hash subclasses" do
+ h = {}
+ h.replace(HashSpecs::ToHashHash[1 => 2])
+ h.should == { 1 => 2 }
+ end
+
+ it "does not retain the default value" do
+ hash = Hash.new(1)
+ hash.replace(b: 2).default.should == nil
+ end
+
+ it "transfers the default value of an argument" do
+ hash = Hash.new(1)
+ { a: 1 }.replace(hash).default.should == 1
+ end
+
+ it "does not retain the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ hash = Hash.new(&pr)
+ hash.replace(b: 2).default_proc.should == nil
+ end
+
+ it "transfers the default_proc of an argument" do
+ pr = proc { |h, k| h[k] = [] }
+ hash = Hash.new(&pr)
+ { a: 1 }.replace(hash).default_proc.should == pr
+ end
+
+ it "does not call the default_proc of an argument" do
+ hash_a = Hash.new { |h, k| k * 5 }
+ hash_b = Hash.new(-> { raise "Should not invoke lambda" })
+ hash_a.replace(hash_b)
+ hash_a.default.should == hash_b.default
+ end
+
+ it "transfers compare_by_identity flag of an argument" do
+ h = { a: 1, c: 3 }
+ h2 = { b: 2, d: 4 }.compare_by_identity
+ h.replace(h2)
+ h.compare_by_identity?.should == true
+ end
+
+ it "does not retain compare_by_identity flag" do
+ h = { a: 1, c: 3 }.compare_by_identity
+ h.replace(b: 2, d: 4)
+ h.compare_by_identity?.should == false
+ end
+
+ it "raises a FrozenError if called on a frozen instance that would not be modified" do
+ -> do
+ HashSpecs.frozen_hash.replace(HashSpecs.frozen_hash)
+ end.should.raise(FrozenError)
+ end
+
+ it "raises a FrozenError if called on a frozen instance that is modified" do
+ -> do
+ HashSpecs.frozen_hash.replace(HashSpecs.empty_frozen_hash)
+ end.should.raise(FrozenError)
+ end
end
diff --git a/spec/ruby/core/hash/ruby2_keywords_hash_spec.rb b/spec/ruby/core/hash/ruby2_keywords_hash_spec.rb
new file mode 100644
index 0000000000..609a53f81f
--- /dev/null
+++ b/spec/ruby/core/hash/ruby2_keywords_hash_spec.rb
@@ -0,0 +1,81 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash.ruby2_keywords_hash?" do
+ it "returns false if the Hash is not a keywords Hash" do
+ Hash.ruby2_keywords_hash?({}).should == false
+ end
+
+ it "returns true if the Hash is a keywords Hash marked by Module#ruby2_keywords" do
+ obj = Class.new {
+ ruby2_keywords def m(*args)
+ args.last
+ end
+ }.new
+ Hash.ruby2_keywords_hash?(obj.m(a: 1)).should == true
+ end
+
+ it "raises TypeError for non-Hash" do
+ -> { Hash.ruby2_keywords_hash?(nil) }.should.raise(TypeError)
+ end
+end
+
+describe "Hash.ruby2_keywords_hash" do
+ it "returns a copy of a Hash and marks the copy as a keywords Hash" do
+ h = {a: 1}.freeze
+ kw = Hash.ruby2_keywords_hash(h)
+ Hash.ruby2_keywords_hash?(h).should == false
+ Hash.ruby2_keywords_hash?(kw).should == true
+ kw.should == h
+ end
+
+ it "returns an instance of the subclass if called on an instance of a subclass of Hash" do
+ h = HashSpecs::MyHash.new
+ h[:a] = 1
+ kw = Hash.ruby2_keywords_hash(h)
+ kw.class.should == HashSpecs::MyHash
+ Hash.ruby2_keywords_hash?(h).should == false
+ Hash.ruby2_keywords_hash?(kw).should == true
+ kw.should == h
+ end
+
+ it "copies instance variables" do
+ h = {a: 1}
+ h.instance_variable_set(:@foo, 42)
+ kw = Hash.ruby2_keywords_hash(h)
+ kw.instance_variable_get(:@foo).should == 42
+ end
+
+ it "copies the hash internals" do
+ h = {a: 1}
+ kw = Hash.ruby2_keywords_hash(h)
+ h[:a] = 2
+ kw[:a].should == 1
+ end
+
+ it "raises TypeError for non-Hash" do
+ -> { Hash.ruby2_keywords_hash(nil) }.should.raise(TypeError)
+ end
+
+ it "retains the default value" do
+ hash = Hash.new(1)
+ Hash.ruby2_keywords_hash(hash).default.should == 1
+ hash[:a] = 1
+ Hash.ruby2_keywords_hash(hash).default.should == 1
+ end
+
+ it "retains the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ hash = Hash.new(&pr)
+ Hash.ruby2_keywords_hash(hash).default_proc.should == pr
+ hash[:a] = 1
+ Hash.ruby2_keywords_hash(hash).default_proc.should == pr
+ end
+
+ it "retains compare_by_identity_flag" do
+ hash = {}.compare_by_identity
+ Hash.ruby2_keywords_hash(hash).compare_by_identity?.should == true
+ hash[:a] = 1
+ Hash.ruby2_keywords_hash(hash).compare_by_identity?.should == true
+ end
+end
diff --git a/spec/ruby/core/hash/select_spec.rb b/spec/ruby/core/hash/select_spec.rb
index 38b0180b0e..60b2ce67c1 100644
--- a/spec/ruby/core/hash/select_spec.rb
+++ b/spec/ruby/core/hash/select_spec.rb
@@ -1,10 +1,112 @@
require_relative '../../spec_helper'
-require_relative 'shared/select'
+require_relative '../enumerable/shared/enumeratorized'
+require_relative 'fixtures/classes'
+require_relative 'shared/iteration'
describe "Hash#select" do
- it_behaves_like :hash_select, :select
+ before :each do
+ @hsh = { 1 => 2, 3 => 4, 5 => 6 }
+ @empty = {}
+ end
+
+ it "yields two arguments: key and value" do
+ all_args = []
+ { 1 => 2, 3 => 4 }.select { |*args| all_args << args }
+ all_args.sort.should == [[1, 2], [3, 4]]
+ end
+
+ it "returns a Hash of entries for which block is true" do
+ a_pairs = { 'a' => 9, 'c' => 4, 'b' => 5, 'd' => 2 }.select { |k,v| v % 2 == 0 }
+ a_pairs.should.instance_of?(Hash)
+ a_pairs.sort.should == [['c', 4], ['d', 2]]
+ end
+
+ it "processes entries with the same order as reject" do
+ h = { a: 9, c: 4, b: 5, d: 2 }
+
+ select_pairs = []
+ reject_pairs = []
+ h.dup.select{ |*pair| select_pairs << pair }
+ h.reject { |*pair| reject_pairs << pair }
+
+ select_pairs.should == reject_pairs
+ end
+
+ it "returns an Enumerator when called on a non-empty hash without a block" do
+ @hsh.select.should.instance_of?(Enumerator)
+ end
+
+ it "returns an Enumerator when called on an empty hash without a block" do
+ @empty.select.should.instance_of?(Enumerator)
+ end
+
+ it "does not retain the default value" do
+ h = Hash.new(1)
+ h.select { true }.default.should == nil
+ h[:a] = 1
+ h.select { true }.default.should == nil
+ end
+
+ it "does not retain the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ h = Hash.new(&pr)
+ h.select { true }.default_proc.should == nil
+ h[:a] = 1
+ h.select { true }.default_proc.should == nil
+ end
+
+ it "retains compare_by_identity flag" do
+ h = { a: 9, c: 4 }.compare_by_identity
+ h2 = h.select { |k, _| k == :a }
+ h2.compare_by_identity?.should == true
+ end
+
+ it_behaves_like :hash_iteration_no_block, :select
+
+ before :each do
+ @object = { 1 => 2, 3 => 4, 5 => 6 }
+ end
+ it_behaves_like :enumeratorized_with_origin_size, :select
end
describe "Hash#select!" do
- it_behaves_like :hash_select!, :select!
+ before :each do
+ @hsh = { 1 => 2, 3 => 4, 5 => 6 }
+ @empty = {}
+ end
+
+ it "is equivalent to keep_if if changes are made" do
+ h = { a: 2 }
+ h.select! { |k,v| v <= 1 }.should.equal? h
+
+ h = { 1 => 2, 3 => 4 }
+ all_args_select = []
+ h.dup.select! { |*args| all_args_select << args }
+ all_args_select.should == [[1, 2], [3, 4]]
+ end
+
+ it "removes all entries if the block is false" do
+ h = { a: 1, b: 2, c: 3 }
+ h.select! { |k,v| false }.should.equal?(h)
+ h.should == {}
+ end
+
+ it "returns nil if no changes were made" do
+ { a: 1 }.select! { |k,v| v <= 1 }.should == nil
+ end
+
+ it "raises a FrozenError if called on an empty frozen instance" do
+ -> { HashSpecs.empty_frozen_hash.select! { false } }.should.raise(FrozenError)
+ end
+
+ it "raises a FrozenError if called on a frozen instance that would not be modified" do
+ -> { HashSpecs.frozen_hash.select! { true } }.should.raise(FrozenError)
+ end
+
+ it_behaves_like :hash_iteration_no_block, :select!
+
+ before :each do
+ @object = { 1 => 2, 3 => 4, 5 => 6 }
+ end
+ it_behaves_like :enumeratorized_with_origin_size, :select!
end
diff --git a/spec/ruby/core/hash/shared/comparison.rb b/spec/ruby/core/hash/shared/comparison.rb
index 07564e4cec..2b62837232 100644
--- a/spec/ruby/core/hash/shared/comparison.rb
+++ b/spec/ruby/core/hash/shared/comparison.rb
@@ -1,15 +1,15 @@
describe :hash_comparison, shared: true do
it "raises a TypeError if the right operand is not a hash" do
- -> { { a: 1 }.send(@method, 1) }.should raise_error(TypeError)
- -> { { a: 1 }.send(@method, nil) }.should raise_error(TypeError)
- -> { { a: 1 }.send(@method, []) }.should raise_error(TypeError)
+ -> { { a: 1 }.send(@method, 1) }.should.raise(TypeError)
+ -> { { a: 1 }.send(@method, nil) }.should.raise(TypeError)
+ -> { { a: 1 }.send(@method, []) }.should.raise(TypeError)
end
it "returns false if both hashes have the same keys but different values" do
h1 = { a: 1 }
h2 = { a: 2 }
- h1.send(@method, h2).should be_false
- h2.send(@method, h1).should be_false
+ h1.send(@method, h2).should == false
+ h2.send(@method, h1).should == false
end
end
diff --git a/spec/ruby/core/hash/shared/each.rb b/spec/ruby/core/hash/shared/each.rb
deleted file mode 100644
index 5e88a35445..0000000000
--- a/spec/ruby/core/hash/shared/each.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-describe :hash_each, shared: true do
-
- # This is inconsistent with below, MRI checks the block arity in rb_hash_each_pair()
- it "yields a [[key, value]] Array for each pair to a block expecting |*args|" do
- all_args = []
- { 1 => 2, 3 => 4 }.send(@method) { |*args| all_args << args }
- all_args.sort.should == [[[1, 2]], [[3, 4]]]
- end
-
- it "yields the key and value of each pair to a block expecting |key, value|" do
- r = {}
- h = { a: 1, b: 2, c: 3, d: 5 }
- h.send(@method) { |k,v| r[k.to_s] = v.to_s }.should equal(h)
- r.should == { "a" => "1", "b" => "2", "c" => "3", "d" => "5" }
- end
-
- it "yields the key only to a block expecting |key,|" do
- ary = []
- h = { "a" => 1, "b" => 2, "c" => 3 }
- h.send(@method) { |k,| ary << k }
- ary.sort.should == ["a", "b", "c"]
- end
-
- ruby_version_is ""..."2.8" do
- it "yields 2 values and not an Array of 2 elements when given a callable of arity 2" do
- obj = Object.new
- def obj.foo(key, value)
- ScratchPad << key << value
- end
-
- ScratchPad.record([])
- { "a" => 1 }.send(@method, &obj.method(:foo))
- ScratchPad.recorded.should == ["a", 1]
-
- ScratchPad.record([])
- { "a" => 1 }.send(@method, &-> key, value { ScratchPad << key << value })
- ScratchPad.recorded.should == ["a", 1]
- end
- end
-
- ruby_version_is "2.8" do
- it "yields an Array of 2 elements when given a callable of arity 2" do
- obj = Object.new
- def obj.foo(key, value)
- end
-
- -> {
- { "a" => 1 }.send(@method, &obj.method(:foo))
- }.should raise_error(ArgumentError)
-
- -> {
- { "a" => 1 }.send(@method, &-> key, value { })
- }.should raise_error(ArgumentError)
- end
- end
-
- it "uses the same order as keys() and values()" do
- h = { a: 1, b: 2, c: 3, d: 5 }
- keys = []
- values = []
-
- h.send(@method) do |k, v|
- keys << k
- values << v
- end
-
- keys.should == h.keys
- values.should == h.values
- end
-
- # Confirming the argument-splatting works from child class for both k, v and [k, v]
- it "properly expands (or not) child class's 'each'-yielded args" do
- cls1 = Class.new(Hash) do
- attr_accessor :k_v
- def each
- super do |k, v|
- @k_v = [k, v]
- yield k, v
- end
- end
- end
-
- cls2 = Class.new(Hash) do
- attr_accessor :k_v
- def each
- super do |k, v|
- @k_v = [k, v]
- yield([k, v])
- end
- end
- end
-
- obj1 = cls1.new
- obj1['a'] = 'b'
- obj1.map {|k, v| [k, v]}.should == [['a', 'b']]
- obj1.k_v.should == ['a', 'b']
-
- obj2 = cls2.new
- obj2['a'] = 'b'
- obj2.map {|k, v| [k, v]}.should == [['a', 'b']]
- obj2.k_v.should == ['a', 'b']
- end
-end
diff --git a/spec/ruby/core/hash/shared/eql.rb b/spec/ruby/core/hash/shared/eql.rb
index d8c33179fc..512e1ad016 100644
--- a/spec/ruby/core/hash/shared/eql.rb
+++ b/spec/ruby/core/hash/shared/eql.rb
@@ -3,7 +3,7 @@ describe :hash_eql, shared: true do
value = mock('x')
value.should_not_receive(:==)
value.should_not_receive(:eql?)
- { 1 => value }.send(@method, { 2 => value }).should be_false
+ { 1 => value }.send(@method, { 2 => value }).should == false
end
it "returns false when the numbers of keys differ without comparing any elements" do
@@ -13,8 +13,8 @@ describe :hash_eql, shared: true do
obj.should_not_receive(:==)
obj.should_not_receive(:eql?)
- {}.send(@method, h).should be_false
- h.send(@method, {}).should be_false
+ {}.send(@method, h).should == false
+ h.send(@method, {}).should == false
end
it "first compares keys via hash" do
@@ -23,7 +23,7 @@ describe :hash_eql, shared: true do
y = mock('y')
y.should_receive(:hash).any_number_of_times.and_return(0)
- { x => 1 }.send(@method, { y => 1 }).should be_false
+ { x => 1 }.send(@method, { y => 1 }).should == false
end
it "does not compare keys with different hash codes via eql?" do
@@ -35,39 +35,39 @@ describe :hash_eql, shared: true do
x.should_receive(:hash).any_number_of_times.and_return(0)
y.should_receive(:hash).any_number_of_times.and_return(1)
- { x => 1 }.send(@method, { y => 1 }).should be_false
+ { x => 1 }.send(@method, { y => 1 }).should == false
end
it "computes equality for recursive hashes" do
h = {}
h[:a] = h
- h.send(@method, h[:a]).should be_true
- (h == h[:a]).should be_true
+ h.send(@method, h[:a]).should == true
+ (h == h[:a]).should == true
end
it "doesn't call to_hash on objects" do
mock_hash = mock("fake hash")
def mock_hash.to_hash() {} end
- {}.send(@method, mock_hash).should be_false
+ {}.send(@method, mock_hash).should == false
end
it "computes equality for complex recursive hashes" do
a, b = {}, {}
a.merge! self: a, other: b
b.merge! self: b, other: a
- a.send(@method, b).should be_true # they both have the same structure!
+ a.send(@method, b).should == true # they both have the same structure!
c = {}
c.merge! other: c, self: c
- c.send(@method, a).should be_true # subtle, but they both have the same structure!
+ c.send(@method, a).should == true # subtle, but they both have the same structure!
a[:delta] = c[:delta] = a
- c.send(@method, a).should be_false # not quite the same structure, as a[:other][:delta] = nil
+ c.send(@method, a).should == false # not quite the same structure, as a[:other][:delta] = nil
c[:delta] = 42
- c.send(@method, a).should be_false
+ c.send(@method, a).should == false
a[:delta] = 42
- c.send(@method, a).should be_false
+ c.send(@method, a).should == false
b[:delta] = 42
- c.send(@method, a).should be_true
+ c.send(@method, a).should == true
end
it "computes equality for recursive hashes & arrays" do
@@ -76,19 +76,19 @@ describe :hash_eql, shared: true do
x << a
y << c
z << b
- b.send(@method, c).should be_true # they clearly have the same structure!
- y.send(@method, z).should be_true
- a.send(@method, b).should be_true # subtle, but they both have the same structure!
- x.send(@method, y).should be_true
+ b.send(@method, c).should == true # they clearly have the same structure!
+ y.send(@method, z).should == true
+ a.send(@method, b).should == true # subtle, but they both have the same structure!
+ x.send(@method, y).should == true
y << x
- y.send(@method, z).should be_false
+ y.send(@method, z).should == false
z << x
- y.send(@method, z).should be_true
+ y.send(@method, z).should == true
a[:foo], a[:bar] = a[:bar], a[:foo]
- a.send(@method, b).should be_false
+ a.send(@method, b).should == false
b[:bar] = b[:foo]
- b.send(@method, c).should be_false
+ b.send(@method, c).should == false
end
end
@@ -100,7 +100,7 @@ describe :hash_eql_additional, shared: true do
def y.==(o) false end
def x.eql?(o) false end
def y.eql?(o) false end
- { 1 => x }.send(@method, { 1 => y }).should be_false
+ { 1 => x }.send(@method, { 1 => y }).should == false
x = mock('x')
y = mock('y')
@@ -108,121 +108,75 @@ describe :hash_eql_additional, shared: true do
def y.==(o) true end
def x.eql?(o) true end
def y.eql?(o) true end
- { 1 => x }.send(@method, { 1 => y }).should be_true
+ { 1 => x }.send(@method, { 1 => y }).should == true
end
it "compares keys with eql? semantics" do
- { 1.0 => "x" }.send(@method, { 1.0 => "x" }).should be_true
- { 1.0 => "x" }.send(@method, { 1.0 => "x" }).should be_true
- { 1 => "x" }.send(@method, { 1.0 => "x" }).should be_false
- { 1.0 => "x" }.send(@method, { 1 => "x" }).should be_false
+ { 1.0 => "x" }.send(@method, { 1.0 => "x" }).should == true
+ { 1.0 => "x" }.send(@method, { 1.0 => "x" }).should == true
+ { 1 => "x" }.send(@method, { 1.0 => "x" }).should == false
+ { 1.0 => "x" }.send(@method, { 1 => "x" }).should == false
end
- it "returns true iff other Hash has the same number of keys and each key-value pair matches" do
+ it "returns true if and only if other Hash has the same number of keys and each key-value pair matches" do
a = { a: 5 }
b = {}
- a.send(@method, b).should be_false
+ a.send(@method, b).should == false
b[:a] = 5
- a.send(@method, b).should be_true
+ a.send(@method, b).should == true
not_supported_on :opal do
c = { "a" => 5 }
- a.send(@method, c).should be_false
+ a.send(@method, c).should == false
end
c = { "A" => 5 }
- a.send(@method, c).should be_false
+ a.send(@method, c).should == false
c = { a: 6 }
- a.send(@method, c).should be_false
+ a.send(@method, c).should == false
end
it "does not call to_hash on hash subclasses" do
- { 5 => 6 }.send(@method, HashSpecs::ToHashHash[5 => 6]).should be_true
+ { 5 => 6 }.send(@method, HashSpecs::ToHashHash[5 => 6]).should == true
end
it "ignores hash class differences" do
h = { 1 => 2, 3 => 4 }
- HashSpecs::MyHash[h].send(@method, h).should be_true
- HashSpecs::MyHash[h].send(@method, HashSpecs::MyHash[h]).should be_true
- h.send(@method, HashSpecs::MyHash[h]).should be_true
+ HashSpecs::MyHash[h].send(@method, h).should == true
+ HashSpecs::MyHash[h].send(@method, HashSpecs::MyHash[h]).should == true
+ h.send(@method, HashSpecs::MyHash[h]).should == true
end
- ruby_version_is '2.7' do
- # Why isn't this true of eql? too ?
- it "compares keys with matching hash codes via eql?" do
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
+ # Why isn't this true of eql? too ?
+ it "compares keys with matching hash codes via eql?" do
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
- def obj.eql?(o)
- return true if self.equal?(o)
- false
- end
-
- obj
+ def obj.eql?(o)
+ return true if self.equal?(o)
+ false
end
- { a[0] => 1 }.send(@method, { a[1] => 1 }).should be_false
-
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
-
- def obj.eql?(o)
- true
- end
-
- obj
- end
-
- { a[0] => 1 }.send(@method, { a[1] => 1 }).should be_true
+ obj
end
- end
- ruby_version_is ''...'2.7' do
- # Why isn't this true of eql? too ?
- it "compares keys with matching hash codes via eql?" do
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
+ { a[0] => 1 }.send(@method, { a[1] => 1 }).should == false
- # It's undefined whether the impl does a[0].eql?(a[1]) or
- # a[1].eql?(a[0]) so we taint both.
- def obj.eql?(o)
- return true if self.equal?(o)
- taint
- o.taint
- false
- end
+ a = Array.new(2) do
+ obj = mock('0')
+ obj.should_receive(:hash).at_least(1).and_return(0)
- obj
+ def obj.eql?(o)
+ true
end
- { a[0] => 1 }.send(@method, { a[1] => 1 }).should be_false
- a[0].tainted?.should be_true
- a[1].tainted?.should be_true
-
- a = Array.new(2) do
- obj = mock('0')
- obj.should_receive(:hash).at_least(1).and_return(0)
-
- def obj.eql?(o)
- # It's undefined whether the impl does a[0].send(@method, a[1]) or
- # a[1].send(@method, a[0]) so we taint both.
- taint
- o.taint
- true
- end
-
- obj
- end
-
- { a[0] => 1 }.send(@method, { a[1] => 1 }).should be_true
- a[0].tainted?.should be_true
- a[1].tainted?.should be_true
+ obj
end
+
+ { a[0] => 1 }.send(@method, { a[1] => 1 }).should == true
end
it "compares the values in self to values in other hash" do
@@ -231,20 +185,20 @@ describe :hash_eql_additional, shared: true do
l_val.should_receive(:eql?).with(r_val).and_return(true)
- { 1 => l_val }.eql?({ 1 => r_val }).should be_true
+ { 1 => l_val }.eql?({ 1 => r_val }).should == true
end
end
describe :hash_eql_additional_more, shared: true do
it "returns true if other Hash has the same number of keys and each key-value pair matches, even though the default-value are not same" do
- Hash.new(5).send(@method, Hash.new(1)).should be_true
- Hash.new {|h, k| 1}.send(@method, Hash.new {}).should be_true
- Hash.new {|h, k| 1}.send(@method, Hash.new(2)).should be_true
+ Hash.new(5).send(@method, Hash.new(1)).should == true
+ Hash.new {|h, k| 1}.send(@method, Hash.new {}).should == true
+ Hash.new {|h, k| 1}.send(@method, Hash.new(2)).should == true
d = Hash.new {|h, k| 1}
e = Hash.new {}
d[1] = 2
e[1] = 2
- d.send(@method, e).should be_true
+ d.send(@method, e).should == true
end
end
diff --git a/spec/ruby/core/hash/shared/equal.rb b/spec/ruby/core/hash/shared/equal.rb
deleted file mode 100644
index 43606437fe..0000000000
--- a/spec/ruby/core/hash/shared/equal.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-describe :hash_equal, shared: true do
- it "does not compare values when keys don't match" do
- value = mock('x')
- value.should_not_receive(:==)
- value.should_not_receive(:eql?)
- { 1 => value }.send(@method, { 2 => value }).should be_false
- end
-
- it "returns false when the numbers of keys differ without comparing any elements" do
- obj = mock('x')
- h = { obj => obj }
-
- obj.should_not_receive(:==)
- obj.should_not_receive(:eql?)
-
- {}.send(@method, h).should be_false
- h.send(@method, {}).should be_false
- end
-
- it "first compares keys via hash" do
- x = mock('x')
- x.should_receive(:hash).and_return(0)
- y = mock('y')
- y.should_receive(:hash).and_return(0)
-
- { x => 1 }.send(@method, { y => 1 }).should be_false
- end
-
- it "does not compare keys with different hash codes via eql?" do
- x = mock('x')
- y = mock('y')
- x.should_not_receive(:eql?)
- y.should_not_receive(:eql?)
-
- x.should_receive(:hash).and_return(0)
- y.should_receive(:hash).and_return(1)
-
- def x.hash() 0 end
- def y.hash() 1 end
-
- { x => 1 }.send(@method, { y => 1 }).should be_false
- end
-
- it "computes equality for recursive hashes" do
- h = {}
- h[:a] = h
- h.send(@method, h[:a]).should be_true
- (h == h[:a]).should be_true
- end
-
- it "computes equality for complex recursive hashes" do
- a, b = {}, {}
- a.merge! self: a, other: b
- b.merge! self: b, other: a
- a.send(@method, b).should be_true # they both have the same structure!
-
- c = {}
- c.merge! other: c, self: c
- c.send(@method, a).should be_true # subtle, but they both have the same structure!
- a[:delta] = c[:delta] = a
- c.send(@method, a).should be_false # not quite the same structure, as a[:other][:delta] = nil
- c[:delta] = 42
- c.send(@method, a).should be_false
- a[:delta] = 42
- c.send(@method, a).should be_false
- b[:delta] = 42
- c.send(@method, a).should be_true
- end
-
- it "computes equality for recursive hashes & arrays" do
- x, y, z = [], [], []
- a, b, c = {foo: x, bar: 42}, {foo: y, bar: 42}, {foo: z, bar: 42}
- x << a
- y << c
- z << b
- b.send(@method, c).should be_true # they clearly have the same structure!
- y.send(@method, z).should be_true
- a.send(@method, b).should be_true # subtle, but they both have the same structure!
- x.send(@method, y).should be_true
- y << x
- y.send(@method, z).should be_false
- z << x
- y.send(@method, z).should be_true
-
- a[:foo], a[:bar] = a[:bar], a[:foo]
- a.send(@method, b).should be_false
- b[:bar] = b[:foo]
- b.send(@method, c).should be_false
- end
-end
diff --git a/spec/ruby/core/hash/shared/greater_than.rb b/spec/ruby/core/hash/shared/greater_than.rb
index 1f8b9fcfb7..dfe0b80250 100644
--- a/spec/ruby/core/hash/shared/greater_than.rb
+++ b/spec/ruby/core/hash/shared/greater_than.rb
@@ -5,11 +5,11 @@ describe :hash_greater_than, shared: true do
end
it "returns true if the other hash is a subset of self" do
- @h1.send(@method, @h2).should be_true
+ @h1.send(@method, @h2).should == true
end
it "returns false if the other hash is not a subset of self" do
- @h2.send(@method, @h1).should be_false
+ @h2.send(@method, @h1).should == false
end
it "converts the right operand to a hash before comparing" do
@@ -18,6 +18,6 @@ describe :hash_greater_than, shared: true do
{ a: 1, b: 2 }
end
- @h1.send(@method, o).should be_true
+ @h1.send(@method, o).should == true
end
end
diff --git a/spec/ruby/core/hash/shared/index.rb b/spec/ruby/core/hash/shared/index.rb
deleted file mode 100644
index 4858ba85f5..0000000000
--- a/spec/ruby/core/hash/shared/index.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative '../../../spec_helper'
-require_relative '../fixtures/classes'
-
-describe :hash_index, shared: true do
- it "returns the corresponding key for value" do
- { 2 => 'a', 1 => 'b' }.send(@method, 'b').should == 1
- end
-
- it "returns nil if the value is not found" do
- { a: -1, b: 3.14, c: 2.718 }.send(@method, 1).should be_nil
- end
-
- it "doesn't return default value if the value is not found" do
- Hash.new(5).send(@method, 5).should be_nil
- end
-
- it "compares values using ==" do
- { 1 => 0 }.send(@method, 0.0).should == 1
- { 1 => 0.0 }.send(@method, 0).should == 1
-
- needle = mock('needle')
- inhash = mock('inhash')
- inhash.should_receive(:==).with(needle).and_return(true)
-
- { 1 => inhash }.send(@method, needle).should == 1
- end
-end
diff --git a/spec/ruby/core/hash/shared/iteration.rb b/spec/ruby/core/hash/shared/iteration.rb
index d27c2443f8..322e4b9a2f 100644
--- a/spec/ruby/core/hash/shared/iteration.rb
+++ b/spec/ruby/core/hash/shared/iteration.rb
@@ -5,15 +5,15 @@ describe :hash_iteration_no_block, shared: true do
end
it "returns an Enumerator if called on a non-empty hash without a block" do
- @hsh.send(@method).should be_an_instance_of(Enumerator)
+ @hsh.send(@method).should.instance_of?(Enumerator)
end
it "returns an Enumerator if called on an empty hash without a block" do
- @empty.send(@method).should be_an_instance_of(Enumerator)
+ @empty.send(@method).should.instance_of?(Enumerator)
end
it "returns an Enumerator if called on a frozen instance" do
@hsh.freeze
- @hsh.send(@method).should be_an_instance_of(Enumerator)
+ @hsh.send(@method).should.instance_of?(Enumerator)
end
end
diff --git a/spec/ruby/core/hash/shared/key.rb b/spec/ruby/core/hash/shared/key.rb
deleted file mode 100644
index 17f9f81457..0000000000
--- a/spec/ruby/core/hash/shared/key.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-describe :hash_key_p, shared: true do
- it "returns true if argument is a key" do
- h = { a: 1, b: 2, c: 3, 4 => 0 }
- h.send(@method, :a).should == true
- h.send(@method, :b).should == true
- h.send(@method, 2).should == false
- h.send(@method, 4).should == true
-
- not_supported_on :opal do
- h.send(@method, 'b').should == false
- h.send(@method, 4.0).should == false
- end
- end
-
- it "returns true if the key's matching value was nil" do
- { xyz: nil }.send(@method, :xyz).should == true
- end
-
- it "returns true if the key's matching value was false" do
- { xyz: false }.send(@method, :xyz).should == true
- end
-
- it "returns true if the key is nil" do
- { nil => 'b' }.send(@method, nil).should == true
- { nil => nil }.send(@method, nil).should == true
- end
-
- it "compares keys with the same #hash value via #eql?" do
- x = mock('x')
- x.stub!(:hash).and_return(42)
-
- y = mock('y')
- y.stub!(:hash).and_return(42)
- y.should_receive(:eql?).and_return(false)
-
- { x => nil }.send(@method, y).should == false
- end
-end
diff --git a/spec/ruby/core/hash/shared/length.rb b/spec/ruby/core/hash/shared/length.rb
deleted file mode 100644
index 24f5563759..0000000000
--- a/spec/ruby/core/hash/shared/length.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-describe :hash_length, shared: true do
- it "returns the number of entries" do
- { a: 1, b: 'c' }.send(@method).should == 2
- h = { a: 1, b: 2 }
- h[:a] = 2
- h.send(@method).should == 2
- { a: 1, b: 1, c: 1 }.send(@method).should == 3
- {}.send(@method).should == 0
- Hash.new(5).send(@method).should == 0
- Hash.new { 5 }.send(@method).should == 0
- end
-end
diff --git a/spec/ruby/core/hash/shared/less_than.rb b/spec/ruby/core/hash/shared/less_than.rb
index cdc6f14546..071b3e97bb 100644
--- a/spec/ruby/core/hash/shared/less_than.rb
+++ b/spec/ruby/core/hash/shared/less_than.rb
@@ -5,11 +5,11 @@ describe :hash_less_than, shared: true do
end
it "returns true if self is a subset of the other hash" do
- @h1.send(@method, @h2).should be_true
+ @h1.send(@method, @h2).should == true
end
it "returns false if self is not a subset of the other hash" do
- @h2.send(@method, @h1).should be_false
+ @h2.send(@method, @h1).should == false
end
it "converts the right operand to a hash before comparing" do
@@ -18,6 +18,6 @@ describe :hash_less_than, shared: true do
{ a: 1, b: 2, c: 3 }
end
- @h1.send(@method, o).should be_true
+ @h1.send(@method, o).should == true
end
end
diff --git a/spec/ruby/core/hash/shared/replace.rb b/spec/ruby/core/hash/shared/replace.rb
deleted file mode 100644
index bea64384bb..0000000000
--- a/spec/ruby/core/hash/shared/replace.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-describe :hash_replace, shared: true do
- it "replaces the contents of self with other" do
- h = { a: 1, b: 2 }
- h.send(@method, c: -1, d: -2).should equal(h)
- h.should == { c: -1, d: -2 }
- end
-
- it "tries to convert the passed argument to a hash using #to_hash" do
- obj = mock('{1=>2,3=>4}')
- obj.should_receive(:to_hash).and_return({ 1 => 2, 3 => 4 })
-
- h = {}
- h.send(@method, obj)
- h.should == { 1 => 2, 3 => 4 }
- end
-
- it "calls to_hash on hash subclasses" do
- h = {}
- h.send(@method, HashSpecs::ToHashHash[1 => 2])
- h.should == { 1 => 2 }
- end
-
- it "does not transfer default values" do
- hash_a = {}
- hash_b = Hash.new(5)
- hash_a.send(@method, hash_b)
- hash_a.default.should == 5
-
- hash_a = {}
- hash_b = Hash.new { |h, k| k * 2 }
- hash_a.send(@method, hash_b)
- hash_a.default(5).should == 10
-
- hash_a = Hash.new { |h, k| k * 5 }
- hash_b = Hash.new(-> { raise "Should not invoke lambda" })
- hash_a.send(@method, hash_b)
- hash_a.default.should == hash_b.default
- end
-
- it "raises a FrozenError if called on a frozen instance that would not be modified" do
- -> do
- HashSpecs.frozen_hash.send(@method, HashSpecs.frozen_hash)
- end.should raise_error(FrozenError)
- end
-
- it "raises a FrozenError if called on a frozen instance that is modified" do
- -> do
- HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash)
- end.should raise_error(FrozenError)
- end
-end
diff --git a/spec/ruby/core/hash/shared/select.rb b/spec/ruby/core/hash/shared/select.rb
deleted file mode 100644
index 5170af50d6..0000000000
--- a/spec/ruby/core/hash/shared/select.rb
+++ /dev/null
@@ -1,91 +0,0 @@
-require_relative '../../../spec_helper'
-require_relative '../fixtures/classes'
-require_relative '../shared/iteration'
-require_relative '../../enumerable/shared/enumeratorized'
-
-describe :hash_select, shared: true do
- before :each do
- @hsh = { 1 => 2, 3 => 4, 5 => 6 }
- @empty = {}
- end
-
- it "yields two arguments: key and value" do
- all_args = []
- { 1 => 2, 3 => 4 }.send(@method) { |*args| all_args << args }
- all_args.sort.should == [[1, 2], [3, 4]]
- end
-
- it "returns a Hash of entries for which block is true" do
- a_pairs = { 'a' => 9, 'c' => 4, 'b' => 5, 'd' => 2 }.send(@method) { |k,v| v % 2 == 0 }
- a_pairs.should be_an_instance_of(Hash)
- a_pairs.sort.should == [['c', 4], ['d', 2]]
- end
-
- it "processes entries with the same order as reject" do
- h = { a: 9, c: 4, b: 5, d: 2 }
-
- select_pairs = []
- reject_pairs = []
- h.dup.send(@method) { |*pair| select_pairs << pair }
- h.reject { |*pair| reject_pairs << pair }
-
- select_pairs.should == reject_pairs
- end
-
- it "returns an Enumerator when called on a non-empty hash without a block" do
- @hsh.send(@method).should be_an_instance_of(Enumerator)
- end
-
- it "returns an Enumerator when called on an empty hash without a block" do
- @empty.send(@method).should be_an_instance_of(Enumerator)
- end
-
- it_should_behave_like :hash_iteration_no_block
-
- before :each do
- @object = { 1 => 2, 3 => 4, 5 => 6 }
- end
- it_should_behave_like :enumeratorized_with_origin_size
-end
-
-describe :hash_select!, shared: true do
- before :each do
- @hsh = { 1 => 2, 3 => 4, 5 => 6 }
- @empty = {}
- end
-
- it "is equivalent to keep_if if changes are made" do
- h = { a: 2 }
- h.send(@method) { |k,v| v <= 1 }.should equal h
-
- h = { 1 => 2, 3 => 4 }
- all_args_select = []
- h.dup.send(@method) { |*args| all_args_select << args }
- all_args_select.should == [[1, 2], [3, 4]]
- end
-
- it "removes all entries if the block is false" do
- h = { a: 1, b: 2, c: 3 }
- h.send(@method) { |k,v| false }.should equal(h)
- h.should == {}
- end
-
- it "returns nil if no changes were made" do
- { a: 1 }.send(@method) { |k,v| v <= 1 }.should == nil
- end
-
- it "raises a FrozenError if called on an empty frozen instance" do
- -> { HashSpecs.empty_frozen_hash.send(@method) { false } }.should raise_error(FrozenError)
- end
-
- it "raises a FrozenError if called on a frozen instance that would not be modified" do
- -> { HashSpecs.frozen_hash.send(@method) { true } }.should raise_error(FrozenError)
- end
-
- it_should_behave_like :hash_iteration_no_block
-
- before :each do
- @object = { 1 => 2, 3 => 4, 5 => 6 }
- end
- it_should_behave_like :enumeratorized_with_origin_size
-end
diff --git a/spec/ruby/core/hash/shared/store.rb b/spec/ruby/core/hash/shared/store.rb
deleted file mode 100644
index 84ffb41e33..0000000000
--- a/spec/ruby/core/hash/shared/store.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :hash_store, shared: true do
- it "associates the key with the value and return the value" do
- h = { a: 1 }
- h.send(@method, :b, 2).should == 2
- h.should == { b:2, a:1 }
- end
-
- it "duplicates string keys using dup semantics" do
- # dup doesn't copy singleton methods
- key = "foo"
- def key.reverse() "bar" end
- h = {}
- h.send(@method, key, 0)
- h.keys[0].reverse.should == "oof"
- end
-
- it "stores unequal keys that hash to the same value" do
- h = {}
- k1 = ["x"]
- k2 = ["y"]
- # So they end up in the same bucket
- k1.should_receive(:hash).and_return(0)
- k2.should_receive(:hash).and_return(0)
-
- h.send(@method, k1, 1)
- h.send(@method, k2, 2)
- h.size.should == 2
- end
-
- it "accepts keys with private #hash method" do
- key = HashSpecs::KeyWithPrivateHash.new
- h = {}
- h.send(@method, key, "foo")
- h[key].should == "foo"
- end
-
- it " accepts keys with a Bignum hash" do
- o = mock(hash: 1 << 100)
- h = {}
- h[o] = 1
- h[o].should == 1
- end
-
- it "duplicates and freezes string keys" do
- key = "foo"
- h = {}
- h.send(@method, key, 0)
- key << "bar"
-
- h.should == { "foo" => 0 }
- h.keys[0].should.frozen?
- end
-
- it "doesn't duplicate and freeze already frozen string keys" do
- key = "foo".freeze
- h = {}
- h.send(@method, key, 0)
- h.keys[0].should equal(key)
- end
-
- it "keeps the existing key in the hash if there is a matching one" do
- h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 }
- key1 = HashSpecs::ByValueKey.new(13)
- key2 = HashSpecs::ByValueKey.new(13)
- h[key1] = 41
- key_in_hash = h.keys.last
- key_in_hash.should equal(key1)
- h[key2] = 42
- last_key = h.keys.last
- last_key.should equal(key_in_hash)
- last_key.should_not equal(key2)
- end
-
- it "keeps the existing String key in the hash if there is a matching one" do
- h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 }
- key1 = "foo"
- key2 = "foo"
- key1.should_not equal(key2)
- h[key1] = 41
- frozen_key = h.keys.last
- frozen_key.should_not equal(key1)
- h[key2] = 42
- h.keys.last.should equal(frozen_key)
- h.keys.last.should_not equal(key2)
- end
-
- it "raises a FrozenError if called on a frozen instance" do
- -> { HashSpecs.frozen_hash.send(@method, 1, 2) }.should raise_error(FrozenError)
- end
-
- it "does not raise an exception if changing the value of an existing key during iteration" do
- hash = {1 => 2, 3 => 4, 5 => 6}
- hash.each { hash.send(@method, 1, :foo) }
- hash.should == {1 => :foo, 3 => 4, 5 => 6}
- end
-end
diff --git a/spec/ruby/core/hash/shared/to_s.rb b/spec/ruby/core/hash/shared/to_s.rb
deleted file mode 100644
index b0e3705d01..0000000000
--- a/spec/ruby/core/hash/shared/to_s.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-require_relative '../../../spec_helper'
-require_relative '../fixtures/classes'
-
-describe :hash_to_s, shared: true do
-
- it "returns a string representation with same order as each()" do
- h = { a: [1, 2], b: -2, d: -6, nil => nil }
-
- pairs = []
- h.each do |key, value|
- pairs << key.inspect + '=>' + value.inspect
- end
-
- str = '{' + pairs.join(', ') + '}'
- h.send(@method).should == str
- end
-
- it "calls #inspect on keys and values" do
- key = mock('key')
- val = mock('val')
- key.should_receive(:inspect).and_return('key')
- val.should_receive(:inspect).and_return('val')
-
- { key => val }.send(@method).should == '{key=>val}'
- end
-
- it "does not call #to_s on a String returned from #inspect" do
- str = "abc"
- str.should_not_receive(:to_s)
-
- { a: str }.send(@method).should == '{:a=>"abc"}'
- end
-
- it "calls #to_s on the object returned from #inspect if the Object isn't a String" do
- obj = mock("Hash#inspect/to_s calls #to_s")
- obj.should_receive(:inspect).and_return(obj)
- obj.should_receive(:to_s).and_return("abc")
-
- { a: obj }.send(@method).should == "{:a=>abc}"
- end
-
- it "does not call #to_str on the object returned from #inspect when it is not a String" do
- obj = mock("Hash#inspect/to_s does not call #to_str")
- obj.should_receive(:inspect).and_return(obj)
- obj.should_not_receive(:to_str)
-
- { a: obj }.send(@method).should =~ /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/
- end
-
- it "does not call #to_str on the object returned from #to_s when it is not a String" do
- obj = mock("Hash#inspect/to_s does not call #to_str on #to_s result")
- obj.should_receive(:inspect).and_return(obj)
- obj.should_receive(:to_s).and_return(obj)
- obj.should_not_receive(:to_str)
-
- { a: obj }.send(@method).should =~ /^\{:a=>#<MockObject:0x[0-9a-f]+>\}$/
- end
-
- it "does not swallow exceptions raised by #to_s" do
- obj = mock("Hash#inspect/to_s does not swallow #to_s exceptions")
- obj.should_receive(:inspect).and_return(obj)
- obj.should_receive(:to_s).and_raise(Exception)
-
- -> { { a: obj }.send(@method) }.should raise_error(Exception)
- end
-
- it "handles hashes with recursive values" do
- x = {}
- x[0] = x
- x.send(@method).should == '{0=>{...}}'
-
- x = {}
- y = {}
- x[0] = y
- y[1] = x
- x.send(@method).should == "{0=>{1=>{...}}}"
- y.send(@method).should == "{1=>{0=>{...}}}"
- end
-
- ruby_version_is ''...'2.7' do
- it "returns a tainted string if self is tainted and not empty" do
- {}.taint.send(@method).tainted?.should be_false
- { nil => nil }.taint.send(@method).tainted?.should be_true
- end
-
- it "returns an untrusted string if self is untrusted and not empty" do
- {}.untrust.send(@method).untrusted?.should be_false
- { nil => nil }.untrust.send(@method).untrusted?.should be_true
- end
- end
-
- it "does not raise if inspected result is not default external encoding" do
- utf_16be = mock("utf_16be")
- utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE))
-
- {a: utf_16be}.send(@method).should == '{:a=>"utf_16be \u3042"}'
- end
-end
diff --git a/spec/ruby/core/hash/shared/update.rb b/spec/ruby/core/hash/shared/update.rb
deleted file mode 100644
index 4bb20e5b85..0000000000
--- a/spec/ruby/core/hash/shared/update.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-describe :hash_update, shared: true do
- it "adds the entries from other, overwriting duplicate keys. Returns self" do
- h = { _1: 'a', _2: '3' }
- h.send(@method, _1: '9', _9: 2).should equal(h)
- h.should == { _1: "9", _2: "3", _9: 2 }
- end
-
- it "sets any duplicate key to the value of block if passed a block" do
- h1 = { a: 2, b: -1 }
- h2 = { a: -2, c: 1 }
- h1.send(@method, h2) { |k,x,y| 3.14 }.should equal(h1)
- h1.should == { c: 1, b: -1, a: 3.14 }
-
- h1.send(@method, h1) { nil }
- h1.should == { a: nil, b: nil, c: nil }
- end
-
- it "tries to convert the passed argument to a hash using #to_hash" do
- obj = mock('{1=>2}')
- obj.should_receive(:to_hash).and_return({ 1 => 2 })
- { 3 => 4 }.send(@method, obj).should == { 1 => 2, 3 => 4 }
- end
-
- it "does not call to_hash on hash subclasses" do
- { 3 => 4 }.send(@method, HashSpecs::ToHashHash[1 => 2]).should == { 1 => 2, 3 => 4 }
- end
-
- it "processes entries with same order as merge()" do
- h = { 1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => [] }
- merge_bang_pairs = []
- merge_pairs = []
- h.merge(h) { |*arg| merge_pairs << arg }
- h.send(@method, h) { |*arg| merge_bang_pairs << arg }
- merge_bang_pairs.should == merge_pairs
- end
-
- it "raises a FrozenError on a frozen instance that is modified" do
- -> do
- HashSpecs.frozen_hash.send(@method, 1 => 2)
- end.should raise_error(FrozenError)
- end
-
- it "checks frozen status before coercing an object with #to_hash" do
- obj = mock("to_hash frozen")
- # This is necessary because mock cleanup code cannot run on the frozen
- # object.
- def obj.to_hash() raise Exception, "should not receive #to_hash" end
- obj.freeze
-
- -> { HashSpecs.frozen_hash.send(@method, obj) }.should raise_error(FrozenError)
- end
-
- # see redmine #1571
- it "raises a FrozenError on a frozen instance that would not be modified" do
- -> do
- HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash)
- end.should raise_error(FrozenError)
- end
-
- it "does not raise an exception if changing the value of an existing key during iteration" do
- hash = {1 => 2, 3 => 4, 5 => 6}
- hash2 = {1 => :foo, 3 => :bar}
- hash.each { hash.send(@method, hash2) }
- hash.should == {1 => :foo, 3 => :bar, 5 => 6}
- end
-
- ruby_version_is "2.6" do
- it "accepts multiple hashes" do
- result = { a: 1 }.send(@method, { b: 2 }, { c: 3 }, { d: 4 })
- result.should == { a: 1, b: 2, c: 3, d: 4 }
- end
-
- it "accepts zero arguments" do
- hash = { a: 1 }
- hash.send(@method).should eql(hash)
- end
- end
-end
diff --git a/spec/ruby/core/hash/shared/value.rb b/spec/ruby/core/hash/shared/value.rb
deleted file mode 100644
index aac76c253e..0000000000
--- a/spec/ruby/core/hash/shared/value.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-describe :hash_value_p, shared: true do
- it "returns true if the value exists in the hash" do
- { a: :b }.send(@method, :a).should == false
- { 1 => 2 }.send(@method, 2).should == true
- h = Hash.new(5)
- h.send(@method, 5).should == false
- h = Hash.new { 5 }
- h.send(@method, 5).should == false
- end
-
- it "uses == semantics for comparing values" do
- { 5 => 2.0 }.send(@method, 2).should == true
- end
-end
diff --git a/spec/ruby/core/hash/shared/values_at.rb b/spec/ruby/core/hash/shared/values_at.rb
deleted file mode 100644
index ef3b0e8ba0..0000000000
--- a/spec/ruby/core/hash/shared/values_at.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-describe :hash_values_at, shared: true do
- it "returns an array of values for the given keys" do
- h = { a: 9, b: 'a', c: -10, d: nil }
- h.send(@method).should be_kind_of(Array)
- h.send(@method).should == []
- h.send(@method, :a, :d, :b).should be_kind_of(Array)
- h.send(@method, :a, :d, :b).should == [9, nil, 'a']
- end
-end
diff --git a/spec/ruby/core/hash/shift_spec.rb b/spec/ruby/core/hash/shift_spec.rb
index 9d43e640f5..6095d2e55f 100644
--- a/spec/ruby/core/hash/shift_spec.rb
+++ b/spec/ruby/core/hash/shift_spec.rb
@@ -8,7 +8,7 @@ describe "Hash#shift" do
h.size.times do |i|
r = h.shift
- r.should be_kind_of(Array)
+ r.should.is_a?(Array)
h2[r.first].should == r.last
h.size.should == h2.size - i - 1
end
@@ -30,23 +30,22 @@ describe "Hash#shift" do
h.should == {}
end
- it "calls #default with nil if the Hash is empty" do
+ it "returns nil if the Hash is empty" do
h = {}
def h.default(key)
- key.should == nil
- :foo
+ raise
end
- h.shift.should == :foo
+ h.shift.should == nil
end
it "returns nil from an empty hash" do
{}.shift.should == nil
end
- it "returns (computed) default for empty hashes" do
- Hash.new(5).shift.should == 5
+ it "returns nil for empty hashes with defaults and default procs" do
+ Hash.new(5).shift.should == nil
h = Hash.new { |*args| args }
- h.shift.should == [h, nil]
+ h.shift.should == nil
end
it "preserves Hash invariants when removing the last item" do
@@ -58,8 +57,8 @@ describe "Hash#shift" do
end
it "raises a FrozenError if called on a frozen instance" do
- -> { HashSpecs.frozen_hash.shift }.should raise_error(FrozenError)
- -> { HashSpecs.empty_frozen_hash.shift }.should raise_error(FrozenError)
+ -> { HashSpecs.frozen_hash.shift }.should.raise(FrozenError)
+ -> { HashSpecs.empty_frozen_hash.shift }.should.raise(FrozenError)
end
it "works when the hash is at capacity" do
diff --git a/spec/ruby/core/hash/size_spec.rb b/spec/ruby/core/hash/size_spec.rb
index 1e8abd8d97..5e5008a5dc 100644
--- a/spec/ruby/core/hash/size_spec.rb
+++ b/spec/ruby/core/hash/size_spec.rb
@@ -1,7 +1,14 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/length'
describe "Hash#size" do
- it_behaves_like :hash_length, :size
+ it "returns the number of entries" do
+ { a: 1, b: 'c' }.size.should == 2
+ h = { a: 1, b: 2 }
+ h[:a] = 2
+ h.size.should == 2
+ { a: 1, b: 1, c: 1 }.size.should == 3
+ {}.size.should == 0
+ Hash.new(5).size.should == 0
+ Hash.new { 5 }.size.should == 0
+ end
end
diff --git a/spec/ruby/core/hash/slice_spec.rb b/spec/ruby/core/hash/slice_spec.rb
index e3046d83d7..fd93254517 100644
--- a/spec/ruby/core/hash/slice_spec.rb
+++ b/spec/ruby/core/hash/slice_spec.rb
@@ -7,8 +7,8 @@ describe "Hash#slice" do
it "returns a new empty hash without arguments" do
ret = @hash.slice
- ret.should_not equal(@hash)
- ret.should be_an_instance_of(Hash)
+ ret.should_not.equal?(@hash)
+ ret.should.instance_of?(Hash)
ret.should == {}
end
@@ -50,4 +50,25 @@ describe "Hash#slice" do
ScratchPad.recorded.should == []
end
+
+ it "does not retain the default value" do
+ h = Hash.new(1)
+ h.slice(:a).default.should == nil
+ h[:a] = 1
+ h.slice(:a).default.should == nil
+ end
+
+ it "does not retain the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ h = Hash.new(&pr)
+ h.slice(:a).default_proc.should == nil
+ h[:a] = 1
+ h.slice(:a).default_proc.should == nil
+ end
+
+ it "retains compare_by_identity flag" do
+ h = { a: 9, c: 4 }.compare_by_identity
+ h2 = h.slice(:a)
+ h2.compare_by_identity?.should == true
+ end
end
diff --git a/spec/ruby/core/hash/store_spec.rb b/spec/ruby/core/hash/store_spec.rb
index 7e975380ec..7017d8ba2b 100644
--- a/spec/ruby/core/hash/store_spec.rb
+++ b/spec/ruby/core/hash/store_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/store'
describe "Hash#store" do
- it_behaves_like :hash_store, :store
+ it "is an alias of Hash#[]=" do
+ Hash.instance_method(:store).should == Hash.instance_method(:[]=)
+ end
end
diff --git a/spec/ruby/core/hash/to_a_spec.rb b/spec/ruby/core/hash/to_a_spec.rb
index 46f871389a..8c638db6c3 100644
--- a/spec/ruby/core/hash/to_a_spec.rb
+++ b/spec/ruby/core/hash/to_a_spec.rb
@@ -10,7 +10,7 @@ describe "Hash#to_a" do
pairs << [key, value]
end
- h.to_a.should be_kind_of(Array)
+ h.to_a.should.is_a?(Array)
h.to_a.should == pairs
end
@@ -23,17 +23,7 @@ describe "Hash#to_a" do
end
ent = h.entries
- ent.should be_kind_of(Array)
+ ent.should.is_a?(Array)
ent.should == pairs
end
-
- ruby_version_is ''...'2.7' do
- it "returns a tainted array if self is tainted" do
- {}.taint.to_a.tainted?.should be_true
- end
-
- it "returns an untrusted array if self is untrusted" do
- {}.untrust.to_a.untrusted?.should be_true
- end
- end
end
diff --git a/spec/ruby/core/hash/to_h_spec.rb b/spec/ruby/core/hash/to_h_spec.rb
index d6eaac9f33..5d5a280dac 100644
--- a/spec/ruby/core/hash/to_h_spec.rb
+++ b/spec/ruby/core/hash/to_h_spec.rb
@@ -4,7 +4,7 @@ require_relative 'fixtures/classes'
describe "Hash#to_h" do
it "returns self for Hash instances" do
h = {}
- h.to_h.should equal(h)
+ h.to_h.should.equal?(h)
end
describe "when called on a subclass of Hash" do
@@ -14,61 +14,93 @@ describe "Hash#to_h" do
end
it "returns a new Hash instance" do
- @h.to_h.should be_an_instance_of(Hash)
+ @h.to_h.should.instance_of?(Hash)
@h.to_h.should == @h
@h[:foo].should == :bar
end
- it "copies the default" do
+ it "retains the default" do
@h.default = 42
@h.to_h.default.should == 42
@h[:hello].should == 42
end
- it "copies the default_proc" do
+ it "retains the default_proc" do
@h.default_proc = prc = Proc.new{ |h, k| h[k] = 2 * k }
@h.to_h.default_proc.should == prc
@h[42].should == 84
end
+
+ it "retains compare_by_identity flag" do
+ @h.compare_by_identity
+ @h.to_h.compare_by_identity?.should == true
+ end
end
- ruby_version_is "2.6" do
- context "with block" do
- it "converts [key, value] pairs returned by the block to a hash" do
- { a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v]}.should == { "a" => 1, "b" => 4 }
- end
-
- it "raises ArgumentError if block returns longer or shorter array" do
- -> do
- { a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v, 1] }
- end.should raise_error(ArgumentError, /element has wrong array length/)
-
- -> do
- { a: 1, b: 2 }.to_h { |k, v| [k] }
- end.should raise_error(ArgumentError, /element has wrong array length/)
- end
-
- it "raises TypeError if block returns something other than Array" do
- -> do
- { a: 1, b: 2 }.to_h { |k, v| "not-array" }
- end.should raise_error(TypeError, /wrong element type String/)
- end
-
- it "coerces returned pair to Array with #to_ary" do
- x = mock('x')
- x.stub!(:to_ary).and_return([:b, 'b'])
-
- { a: 1 }.to_h { |k| x }.should == { :b => 'b' }
- end
-
- it "does not coerce returned pair to Array with #to_a" do
- x = mock('x')
- x.stub!(:to_a).and_return([:b, 'b'])
-
- -> do
- { a: 1 }.to_h { |k| x }
- end.should raise_error(TypeError, /wrong element type MockObject/)
- end
+ context "with block" do
+ it "converts [key, value] pairs returned by the block to a hash" do
+ { a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v]}.should == { "a" => 1, "b" => 4 }
+ end
+
+ it "passes to a block each pair's key and value as separate arguments" do
+ ScratchPad.record []
+ { a: 1, b: 2 }.to_h { |k, v| ScratchPad << [k, v]; [k, v] }
+ ScratchPad.recorded.sort.should == [[:a, 1], [:b, 2]]
+
+ ScratchPad.record []
+ { a: 1, b: 2 }.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
+ ScratchPad.recorded.sort.should == [[:a, 1], [:b, 2]]
+ end
+
+ it "raises ArgumentError if block returns longer or shorter array" do
+ -> do
+ { a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v, 1] }
+ end.should.raise(ArgumentError, /element has wrong array length/)
+
+ -> do
+ { a: 1, b: 2 }.to_h { |k, v| [k] }
+ end.should.raise(ArgumentError, /element has wrong array length/)
+ end
+
+ it "raises TypeError if block returns something other than Array" do
+ -> do
+ { a: 1, b: 2 }.to_h { |k, v| "not-array" }
+ end.should.raise(TypeError, /wrong element type String/)
+ end
+
+ it "coerces returned pair to Array with #to_ary" do
+ x = mock('x')
+ x.stub!(:to_ary).and_return([:b, 'b'])
+
+ { a: 1 }.to_h { |k| x }.should == { :b => 'b' }
+ end
+
+ it "does not coerce returned pair to Array with #to_a" do
+ x = mock('x')
+ x.stub!(:to_a).and_return([:b, 'b'])
+
+ -> do
+ { a: 1 }.to_h { |k| x }
+ end.should.raise(TypeError, /wrong element type MockObject/)
+ end
+
+ it "does not retain the default value" do
+ h = Hash.new(1)
+ h2 = h.to_h { |k, v| [k.to_s, v*v]}
+ h2.default.should == nil
+ end
+
+ it "does not retain the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ h = Hash.new(&pr)
+ h2 = h.to_h { |k, v| [k.to_s, v*v]}
+ h2.default_proc.should == nil
+ end
+
+ it "does not retain compare_by_identity flag" do
+ h = { a: 9, c: 4 }.compare_by_identity
+ h2 = h.to_h { |k, v| [k.to_s, v*v]}
+ h2.compare_by_identity?.should == false
end
end
end
diff --git a/spec/ruby/core/hash/to_hash_spec.rb b/spec/ruby/core/hash/to_hash_spec.rb
index f479fa1fb2..f5622b3d9c 100644
--- a/spec/ruby/core/hash/to_hash_spec.rb
+++ b/spec/ruby/core/hash/to_hash_spec.rb
@@ -4,11 +4,11 @@ require_relative 'fixtures/classes'
describe "Hash#to_hash" do
it "returns self for Hash instances" do
h = {}
- h.to_hash.should equal(h)
+ h.to_hash.should.equal?(h)
end
it "returns self for instances of subclasses of Hash" do
h = HashSpecs::MyHash.new
- h.to_hash.should equal(h)
+ h.to_hash.should.equal?(h)
end
end
diff --git a/spec/ruby/core/hash/to_proc_spec.rb b/spec/ruby/core/hash/to_proc_spec.rb
index 73c96bdf08..bc4756600d 100644
--- a/spec/ruby/core/hash/to_proc_spec.rb
+++ b/spec/ruby/core/hash/to_proc_spec.rb
@@ -11,7 +11,7 @@ describe "Hash#to_proc" do
end
it "returns an instance of Proc" do
- @hash.to_proc.should be_an_instance_of Proc
+ @hash.to_proc.should.instance_of? Proc
end
describe "the returned proc" do
@@ -19,41 +19,33 @@ describe "Hash#to_proc" do
@proc = @hash.to_proc
end
- ruby_version_is ""..."2.8" do
- it "is not a lambda" do
- @proc.should_not.lambda?
- end
+ it "is a lambda" do
+ @proc.should.lambda?
end
- ruby_version_is "2.8" do
- it "is a lambda" do
- @proc.should.lambda?
- end
-
- it "has an arity of 1" do
- @proc.arity.should == 1
- end
+ it "has an arity of 1" do
+ @proc.arity.should == 1
end
it "raises ArgumentError if not passed exactly one argument" do
-> {
@proc.call
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@proc.call 1, 2
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
context "with a stored key" do
it "returns the paired value" do
- @proc.call(@key).should equal(@value)
+ @proc.call(@key).should.equal?(@value)
end
end
context "passed as a block" do
it "retrieves the hash's values" do
- [@key].map(&@proc)[0].should equal(@value)
+ [@key].map(&@proc)[0].should.equal?(@value)
end
context "to instance_exec" do
@@ -71,7 +63,7 @@ describe "Hash#to_proc" do
context "with no stored key" do
it "returns nil" do
- @proc.call(@unstored).should be_nil
+ @proc.call(@unstored).should == nil
end
context "when the hash has a default value" do
@@ -80,7 +72,7 @@ describe "Hash#to_proc" do
end
it "returns the default value" do
- @proc.call(@unstored).should equal(@default)
+ @proc.call(@unstored).should.equal?(@default)
end
end
@@ -93,7 +85,7 @@ describe "Hash#to_proc" do
end
it "raises an ArgumentError when calling #call on the Proc with no arguments" do
- -> { @hash.to_proc.call }.should raise_error(ArgumentError)
+ -> { @hash.to_proc.call }.should.raise(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/hash/to_s_spec.rb b/spec/ruby/core/hash/to_s_spec.rb
index e52b09962e..2915db6ef8 100644
--- a/spec/ruby/core/hash/to_s_spec.rb
+++ b/spec/ruby/core/hash/to_s_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/to_s'
describe "Hash#to_s" do
- it_behaves_like :hash_to_s, :to_s
+ it "is an alias of Hash#inspect" do
+ Hash.instance_method(:to_s).should == Hash.instance_method(:inspect)
+ end
end
diff --git a/spec/ruby/core/hash/transform_keys_spec.rb b/spec/ruby/core/hash/transform_keys_spec.rb
index 2c5d4124e0..d37a2b8616 100644
--- a/spec/ruby/core/hash/transform_keys_spec.rb
+++ b/spec/ruby/core/hash/transform_keys_spec.rb
@@ -7,8 +7,8 @@ describe "Hash#transform_keys" do
it "returns new hash" do
ret = @hash.transform_keys(&:succ)
- ret.should_not equal(@hash)
- ret.should be_an_instance_of(Hash)
+ ret.should_not.equal?(@hash)
+ ret.should.instance_of?(Hash)
end
it "sets the result as transformed keys with the given block" do
@@ -22,13 +22,13 @@ describe "Hash#transform_keys" do
it "makes both hashes to share values" do
value = [1, 2, 3]
new_hash = { a: value }.transform_keys(&:upcase)
- new_hash[:A].should equal(value)
+ new_hash[:A].should.equal?(value)
end
context "when no block is given" do
it "returns a sized Enumerator" do
enumerator = @hash.transform_keys
- enumerator.should be_an_instance_of(Enumerator)
+ enumerator.should.instance_of?(Enumerator)
enumerator.size.should == @hash.size
enumerator.each(&:succ).should == { b: 1, c: 2, d: 3 }
end
@@ -42,6 +42,39 @@ describe "Hash#transform_keys" do
r.keys.should == [:xfoo]
r.class.should == Hash
end
+
+ it "allows a hash argument" do
+ @hash.transform_keys({ a: :A, b: :B, c: :C }).should == { A: 1, B: 2, C: 3 }
+ end
+
+ it "allows a partial transformation of keys when using a hash argument" do
+ @hash.transform_keys({ a: :A, c: :C }).should == { A: 1, b: 2, C: 3 }
+ end
+
+ it "allows a combination of hash and block argument" do
+ @hash.transform_keys({ a: :A }, &:to_s).should == { A: 1, 'b' => 2, 'c' => 3 }
+ end
+
+ it "does not retain the default value" do
+ h = Hash.new(1)
+ h.transform_keys(&:succ).default.should == nil
+ h[:a] = 1
+ h.transform_keys(&:succ).default.should == nil
+ end
+
+ it "does not retain the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ h = Hash.new(&pr)
+ h.transform_values(&:succ).default_proc.should == nil
+ h[:a] = 1
+ h.transform_values(&:succ).default_proc.should == nil
+ end
+
+ it "does not retain compare_by_identity flag" do
+ h = { a: 9, c: 4 }.compare_by_identity
+ h2 = h.transform_keys(&:succ)
+ h2.compare_by_identity?.should == false
+ end
end
describe "Hash#transform_keys!" do
@@ -51,7 +84,7 @@ describe "Hash#transform_keys!" do
end
it "returns self" do
- @hash.transform_keys!(&:succ).should equal(@hash)
+ @hash.transform_keys!(&:succ).should.equal?(@hash)
end
it "updates self as transformed values with the given block" do
@@ -59,39 +92,17 @@ describe "Hash#transform_keys!" do
@hash.should == { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4 }
end
- # https://bugs.ruby-lang.org/issues/14380
- ruby_version_is ""..."2.5.1" do
- it "does not prevent conflicts between new keys and old ones" do
- @hash.transform_keys!(&:succ)
- @hash.should == { e: 1 }
- end
+ it "prevents conflicts between new keys and old ones" do
+ @hash.transform_keys!(&:succ)
+ @hash.should == { b: 1, c: 2, d: 3, e: 4 }
end
- ruby_version_is "2.5.1" do
- it "prevents conflicts between new keys and old ones" do
- @hash.transform_keys!(&:succ)
- @hash.should == { b: 1, c: 2, d: 3, e: 4 }
- end
- end
-
- ruby_version_is ""..."2.5.1" do
- it "partially modifies the contents if we broke from the block" do
- @hash.transform_keys! do |v|
- break if v == :c
- v.succ
- end
- @hash.should == { c: 1, d: 4 }
- end
- end
-
- ruby_version_is "2.5.1" do
- it "returns the processed 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 }
+ it "returns the processed keys and non evaluated keys if we break 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
it "keeps later pair if new keys conflict" do
@@ -101,29 +112,38 @@ describe "Hash#transform_keys!" do
context "when no block is given" do
it "returns a sized Enumerator" do
enumerator = @hash.transform_keys!
- enumerator.should be_an_instance_of(Enumerator)
+ enumerator.should.instance_of?(Enumerator)
enumerator.size.should == @hash.size
enumerator.each(&:upcase).should == { A: 1, B: 2, C: 3, D: 4 }
end
end
+ it "allows a hash argument" do
+ @hash.transform_keys!({ a: :A, b: :B, c: :C, d: :D })
+ @hash.should == { A: 1, B: 2, C: 3, D: 4 }
+ end
+
describe "on frozen instance" do
before :each do
@hash.freeze
end
it "raises a FrozenError on an empty hash" do
- ->{ {}.freeze.transform_keys!(&:upcase) }.should raise_error(FrozenError)
+ ->{ {}.freeze.transform_keys!(&:upcase) }.should.raise(FrozenError)
end
it "keeps pairs and raises a FrozenError" do
- ->{ @hash.transform_keys!(&:upcase) }.should raise_error(FrozenError)
+ ->{ @hash.transform_keys!(&:upcase) }.should.raise(FrozenError)
@hash.should == @initial_pairs
end
+ it "raises a FrozenError on hash argument" do
+ ->{ @hash.transform_keys!({ a: :A, b: :B, c: :C }) }.should.raise(FrozenError)
+ end
+
context "when no block is given" do
it "does not raise an exception" do
- @hash.transform_keys!.should be_an_instance_of(Enumerator)
+ @hash.transform_keys!.should.instance_of?(Enumerator)
end
end
end
diff --git a/spec/ruby/core/hash/transform_values_spec.rb b/spec/ruby/core/hash/transform_values_spec.rb
index acb469416a..b19543a9f4 100644
--- a/spec/ruby/core/hash/transform_values_spec.rb
+++ b/spec/ruby/core/hash/transform_values_spec.rb
@@ -7,8 +7,8 @@ describe "Hash#transform_values" do
it "returns new hash" do
ret = @hash.transform_values(&:succ)
- ret.should_not equal(@hash)
- ret.should be_an_instance_of(Hash)
+ ret.should_not.equal?(@hash)
+ ret.should.instance_of?(Hash)
end
it "sets the result as transformed values with the given block" do
@@ -19,13 +19,13 @@ describe "Hash#transform_values" do
key = [1, 2, 3]
new_hash = { key => 1 }.transform_values(&:succ)
new_hash[key].should == 2
- new_hash.keys[0].should equal(key)
+ new_hash.keys[0].should.equal?(key)
end
context "when no block is given" do
it "returns a sized Enumerator" do
enumerator = @hash.transform_values
- enumerator.should be_an_instance_of(Enumerator)
+ enumerator.should.instance_of?(Enumerator)
enumerator.size.should == @hash.size
enumerator.each(&:succ).should == { a: 2, b: 3, c: 4 }
end
@@ -39,6 +39,27 @@ describe "Hash#transform_values" do
r[:foo].should == 84
r.class.should == Hash
end
+
+ it "does not retain the default value" do
+ h = Hash.new(1)
+ h.transform_values(&:succ).default.should == nil
+ h[:a] = 1
+ h.transform_values(&:succ).default.should == nil
+ end
+
+ it "does not retain the default_proc" do
+ pr = proc { |h, k| h[k] = [] }
+ h = Hash.new(&pr)
+ h.transform_values(&:succ).default_proc.should == nil
+ h[:a] = 1
+ h.transform_values(&:succ).default_proc.should == nil
+ end
+
+ it "retains compare_by_identity flag" do
+ h = { a: 9, c: 4 }.compare_by_identity
+ h2 = h.transform_values(&:succ)
+ h2.compare_by_identity?.should == true
+ end
end
describe "Hash#transform_values!" do
@@ -48,7 +69,7 @@ describe "Hash#transform_values!" do
end
it "returns self" do
- @hash.transform_values!(&:succ).should equal(@hash)
+ @hash.transform_values!(&:succ).should.equal?(@hash)
end
it "updates self as transformed values with the given block" do
@@ -67,7 +88,7 @@ describe "Hash#transform_values!" do
context "when no block is given" do
it "returns a sized Enumerator" do
enumerator = @hash.transform_values!
- enumerator.should be_an_instance_of(Enumerator)
+ enumerator.should.instance_of?(Enumerator)
enumerator.size.should == @hash.size
enumerator.each(&:succ)
@hash.should == { a: 2, b: 3, c: 4 }
@@ -80,17 +101,17 @@ describe "Hash#transform_values!" do
end
it "raises a FrozenError on an empty hash" do
- ->{ {}.freeze.transform_values!(&:succ) }.should raise_error(FrozenError)
+ ->{ {}.freeze.transform_values!(&:succ) }.should.raise(FrozenError)
end
it "keeps pairs and raises a FrozenError" do
- ->{ @hash.transform_values!(&:succ) }.should raise_error(FrozenError)
+ ->{ @hash.transform_values!(&:succ) }.should.raise(FrozenError)
@hash.should == @initial_pairs
end
context "when no block is given" do
it "does not raise an exception" do
- @hash.transform_values!.should be_an_instance_of(Enumerator)
+ @hash.transform_values!.should.instance_of?(Enumerator)
end
end
end
diff --git a/spec/ruby/core/hash/try_convert_spec.rb b/spec/ruby/core/hash/try_convert_spec.rb
index 44195c5010..c321183356 100644
--- a/spec/ruby/core/hash/try_convert_spec.rb
+++ b/spec/ruby/core/hash/try_convert_spec.rb
@@ -4,47 +4,47 @@ require_relative 'fixtures/classes'
describe "Hash.try_convert" do
it "returns the argument if it's a Hash" do
x = Hash.new
- Hash.try_convert(x).should equal(x)
+ Hash.try_convert(x).should.equal?(x)
end
it "returns the argument if it's a kind of Hash" do
x = HashSpecs::MyHash.new
- Hash.try_convert(x).should equal(x)
+ Hash.try_convert(x).should.equal?(x)
end
it "returns nil when the argument does not respond to #to_hash" do
- Hash.try_convert(Object.new).should be_nil
+ Hash.try_convert(Object.new).should == nil
end
it "sends #to_hash to the argument and returns the result if it's nil" do
obj = mock("to_hash")
obj.should_receive(:to_hash).and_return(nil)
- Hash.try_convert(obj).should be_nil
+ Hash.try_convert(obj).should == nil
end
it "sends #to_hash to the argument and returns the result if it's a Hash" do
x = Hash.new
obj = mock("to_hash")
obj.should_receive(:to_hash).and_return(x)
- Hash.try_convert(obj).should equal(x)
+ Hash.try_convert(obj).should.equal?(x)
end
it "sends #to_hash to the argument and returns the result if it's a kind of Hash" do
x = HashSpecs::MyHash.new
obj = mock("to_hash")
obj.should_receive(:to_hash).and_return(x)
- Hash.try_convert(obj).should equal(x)
+ Hash.try_convert(obj).should.equal?(x)
end
it "sends #to_hash to the argument and raises TypeError if it's not a kind of Hash" do
obj = mock("to_hash")
obj.should_receive(:to_hash).and_return(Object.new)
- -> { Hash.try_convert obj }.should raise_error(TypeError)
+ -> { Hash.try_convert obj }.should raise_consistent_error(TypeError, "can't convert MockObject into Hash (MockObject#to_hash gives Object)")
end
it "does not rescue exceptions raised by #to_hash" do
obj = mock("to_hash")
obj.should_receive(:to_hash).and_raise(RuntimeError)
- -> { Hash.try_convert obj }.should raise_error(RuntimeError)
+ -> { Hash.try_convert obj }.should.raise(RuntimeError)
end
end
diff --git a/spec/ruby/core/hash/update_spec.rb b/spec/ruby/core/hash/update_spec.rb
index 0975045ad1..f3a3e6b4db 100644
--- a/spec/ruby/core/hash/update_spec.rb
+++ b/spec/ruby/core/hash/update_spec.rb
@@ -1,7 +1,79 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-require_relative 'shared/update'
describe "Hash#update" do
- it_behaves_like :hash_update, :update
+ it "adds the entries from other, overwriting duplicate keys. Returns self" do
+ h = { _1: 'a', _2: '3' }
+ h.update(_1: '9', _9: 2).should.equal?(h)
+ h.should == { _1: "9", _2: "3", _9: 2 }
+ end
+
+ it "sets any duplicate key to the value of block if passed a block" do
+ h1 = { a: 2, b: -1 }
+ h2 = { a: -2, c: 1 }
+ h1.update(h2) { |k,x,y| 3.14 }.should.equal?(h1)
+ h1.should == { c: 1, b: -1, a: 3.14 }
+
+ h1.update(h1) { nil }
+ h1.should == { a: nil, b: nil, c: nil }
+ end
+
+ it "tries to convert the passed argument to a hash using #to_hash" do
+ obj = mock('{1=>2}')
+ obj.should_receive(:to_hash).and_return({ 1 => 2 })
+ { 3 => 4 }.update(obj).should == { 1 => 2, 3 => 4 }
+ end
+
+ it "does not call to_hash on hash subclasses" do
+ { 3 => 4 }.update(HashSpecs::ToHashHash[1 => 2]).should == { 1 => 2, 3 => 4 }
+ end
+
+ it "processes entries with same order as merge()" do
+ h = { 1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => [] }
+ merge_bang_pairs = []
+ merge_pairs = []
+ h.merge(h) { |*arg| merge_pairs << arg }
+ h.update(h) { |*arg| merge_bang_pairs << arg }
+ merge_bang_pairs.should == merge_pairs
+ end
+
+ it "raises a FrozenError on a frozen instance that is modified" do
+ -> do
+ HashSpecs.frozen_hash.update(1 => 2)
+ end.should.raise(FrozenError)
+ end
+
+ it "checks frozen status before coercing an object with #to_hash" do
+ obj = mock("to_hash frozen")
+ # This is necessary because mock cleanup code cannot run on the frozen
+ # object.
+ def obj.to_hash() raise Exception, "should not receive #to_hash" end
+ obj.freeze
+
+ -> { HashSpecs.frozen_hash.update(obj) }.should.raise(FrozenError)
+ end
+
+ # see redmine #1571
+ it "raises a FrozenError on a frozen instance that would not be modified" do
+ -> do
+ HashSpecs.frozen_hash.update(HashSpecs.empty_frozen_hash)
+ end.should.raise(FrozenError)
+ end
+
+ it "does not raise an exception if changing the value of an existing key during iteration" do
+ hash = {1 => 2, 3 => 4, 5 => 6}
+ hash2 = {1 => :foo, 3 => :bar}
+ hash.each { hash.update(hash2) }
+ hash.should == {1 => :foo, 3 => :bar, 5 => 6}
+ end
+
+ it "accepts multiple hashes" do
+ result = { a: 1 }.update({ b: 2 }, { c: 3 }, { d: 4 })
+ result.should == { a: 1, b: 2, c: 3, d: 4 }
+ end
+
+ it "accepts zero arguments" do
+ hash = { a: 1 }
+ hash.update.should.eql?(hash)
+ end
end
diff --git a/spec/ruby/core/hash/value_spec.rb b/spec/ruby/core/hash/value_spec.rb
index 0ab16a5d1b..9cfbe576d2 100644
--- a/spec/ruby/core/hash/value_spec.rb
+++ b/spec/ruby/core/hash/value_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/value'
describe "Hash#value?" do
- it_behaves_like :hash_value_p, :value?
+ it "is an alias of Hash#has_value?" do
+ Hash.instance_method(:value?).should == Hash.instance_method(:has_value?)
+ end
end
diff --git a/spec/ruby/core/hash/values_at_spec.rb b/spec/ruby/core/hash/values_at_spec.rb
index b620a279ba..78dcd8df6a 100644
--- a/spec/ruby/core/hash/values_at_spec.rb
+++ b/spec/ruby/core/hash/values_at_spec.rb
@@ -1,7 +1,11 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/values_at'
describe "Hash#values_at" do
- it_behaves_like :hash_values_at, :values_at
+ it "returns an array of values for the given keys" do
+ h = { a: 9, b: 'a', c: -10, d: nil }
+ h.values_at.should.is_a?(Array)
+ h.values_at.should == []
+ h.values_at(:a, :d, :b).should.is_a?(Array)
+ h.values_at(:a, :d, :b).should == [9, nil, 'a']
+ end
end
diff --git a/spec/ruby/core/hash/values_spec.rb b/spec/ruby/core/hash/values_spec.rb
index 9f2a481a48..1fe5f48ad6 100644
--- a/spec/ruby/core/hash/values_spec.rb
+++ b/spec/ruby/core/hash/values_spec.rb
@@ -4,7 +4,7 @@ require_relative 'fixtures/classes'
describe "Hash#values" do
it "returns an array of values" do
h = { 1 => :a, 'a' => :a, 'the' => 'lang' }
- h.values.should be_kind_of(Array)
+ h.values.should.is_a?(Array)
h.values.sort {|a, b| a.to_s <=> b.to_s}.should == [:a, :a, 'lang']
end
end
diff --git a/spec/ruby/core/integer/allbits_spec.rb b/spec/ruby/core/integer/allbits_spec.rb
index 11acb52e1f..6023cc32bf 100644
--- a/spec/ruby/core/integer/allbits_spec.rb
+++ b/spec/ruby/core/integer/allbits_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "Integer#allbits?" do
- it "returns true iff all the bits of the argument are set in the receiver" do
+ it "returns true if and only if all the bits of the argument are set in the receiver" do
42.allbits?(42).should == true
0b1010_1010.allbits?(0b1000_0010).should == true
0b1010_1010.allbits?(0b1000_0001).should == false
@@ -30,8 +30,8 @@ describe "Integer#allbits?" do
-> {
(obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10])
13.allbits?(obj)
- }.should raise_error(TypeError)
- -> { 13.allbits?("10") }.should raise_error(TypeError)
- -> { 13.allbits?(:symbol) }.should raise_error(TypeError)
+ }.should.raise(TypeError)
+ -> { 13.allbits?("10") }.should.raise(TypeError)
+ -> { 13.allbits?(:symbol) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/integer/anybits_spec.rb b/spec/ruby/core/integer/anybits_spec.rb
index 7e510fd00b..1ea47b76dc 100644
--- a/spec/ruby/core/integer/anybits_spec.rb
+++ b/spec/ruby/core/integer/anybits_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "Integer#anybits?" do
- it "returns true iff all the bits of the argument are set in the receiver" do
+ it "returns true if and only if all the bits of the argument are set in the receiver" do
42.anybits?(42).should == true
0b1010_1010.anybits?(0b1000_0010).should == true
0b1010_1010.anybits?(0b1000_0001).should == true
@@ -29,8 +29,8 @@ describe "Integer#anybits?" do
-> {
(obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10])
13.anybits?(obj)
- }.should raise_error(TypeError)
- -> { 13.anybits?("10") }.should raise_error(TypeError)
- -> { 13.anybits?(:symbol) }.should raise_error(TypeError)
+ }.should.raise(TypeError)
+ -> { 13.anybits?("10") }.should.raise(TypeError)
+ -> { 13.anybits?(:symbol) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/integer/bit_and_spec.rb b/spec/ruby/core/integer/bit_and_spec.rb
index 15a8026855..4098ad7c7b 100644
--- a/spec/ruby/core/integer/bit_and_spec.rb
+++ b/spec/ruby/core/integer/bit_and_spec.rb
@@ -30,19 +30,19 @@ describe "Integer#&" do
it "coerces the rhs and calls #coerce" do
obj = mock("fixnum bit and")
- obj.should_receive(:coerce).with(6).and_return([3, 6])
+ obj.should_receive(:coerce).with(6).and_return([6, 3])
(6 & obj).should == 2
end
it "raises a TypeError when passed a Float" do
- -> { (3 & 3.4) }.should raise_error(TypeError)
+ -> { (3 & 3.4) }.should.raise(TypeError)
end
it "raises a TypeError and does not call #to_int when defined on an object" do
obj = mock("fixnum bit and")
obj.should_not_receive(:to_int)
- -> { 3 & obj }.should raise_error(TypeError)
+ -> { 3 & obj }.should.raise(TypeError)
end
end
@@ -55,24 +55,24 @@ describe "Integer#&" do
@bignum = bignum_value(5)
(@bignum & 3).should == 1
(@bignum & 52).should == 4
- (@bignum & bignum_value(9921)).should == 9223372036854775809
+ (@bignum & bignum_value(9921)).should == 18446744073709551617
((2*bignum_value) & 1).should == 0
- ((2*bignum_value) & (2*bignum_value)).should == 18446744073709551616
+ ((2*bignum_value) & (2*bignum_value)).should == 36893488147419103232
end
it "returns self bitwise AND other when one operand is negative" do
((2*bignum_value) & -1).should == (2*bignum_value)
((4*bignum_value) & -1).should == (4*bignum_value)
- (@bignum & -0xffffffffffffff5).should == 9223372036854775809
+ (@bignum & -0xffffffffffffff5).should == 18446744073709551617
(@bignum & -@bignum).should == 1
- (@bignum & -0x8000000000000000).should == 9223372036854775808
+ (@bignum & -0x8000000000000000).should == 18446744073709551616
end
it "returns self bitwise AND other when both operands are negative" do
- (-@bignum & -0x4000000000000005).should == -13835058055282163717
- (-@bignum & -@bignum).should == -9223372036854775813
- (-@bignum & -0x4000000000000000).should == -13835058055282163712
+ (-@bignum & -0x4000000000000005).should == -23058430092136939525
+ (-@bignum & -@bignum).should == -18446744073709551621
+ (-@bignum & -0x4000000000000000).should == -23058430092136939520
end
it "returns self bitwise AND other when both are negative and a multiple in bitsize of Fixnum::MIN" do
@@ -84,14 +84,14 @@ describe "Integer#&" do
end
it "raises a TypeError when passed a Float" do
- -> { (@bignum & 3.4) }.should raise_error(TypeError)
+ -> { (@bignum & 3.4) }.should.raise(TypeError)
end
it "raises a TypeError and does not call #to_int when defined on an object" do
obj = mock("bignum bit and")
obj.should_not_receive(:to_int)
- -> { @bignum & obj }.should raise_error(TypeError)
+ -> { @bignum & obj }.should.raise(TypeError)
end
end
end
diff --git a/spec/ruby/core/integer/bit_or_spec.rb b/spec/ruby/core/integer/bit_or_spec.rb
index e486eeec10..c380cd729e 100644
--- a/spec/ruby/core/integer/bit_or_spec.rb
+++ b/spec/ruby/core/integer/bit_or_spec.rb
@@ -7,22 +7,43 @@ describe "Integer#|" do
(5 | 4).should == 5
(5 | 6).should == 7
(248 | 4096).should == 4344
- (0xffff | bignum_value + 0xf0f0).should == 0x8000_0000_0000_ffff
+ (0xffff | bignum_value + 0xf0f0).should == 0x1_0000_0000_0000_ffff
+ end
+
+ it "returns self bitwise OR other when one operand is negative" do
+ ((1 << 33) | -1).should == -1
+ (-1 | (1 << 33)).should == -1
+
+ ((-(1<<33)-1) | 5).should == -8589934593
+ (5 | (-(1<<33)-1)).should == -8589934593
+ end
+
+ it "returns self bitwise OR other when both operands are negative" do
+ (-5 | -1).should == -1
+ (-3 | -4).should == -3
+ (-12 | -13).should == -9
+ (-13 | -12).should == -9
end
it "returns self bitwise OR a bignum" do
(-1 | 2**64).should == -1
end
+ it "coerces the rhs and calls #coerce" do
+ obj = mock("fixnum bit or")
+ obj.should_receive(:coerce).with(6).and_return([6, 3])
+ (6 | obj).should == 7
+ end
+
it "raises a TypeError when passed a Float" do
- -> { (3 | 3.4) }.should raise_error(TypeError)
+ -> { (3 | 3.4) }.should.raise(TypeError)
end
it "raises a TypeError and does not call #to_int when defined on an object" do
obj = mock("integer bit or")
obj.should_not_receive(:to_int)
- -> { 3 | obj }.should raise_error(TypeError)
+ -> { 3 | obj }.should.raise(TypeError)
end
end
@@ -32,20 +53,20 @@ describe "Integer#|" do
end
it "returns self bitwise OR other" do
- (@bignum | 2).should == 9223372036854775819
- (@bignum | 9).should == 9223372036854775819
- (@bignum | bignum_value).should == 9223372036854775819
+ (@bignum | 2).should == 18446744073709551627
+ (@bignum | 9).should == 18446744073709551627
+ (@bignum | bignum_value).should == 18446744073709551627
end
it "returns self bitwise OR other when one operand is negative" do
- (@bignum | -0x40000000000000000).should == -64563604257983430645
+ (@bignum | -0x40000000000000000).should == -55340232221128654837
(@bignum | -@bignum).should == -1
(@bignum | -0x8000000000000000).should == -9223372036854775797
end
it "returns self bitwise OR other when both operands are negative" do
(-@bignum | -0x4000000000000005).should == -1
- (-@bignum | -@bignum).should == -9223372036854775819
+ (-@bignum | -@bignum).should == -18446744073709551627
(-@bignum | -0x4000000000000000).should == -11
end
@@ -53,16 +74,16 @@ describe "Integer#|" do
not_supported_on :opal do
-> {
bignum_value | bignum_value(0xffff).to_f
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
- -> { @bignum | 9.9 }.should raise_error(TypeError)
+ -> { @bignum | 9.9 }.should.raise(TypeError)
end
it "raises a TypeError and does not call #to_int when defined on an object" do
obj = mock("bignum bit or")
obj.should_not_receive(:to_int)
- -> { @bignum | obj }.should raise_error(TypeError)
+ -> { @bignum | obj }.should.raise(TypeError)
end
end
end
diff --git a/spec/ruby/core/integer/bit_xor_spec.rb b/spec/ruby/core/integer/bit_xor_spec.rb
index ac8826a52f..c0cf8405f7 100644
--- a/spec/ruby/core/integer/bit_xor_spec.rb
+++ b/spec/ruby/core/integer/bit_xor_spec.rb
@@ -5,22 +5,43 @@ describe "Integer#^" do
it "returns self bitwise EXCLUSIVE OR other" do
(3 ^ 5).should == 6
(-2 ^ -255).should == 255
- (5 ^ bignum_value + 0xffff_ffff).should == 0x8000_0000_ffff_fffa
+ (5 ^ bignum_value + 0xffff_ffff).should == 0x1_0000_0000_ffff_fffa
+ end
+
+ it "returns self bitwise XOR other when one operand is negative" do
+ ((1 << 33) ^ -1).should == -8589934593
+ (-1 ^ (1 << 33)).should == -8589934593
+
+ ((-(1<<33)-1) ^ 5).should == -8589934598
+ (5 ^ (-(1<<33)-1)).should == -8589934598
+ end
+
+ it "returns self bitwise XOR other when both operands are negative" do
+ (-5 ^ -1).should == 4
+ (-3 ^ -4).should == 1
+ (-12 ^ -13).should == 7
+ (-13 ^ -12).should == 7
end
it "returns self bitwise EXCLUSIVE OR a bignum" do
(-1 ^ 2**64).should == -18446744073709551617
end
+ it "coerces the rhs and calls #coerce" do
+ obj = mock("fixnum bit xor")
+ obj.should_receive(:coerce).with(6).and_return([6, 3])
+ (6 ^ obj).should == 5
+ end
+
it "raises a TypeError when passed a Float" do
- -> { (3 ^ 3.4) }.should raise_error(TypeError)
+ -> { (3 ^ 3.4) }.should.raise(TypeError)
end
it "raises a TypeError and does not call #to_int when defined on an object" do
obj = mock("integer bit xor")
obj.should_not_receive(:to_int)
- -> { 3 ^ obj }.should raise_error(TypeError)
+ -> { 3 ^ obj }.should.raise(TypeError)
end
end
@@ -30,21 +51,21 @@ describe "Integer#^" do
end
it "returns self bitwise EXCLUSIVE OR other" do
- (@bignum ^ 2).should == 9223372036854775824
+ (@bignum ^ 2).should == 18446744073709551632
(@bignum ^ @bignum).should == 0
- (@bignum ^ 14).should == 9223372036854775836
+ (@bignum ^ 14).should == 18446744073709551644
end
it "returns self bitwise EXCLUSIVE OR other when one operand is negative" do
- (@bignum ^ -0x40000000000000000).should == -64563604257983430638
+ (@bignum ^ -0x40000000000000000).should == -55340232221128654830
(@bignum ^ -@bignum).should == -4
- (@bignum ^ -0x8000000000000000).should == -18446744073709551598
+ (@bignum ^ -0x8000000000000000).should == -27670116110564327406
end
it "returns self bitwise EXCLUSIVE OR other when both operands are negative" do
- (-@bignum ^ -0x40000000000000000).should == 64563604257983430638
+ (-@bignum ^ -0x40000000000000000).should == 55340232221128654830
(-@bignum ^ -@bignum).should == 0
- (-@bignum ^ -0x4000000000000000).should == 13835058055282163694
+ (-@bignum ^ -0x4000000000000000).should == 23058430092136939502
end
it "returns self bitwise EXCLUSIVE OR other when all bits are 1 and other value is negative" do
@@ -57,16 +78,16 @@ describe "Integer#^" do
not_supported_on :opal do
-> {
bignum_value ^ bignum_value(0xffff).to_f
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
- -> { @bignum ^ 14.5 }.should raise_error(TypeError)
+ -> { @bignum ^ 14.5 }.should.raise(TypeError)
end
it "raises a TypeError and does not call #to_int when defined on an object" do
obj = mock("bignum bit xor")
obj.should_not_receive(:to_int)
- -> { @bignum ^ obj }.should raise_error(TypeError)
+ -> { @bignum ^ obj }.should.raise(TypeError)
end
end
end
diff --git a/spec/ruby/core/integer/ceil_spec.rb b/spec/ruby/core/integer/ceil_spec.rb
index 13bdaf838d..395be58fbd 100644
--- a/spec/ruby/core/integer/ceil_spec.rb
+++ b/spec/ruby/core/integer/ceil_spec.rb
@@ -1,19 +1,13 @@
require_relative '../../spec_helper'
require_relative 'shared/to_i'
require_relative 'shared/integer_rounding'
+require_relative 'shared/integer_ceil_precision'
describe "Integer#ceil" do
it_behaves_like :integer_to_i, :ceil
it_behaves_like :integer_rounding_positive_precision, :ceil
- context "precision argument specified as part of the ceil method is negative" do
- it "returns the smallest integer greater than self with at least precision.abs trailing zeros" do
- 18.ceil(-1).should eql(20)
- 18.ceil(-2).should eql(100)
- 18.ceil(-3).should eql(1000)
- -1832.ceil(-1).should eql(-1830)
- -1832.ceil(-2).should eql(-1800)
- -1832.ceil(-3).should eql(-1000)
- end
+ context "with precision" do
+ it_behaves_like :integer_ceil_precision, :Integer
end
end
diff --git a/spec/ruby/core/integer/ceildiv_spec.rb b/spec/ruby/core/integer/ceildiv_spec.rb
new file mode 100644
index 0000000000..91f63832f8
--- /dev/null
+++ b/spec/ruby/core/integer/ceildiv_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+describe "Integer#ceildiv" do
+ it "returns a quotient of division which is rounded up to the nearest integer" do
+ 0.ceildiv(3).should.eql?(0)
+ 1.ceildiv(3).should.eql?(1)
+ 3.ceildiv(3).should.eql?(1)
+ 4.ceildiv(3).should.eql?(2)
+
+ 4.ceildiv(-3).should.eql?(-1)
+ -4.ceildiv(3).should.eql?(-1)
+ -4.ceildiv(-3).should.eql?(2)
+
+ 3.ceildiv(1.2).should.eql?(3)
+ 3.ceildiv(6/5r).should.eql?(3)
+
+ (10**100-11).ceildiv(10**99-1).should.eql?(10)
+ (10**100-9).ceildiv(10**99-1).should.eql?(11)
+ end
+end
diff --git a/spec/ruby/core/integer/chr_spec.rb b/spec/ruby/core/integer/chr_spec.rb
index a8755eeb84..72784e1833 100644
--- a/spec/ruby/core/integer/chr_spec.rb
+++ b/spec/ruby/core/integer/chr_spec.rb
@@ -2,16 +2,20 @@ require_relative '../../spec_helper'
describe "Integer#chr without argument" do
it "returns a String" do
- 17.chr.should be_an_instance_of(String)
+ 17.chr.should.instance_of?(String)
end
it "returns a new String for each call" do
- 82.chr.should_not equal(82.chr)
+ 82.chr.should_not.equal?(82.chr)
end
it "raises a RangeError is self is less than 0" do
- -> { -1.chr }.should raise_error(RangeError)
- -> { -bignum_value.chr }.should raise_error(RangeError)
+ -> { -1.chr }.should.raise(RangeError, /-1 out of char range/)
+ -> { (-bignum_value).chr }.should.raise(RangeError, /bignum out of char range/)
+ end
+
+ it "raises a RangeError if self is too large" do
+ -> { 2206368128.chr(Encoding::UTF_8) }.should.raise(RangeError, /2206368128 out of char range/)
end
describe "when Encoding.default_internal is nil" do
@@ -44,8 +48,8 @@ describe "Integer#chr without argument" do
end
it "raises a RangeError is self is greater than 255" do
- -> { 256.chr }.should raise_error(RangeError)
- -> { bignum_value.chr }.should raise_error(RangeError)
+ -> { 256.chr }.should.raise(RangeError, /256 out of char range/)
+ -> { bignum_value.chr }.should.raise(RangeError, /bignum out of char range/)
end
end
@@ -133,7 +137,7 @@ describe "Integer#chr without argument" do
[620, "TIS-620"]
].each do |integer, encoding_name|
Encoding.default_internal = Encoding.find(encoding_name)
- -> { integer.chr }.should raise_error(RangeError)
+ -> { integer.chr }.should.raise(RangeError, /(invalid codepoint|out of char range)/)
end
end
end
@@ -142,15 +146,15 @@ end
describe "Integer#chr with an encoding argument" do
it "returns a String" do
- 900.chr(Encoding::UTF_8).should be_an_instance_of(String)
+ 900.chr(Encoding::UTF_8).should.instance_of?(String)
end
it "returns a new String for each call" do
- 8287.chr(Encoding::UTF_8).should_not equal(8287.chr(Encoding::UTF_8))
+ 8287.chr(Encoding::UTF_8).should_not.equal?(8287.chr(Encoding::UTF_8))
end
it "accepts a String as an argument" do
- -> { 0xA4A2.chr('euc-jp') }.should_not raise_error
+ -> { 0xA4A2.chr('euc-jp') }.should_not.raise
end
it "converts a String to an Encoding as Encoding.find does" do
@@ -161,12 +165,12 @@ describe "Integer#chr with an encoding argument" do
# http://redmine.ruby-lang.org/issues/4869
it "raises a RangeError is self is less than 0" do
- -> { -1.chr(Encoding::UTF_8) }.should raise_error(RangeError)
- -> { -bignum_value.chr(Encoding::EUC_JP) }.should raise_error(RangeError)
+ -> { -1.chr(Encoding::UTF_8) }.should.raise(RangeError, /-1 out of char range/)
+ -> { (-bignum_value).chr(Encoding::EUC_JP) }.should.raise(RangeError, /bignum out of char range/)
end
it "raises a RangeError if self is too large" do
- -> { 2206368128.chr(Encoding::UTF_8) }.should raise_error(RangeError)
+ -> { 2206368128.chr(Encoding::UTF_8) }.should.raise(RangeError, /2206368128 out of char range/)
end
it "returns a String with the specified encoding" do
@@ -219,25 +223,35 @@ describe "Integer#chr with an encoding argument" do
# #5864
it "raises RangeError if self is invalid as a codepoint in the specified encoding" do
- [ [0x80, "US-ASCII"],
- [0x0100, "BINARY"],
- [0x0100, "EUC-JP"],
- [0xA1A0, "EUC-JP"],
- [0xA1, "EUC-JP"],
- [0x80, "SHIFT_JIS"],
- [0xE0, "SHIFT_JIS"],
- [0x0100, "ISO-8859-9"],
- [620, "TIS-620"],
- [0xD800, "UTF-8"],
- [0xDBFF, "UTF-8"],
- [0xDC00, "UTF-8"],
- [0xDFFF, "UTF-8"],
- [0xD800, "UTF-16"],
- [0xDBFF, "UTF-16"],
- [0xDC00, "UTF-16"],
- [0xDFFF, "UTF-16"],
- ].each do |integer, encoding_name|
- -> { integer.chr(encoding_name) }.should raise_error(RangeError)
- end
+ -> { 0x80.chr("US-ASCII") }.should.raise(RangeError)
+ -> { 0x0100.chr("BINARY") }.should.raise(RangeError)
+ -> { 0x0100.chr("EUC-JP") }.should.raise(RangeError)
+ -> { 0xA1A0.chr("EUC-JP") }.should.raise(RangeError)
+ -> { 0xA1.chr("EUC-JP") }.should.raise(RangeError)
+ -> { 0x80.chr("SHIFT_JIS") }.should.raise(RangeError)
+ -> { 0xE0.chr("SHIFT_JIS") }.should.raise(RangeError)
+ -> { 0x0100.chr("ISO-8859-9") }.should.raise(RangeError)
+ -> { 620.chr("TIS-620") }.should.raise(RangeError)
+ # UTF-16 surrogate range
+ -> { 0xD800.chr("UTF-8") }.should.raise(RangeError)
+ -> { 0xDBFF.chr("UTF-8") }.should.raise(RangeError)
+ -> { 0xDC00.chr("UTF-8") }.should.raise(RangeError)
+ -> { 0xDFFF.chr("UTF-8") }.should.raise(RangeError)
+ # UTF-16 surrogate range
+ -> { 0xD800.chr("UTF-16") }.should.raise(RangeError)
+ -> { 0xDBFF.chr("UTF-16") }.should.raise(RangeError)
+ -> { 0xDC00.chr("UTF-16") }.should.raise(RangeError)
+ -> { 0xDFFF.chr("UTF-16") }.should.raise(RangeError)
+ end
+
+ it 'returns a String encoding self interpreted as a codepoint in the CESU-8 encoding' do
+ # see more details here https://en.wikipedia.org/wiki/CESU-8
+ # code points from U+0000 to U+FFFF is encoded in the same way as in UTF-8
+ 0x0045.chr(Encoding::CESU_8).bytes.should == 0x0045.chr(Encoding::UTF_8).bytes
+
+ # code points in range from U+10000 to U+10FFFF is CESU-8 data containing a 6-byte surrogate pair,
+ # which decodes to a 4-byte UTF-8 string
+ 0x10400.chr(Encoding::CESU_8).bytes.should != 0x10400.chr(Encoding::UTF_8).bytes
+ 0x10400.chr(Encoding::CESU_8).bytes.to_a.should == [0xED, 0xA0, 0x81, 0xED, 0xB0, 0x80]
end
end
diff --git a/spec/ruby/core/integer/coerce_spec.rb b/spec/ruby/core/integer/coerce_spec.rb
index 9a19baf2ea..c0e642da03 100644
--- a/spec/ruby/core/integer/coerce_spec.rb
+++ b/spec/ruby/core/integer/coerce_spec.rb
@@ -1,19 +1,17 @@
require_relative '../../spec_helper'
-require 'bigdecimal'
-
describe "Integer#coerce" do
context "fixnum" do
describe "when given a Fixnum" do
it "returns an array containing two Fixnums" do
1.coerce(2).should == [2, 1]
- 1.coerce(2).map { |i| i.class }.should == [Fixnum, Fixnum]
+ 1.coerce(2).map { |i| i.class }.should == [Integer, Integer]
end
end
describe "when given a String" do
it "raises an ArgumentError when trying to coerce with a non-number String" do
- -> { 1.coerce(":)") }.should raise_error(ArgumentError)
+ -> { 1.coerce(":)") }.should.raise(ArgumentError)
end
it "returns an array containing two Floats" do
@@ -23,7 +21,7 @@ describe "Integer#coerce" do
end
it "raises a TypeError when trying to coerce with nil" do
- -> { 1.coerce(nil) }.should raise_error(TypeError)
+ -> { 1.coerce(nil) }.should.raise(TypeError)
end
it "tries to convert the given Object into a Float by using #to_f" do
@@ -31,13 +29,13 @@ describe "Integer#coerce" do
2.coerce(obj).should == [1.0, 2.0]
(obj = mock('0')).should_receive(:to_f).and_return('0')
- -> { 2.coerce(obj).should == [1.0, 2.0] }.should raise_error(TypeError)
+ -> { 2.coerce(obj).should == [1.0, 2.0] }.should.raise(TypeError)
end
it "raises a TypeError when given an Object that does not respond to #to_f" do
- -> { 1.coerce(mock('x')) }.should raise_error(TypeError)
- -> { 1.coerce(1..4) }.should raise_error(TypeError)
- -> { 1.coerce(:test) }.should raise_error(TypeError)
+ -> { 1.coerce(mock('x')) }.should.raise(TypeError)
+ -> { 1.coerce(1..4) }.should.raise(TypeError)
+ -> { 1.coerce(:test) }.should.raise(TypeError)
end
end
@@ -46,8 +44,8 @@ describe "Integer#coerce" do
a = bignum_value
ary = a.coerce(2)
- ary[0].should be_kind_of(Bignum)
- ary[1].should be_kind_of(Bignum)
+ ary[0].should.is_a?(Integer)
+ ary[1].should.is_a?(Integer)
ary.should == [2, a]
end
@@ -56,18 +54,18 @@ describe "Integer#coerce" do
b = bignum_value
ary = a.coerce(b)
- ary[0].should be_kind_of(Bignum)
- ary[1].should be_kind_of(Bignum)
+ ary[0].should.is_a?(Integer)
+ ary[1].should.is_a?(Integer)
ary.should == [b, a]
end
it "raises a TypeError when not passed a Fixnum or Bignum" do
a = bignum_value
- -> { a.coerce(nil) }.should raise_error(TypeError)
- -> { a.coerce(mock('str')) }.should raise_error(TypeError)
- -> { a.coerce(1..4) }.should raise_error(TypeError)
- -> { a.coerce(:test) }.should raise_error(TypeError)
+ -> { a.coerce(nil) }.should.raise(TypeError)
+ -> { a.coerce(mock('str')) }.should.raise(TypeError)
+ -> { a.coerce(1..4) }.should.raise(TypeError)
+ -> { a.coerce(:test) }.should.raise(TypeError)
end
it "coerces both values to Floats and returns [other, self] when passed a Float" do
@@ -90,15 +88,4 @@ describe "Integer#coerce" do
ary.should == [1.2, a.to_f]
end
end
-
- context "bigdecimal" do
- it "produces Floats" do
- x, y = 3.coerce(BigDecimal("3.4"))
- x.class.should == Float
- x.should == 3.4
- y.class.should == Float
- y.should == 3.0
- end
- end
-
end
diff --git a/spec/ruby/core/integer/comparison_spec.rb b/spec/ruby/core/integer/comparison_spec.rb
index db050aa8b9..cc5cbe2506 100644
--- a/spec/ruby/core/integer/comparison_spec.rb
+++ b/spec/ruby/core/integer/comparison_spec.rb
@@ -116,7 +116,7 @@ describe "Integer#<=>" do
describe "with an Object" do
before :each do
@big = bignum_value
- @num = mock("value for Bignum#<=>")
+ @num = mock("value for Integer#<=>")
end
it "calls #coerce on other" do
@@ -128,17 +128,17 @@ describe "Integer#<=>" do
@num.should_receive(:coerce).with(@big).and_raise(RuntimeError.new("my error"))
-> {
@big <=> @num
- }.should raise_error(RuntimeError, "my error")
+ }.should.raise(RuntimeError, "my error")
end
it "raises an exception if #coerce raises a non-StandardError exception" do
@num.should_receive(:coerce).with(@big).and_raise(Exception)
- -> { @big <=> @num }.should raise_error(Exception)
+ -> { @big <=> @num }.should.raise(Exception)
end
it "returns nil if #coerce does not return an Array" do
@num.should_receive(:coerce).with(@big).and_return(nil)
- (@big <=> @num).should be_nil
+ (@big <=> @num).should == nil
end
it "returns -1 if the coerced value is larger" do
@@ -157,6 +157,14 @@ describe "Integer#<=>" do
end
end
+ describe "with a Float" do
+ it "does not lose precision for values that don't fit in a double" do
+ (bignum_value(1) <=> bignum_value.to_f).should == 1
+ (bignum_value <=> bignum_value.to_f).should == 0
+ ((bignum_value - 1) <=> bignum_value.to_f).should == -1
+ end
+ end
+
# The tests below are taken from matz's revision 23730 for Ruby trunk
it "returns 1 when self is Infinity and other is a Bignum" do
(infinity_value <=> Float::MAX.to_i*2).should == 1
diff --git a/spec/ruby/core/integer/complement_spec.rb b/spec/ruby/core/integer/complement_spec.rb
index eafa5c263f..baf5e6c457 100644
--- a/spec/ruby/core/integer/complement_spec.rb
+++ b/spec/ruby/core/integer/complement_spec.rb
@@ -12,9 +12,9 @@ describe "Integer#~" do
context "bignum" do
it "returns self with each bit flipped" do
- (~bignum_value(48)).should == -9223372036854775857
- (~(-bignum_value(21))).should == 9223372036854775828
- (~bignum_value(1)).should == -9223372036854775810
+ (~bignum_value(48)).should == -18446744073709551665
+ (~(-bignum_value(21))).should == 18446744073709551636
+ (~bignum_value(1)).should == -18446744073709551618
end
end
end
diff --git a/spec/ruby/core/integer/constants_spec.rb b/spec/ruby/core/integer/constants_spec.rb
index 3b8b01e330..937806c72f 100644
--- a/spec/ruby/core/integer/constants_spec.rb
+++ b/spec/ruby/core/integer/constants_spec.rb
@@ -1,25 +1,13 @@
require_relative '../../spec_helper'
describe "Fixnum" do
- it "is unified into Integer" do
- suppress_warning do
- Fixnum.should equal(Integer)
- end
- end
-
- it "is deprecated" do
- -> { Fixnum }.should complain(/constant ::Fixnum is deprecated/)
+ it "is no longer defined" do
+ Object.should_not.const_defined?(:Fixnum)
end
end
describe "Bignum" do
- it "is unified into Integer" do
- suppress_warning do
- Bignum.should equal(Integer)
- end
- end
-
- it "is deprecated" do
- -> { Bignum }.should complain(/constant ::Bignum is deprecated/)
+ it "is no longer defined" do
+ Object.should_not.const_defined?(:Bignum)
end
end
diff --git a/spec/ruby/core/integer/digits_spec.rb b/spec/ruby/core/integer/digits_spec.rb
index 4a8e33980c..c4ebf2cd88 100644
--- a/spec/ruby/core/integer/digits_spec.rb
+++ b/spec/ruby/core/integer/digits_spec.rb
@@ -19,14 +19,23 @@ describe "Integer#digits" do
end
it "raises ArgumentError when calling with a radix less than 2" do
- -> { 12345.digits(1) }.should raise_error(ArgumentError)
+ -> { 12345.digits(1) }.should.raise(ArgumentError)
end
it "raises ArgumentError when calling with a negative radix" do
- -> { 12345.digits(-2) }.should raise_error(ArgumentError)
+ -> { 12345.digits(-2) }.should.raise(ArgumentError)
end
it "raises Math::DomainError when calling digits on a negative number" do
- -> { -12345.digits(7) }.should raise_error(Math::DomainError)
+ -> { -12345.digits(7) }.should.raise(Math::DomainError)
+ end
+
+ it "returns integer values > 9 when base is above 10" do
+ 1234.digits(16).should == [2, 13, 4]
+ end
+
+ it "can be used with base > 37" do
+ 1234.digits(100).should == [34, 12]
+ 980099.digits(100).should == [99, 0, 98]
end
end
diff --git a/spec/ruby/core/integer/div_spec.rb b/spec/ruby/core/integer/div_spec.rb
index 087d012fe0..220ca4a73e 100644
--- a/spec/ruby/core/integer/div_spec.rb
+++ b/spec/ruby/core/integer/div_spec.rb
@@ -36,7 +36,7 @@ describe "Integer#div" do
10.div(y).should == result
end
- it "coerces self and the given argument to Floats and returns self divided by other as Fixnum" do
+ it "coerces self and the given argument to Floats and returns self divided by other as Integer" do
1.div(0.2).should == 5
1.div(0.16).should == 6
1.div(0.169).should == 5
@@ -46,21 +46,21 @@ describe "Integer#div" do
end
it "raises a ZeroDivisionError when the given argument is 0 and a Float" do
- -> { 0.div(0.0) }.should raise_error(ZeroDivisionError)
- -> { 10.div(0.0) }.should raise_error(ZeroDivisionError)
- -> { -10.div(0.0) }.should raise_error(ZeroDivisionError)
+ -> { 0.div(0.0) }.should.raise(ZeroDivisionError)
+ -> { 10.div(0.0) }.should.raise(ZeroDivisionError)
+ -> { -10.div(0.0) }.should.raise(ZeroDivisionError)
end
it "raises a ZeroDivisionError when the given argument is 0 and not a Float" do
- -> { 13.div(0) }.should raise_error(ZeroDivisionError)
- -> { 13.div(-0) }.should raise_error(ZeroDivisionError)
+ -> { 13.div(0) }.should.raise(ZeroDivisionError)
+ -> { 13.div(-0) }.should.raise(ZeroDivisionError)
end
it "raises a TypeError when given a non-numeric argument" do
- -> { 13.div(mock('10')) }.should raise_error(TypeError)
- -> { 5.div("2") }.should raise_error(TypeError)
- -> { 5.div(:"2") }.should raise_error(TypeError)
- -> { 5.div([]) }.should raise_error(TypeError)
+ -> { 13.div(mock('10')) }.should.raise(TypeError)
+ -> { 5.div("2") }.should.raise(TypeError)
+ -> { 5.div(:"2") }.should.raise(TypeError)
+ -> { 5.div([]) }.should.raise(TypeError)
end
end
@@ -70,8 +70,8 @@ describe "Integer#div" do
end
it "returns self divided by other" do
- @bignum.div(4).should == 2305843009213693974
- @bignum.div(Rational(4, 1)).should == 2305843009213693974
+ @bignum.div(4).should == 4611686018427387926
+ @bignum.div(Rational(4, 1)).should == 4611686018427387926
@bignum.div(bignum_value(2)).should == 1
(-(10**50)).div(-(10**40 + 1)).should == 9999999999
@@ -118,29 +118,37 @@ describe "Integer#div" do
end
it "raises a TypeError when given a non-numeric" do
- -> { @bignum.div(mock("10")) }.should raise_error(TypeError)
- -> { @bignum.div("2") }.should raise_error(TypeError)
- -> { @bignum.div(:symbol) }.should raise_error(TypeError)
+ -> { @bignum.div(mock("10")) }.should.raise(TypeError)
+ -> { @bignum.div("2") }.should.raise(TypeError)
+ -> { @bignum.div(:symbol) }.should.raise(TypeError)
end
it "returns a result of integer division of self by a float argument" do
- @bignum.div(4294967295.5).should eql(2147483648)
+ @bignum.div(4294967295.5).should.eql?(4294967296)
not_supported_on :opal do
- @bignum.div(4294967295.0).should eql(2147483648)
- @bignum.div(bignum_value(88).to_f).should eql(1)
- @bignum.div(-bignum_value(88).to_f).should eql(-1)
+ @bignum.div(4294967295.0).should.eql?(4294967297)
+ @bignum.div(bignum_value(88).to_f).should.eql?(1)
+ @bignum.div((-bignum_value(88)).to_f).should.eql?(-1)
end
end
# #5490
it "raises ZeroDivisionError if the argument is 0 and is a Float" do
- -> { @bignum.div(0.0) }.should raise_error(ZeroDivisionError)
- -> { @bignum.div(-0.0) }.should raise_error(ZeroDivisionError)
+ -> { @bignum.div(0.0) }.should.raise(ZeroDivisionError)
+ -> { @bignum.div(-0.0) }.should.raise(ZeroDivisionError)
end
it "raises ZeroDivisionError if the argument is 0 and is not a Float" do
- -> { @bignum.div(0) }.should raise_error(ZeroDivisionError)
- -> { @bignum.div(-0) }.should raise_error(ZeroDivisionError)
+ -> { @bignum.div(0) }.should.raise(ZeroDivisionError)
+ -> { @bignum.div(-0) }.should.raise(ZeroDivisionError)
+ end
+ end
+
+ context "rational" do
+ it "returns self divided by the given argument as an Integer" do
+ 2.div(6/5r).should == 1
+ 1.div(6/5r).should == 0
+ 5.div(6/5r).should == 4
end
end
end
diff --git a/spec/ruby/core/integer/divide_spec.rb b/spec/ruby/core/integer/divide_spec.rb
index 5ac2dc538d..e75432fd83 100644
--- a/spec/ruby/core/integer/divide_spec.rb
+++ b/spec/ruby/core/integer/divide_spec.rb
@@ -12,6 +12,17 @@ describe "Integer#/" do
it "supports dividing negative numbers" do
(-1 / 10).should == -1
+ (-1 / 10**10).should == -1
+ (-1 / 10**20).should == -1
+ end
+
+ it "preservers sign correctly" do
+ (4 / 3).should == 1
+ (4 / -3).should == -2
+ (-4 / 3).should == -2
+ (-4 / -3).should == 1
+ (0 / -3).should == 0
+ (0 / 3).should == 0
end
it "returns result the same class as the argument" do
@@ -21,7 +32,7 @@ describe "Integer#/" do
end
it "raises a ZeroDivisionError if the given argument is zero and not a Float" do
- -> { 1 / 0 }.should raise_error(ZeroDivisionError)
+ -> { 1 / 0 }.should.raise(ZeroDivisionError)
end
it "does NOT raise ZeroDivisionError if the given argument is zero and is a Float" do
@@ -35,9 +46,9 @@ describe "Integer#/" do
end
it "raises a TypeError when given a non-Integer" do
- -> { 13 / mock('10') }.should raise_error(TypeError)
- -> { 13 / "10" }.should raise_error(TypeError)
- -> { 13 / :symbol }.should raise_error(TypeError)
+ -> { 13 / mock('10') }.should.raise(TypeError)
+ -> { 13 / "10" }.should.raise(TypeError)
+ -> { 13 / :symbol }.should.raise(TypeError)
end
end
@@ -47,7 +58,7 @@ describe "Integer#/" do
end
it "returns self divided by other" do
- (@bignum / 4).should == 2305843009213693974
+ (@bignum / 4).should == 4611686018427387926
(@bignum / bignum_value(2)).should == 1
@@ -58,17 +69,26 @@ describe "Integer#/" do
((10**50) / -(10**40 + 1)).should == -10000000000
end
+ it "preservers sign correctly" do
+ (4 / bignum_value).should == 0
+ (4 / -bignum_value).should == -1
+ (-4 / bignum_value).should == -1
+ (-4 / -bignum_value).should == 0
+ (0 / bignum_value).should == 0
+ (0 / -bignum_value).should == 0
+ end
+
it "returns self divided by Float" do
not_supported_on :opal do
- (bignum_value(88) / 4294967295.0).should be_close(2147483648.5, TOLERANCE)
+ (bignum_value(88) / 4294967295.0).should be_close(4294967297.0, TOLERANCE)
end
- (bignum_value(88) / 4294967295.5).should be_close(2147483648.25, TOLERANCE)
+ (bignum_value(88) / 4294967295.5).should be_close(4294967296.5, TOLERANCE)
end
it "returns result the same class as the argument" do
- (@bignum / 4).should == 2305843009213693974
- (@bignum / 4.0).should be_close(2305843009213693974, TOLERANCE)
- (@bignum / Rational(4, 1)).should == Rational(2305843009213693974, 1)
+ (@bignum / 4).should == 4611686018427387926
+ (@bignum / 4.0).should be_close(4611686018427387926, TOLERANCE)
+ (@bignum / Rational(4, 1)).should == Rational(4611686018427387926, 1)
end
it "does NOT raise ZeroDivisionError if other is zero and is a Float" do
@@ -77,13 +97,30 @@ describe "Integer#/" do
end
it "raises a ZeroDivisionError if other is zero and not a Float" do
- -> { @bignum / 0 }.should raise_error(ZeroDivisionError)
+ -> { @bignum / 0 }.should.raise(ZeroDivisionError)
end
it "raises a TypeError when given a non-numeric" do
- -> { @bignum / mock('10') }.should raise_error(TypeError)
- -> { @bignum / "2" }.should raise_error(TypeError)
- -> { @bignum / :symbol }.should raise_error(TypeError)
+ -> { @bignum / mock('10') }.should.raise(TypeError)
+ -> { @bignum / "2" }.should.raise(TypeError)
+ -> { @bignum / :symbol }.should.raise(TypeError)
end
end
+
+ it "coerces the RHS and calls #coerce" do
+ obj = mock("integer plus")
+ obj.should_receive(:coerce).with(6).and_return([6, 3])
+ (6 / obj).should == 2
+ end
+
+ it "coerces the RHS and calls #coerce even if it's private" do
+ obj = Object.new
+ class << obj
+ private def coerce(n)
+ [n, 3]
+ end
+ end
+
+ (6 / obj).should == 2
+ end
end
diff --git a/spec/ruby/core/integer/divmod_spec.rb b/spec/ruby/core/integer/divmod_spec.rb
index d88925caad..db470c5731 100644
--- a/spec/ruby/core/integer/divmod_spec.rb
+++ b/spec/ruby/core/integer/divmod_spec.rb
@@ -14,24 +14,24 @@ describe "Integer#divmod" do
end
it "raises a ZeroDivisionError when the given argument is 0" do
- -> { 13.divmod(0) }.should raise_error(ZeroDivisionError)
- -> { 0.divmod(0) }.should raise_error(ZeroDivisionError)
- -> { -10.divmod(0) }.should raise_error(ZeroDivisionError)
+ -> { 13.divmod(0) }.should.raise(ZeroDivisionError)
+ -> { 0.divmod(0) }.should.raise(ZeroDivisionError)
+ -> { -10.divmod(0) }.should.raise(ZeroDivisionError)
end
it "raises a ZeroDivisionError when the given argument is 0 and a Float" do
- -> { 0.divmod(0.0) }.should raise_error(ZeroDivisionError)
- -> { 10.divmod(0.0) }.should raise_error(ZeroDivisionError)
- -> { -10.divmod(0.0) }.should raise_error(ZeroDivisionError)
+ -> { 0.divmod(0.0) }.should.raise(ZeroDivisionError)
+ -> { 10.divmod(0.0) }.should.raise(ZeroDivisionError)
+ -> { -10.divmod(0.0) }.should.raise(ZeroDivisionError)
end
it "raises a TypeError when given a non-Integer" do
-> {
(obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10)
13.divmod(obj)
- }.should raise_error(TypeError)
- -> { 13.divmod("10") }.should raise_error(TypeError)
- -> { 13.divmod(:symbol) }.should raise_error(TypeError)
+ }.should.raise(TypeError)
+ -> { 13.divmod("10") }.should.raise(TypeError)
+ -> { 13.divmod(:symbol) }.should.raise(TypeError)
end
end
@@ -46,16 +46,16 @@ describe "Integer#divmod" do
# assert(0 < b ? (0 <= r && r < b) : (b < r && r <= 0))
# So, r is always between 0 and b.
it "returns an Array containing quotient and modulus obtained from dividing self by the given argument" do
- @bignum.divmod(4).should == [2305843009213693965, 3]
- @bignum.divmod(13).should == [709490156681136604, 11]
+ @bignum.divmod(4).should == [4611686018427387917, 3]
+ @bignum.divmod(13).should == [1418980313362273205, 6]
- @bignum.divmod(4.5).should == [2049638230412172288, 3.5]
+ @bignum.divmod(4.5).should == [4099276460824344576, 2.5]
not_supported_on :opal do
- @bignum.divmod(4.0).should == [2305843009213693952, 0.0]
- @bignum.divmod(13.0).should == [709490156681136640, 8.0]
+ @bignum.divmod(4.0).should == [4611686018427387904, 0.0]
+ @bignum.divmod(13.0).should == [1418980313362273280, 3.0]
- @bignum.divmod(2.0).should == [4611686018427387904, 0.0]
+ @bignum.divmod(2.0).should == [9223372036854775808, 0.0]
end
@bignum.divmod(bignum_value).should == [1, 55]
@@ -94,24 +94,24 @@ describe "Integer#divmod" do
end
it "raises a ZeroDivisionError when the given argument is 0" do
- -> { @bignum.divmod(0) }.should raise_error(ZeroDivisionError)
- -> { (-@bignum).divmod(0) }.should raise_error(ZeroDivisionError)
+ -> { @bignum.divmod(0) }.should.raise(ZeroDivisionError)
+ -> { (-@bignum).divmod(0) }.should.raise(ZeroDivisionError)
end
# Behaviour established as correct in r23953
it "raises a FloatDomainError if other is NaN" do
- -> { @bignum.divmod(nan_value) }.should raise_error(FloatDomainError)
+ -> { @bignum.divmod(nan_value) }.should.raise(FloatDomainError)
end
it "raises a ZeroDivisionError when the given argument is 0 and a Float" do
- -> { @bignum.divmod(0.0) }.should raise_error(ZeroDivisionError)
- -> { (-@bignum).divmod(0.0) }.should raise_error(ZeroDivisionError)
+ -> { @bignum.divmod(0.0) }.should.raise(ZeroDivisionError)
+ -> { (-@bignum).divmod(0.0) }.should.raise(ZeroDivisionError)
end
it "raises a TypeError when the given argument is not an Integer" do
- -> { @bignum.divmod(mock('10')) }.should raise_error(TypeError)
- -> { @bignum.divmod("10") }.should raise_error(TypeError)
- -> { @bignum.divmod(:symbol) }.should raise_error(TypeError)
+ -> { @bignum.divmod(mock('10')) }.should.raise(TypeError)
+ -> { @bignum.divmod("10") }.should.raise(TypeError)
+ -> { @bignum.divmod(:symbol) }.should.raise(TypeError)
end
end
end
diff --git a/spec/ruby/core/integer/downto_spec.rb b/spec/ruby/core/integer/downto_spec.rb
index af7a7e36b9..a244d3df55 100644
--- a/spec/ruby/core/integer/downto_spec.rb
+++ b/spec/ruby/core/integer/downto_spec.rb
@@ -1,6 +1,6 @@
require_relative '../../spec_helper'
-describe "Integer#downto [stop] when self and stop are Fixnums" do
+describe "Integer#downto [stop] when self and stop are Integers" do
it "does not yield when stop is greater than self" do
result = []
5.downto(6) { |x| result << x }
@@ -27,8 +27,8 @@ describe "Integer#downto [stop] when self and stop are Fixnums" do
end
it "raises an ArgumentError for invalid endpoints" do
- -> {1.downto("A") {|x| p x } }.should raise_error(ArgumentError)
- -> {1.downto(nil) {|x| p x } }.should raise_error(ArgumentError)
+ -> {1.downto("A") {|x| p x } }.should.raise(ArgumentError)
+ -> {1.downto(nil) {|x| p x } }.should.raise(ArgumentError)
end
describe "when no block is given" do
@@ -45,9 +45,9 @@ describe "Integer#downto [stop] when self and stop are Fixnums" do
describe "size" do
it "raises an ArgumentError for invalid endpoints" do
enum = 1.downto("A")
- -> { enum.size }.should raise_error(ArgumentError)
+ -> { enum.size }.should.raise(ArgumentError)
enum = 1.downto(nil)
- -> { enum.size }.should raise_error(ArgumentError)
+ -> { enum.size }.should.raise(ArgumentError)
end
it "returns self - stop + 1" do
diff --git a/spec/ruby/core/integer/dup_spec.rb b/spec/ruby/core/integer/dup_spec.rb
index 7f4d512465..3e5739ad37 100644
--- a/spec/ruby/core/integer/dup_spec.rb
+++ b/spec/ruby/core/integer/dup_spec.rb
@@ -3,11 +3,11 @@ require_relative '../../spec_helper'
describe "Integer#dup" do
it "returns self for small integers" do
integer = 1_000
- integer.dup.should equal(integer)
+ integer.dup.should.equal?(integer)
end
it "returns self for large integers" do
integer = 4_611_686_018_427_387_905
- integer.dup.should equal(integer)
+ integer.dup.should.equal?(integer)
end
end
diff --git a/spec/ruby/core/integer/element_reference_spec.rb b/spec/ruby/core/integer/element_reference_spec.rb
index 4c236a11e7..c69ec2315d 100644
--- a/spec/ruby/core/integer/element_reference_spec.rb
+++ b/spec/ruby/core/integer/element_reference_spec.rb
@@ -59,13 +59,13 @@ describe "Integer#[]" do
end
it "raises a TypeError when passed a String" do
- -> { 3["3"] }.should raise_error(TypeError)
+ -> { 3["3"] }.should.raise(TypeError)
end
it "raises a TypeError when #to_int does not return an Integer" do
obj = mock('asdf')
obj.should_receive(:to_int).and_return("asdf")
- -> { 3[obj] }.should raise_error(TypeError)
+ -> { 3[obj] }.should.raise(TypeError)
end
it "calls #to_int to coerce a String to a Bignum and returns 0" do
@@ -79,87 +79,79 @@ describe "Integer#[]" do
3[bignum_value.to_f].should == 0
end
- ruby_version_is "2.7" do
- context "when index and length passed" do
- it "returns specified number of bits from specified position" do
- 0b101001101[2, 4].should == 0b0011
- 0b101001101[2, 5].should == 0b10011
- 0b101001101[2, 7].should == 0b1010011
- end
+ context "when index and length passed" do
+ it "returns specified number of bits from specified position" do
+ 0b101001101[2, 4].should == 0b0011
+ 0b101001101[2, 5].should == 0b10011
+ 0b101001101[2, 7].should == 0b1010011
+ end
- it "ensures n[i, len] equals to (n >> i) & ((1 << len) - 1)" do
- n = 0b101001101; i = 2; len = 4
- n[i, len].should == (n >> i) & ((1 << len) - 1)
- end
+ it "ensures n[i, len] equals to (n >> i) & ((1 << len) - 1)" do
+ n = 0b101001101; i = 2; len = 4
+ n[i, len].should == (n >> i) & ((1 << len) - 1)
+ end
- it "moves start position to the most significant bits when negative index passed" do
- 0b000001[-1, 4].should == 0b10
- 0b000001[-2, 4].should == 0b100
- 0b000001[-3, 4].should == 0b1000
- end
+ it "moves start position to the most significant bits when negative index passed" do
+ 0b000001[-1, 4].should == 0b10
+ 0b000001[-2, 4].should == 0b100
+ 0b000001[-3, 4].should == 0b1000
+ end
- it "ignores negative length" do
- 0b101001101[1, -1].should == 0b10100110
- 0b101001101[2, -1].should == 0b1010011
- 0b101001101[3, -1].should == 0b101001
+ it "ignores negative length" do
+ 0b101001101[1, -1].should == 0b10100110
+ 0b101001101[2, -1].should == 0b1010011
+ 0b101001101[3, -1].should == 0b101001
- 0b101001101[3, -5].should == 0b101001
- 0b101001101[3, -15].should == 0b101001
- 0b101001101[3, -125].should == 0b101001
- end
+ 0b101001101[3, -5].should == 0b101001
+ 0b101001101[3, -15].should == 0b101001
+ 0b101001101[3, -125].should == 0b101001
end
+ end
- context "when range passed" do
- it "returns bits specified by range" do
- 0b101001101[2..5].should == 0b0011
- 0b101001101[2..6].should == 0b10011
- 0b101001101[2..8].should == 0b1010011
- end
+ context "when range passed" do
+ it "returns bits specified by range" do
+ 0b101001101[2..5].should == 0b0011
+ 0b101001101[2..6].should == 0b10011
+ 0b101001101[2..8].should == 0b1010011
+ end
- it "ensures n[i..j] equals to (n >> i) & ((1 << (j - i + 1)) - 1)" do
- n = 0b101001101; i = 2; j = 5
- n[i..j].should == (n >> i) & ((1 << (j - i + 1)) - 1)
- end
+ it "ensures n[i..j] equals to (n >> i) & ((1 << (j - i + 1)) - 1)" do
+ n = 0b101001101; i = 2; j = 5
+ n[i..j].should == (n >> i) & ((1 << (j - i + 1)) - 1)
+ end
- it "ensures n[i..] equals to (n >> i)" do
- eval("0b101001101[3..]").should == 0b101001101 >> 3
- end
+ it "ensures n[i..] equals to (n >> i)" do
+ eval("0b101001101[3..]").should == 0b101001101 >> 3
+ end
- it "moves lower boundary to the most significant bits when negative value passed" do
- 0b000001[-1, 4].should == 0b10
- 0b000001[-2, 4].should == 0b100
- 0b000001[-3, 4].should == 0b1000
- end
+ it "moves lower boundary to the most significant bits when negative value passed" do
+ 0b000001[-1, 4].should == 0b10
+ 0b000001[-2, 4].should == 0b100
+ 0b000001[-3, 4].should == 0b1000
+ end
- it "ignores negative upper boundary" do
- 0b101001101[1..-1].should == 0b10100110
- 0b101001101[1..-2].should == 0b10100110
- 0b101001101[1..-3].should == 0b10100110
- end
+ it "ignores upper boundary smaller than lower boundary" do
+ 0b101001101[4..1].should == 0b10100
+ 0b101001101[4..2].should == 0b10100
+ 0b101001101[-4..-5].should == 0b1010011010000
+ end
- it "ignores upper boundary smaller than lower boundary" do
- 0b101001101[4..1].should == 0b10100
- 0b101001101[4..2].should == 0b10100
- 0b101001101[4..3].should == 0b10100
- end
+ it "raises FloatDomainError if any boundary is infinity" do
+ -> { 0x0001[3..Float::INFINITY] }.should.raise(FloatDomainError, /Infinity/)
+ -> { 0x0001[-Float::INFINITY..3] }.should.raise(FloatDomainError, /-Infinity/)
+ end
- it "raises FloatDomainError if any boundary is infinity" do
- -> { 0x0001[3..Float::INFINITY] }.should raise_error(FloatDomainError, /Infinity/)
- -> { 0x0001[-Float::INFINITY..3] }.should raise_error(FloatDomainError, /-Infinity/)
+ context "when passed (..i)" do
+ it "returns 0 if all i bits equal 0" do
+ eval("0b10000[..1]").should == 0
+ eval("0b10000[..2]").should == 0
+ eval("0b10000[..3]").should == 0
end
- context "when passed (..i)" do
- it "returns 0 if all i bits equal 0" do
- eval("0b10000[..1]").should == 0
- eval("0b10000[..2]").should == 0
- eval("0b10000[..3]").should == 0
- end
-
- it "raises ArgumentError if any of i bit equals 1" do
- -> {
- eval("0b111110[..3]")
- }.should raise_error(ArgumentError, /The beginless range for Integer#\[\] results in infinity/)
- end
+ it "raises ArgumentError if any of i bit equals 1" do
+ -> {
+ eval("0b111110[..3]")
+ }.should.raise(ArgumentError, /The beginless range for Integer#\[\] results in infinity/)
end
end
end
@@ -187,10 +179,10 @@ describe "Integer#[]" do
it "raises a TypeError when the given argument can't be converted to Integer" do
obj = mock('asdf')
- -> { @bignum[obj] }.should raise_error(TypeError)
+ -> { @bignum[obj] }.should.raise(TypeError)
obj.should_receive(:to_int).and_return("asdf")
- -> { @bignum[obj] }.should raise_error(TypeError)
+ -> { @bignum[obj] }.should.raise(TypeError)
end
end
end
diff --git a/spec/ruby/core/integer/eql_spec.rb b/spec/ruby/core/integer/eql_spec.rb
new file mode 100644
index 0000000000..9c80173206
--- /dev/null
+++ b/spec/ruby/core/integer/eql_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+
+describe "Integer#eql?" do
+ context "bignum" do
+ it "returns true for the same value" do
+ bignum_value.eql?(bignum_value).should == true
+ end
+
+ it "returns false for a different Integer value" do
+ bignum_value.eql?(bignum_value(1)).should == false
+ end
+
+ it "returns false for a Float with the same numeric value" do
+ bignum_value.eql?(bignum_value.to_f).should == false
+ end
+
+ it "returns false for a Rational with the same numeric value" do
+ bignum_value.eql?(Rational(bignum_value)).should == false
+ end
+
+ it "returns false for a Fixnum-range Integer" do
+ bignum_value.eql?(42).should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/even_spec.rb b/spec/ruby/core/integer/even_spec.rb
index a314cc6b19..578979320e 100644
--- a/spec/ruby/core/integer/even_spec.rb
+++ b/spec/ruby/core/integer/even_spec.rb
@@ -3,38 +3,38 @@ require_relative '../../spec_helper'
describe "Integer#even?" do
context "fixnum" do
it "returns true for a Fixnum when it is an even number" do
- (-2).even?.should be_true
- (-1).even?.should be_false
+ (-2).even?.should == true
+ (-1).even?.should == false
- 0.even?.should be_true
- 1.even?.should be_false
- 2.even?.should be_true
+ 0.even?.should == true
+ 1.even?.should == false
+ 2.even?.should == true
end
it "returns true for a Bignum when it is an even number" do
- bignum_value(0).even?.should be_true
- bignum_value(1).even?.should be_false
+ bignum_value(0).even?.should == true
+ bignum_value(1).even?.should == false
- (-bignum_value(0)).even?.should be_true
- (-bignum_value(1)).even?.should be_false
+ (-bignum_value(0)).even?.should == true
+ (-bignum_value(1)).even?.should == false
end
end
context "bignum" do
it "returns true if self is even and positive" do
- (10000**10).even?.should be_true
+ (10000**10).even?.should == true
end
it "returns true if self is even and negative" do
- (-10000**10).even?.should be_true
+ (-10000**10).even?.should == true
end
it "returns false if self is odd and positive" do
- (9879**976).even?.should be_false
+ (9879**976).even?.should == false
end
it "returns false if self is odd and negative" do
- (-9879**976).even?.should be_false
+ (-9879**976).even?.should == false
end
end
end
diff --git a/spec/ruby/core/integer/fdiv_spec.rb b/spec/ruby/core/integer/fdiv_spec.rb
index 6de170278f..7854d66dec 100644
--- a/spec/ruby/core/integer/fdiv_spec.rb
+++ b/spec/ruby/core/integer/fdiv_spec.rb
@@ -9,13 +9,64 @@ describe "Integer#fdiv" do
8.fdiv(bignum_value).should be_close(8.673617379884035e-19, TOLERANCE)
end
+ it "performs floating-point division between self bignum and a bignum" do
+ num = 1000000000000000000000000000000000048148248609680896326399448564623182963452541226153892315137780403285956264146010000000000000000000000000000000000048148248609680896326399448564623182963452541226153892315137780403285956264146010000000000000000000000000000000000048148248609680896326399448564623182963452541226153892315137780403285956264146009
+ den = 2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ num.fdiv(den).should == 500.0
+ end
+
+ it "rounds to the correct value for bignums" do
+ den = 9 * 10**342
+
+ num = 1 * 10**344
+ num.fdiv(den).should == 11.11111111111111
+
+ num = 1 * 10**343
+ num.fdiv(den).should == 1.1111111111111112
+
+ num = 1 * 10**342
+ num.fdiv(den).should == 0.1111111111111111
+
+ num = 2 * 10**342
+ num.fdiv(den).should == 0.2222222222222222
+
+ num = 3 * 10**342
+ num.fdiv(den).should == 0.3333333333333333
+
+ num = 4 * 10**342
+ num.fdiv(den).should == 0.4444444444444444
+
+ num = 5 * 10**342
+ num.fdiv(den).should == 0.5555555555555556
+
+ num = 6 * 10**342
+ num.fdiv(den).should == 0.6666666666666666
+
+ num = 7 * 10**342
+ num.fdiv(den).should == 0.7777777777777778
+
+ num = 8 * 10**342
+ num.fdiv(den).should == 0.8888888888888888
+
+ num = 9 * 10**342
+ num.fdiv(den).should == 1.0
+
+ num = -5 * 10**342
+ num.fdiv(den).should == -0.5555555555555556
+ end
+
+ it "rounds to the correct float for bignum denominators" do
+ 1.fdiv(10**324).should == 0.0
+ 1.fdiv(10**323).should == 1.0e-323
+ end
+
it "performs floating-point division between self and a Float" do
8.fdiv(9.0).should be_close(0.888888888888889, TOLERANCE)
end
it "returns NaN when the argument is NaN" do
- -1.fdiv(nan_value).nan?.should be_true
- 1.fdiv(nan_value).nan?.should be_true
+ -1.fdiv(nan_value).nan?.should == true
+ 1.fdiv(nan_value).nan?.should == true
end
it "returns Infinity when the argument is 0" do
@@ -35,11 +86,11 @@ describe "Integer#fdiv" do
end
it "raises a TypeError when argument isn't numeric" do
- -> { 1.fdiv(mock('non-numeric')) }.should raise_error(TypeError)
+ -> { 1.fdiv(mock('non-numeric')) }.should.raise(TypeError)
end
it "raises an ArgumentError when passed multiple arguments" do
- -> { 1.fdiv(6,0.2) }.should raise_error(ArgumentError)
+ -> { 1.fdiv(6,0.2) }.should.raise(ArgumentError)
end
it "follows the coercion protocol" do
diff --git a/spec/ruby/core/integer/fixtures/classes.rb b/spec/ruby/core/integer/fixtures/classes.rb
index 6ebfbd1565..65948efb9f 100644
--- a/spec/ruby/core/integer/fixtures/classes.rb
+++ b/spec/ruby/core/integer/fixtures/classes.rb
@@ -1,4 +1,14 @@
module IntegerSpecs
class CoerceError < StandardError
end
+
+ class CoercibleNumeric
+ def initialize(v) @v = v end
+ def coerce(other) [self.class.new(other), self] end
+ def >(other) @v.to_i > other.to_i end
+ def >=(other) @v.to_i >= other.to_i end
+ def <(other) @v.to_i < other.to_i end
+ def <=(other) @v.to_i <= other.to_i end
+ def to_i() @v.to_i end
+ end
end
diff --git a/spec/ruby/core/integer/floor_spec.rb b/spec/ruby/core/integer/floor_spec.rb
index aaa816fdc5..8fb84d58cb 100644
--- a/spec/ruby/core/integer/floor_spec.rb
+++ b/spec/ruby/core/integer/floor_spec.rb
@@ -1,19 +1,13 @@
require_relative '../../spec_helper'
require_relative 'shared/to_i'
require_relative 'shared/integer_rounding'
+require_relative 'shared/integer_floor_precision'
describe "Integer#floor" do
it_behaves_like :integer_to_i, :floor
it_behaves_like :integer_rounding_positive_precision, :floor
- context "precision argument specified as part of the floor method is negative" do
- it "returns the largest integer less than self with at least precision.abs trailing zeros" do
- 1832.floor(-1).should eql(1830)
- 1832.floor(-2).should eql(1800)
- 1832.floor(-3).should eql(1000)
- -1832.floor(-1).should eql(-1840)
- -1832.floor(-2).should eql(-1900)
- -1832.floor(-3).should eql(-2000)
- end
+ context "with precision" do
+ it_behaves_like :integer_floor_precision, :Integer
end
end
diff --git a/spec/ruby/core/integer/gcd_spec.rb b/spec/ruby/core/integer/gcd_spec.rb
index 8aa654a16a..1fff785927 100644
--- a/spec/ruby/core/integer/gcd_spec.rb
+++ b/spec/ruby/core/integer/gcd_spec.rb
@@ -7,8 +7,8 @@ describe "Integer#gcd" do
end
it "returns an Integer" do
- 36.gcd(6).should be_kind_of(Integer)
- 4.gcd(20981).should be_kind_of(Integer)
+ 36.gcd(6).should.is_a?(Integer)
+ 4.gcd(20981).should.is_a?(Integer)
end
it "returns the greatest common divisor of self and argument" do
@@ -33,13 +33,13 @@ describe "Integer#gcd" do
it "accepts a Bignum argument" do
bignum = 9999**99
- bignum.should be_kind_of(Bignum)
+ bignum.should.is_a?(Integer)
99.gcd(bignum).should == 99
end
it "works if self is a Bignum" do
bignum = 9999**99
- bignum.should be_kind_of(Bignum)
+ bignum.should.is_a?(Integer)
bignum.gcd(99).should == 99
end
@@ -55,15 +55,15 @@ describe "Integer#gcd" do
end
it "raises an ArgumentError if not given an argument" do
- -> { 12.gcd }.should raise_error(ArgumentError)
+ -> { 12.gcd }.should.raise(ArgumentError)
end
it "raises an ArgumentError if given more than one argument" do
- -> { 12.gcd(30, 20) }.should raise_error(ArgumentError)
+ -> { 12.gcd(30, 20) }.should.raise(ArgumentError)
end
it "raises a TypeError unless the argument is an Integer" do
- -> { 39.gcd(3.8) }.should raise_error(TypeError)
- -> { 45872.gcd([]) }.should raise_error(TypeError)
+ -> { 39.gcd(3.8) }.should.raise(TypeError)
+ -> { 45872.gcd([]) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/integer/gcdlcm_spec.rb b/spec/ruby/core/integer/gcdlcm_spec.rb
index 5b3669e62a..419bf9f275 100644
--- a/spec/ruby/core/integer/gcdlcm_spec.rb
+++ b/spec/ruby/core/integer/gcdlcm_spec.rb
@@ -7,8 +7,8 @@ describe "Integer#gcdlcm" do
end
it "returns an Array" do
- 36.gcdlcm(6).should be_kind_of(Array)
- 4.gcdlcm(20981).should be_kind_of(Array)
+ 36.gcdlcm(6).should.is_a?(Array)
+ 4.gcdlcm(20981).should.is_a?(Array)
end
it "returns a two-element Array" do
@@ -28,26 +28,26 @@ describe "Integer#gcdlcm" do
it "accepts a Bignum argument" do
bignum = 91999**99
- bignum.should be_kind_of(Bignum)
+ bignum.should.is_a?(Integer)
99.gcdlcm(bignum).should == [99.gcd(bignum), 99.lcm(bignum)]
end
it "works if self is a Bignum" do
bignum = 9999**89
- bignum.should be_kind_of(Bignum)
+ bignum.should.is_a?(Integer)
bignum.gcdlcm(99).should == [bignum.gcd(99), bignum.lcm(99)]
end
it "raises an ArgumentError if not given an argument" do
- -> { 12.gcdlcm }.should raise_error(ArgumentError)
+ -> { 12.gcdlcm }.should.raise(ArgumentError)
end
it "raises an ArgumentError if given more than one argument" do
- -> { 12.gcdlcm(30, 20) }.should raise_error(ArgumentError)
+ -> { 12.gcdlcm(30, 20) }.should.raise(ArgumentError)
end
it "raises a TypeError unless the argument is an Integer" do
- -> { 39.gcdlcm(3.8) }.should raise_error(TypeError)
- -> { 45872.gcdlcm([]) }.should raise_error(TypeError)
+ -> { 39.gcdlcm(3.8) }.should.raise(TypeError)
+ -> { 45872.gcdlcm([]) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/integer/gt_spec.rb b/spec/ruby/core/integer/gt_spec.rb
index f0179e566d..75436144cf 100644
--- a/spec/ruby/core/integer/gt_spec.rb
+++ b/spec/ruby/core/integer/gt_spec.rb
@@ -17,8 +17,8 @@ describe "Integer#>" do
end
it "raises an ArgumentError when given a non-Integer" do
- -> { 5 > "4" }.should raise_error(ArgumentError)
- -> { 5 > mock('x') }.should raise_error(ArgumentError)
+ -> { 5 > "4" }.should.raise(ArgumentError)
+ -> { 5 > mock('x') }.should.raise(ArgumentError)
end
end
@@ -36,8 +36,13 @@ describe "Integer#>" do
end
it "raises an ArgumentError when given a non-Integer" do
- -> { @bignum > "4" }.should raise_error(ArgumentError)
- -> { @bignum > mock('str') }.should raise_error(ArgumentError)
+ -> { @bignum > "4" }.should.raise(ArgumentError)
+ -> { @bignum > mock('str') }.should.raise(ArgumentError)
+ end
+
+ it "dispatches the correct operator after coercion" do
+ (bignum_value > IntegerSpecs::CoercibleNumeric.new(1)).should == true
+ (bignum_value > IntegerSpecs::CoercibleNumeric.new(bignum_value * 2)).should == false
end
end
end
diff --git a/spec/ruby/core/integer/gte_spec.rb b/spec/ruby/core/integer/gte_spec.rb
index 6237fc2c51..e5cb892efd 100644
--- a/spec/ruby/core/integer/gte_spec.rb
+++ b/spec/ruby/core/integer/gte_spec.rb
@@ -18,8 +18,8 @@ describe "Integer#>=" do
end
it "raises an ArgumentError when given a non-Integer" do
- -> { 5 >= "4" }.should raise_error(ArgumentError)
- -> { 5 >= mock('x') }.should raise_error(ArgumentError)
+ -> { 5 >= "4" }.should.raise(ArgumentError)
+ -> { 5 >= mock('x') }.should.raise(ArgumentError)
end
end
@@ -36,8 +36,13 @@ describe "Integer#>=" do
end
it "raises an ArgumentError when given a non-Integer" do
- -> { @bignum >= "4" }.should raise_error(ArgumentError)
- -> { @bignum >= mock('str') }.should raise_error(ArgumentError)
+ -> { @bignum >= "4" }.should.raise(ArgumentError)
+ -> { @bignum >= mock('str') }.should.raise(ArgumentError)
+ end
+
+ it "dispatches the correct operator after coercion" do
+ (bignum_value >= IntegerSpecs::CoercibleNumeric.new(1)).should == true
+ (bignum_value >= IntegerSpecs::CoercibleNumeric.new(bignum_value * 2)).should == false
end
end
end
diff --git a/spec/ruby/core/integer/integer_spec.rb b/spec/ruby/core/integer/integer_spec.rb
index 2d5d2e3e92..19a962d548 100644
--- a/spec/ruby/core/integer/integer_spec.rb
+++ b/spec/ruby/core/integer/integer_spec.rb
@@ -6,8 +6,8 @@ describe "Integer" do
end
it "is the class of both small and large integers" do
- 42.class.should equal(Integer)
- bignum_value.class.should equal(Integer)
+ 42.class.should.equal?(Integer)
+ bignum_value.class.should.equal?(Integer)
end
end
diff --git a/spec/ruby/core/integer/lcm_spec.rb b/spec/ruby/core/integer/lcm_spec.rb
index 77d3ad3488..6659247d1e 100644
--- a/spec/ruby/core/integer/lcm_spec.rb
+++ b/spec/ruby/core/integer/lcm_spec.rb
@@ -7,8 +7,8 @@ describe "Integer#lcm" do
end
it "returns an Integer" do
- 36.lcm(6).should be_kind_of(Integer)
- 4.lcm(20981).should be_kind_of(Integer)
+ 36.lcm(6).should.is_a?(Integer)
+ 4.lcm(20981).should.is_a?(Integer)
end
it "returns the least common multiple of self and argument" do
@@ -33,26 +33,26 @@ describe "Integer#lcm" do
it "accepts a Bignum argument" do
bignum = 9999**99
- bignum.should be_kind_of(Bignum)
+ bignum.should.is_a?(Integer)
99.lcm(bignum).should == bignum
end
it "works if self is a Bignum" do
bignum = 9999**99
- bignum.should be_kind_of(Bignum)
+ bignum.should.is_a?(Integer)
bignum.lcm(99).should == bignum
end
it "raises an ArgumentError if not given an argument" do
- -> { 12.lcm }.should raise_error(ArgumentError)
+ -> { 12.lcm }.should.raise(ArgumentError)
end
it "raises an ArgumentError if given more than one argument" do
- -> { 12.lcm(30, 20) }.should raise_error(ArgumentError)
+ -> { 12.lcm(30, 20) }.should.raise(ArgumentError)
end
it "raises a TypeError unless the argument is an Integer" do
- -> { 39.lcm(3.8) }.should raise_error(TypeError)
- -> { 45872.lcm([]) }.should raise_error(TypeError)
+ -> { 39.lcm(3.8) }.should.raise(TypeError)
+ -> { 45872.lcm([]) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/integer/left_shift_spec.rb b/spec/ruby/core/integer/left_shift_spec.rb
index f9a8c9bbd1..7eedb91228 100644
--- a/spec/ruby/core/integer/left_shift_spec.rb
+++ b/spec/ruby/core/integer/left_shift_spec.rb
@@ -56,15 +56,15 @@ describe "Integer#<< (with n << m)" do
(3 << -bignum_value).should == 0
end
- it "returns an Bignum == fixnum_max * 2 when fixnum_max << 1 and n > 0" do
+ it "returns a Bignum == fixnum_max * 2 when fixnum_max << 1 and n > 0" do
result = fixnum_max << 1
- result.should be_an_instance_of(Bignum)
+ result.should.instance_of?(Integer)
result.should == fixnum_max * 2
end
- it "returns an Bignum == fixnum_min * 2 when fixnum_min << 1 and n < 0" do
+ it "returns a Bignum == fixnum_min * 2 when fixnum_min << 1 and n < 0" do
result = fixnum_min << 1
- result.should be_an_instance_of(Bignum)
+ result.should.instance_of?(Integer)
result.should == fixnum_min * 2
end
@@ -82,21 +82,21 @@ describe "Integer#<< (with n << m)" do
obj = mock("a string")
obj.should_receive(:to_int).and_return("asdf")
- -> { 3 << obj }.should raise_error(TypeError)
+ -> { 3 << obj }.should.raise(TypeError)
end
it "raises a TypeError when passed nil" do
- -> { 3 << nil }.should raise_error(TypeError)
+ -> { 3 << nil }.should.raise(TypeError)
end
it "raises a TypeError when passed a String" do
- -> { 3 << "4" }.should raise_error(TypeError)
+ -> { 3 << "4" }.should.raise(TypeError)
end
end
context "bignum" do
before :each do
- @bignum = bignum_value * 16
+ @bignum = bignum_value * 8 # 2 ** 67
end
it "returns n shifted left m bits when n > 0, m > 0" do
@@ -127,19 +127,15 @@ describe "Integer#<< (with n << m)" do
(@bignum << -68).should == 0
end
- it "returns 0 when m < 0 and m is a Bignum" do
- (@bignum << -bignum_value).should == 0
- end
-
it "returns a Fixnum == fixnum_max when (fixnum_max * 2) << -1 and n > 0" do
result = (fixnum_max * 2) << -1
- result.should be_an_instance_of(Fixnum)
+ result.should.instance_of?(Integer)
result.should == fixnum_max
end
it "returns a Fixnum == fixnum_min when (fixnum_min * 2) << -1 and n < 0" do
result = (fixnum_min * 2) << -1
- result.should be_an_instance_of(Fixnum)
+ result.should.instance_of?(Integer)
result.should == fixnum_min
end
@@ -154,15 +150,62 @@ describe "Integer#<< (with n << m)" do
obj = mock("a string")
obj.should_receive(:to_int).and_return("asdf")
- -> { @bignum << obj }.should raise_error(TypeError)
+ -> { @bignum << obj }.should.raise(TypeError)
end
it "raises a TypeError when passed nil" do
- -> { @bignum << nil }.should raise_error(TypeError)
+ -> { @bignum << nil }.should.raise(TypeError)
end
it "raises a TypeError when passed a String" do
- -> { @bignum << "4" }.should raise_error(TypeError)
+ -> { @bignum << "4" }.should.raise(TypeError)
+ end
+ end
+
+ context "when m is a bignum or larger than int" do
+ it "returns -1 when m < 0 and n < 0" do
+ (-1 << -bignum_value).should == -1
+ (-1 << -(2**40)).should == -1
+
+ (-bignum_value << -bignum_value).should == -1
+ (-bignum_value << -(2**40)).should == -1
+ end
+
+ it "returns 0 when m < 0 and n >= 0" do
+ (0 << -bignum_value).should == 0
+ (1 << -bignum_value).should == 0
+ (bignum_value << -bignum_value).should == 0
+
+ (0 << -(2**40)).should == 0
+ (1 << -(2**40)).should == 0
+ (bignum_value << -(2**40)).should == 0
+ end
+
+ it "returns 0 when m > 0 long and n == 0" do
+ (0 << (2**40)).should == 0
+ end
+
+ it "returns 0 when m > 0 bignum and n == 0" do
+ (0 << bignum_value).should == 0
+ end
+
+ it "raises RangeError when m > 0 and n != 0" do
+ # https://bugs.ruby-lang.org/issues/18518#note-9
+ limit = RUBY_ENGINE == 'ruby' ? 2**67 : 2**32
+
+ coerce_long = mock("long")
+ coerce_long.stub!(:to_int).and_return(limit)
+ coerce_bignum = mock("bignum")
+ coerce_bignum.stub!(:to_int).and_return(bignum_value)
+ exps = [limit, coerce_long]
+ exps << bignum_value << coerce_bignum if bignum_value >= limit
+
+ exps.each { |exp|
+ -> { (1 << exp) }.should.raise(RangeError, 'shift width too big')
+ -> { (-1 << exp) }.should.raise(RangeError, 'shift width too big')
+ -> { (bignum_value << exp) }.should.raise(RangeError, 'shift width too big')
+ -> { (-bignum_value << exp) }.should.raise(RangeError, 'shift width too big')
+ }
end
end
end
diff --git a/spec/ruby/core/integer/lt_spec.rb b/spec/ruby/core/integer/lt_spec.rb
index c182f82555..dd1391d093 100644
--- a/spec/ruby/core/integer/lt_spec.rb
+++ b/spec/ruby/core/integer/lt_spec.rb
@@ -17,8 +17,8 @@ describe "Integer#<" do
end
it "raises an ArgumentError when given a non-Integer" do
- -> { 5 < "4" }.should raise_error(ArgumentError)
- -> { 5 < mock('x') }.should raise_error(ArgumentError)
+ -> { 5 < "4" }.should.raise(ArgumentError)
+ -> { 5 < mock('x') }.should.raise(ArgumentError)
end
end
@@ -38,8 +38,13 @@ describe "Integer#<" do
end
it "raises an ArgumentError when given a non-Integer" do
- -> { @bignum < "4" }.should raise_error(ArgumentError)
- -> { @bignum < mock('str') }.should raise_error(ArgumentError)
+ -> { @bignum < "4" }.should.raise(ArgumentError)
+ -> { @bignum < mock('str') }.should.raise(ArgumentError)
+ end
+
+ it "dispatches the correct operator after coercion" do
+ (bignum_value < IntegerSpecs::CoercibleNumeric.new(bignum_value * 2)).should == true
+ (bignum_value < IntegerSpecs::CoercibleNumeric.new(1)).should == false
end
end
end
diff --git a/spec/ruby/core/integer/lte_spec.rb b/spec/ruby/core/integer/lte_spec.rb
index 65b71d77f2..9ef1c9f604 100644
--- a/spec/ruby/core/integer/lte_spec.rb
+++ b/spec/ruby/core/integer/lte_spec.rb
@@ -18,8 +18,8 @@ describe "Integer#<=" do
end
it "raises an ArgumentError when given a non-Integer" do
- -> { 5 <= "4" }.should raise_error(ArgumentError)
- -> { 5 <= mock('x') }.should raise_error(ArgumentError)
+ -> { 5 <= "4" }.should.raise(ArgumentError)
+ -> { 5 <= mock('x') }.should.raise(ArgumentError)
end
end
@@ -46,8 +46,13 @@ describe "Integer#<=" do
end
it "raises an ArgumentError when given a non-Integer" do
- -> { @bignum <= "4" }.should raise_error(ArgumentError)
- -> { @bignum <= mock('str') }.should raise_error(ArgumentError)
+ -> { @bignum <= "4" }.should.raise(ArgumentError)
+ -> { @bignum <= mock('str') }.should.raise(ArgumentError)
+ end
+
+ it "dispatches the correct operator after coercion" do
+ (bignum_value <= IntegerSpecs::CoercibleNumeric.new(bignum_value * 2)).should == true
+ (bignum_value <= IntegerSpecs::CoercibleNumeric.new(1)).should == false
end
end
end
diff --git a/spec/ruby/core/integer/minus_spec.rb b/spec/ruby/core/integer/minus_spec.rb
index ce50c8752f..5fd3a81a72 100644
--- a/spec/ruby/core/integer/minus_spec.rb
+++ b/spec/ruby/core/integer/minus_spec.rb
@@ -10,16 +10,16 @@ describe "Integer#-" do
(9237212 - 5_280).should == 9231932
(781 - 0.5).should == 780.5
- (2_560_496 - bignum_value).should == -9223372036852215312
+ (2_560_496 - bignum_value).should == -18446744073706991120
end
it "raises a TypeError when given a non-Integer" do
-> {
(obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10)
13 - obj
- }.should raise_error(TypeError)
- -> { 13 - "10" }.should raise_error(TypeError)
- -> { 13 - :symbol }.should raise_error(TypeError)
+ }.should.raise(TypeError)
+ -> { 13 - "10" }.should.raise(TypeError)
+ -> { 13 - :symbol }.should.raise(TypeError)
end
end
@@ -29,15 +29,32 @@ describe "Integer#-" do
end
it "returns self minus the given Integer" do
- (@bignum - 9).should == 9223372036854776113
- (@bignum - 12.57).should be_close(9223372036854776109.43, TOLERANCE)
+ (@bignum - 9).should == 18446744073709551921
+ (@bignum - 12.57).should be_close(18446744073709551917.43, TOLERANCE)
(@bignum - bignum_value(42)).should == 272
end
it "raises a TypeError when given a non-Integer" do
- -> { @bignum - mock('10') }.should raise_error(TypeError)
- -> { @bignum - "10" }.should raise_error(TypeError)
- -> { @bignum - :symbol }.should raise_error(TypeError)
+ -> { @bignum - mock('10') }.should.raise(TypeError)
+ -> { @bignum - "10" }.should.raise(TypeError)
+ -> { @bignum - :symbol }.should.raise(TypeError)
end
end
+
+ it "coerces the RHS and calls #coerce" do
+ obj = mock("integer plus")
+ obj.should_receive(:coerce).with(5).and_return([5, 10])
+ (5 - obj).should == -5
+ end
+
+ it "coerces the RHS and calls #coerce even if it's private" do
+ obj = Object.new
+ class << obj
+ private def coerce(n)
+ [n, 10]
+ end
+ end
+
+ (5 - obj).should == -5
+ end
end
diff --git a/spec/ruby/core/integer/multiply_spec.rb b/spec/ruby/core/integer/multiply_spec.rb
index d1e9c2640d..7f30eebaa5 100644
--- a/spec/ruby/core/integer/multiply_spec.rb
+++ b/spec/ruby/core/integer/multiply_spec.rb
@@ -10,7 +10,7 @@ describe "Integer#*" do
(1342177 * 800).should == 1073741600
(65536 * 65536).should == 4294967296
- (256 * bignum_value).should == 2361183241434822606848
+ (256 * bignum_value).should == 4722366482869645213696
(6712 * 0.25).should == 1678.0
end
@@ -18,9 +18,9 @@ describe "Integer#*" do
-> {
(obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10)
13 * obj
- }.should raise_error(TypeError)
- -> { 13 * "10" }.should raise_error(TypeError)
- -> { 13 * :symbol }.should raise_error(TypeError)
+ }.should.raise(TypeError)
+ -> { 13 * "10" }.should.raise(TypeError)
+ -> { 13 * :symbol }.should.raise(TypeError)
end
end
@@ -32,14 +32,14 @@ describe "Integer#*" do
it "returns self multiplied by the given Integer" do
(@bignum * (1/bignum_value(0xffff).to_f)).should be_close(1.0, TOLERANCE)
(@bignum * (1/bignum_value(0xffff).to_f)).should be_close(1.0, TOLERANCE)
- (@bignum * 10).should == 92233720368547765800
- (@bignum * (@bignum - 40)).should == 85070591730234629737795195287525433200
+ (@bignum * 10).should == 184467440737095523880
+ (@bignum * (@bignum - 40)).should == 340282366920938491207277694290934407024
end
it "raises a TypeError when given a non-Integer" do
- -> { @bignum * mock('10') }.should raise_error(TypeError)
- -> { @bignum * "10" }.should raise_error(TypeError)
- -> { @bignum * :symbol }.should raise_error(TypeError)
+ -> { @bignum * mock('10') }.should.raise(TypeError)
+ -> { @bignum * "10" }.should.raise(TypeError)
+ -> { @bignum * :symbol }.should.raise(TypeError)
end
end
end
diff --git a/spec/ruby/core/integer/nobits_spec.rb b/spec/ruby/core/integer/nobits_spec.rb
index b132a4a724..f1a3aca9d5 100644
--- a/spec/ruby/core/integer/nobits_spec.rb
+++ b/spec/ruby/core/integer/nobits_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "Integer#nobits?" do
- it "returns true iff all no bits of the argument are set in the receiver" do
+ it "returns true if and only if all no bits of the argument are set in the receiver" do
42.nobits?(42).should == false
0b1010_1010.nobits?(0b1000_0010).should == false
0b1010_1010.nobits?(0b1000_0001).should == false
@@ -29,8 +29,8 @@ describe "Integer#nobits?" do
-> {
(obj = mock('10')).should_receive(:coerce).any_number_of_times.and_return([42,10])
13.nobits?(obj)
- }.should raise_error(TypeError)
- -> { 13.nobits?("10") }.should raise_error(TypeError)
- -> { 13.nobits?(:symbol) }.should raise_error(TypeError)
+ }.should.raise(TypeError)
+ -> { 13.nobits?("10") }.should.raise(TypeError)
+ -> { 13.nobits?(:symbol) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/integer/odd_spec.rb b/spec/ruby/core/integer/odd_spec.rb
index dd779fa44b..f9e50ec790 100644
--- a/spec/ruby/core/integer/odd_spec.rb
+++ b/spec/ruby/core/integer/odd_spec.rb
@@ -3,36 +3,36 @@ require_relative '../../spec_helper'
describe "Integer#odd?" do
context "fixnum" do
it "returns true when self is an odd number" do
- (-2).odd?.should be_false
- (-1).odd?.should be_true
+ (-2).odd?.should == false
+ (-1).odd?.should == true
- 0.odd?.should be_false
- 1.odd?.should be_true
- 2.odd?.should be_false
+ 0.odd?.should == false
+ 1.odd?.should == true
+ 2.odd?.should == false
- bignum_value(0).odd?.should be_false
- bignum_value(1).odd?.should be_true
+ bignum_value(0).odd?.should == false
+ bignum_value(1).odd?.should == true
- (-bignum_value(0)).odd?.should be_false
- (-bignum_value(1)).odd?.should be_true
+ (-bignum_value(0)).odd?.should == false
+ (-bignum_value(1)).odd?.should == true
end
end
context "bignum" do
it "returns true if self is odd and positive" do
- (987279**19).odd?.should be_true
+ (987279**19).odd?.should == true
end
it "returns true if self is odd and negative" do
- (-9873389**97).odd?.should be_true
+ (-9873389**97).odd?.should == true
end
it "returns false if self is even and positive" do
- (10000000**10).odd?.should be_false
+ (10000000**10).odd?.should == false
end
it "returns false if self is even and negative" do
- (-1000000**100).odd?.should be_false
+ (-1000000**100).odd?.should == false
end
end
end
diff --git a/spec/ruby/core/integer/ord_spec.rb b/spec/ruby/core/integer/ord_spec.rb
index bcb57bea98..8b7dfe460d 100644
--- a/spec/ruby/core/integer/ord_spec.rb
+++ b/spec/ruby/core/integer/ord_spec.rb
@@ -2,16 +2,16 @@ require_relative '../../spec_helper'
describe "Integer#ord" do
it "returns self" do
- 20.ord.should eql(20)
- 40.ord.should eql(40)
+ 20.ord.should.eql?(20)
+ 40.ord.should.eql?(40)
- 0.ord.should eql(0)
- (-10).ord.should eql(-10)
+ 0.ord.should.eql?(0)
+ (-10).ord.should.eql?(-10)
- ?a.ord.should eql(97)
- ?Z.ord.should eql(90)
+ ?a.ord.should.eql?(97)
+ ?Z.ord.should.eql?(90)
- bignum_value.ord.should eql(bignum_value)
- (-bignum_value).ord.should eql(-bignum_value)
+ bignum_value.ord.should.eql?(bignum_value)
+ (-bignum_value).ord.should.eql?(-bignum_value)
end
end
diff --git a/spec/ruby/core/integer/plus_spec.rb b/spec/ruby/core/integer/plus_spec.rb
index be626c3305..b684377bd5 100644
--- a/spec/ruby/core/integer/plus_spec.rb
+++ b/spec/ruby/core/integer/plus_spec.rb
@@ -9,7 +9,7 @@ describe "Integer#+" do
(491 + 2).should == 493
(90210 + 10).should == 90220
- (9 + bignum_value).should == 9223372036854775817
+ (9 + bignum_value).should == 18446744073709551625
(1001 + 5.219).should == 1006.219
end
@@ -17,9 +17,9 @@ describe "Integer#+" do
-> {
(obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10)
13 + obj
- }.should raise_error(TypeError)
- -> { 13 + "10" }.should raise_error(TypeError)
- -> { 13 + :symbol }.should raise_error(TypeError)
+ }.should.raise(TypeError)
+ -> { 13 + "10" }.should.raise(TypeError)
+ -> { 13 + :symbol }.should.raise(TypeError)
end
end
@@ -29,15 +29,47 @@ describe "Integer#+" do
end
it "returns self plus the given Integer" do
- (@bignum + 4).should == 9223372036854775888
- (@bignum + 4.2).should be_close(9223372036854775888.2, TOLERANCE)
- (@bignum + bignum_value(3)).should == 18446744073709551695
+ (@bignum + 4).should == 18446744073709551696
+ (@bignum + 4.2).should be_close(18446744073709551696.2, TOLERANCE)
+ (@bignum + bignum_value(3)).should == 36893488147419103311
end
it "raises a TypeError when given a non-Integer" do
- -> { @bignum + mock('10') }.should raise_error(TypeError)
- -> { @bignum + "10" }.should raise_error(TypeError)
- -> { @bignum + :symbol}.should raise_error(TypeError)
+ -> { @bignum + mock('10') }.should.raise(TypeError)
+ -> { @bignum + "10" }.should.raise(TypeError)
+ -> { @bignum + :symbol}.should.raise(TypeError)
end
end
+
+ it "can be redefined" do
+ code = <<~RUBY
+ class Integer
+ alias_method :old_plus, :+
+ def +(other)
+ self - other
+ end
+ end
+ result = 1 + 2
+ Integer.alias_method :+, :old_plus
+ print result
+ RUBY
+ ruby_exe(code).should == "-1"
+ end
+
+ it "coerces the RHS and calls #coerce" do
+ obj = mock("integer plus")
+ obj.should_receive(:coerce).with(6).and_return([6, 3])
+ (6 + obj).should == 9
+ end
+
+ it "coerces the RHS and calls #coerce even if it's private" do
+ obj = Object.new
+ class << obj
+ private def coerce(n)
+ [n, 3]
+ end
+ end
+
+ (6 + obj).should == 9
+ end
end
diff --git a/spec/ruby/core/integer/pow_spec.rb b/spec/ruby/core/integer/pow_spec.rb
index 4712911095..12a3839c40 100644
--- a/spec/ruby/core/integer/pow_spec.rb
+++ b/spec/ruby/core/integer/pow_spec.rb
@@ -16,36 +16,36 @@ describe "Integer#pow" do
end
it "works well with bignums" do
- 2.pow(61, 5843009213693951).should eql 3697379018277258
- 2.pow(62, 5843009213693952).should eql 1551748822859776
- 2.pow(63, 5843009213693953).should eql 3103497645717974
- 2.pow(64, 5843009213693954).should eql 363986077738838
+ 2.pow(61, 5843009213693951).should.eql? 3697379018277258
+ 2.pow(62, 5843009213693952).should.eql? 1551748822859776
+ 2.pow(63, 5843009213693953).should.eql? 3103497645717974
+ 2.pow(64, 5843009213693954).should.eql? 363986077738838
end
it "handles sign like #divmod does" do
- 2.pow(5, 12).should == 8
- 2.pow(5, -12).should == -4
- -2.pow(5, 12).should == 4
+ 2.pow(5, 12).should == 8
+ 2.pow(5, -12).should == -4
+ -2.pow(5, 12).should == 4
-2.pow(5, -12).should == -8
end
it "ensures all arguments are integers" do
- -> { 2.pow(5, 12.0) }.should raise_error(TypeError, /2nd argument not allowed unless all arguments are integers/)
- -> { 2.pow(5, Rational(12, 1)) }.should raise_error(TypeError, /2nd argument not allowed unless all arguments are integers/)
+ -> { 2.pow(5, 12.0) }.should.raise(TypeError, /2nd argument not allowed unless all arguments are integers/)
+ -> { 2.pow(5, Rational(12, 1)) }.should.raise(TypeError, /2nd argument not allowed unless all arguments are integers/)
end
it "raises TypeError for non-numeric value" do
- -> { 2.pow(5, "12") }.should raise_error(TypeError)
- -> { 2.pow(5, []) }.should raise_error(TypeError)
- -> { 2.pow(5, nil) }.should raise_error(TypeError)
+ -> { 2.pow(5, "12") }.should.raise(TypeError)
+ -> { 2.pow(5, []) }.should.raise(TypeError)
+ -> { 2.pow(5, nil) }.should.raise(TypeError)
end
it "raises a ZeroDivisionError when the given argument is 0" do
- -> { 2.pow(5, 0) }.should raise_error(ZeroDivisionError)
+ -> { 2.pow(5, 0) }.should.raise(ZeroDivisionError)
end
it "raises a RangeError when the first argument is negative and the second argument is present" do
- -> { 2.pow(-5, 1) }.should raise_error(RangeError)
+ -> { 2.pow(-5, 1) }.should.raise(RangeError)
end
end
end
diff --git a/spec/ruby/core/integer/pred_spec.rb b/spec/ruby/core/integer/pred_spec.rb
index 86750ebfd1..fce536b5a2 100644
--- a/spec/ruby/core/integer/pred_spec.rb
+++ b/spec/ruby/core/integer/pred_spec.rb
@@ -2,10 +2,10 @@ require_relative '../../spec_helper'
describe "Integer#pred" do
it "returns the Integer equal to self - 1" do
- 0.pred.should eql(-1)
- -1.pred.should eql(-2)
- bignum_value.pred.should eql(bignum_value(-1))
- fixnum_min.pred.should eql(fixnum_min - 1)
- 20.pred.should eql(19)
+ 0.pred.should.eql?(-1)
+ -1.pred.should.eql?(-2)
+ bignum_value.pred.should.eql?(bignum_value(-1))
+ fixnum_min.pred.should.eql?(fixnum_min - 1)
+ 20.pred.should.eql?(19)
end
end
diff --git a/spec/ruby/core/integer/rationalize_spec.rb b/spec/ruby/core/integer/rationalize_spec.rb
index 09d741af33..272eca6911 100644
--- a/spec/ruby/core/integer/rationalize_spec.rb
+++ b/spec/ruby/core/integer/rationalize_spec.rb
@@ -12,7 +12,7 @@ describe "Integer#rationalize" do
it "returns a Rational object" do
@numbers.each do |number|
- number.rationalize.should be_an_instance_of(Rational)
+ number.rationalize.should.instance_of?(Rational)
end
end
@@ -33,7 +33,7 @@ describe "Integer#rationalize" do
end
it "raises ArgumentError when passed more than one argument" do
- -> { 1.rationalize(0.1, 0.1) }.should raise_error(ArgumentError)
- -> { 1.rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError)
+ -> { 1.rationalize(0.1, 0.1) }.should.raise(ArgumentError)
+ -> { 1.rationalize(0.1, 0.1, 2) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/integer/remainder_spec.rb b/spec/ruby/core/integer/remainder_spec.rb
index cd10dad6f2..deb81fb2a5 100644
--- a/spec/ruby/core/integer/remainder_spec.rb
+++ b/spec/ruby/core/integer/remainder_spec.rb
@@ -15,17 +15,17 @@ describe "Integer#remainder" do
end
it "keeps sign of self" do
- 5.remainder( 3).should == 2
- 5.remainder(-3).should == 2
+ 5.remainder( 3).should == 2
+ 5.remainder(-3).should == 2
-5.remainder( 3).should == -2
-5.remainder(-3).should == -2
end
it "raises TypeError if passed non-numeric argument" do
- -> { 5.remainder("3") }.should raise_error(TypeError)
- -> { 5.remainder(:"3") }.should raise_error(TypeError)
- -> { 5.remainder([]) }.should raise_error(TypeError)
- -> { 5.remainder(nil) }.should raise_error(TypeError)
+ -> { 5.remainder("3") }.should.raise(TypeError)
+ -> { 5.remainder(:"3") }.should.raise(TypeError)
+ -> { 5.remainder([]) }.should.raise(TypeError)
+ -> { 5.remainder(nil) }.should.raise(TypeError)
end
end
@@ -33,19 +33,19 @@ describe "Integer#remainder" do
it "returns the remainder of dividing self by other" do
a = bignum_value(79)
a.remainder(2).should == 1
- a.remainder(97.345).should be_close(46.5674996147722, TOLERANCE)
+ a.remainder(97.345).should be_close(93.1349992295444, TOLERANCE)
a.remainder(bignum_value).should == 79
end
it "raises a ZeroDivisionError if other is zero and not a Float" do
- -> { bignum_value(66).remainder(0) }.should raise_error(ZeroDivisionError)
+ -> { bignum_value(66).remainder(0) }.should.raise(ZeroDivisionError)
end
it "does raises ZeroDivisionError if other is zero and a Float" do
a = bignum_value(7)
b = bignum_value(32)
- -> { a.remainder(0.0) }.should raise_error(ZeroDivisionError)
- -> { b.remainder(-0.0) }.should raise_error(ZeroDivisionError)
+ -> { a.remainder(0.0) }.should.raise(ZeroDivisionError)
+ -> { b.remainder(-0.0) }.should.raise(ZeroDivisionError)
end
end
end
diff --git a/spec/ruby/core/integer/right_shift_spec.rb b/spec/ruby/core/integer/right_shift_spec.rb
index 1eac6cb5bc..4281d3b7ab 100644
--- a/spec/ruby/core/integer/right_shift_spec.rb
+++ b/spec/ruby/core/integer/right_shift_spec.rb
@@ -52,19 +52,15 @@ describe "Integer#>> (with n >> m)" do
(-7 >> 64).should == -1
end
- it "returns 0 when m is a bignum" do
- (3 >> bignum_value).should == 0
- end
-
- it "returns an Bignum == fixnum_max * 2 when fixnum_max >> -1 and n > 0" do
+ it "returns a Bignum == fixnum_max * 2 when fixnum_max >> -1 and n > 0" do
result = fixnum_max >> -1
- result.should be_an_instance_of(Bignum)
+ result.should.instance_of?(Integer)
result.should == fixnum_max * 2
end
- it "returns an Bignum == fixnum_min * 2 when fixnum_min >> -1 and n < 0" do
+ it "returns a Bignum == fixnum_min * 2 when fixnum_min >> -1 and n < 0" do
result = fixnum_min >> -1
- result.should be_an_instance_of(Bignum)
+ result.should.instance_of?(Integer)
result.should == fixnum_min * 2
end
@@ -82,21 +78,21 @@ describe "Integer#>> (with n >> m)" do
obj = mock("a string")
obj.should_receive(:to_int).and_return("asdf")
- -> { 3 >> obj }.should raise_error(TypeError)
+ -> { 3 >> obj }.should.raise(TypeError)
end
it "raises a TypeError when passed nil" do
- -> { 3 >> nil }.should raise_error(TypeError)
+ -> { 3 >> nil }.should.raise(TypeError)
end
it "raises a TypeError when passed a String" do
- -> { 3 >> "4" }.should raise_error(TypeError)
+ -> { 3 >> "4" }.should.raise(TypeError)
end
end
context "bignum" do
before :each do
- @bignum = bignum_value * 16
+ @bignum = bignum_value * 8 # 2 ** 67
end
it "returns n shifted right m bits when n > 0, m > 0" do
@@ -153,19 +149,15 @@ describe "Integer#>> (with n >> m)" do
(@bignum >> 68).should == 0
end
- it "returns 0 when m is a Bignum" do
- (@bignum >> bignum_value).should == 0
- end
-
it "returns a Fixnum == fixnum_max when (fixnum_max * 2) >> 1 and n > 0" do
result = (fixnum_max * 2) >> 1
- result.should be_an_instance_of(Fixnum)
+ result.should.instance_of?(Integer)
result.should == fixnum_max
end
it "returns a Fixnum == fixnum_min when (fixnum_min * 2) >> 1 and n < 0" do
result = (fixnum_min * 2) >> 1
- result.should be_an_instance_of(Fixnum)
+ result.should.instance_of?(Integer)
result.should == fixnum_min
end
@@ -180,15 +172,62 @@ describe "Integer#>> (with n >> m)" do
obj = mock("a string")
obj.should_receive(:to_int).and_return("asdf")
- -> { @bignum >> obj }.should raise_error(TypeError)
+ -> { @bignum >> obj }.should.raise(TypeError)
end
it "raises a TypeError when passed nil" do
- -> { @bignum >> nil }.should raise_error(TypeError)
+ -> { @bignum >> nil }.should.raise(TypeError)
end
it "raises a TypeError when passed a String" do
- -> { @bignum >> "4" }.should raise_error(TypeError)
+ -> { @bignum >> "4" }.should.raise(TypeError)
+ end
+ end
+
+ context "when m is a bignum or larger than int" do
+ it "returns -1 when m > 0 and n < 0" do
+ (-1 >> bignum_value).should == -1
+ (-1 >> (2**40)).should == -1
+
+ (-bignum_value >> bignum_value).should == -1
+ (-bignum_value >> (2**40)).should == -1
+ end
+
+ it "returns 0 when m > 0 and n >= 0" do
+ (0 >> bignum_value).should == 0
+ (1 >> bignum_value).should == 0
+ (bignum_value >> bignum_value).should == 0
+
+ (0 >> (2**40)).should == 0
+ (1 >> (2**40)).should == 0
+ (bignum_value >> (2**40)).should == 0
+ end
+
+ it "returns 0 when m < 0 long and n == 0" do
+ (0 >> -(2**40)).should == 0
+ end
+
+ it "returns 0 when m < 0 bignum and n == 0" do
+ (0 >> -bignum_value).should == 0
+ end
+
+ it "raises RangeError when m < 0 and n != 0" do
+ # https://bugs.ruby-lang.org/issues/18518#note-9
+ limit = RUBY_ENGINE == 'ruby' ? 2**67 : 2**32
+
+ coerce_long = mock("long")
+ coerce_long.stub!(:to_int).and_return(-limit)
+ coerce_bignum = mock("bignum")
+ coerce_bignum.stub!(:to_int).and_return(-bignum_value)
+ exps = [-limit, coerce_long]
+ exps << -bignum_value << coerce_bignum if bignum_value >= limit
+
+ exps.each { |exp|
+ -> { (1 >> exp) }.should.raise(RangeError, 'shift width too big')
+ -> { (-1 >> exp) }.should.raise(RangeError, 'shift width too big')
+ -> { (bignum_value >> exp) }.should.raise(RangeError, 'shift width too big')
+ -> { (-bignum_value >> exp) }.should.raise(RangeError, 'shift width too big')
+ }
end
end
end
diff --git a/spec/ruby/core/integer/round_spec.rb b/spec/ruby/core/integer/round_spec.rb
index 45ac126fd3..a3f11e7a76 100644
--- a/spec/ruby/core/integer/round_spec.rb
+++ b/spec/ruby/core/integer/round_spec.rb
@@ -8,39 +8,37 @@ describe "Integer#round" do
# redmine:5228
it "returns itself rounded if passed a negative value" do
- +249.round(-2).should eql(+200)
- -249.round(-2).should eql(-200)
- (+25 * 10**70 - 1).round(-71).should eql(+20 * 10**70)
- (-25 * 10**70 + 1).round(-71).should eql(-20 * 10**70)
+ +249.round(-2).should.eql?(+200)
+ -249.round(-2).should.eql?(-200)
+ (+25 * 10**70 - 1).round(-71).should.eql?(+20 * 10**70)
+ (-25 * 10**70 + 1).round(-71).should.eql?(-20 * 10**70)
end
it "returns itself rounded to nearest if passed a negative value" do
- +250.round(-2).should eql(+300)
- -250.round(-2).should eql(-300)
- (+25 * 10**70).round(-71).should eql(+30 * 10**70)
- (-25 * 10**70).round(-71).should eql(-30 * 10**70)
+ +250.round(-2).should.eql?(+300)
+ -250.round(-2).should.eql?(-300)
+ (+25 * 10**70).round(-71).should.eql?(+30 * 10**70)
+ (-25 * 10**70).round(-71).should.eql?(-30 * 10**70)
end
- platform_is_not wordsize: 32 do
- it "raises a RangeError when passed a big negative value" do
- -> { 42.round(fixnum_min) }.should raise_error(RangeError)
- end
+ it "raises a RangeError when passed a big negative value" do
+ -> { 42.round(min_long - 1) }.should.raise(RangeError)
end
it "raises a RangeError when passed Float::INFINITY" do
- -> { 42.round(Float::INFINITY) }.should raise_error(RangeError)
+ -> { 42.round(Float::INFINITY) }.should.raise(RangeError)
end
it "raises a RangeError when passed a beyond signed int" do
- -> { 42.round(1<<31) }.should raise_error(RangeError)
+ -> { 42.round(1<<31) }.should.raise(RangeError)
end
it "raises a TypeError when passed a String" do
- -> { 42.round("4") }.should raise_error(TypeError)
+ -> { 42.round("4") }.should.raise(TypeError)
end
it "raises a TypeError when its argument cannot be converted to an Integer" do
- -> { 42.round(nil) }.should raise_error(TypeError)
+ -> { 42.round(nil) }.should.raise(TypeError)
end
it "calls #to_int on the argument to convert it to an Integer" do
@@ -52,32 +50,32 @@ describe "Integer#round" do
it "raises a TypeError when #to_int does not return an Integer" do
obj = mock("Object")
obj.stub!(:to_int).and_return([])
- -> { 42.round(obj) }.should raise_error(TypeError)
+ -> { 42.round(obj) }.should.raise(TypeError)
end
it "returns different rounded values depending on the half option" do
- 25.round(-1, half: :up).should eql(30)
- 25.round(-1, half: :down).should eql(20)
- 25.round(-1, half: :even).should eql(20)
- 25.round(-1, half: nil).should eql(30)
- 35.round(-1, half: :up).should eql(40)
- 35.round(-1, half: :down).should eql(30)
- 35.round(-1, half: :even).should eql(40)
- 35.round(-1, half: nil).should eql(40)
- (-25).round(-1, half: :up).should eql(-30)
- (-25).round(-1, half: :down).should eql(-20)
- (-25).round(-1, half: :even).should eql(-20)
- (-25).round(-1, half: nil).should eql(-30)
+ 25.round(-1, half: :up).should.eql?(30)
+ 25.round(-1, half: :down).should.eql?(20)
+ 25.round(-1, half: :even).should.eql?(20)
+ 25.round(-1, half: nil).should.eql?(30)
+ 35.round(-1, half: :up).should.eql?(40)
+ 35.round(-1, half: :down).should.eql?(30)
+ 35.round(-1, half: :even).should.eql?(40)
+ 35.round(-1, half: nil).should.eql?(40)
+ (-25).round(-1, half: :up).should.eql?(-30)
+ (-25).round(-1, half: :down).should.eql?(-20)
+ (-25).round(-1, half: :even).should.eql?(-20)
+ (-25).round(-1, half: nil).should.eql?(-30)
end
it "returns itself if passed a positive precision and the half option" do
- 35.round(1, half: :up).should eql(35)
- 35.round(1, half: :down).should eql(35)
- 35.round(1, half: :even).should eql(35)
+ 35.round(1, half: :up).should.eql?(35)
+ 35.round(1, half: :down).should.eql?(35)
+ 35.round(1, half: :even).should.eql?(35)
end
it "raises ArgumentError for an unknown rounding mode" do
- -> { 42.round(-1, half: :foo) }.should raise_error(ArgumentError, /invalid rounding mode: foo/)
- -> { 42.round(1, half: :foo) }.should raise_error(ArgumentError, /invalid rounding mode: foo/)
+ -> { 42.round(-1, half: :foo) }.should.raise(ArgumentError, /invalid rounding mode: foo/)
+ -> { 42.round(1, half: :foo) }.should.raise(ArgumentError, /invalid rounding mode: foo/)
end
end
diff --git a/spec/ruby/core/integer/shared/abs.rb b/spec/ruby/core/integer/shared/abs.rb
index 946aa21864..43844c9065 100644
--- a/spec/ruby/core/integer/shared/abs.rb
+++ b/spec/ruby/core/integer/shared/abs.rb
@@ -11,8 +11,8 @@ describe :integer_abs, shared: true do
context "bignum" do
it "returns the absolute bignum value" do
- bignum_value(39).send(@method).should == 9223372036854775847
- (-bignum_value(18)).send(@method).should == 9223372036854775826
+ bignum_value(39).send(@method).should == 18446744073709551655
+ (-bignum_value(18)).send(@method).should == 18446744073709551634
end
end
end
diff --git a/spec/ruby/core/integer/shared/arithmetic_coerce.rb b/spec/ruby/core/integer/shared/arithmetic_coerce.rb
index 4c0cbcb999..561b18fe52 100644
--- a/spec/ruby/core/integer/shared/arithmetic_coerce.rb
+++ b/spec/ruby/core/integer/shared/arithmetic_coerce.rb
@@ -1,31 +1,11 @@
require_relative '../fixtures/classes'
-describe :integer_arithmetic_coerce_rescue, shared: true do
- it "rescues exception (StandardError and subclasses) raised in other#coerce and raises TypeError" do
- b = mock("numeric with failed #coerce")
- b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError)
-
- # e.g. 1 + b
- -> { 1.send(@method, b) }.should raise_error(TypeError, /MockObject can't be coerced into Integer/)
- end
-
- it "does not rescue Exception and StandardError siblings raised in other#coerce" do
- [Exception, NoMemoryError].each do |exception|
- b = mock("numeric with failed #coerce")
- b.should_receive(:coerce).and_raise(exception)
-
- # e.g. 1 + b
- -> { 1.send(@method, b) }.should raise_error(exception)
- end
- end
-end
-
describe :integer_arithmetic_coerce_not_rescue, shared: true do
it "does not rescue exception raised in other#coerce" do
b = mock("numeric with failed #coerce")
b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError)
# e.g. 1 + b
- -> { 1.send(@method, b) }.should raise_error(IntegerSpecs::CoerceError)
+ -> { 1.send(@method, b) }.should.raise(IntegerSpecs::CoerceError)
end
end
diff --git a/spec/ruby/core/integer/shared/comparison_coerce.rb b/spec/ruby/core/integer/shared/comparison_coerce.rb
index 50437f77f5..4bb7404183 100644
--- a/spec/ruby/core/integer/shared/comparison_coerce.rb
+++ b/spec/ruby/core/integer/shared/comparison_coerce.rb
@@ -1,33 +1,11 @@
require_relative '../fixtures/classes'
-describe :integer_comparison_coerce_rescue, shared: true do
- it "rescues exception (StandardError and subclasses) raised in other#coerce and raises ArgumentError" do
- b = mock("numeric with failed #coerce")
- b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError)
-
- # e.g. 1 > b
- -> {
- -> { 1.send(@method, b) }.should raise_error(ArgumentError, /comparison of Integer with MockObject failed/)
- }.should complain(/Numerical comparison operators will no more rescue exceptions of #coerce/)
- end
-
- it "does not rescue Exception and StandardError siblings raised in other#coerce" do
- [Exception, NoMemoryError].each do |exception|
- b = mock("numeric with failed #coerce")
- b.should_receive(:coerce).and_raise(exception)
-
- # e.g. 1 > b
- -> { 1.send(@method, b) }.should raise_error(exception)
- end
- end
-end
-
describe :integer_comparison_coerce_not_rescue, shared: true do
it "does not rescue exception raised in other#coerce" do
b = mock("numeric with failed #coerce")
b.should_receive(:coerce).and_raise(IntegerSpecs::CoerceError)
# e.g. 1 > b
- -> { 1.send(@method, b) }.should raise_error(IntegerSpecs::CoerceError)
+ -> { 1.send(@method, b) }.should.raise(IntegerSpecs::CoerceError)
end
end
diff --git a/spec/ruby/core/integer/shared/equal.rb b/spec/ruby/core/integer/shared/equal.rb
index 03416b60f5..c621ba3f81 100644
--- a/spec/ruby/core/integer/shared/equal.rb
+++ b/spec/ruby/core/integer/shared/equal.rb
@@ -4,14 +4,14 @@ describe :integer_equal, shared: true do
1.send(@method, 1).should == true
9.send(@method, 5).should == false
- # Actually, these call Float#==, Bignum#== etc.
+ # Actually, these call Float#==, Integer#== etc.
9.send(@method, 9.0).should == true
9.send(@method, 9.01).should == false
10.send(@method, bignum_value).should == false
end
- it "calls 'other == self' if the given argument is not a Integer" do
+ it "calls 'other == self' if the given argument is not an Integer" do
1.send(@method, '*').should == false
obj = mock('one other')
@@ -54,5 +54,10 @@ describe :integer_equal, shared: true do
@bignum.send(@method, obj).should == true
@bignum.send(@method, obj).should == false
end
+
+ it "does not lose precision when comparing with a Float" do
+ (bignum_value(1).send(@method, bignum_value.to_f)).should == false
+ (bignum_value.send(@method, bignum_value.to_f)).should == true
+ end
end
end
diff --git a/spec/ruby/core/integer/shared/exponent.rb b/spec/ruby/core/integer/shared/exponent.rb
index f292cc2448..0be13859f9 100644
--- a/spec/ruby/core/integer/shared/exponent.rb
+++ b/spec/ruby/core/integer/shared/exponent.rb
@@ -1,58 +1,78 @@
describe :integer_exponent, shared: true do
context "fixnum" do
it "returns self raised to the given power" do
- 2.send(@method, 0).should eql 1
- 2.send(@method, 1).should eql 2
- 2.send(@method, 2).should eql 4
+ 2.send(@method, 0).should.eql? 1
+ 2.send(@method, 1).should.eql? 2
+ 2.send(@method, 2).should.eql? 4
- 9.send(@method, 0.5).should eql 3.0
- 9.send(@method, Rational(1, 2)).should eql 3.0
+ 9.send(@method, 0.5).should.eql? 3.0
+ 9.send(@method, Rational(1, 2)).should.eql? 3.0
5.send(@method, -1).to_f.to_s.should == '0.2'
- 2.send(@method, 40).should eql 1099511627776
+ 2.send(@method, 40).should.eql? 1099511627776
end
it "overflows the answer to a bignum transparently" do
- 2.send(@method, 29).should eql 536870912
- 2.send(@method, 30).should eql 1073741824
- 2.send(@method, 31).should eql 2147483648
- 2.send(@method, 32).should eql 4294967296
+ 2.send(@method, 29).should.eql? 536870912
+ 2.send(@method, 30).should.eql? 1073741824
+ 2.send(@method, 31).should.eql? 2147483648
+ 2.send(@method, 32).should.eql? 4294967296
- 2.send(@method, 61).should eql 2305843009213693952
- 2.send(@method, 62).should eql 4611686018427387904
- 2.send(@method, 63).should eql 9223372036854775808
- 2.send(@method, 64).should eql 18446744073709551616
- 8.send(@method, 23).should eql 590295810358705651712
+ 2.send(@method, 61).should.eql? 2305843009213693952
+ 2.send(@method, 62).should.eql? 4611686018427387904
+ 2.send(@method, 63).should.eql? 9223372036854775808
+ 2.send(@method, 64).should.eql? 18446744073709551616
+ 8.send(@method, 23).should.eql? 590295810358705651712
end
it "raises negative numbers to the given power" do
- (-2).send(@method, 29).should eql(-536870912)
- (-2).send(@method, 30).should eql(1073741824)
- (-2).send(@method, 31).should eql(-2147483648)
- (-2).send(@method, 32).should eql(4294967296)
+ (-2).send(@method, 29).should.eql?(-536870912)
+ (-2).send(@method, 30).should.eql?(1073741824)
+ (-2).send(@method, 31).should.eql?(-2147483648)
+ (-2).send(@method, 32).should.eql?(4294967296)
+ (-2).send(@method, 33).should.eql?(-8589934592)
- (-2).send(@method, 61).should eql(-2305843009213693952)
- (-2).send(@method, 62).should eql(4611686018427387904)
- (-2).send(@method, 63).should eql(-9223372036854775808)
- (-2).send(@method, 64).should eql(18446744073709551616)
+ (-2).send(@method, 61).should.eql?(-2305843009213693952)
+ (-2).send(@method, 62).should.eql?(4611686018427387904)
+ (-2).send(@method, 63).should.eql?(-9223372036854775808)
+ (-2).send(@method, 64).should.eql?(18446744073709551616)
+ (-2).send(@method, 65).should.eql?(-36893488147419103232)
end
it "can raise 1 to a bignum safely" do
- 1.send(@method, 4611686018427387904).should eql 1
+ 1.send(@method, 4611686018427387904).should.eql? 1
end
it "can raise -1 to a bignum safely" do
- (-1).send(@method, 4611686018427387904).should eql(1)
- (-1).send(@method, 4611686018427387905).should eql(-1)
+ (-1).send(@method, 4611686018427387904).should.eql?(1)
+ (-1).send(@method, 4611686018427387905).should.eql?(-1)
end
- it "returns Float::INFINITY when the number is too big" do
- 2.send(@method, 427387904).should == Float::INFINITY
+ ruby_version_is ""..."3.4" do
+ it "returns Float::INFINITY when the number is too big" do
+ -> {
+ 2.send(@method, 427387904).should == Float::INFINITY
+ }.should complain(/warning: in a\*\*b, b may be too big/)
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "returns an Integer for results larger than the old 32MB limit" do
+ # 2 ** 40000000 requires 40000001 bits
+ # This exceeds the old 32MB limit but is within the new 16GB limit
+ result = 2.send(@method, 40000000)
+ result.should.is_a?(Integer)
+ result.bit_length.should == 40000001
+ end
+
+ it "raises an ArgumentError when the result size exceeds the limit" do
+ -> { 100000000.send(@method, 1000000000) }.should.raise(ArgumentError)
+ end
end
it "raises a ZeroDivisionError for 0 ** -1" do
- -> { 0.send(@method, -1) }.should raise_error(ZeroDivisionError)
- -> { 0.send(@method, Rational(-1, 1)) }.should raise_error(ZeroDivisionError)
+ -> { 0.send(@method, -1) }.should.raise(ZeroDivisionError)
+ -> { 0.send(@method, Rational(-1, 1)) }.should.raise(ZeroDivisionError)
end
it "returns Float::INFINITY for 0 ** -1.0" do
@@ -60,9 +80,9 @@ describe :integer_exponent, shared: true do
end
it "raises a TypeError when given a non-numeric power" do
- -> { 13.send(@method, "10") }.should raise_error(TypeError)
- -> { 13.send(@method, :symbol) }.should raise_error(TypeError)
- -> { 13.send(@method, nil) }.should raise_error(TypeError)
+ -> { 13.send(@method, "10") }.should.raise(TypeError)
+ -> { 13.send(@method, :symbol) }.should.raise(TypeError)
+ -> { 13.send(@method, nil) }.should.raise(TypeError)
end
it "coerces power and calls #**" do
@@ -94,25 +114,49 @@ describe :integer_exponent, shared: true do
end
it "returns self raised to other power" do
- (@bignum.send(@method, 4)).should == 7237005577332262361485077344629993318496048279512298547155833600056910050625
- (@bignum.send(@method, 1.2)).should be_close(57262152889751597425762.57804, TOLERANCE)
+ (@bignum.send(@method, 4)).should == 115792089237316196603666111261383895964500887398800252671052694326794607293761
+ (@bignum.send(@method, 1.3)).should be_close(11109528802438156839288832.0, TOLERANCE)
end
it "raises a TypeError when given a non-Integer" do
- -> { @bignum.send(@method, mock('10')) }.should raise_error(TypeError)
- -> { @bignum.send(@method, "10") }.should raise_error(TypeError)
- -> { @bignum.send(@method, :symbol) }.should raise_error(TypeError)
- end
-
- it "switch to a Float when the values is too big" do
- flt = @bignum.send(@method, @bignum)
- flt.should be_kind_of(Float)
- flt.infinite?.should == 1
+ -> { @bignum.send(@method, mock('10')) }.should.raise(TypeError)
+ -> { @bignum.send(@method, "10") }.should.raise(TypeError)
+ -> { @bignum.send(@method, :symbol) }.should.raise(TypeError)
+ end
+
+ ruby_version_is ""..."3.4" do
+ it "switch to a Float when the values is too big" do
+ flt = nil
+ -> {
+ flt = @bignum.send(@method, @bignum)
+ }.should complain(/warning: in a\*\*b, b may be too big/)
+ flt.should.is_a?(Float)
+ flt.infinite?.should == 1
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "returns an Integer for large Bignum results exceeding the old limit" do
+ # (2 ** 70) ** 500000 requires 35000001 bits
+ # This exceeds the old 32MB limit but is within the new 16GB limit
+ bignum_base = 2 ** 70
+ result = bignum_base.send(@method, 500000)
+ result.should.is_a?(Integer)
+ result.bit_length.should == 35000001
+ end
+
+ it "raises an ArgumentError when Bignum result exceeds the limit" do
+ # @bignum ** @bignum would require enormous memory
+ -> {
+ @bignum.send(@method, @bignum)
+ }.should.raise(ArgumentError)
+ end
end
it "returns a complex number when negative and raised to a fractional power" do
- ((-@bignum).send(@method, (1.0/3))) .should be_close(Complex(1048576,1816186.907597341), TOLERANCE)
- ((-@bignum).send(@method, Rational(1,3))).should be_close(Complex(1048576,1816186.907597341), TOLERANCE)
+ (-bignum_value).send(@method, (1.0/2)).should be_close(Complex(0.0, 4294967296.0), TOLERANCE)
+ (-@bignum).send(@method, (1.0/3)) .should be_close(Complex(1321122.9748145656, 2288252.1154253655), TOLERANCE)
+ (-@bignum).send(@method, Rational(1,3)).should be_close(Complex(1321122.9748145656, 2288252.1154253655), TOLERANCE)
end
end
end
diff --git a/spec/ruby/core/integer/shared/integer_ceil_precision.rb b/spec/ruby/core/integer/shared/integer_ceil_precision.rb
new file mode 100644
index 0000000000..b23c17937f
--- /dev/null
+++ b/spec/ruby/core/integer/shared/integer_ceil_precision.rb
@@ -0,0 +1,54 @@
+describe :integer_ceil_precision, shared: true do
+ context "precision is zero" do
+ it "returns Integer equal to self" do
+ send(@method, 0).ceil(0).should.eql?(0)
+ send(@method, 123).ceil(0).should.eql?(123)
+ send(@method, -123).ceil(0).should.eql?(-123)
+ end
+ end
+
+ context "precision is positive" do
+ it "returns self" do
+ send(@method, 0).ceil(1).should.eql?(send(@method, 0))
+ send(@method, 0).ceil(10).should.eql?(send(@method, 0))
+
+ send(@method, 123).ceil(10).should.eql?(send(@method, 123))
+ send(@method, -123).ceil(10).should.eql?(send(@method, -123))
+ end
+ end
+
+ context "precision is negative" do
+ it "always returns 0 when self is 0" do
+ send(@method, 0).ceil(-1).should.eql?(0)
+ send(@method, 0).ceil(-10).should.eql?(0)
+ end
+
+ it "returns Integer equal to self if there are already at least precision.abs trailing zeros" do
+ send(@method, 10).ceil(-1).should.eql?(10)
+ send(@method, 100).ceil(-1).should.eql?(100)
+ send(@method, 100).ceil(-2).should.eql?(100)
+ send(@method, -10).ceil(-1).should.eql?(-10)
+ send(@method, -100).ceil(-1).should.eql?(-100)
+ send(@method, -100).ceil(-2).should.eql?(-100)
+ end
+
+ it "returns smallest Integer greater than self with at least precision.abs trailing zeros" do
+ send(@method, 123).ceil(-1).should.eql?(130)
+ send(@method, 123).ceil(-2).should.eql?(200)
+ send(@method, 123).ceil(-3).should.eql?(1000)
+
+ send(@method, -123).ceil(-1).should.eql?(-120)
+ send(@method, -123).ceil(-2).should.eql?(-100)
+ send(@method, -123).ceil(-3).should.eql?(0)
+
+ send(@method, 100).ceil(-3).should.eql?(1000)
+ send(@method, -100).ceil(-3).should.eql?(0)
+ end
+
+ # Bug #20654
+ it "returns 10**precision.abs when precision.abs has more digits than self" do
+ send(@method, 123).ceil(-20).should.eql?(100000000000000000000)
+ send(@method, 123).ceil(-50).should.eql?(100000000000000000000000000000000000000000000000000)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/shared/integer_floor_precision.rb b/spec/ruby/core/integer/shared/integer_floor_precision.rb
new file mode 100644
index 0000000000..6247907d4c
--- /dev/null
+++ b/spec/ruby/core/integer/shared/integer_floor_precision.rb
@@ -0,0 +1,42 @@
+describe :integer_floor_precision, shared: true do
+ context "precision is zero" do
+ it "returns integer self" do
+ send(@method, 0).floor(0).should.eql?(0)
+ send(@method, 123).floor(0).should.eql?(123)
+ send(@method, -123).floor(0).should.eql?(-123)
+ end
+ end
+
+ context "precision is positive" do
+ it "returns self" do
+ send(@method, 0).floor(1).should.eql?(send(@method, 0))
+ send(@method, 0).floor(10).should.eql?(send(@method, 0))
+
+ send(@method, 123).floor(10).should.eql?(send(@method, 123))
+ send(@method, -123).floor(10).should.eql?(send(@method, -123))
+ end
+ end
+
+ context "precision is negative" do
+ it "always returns 0 when self is 0" do
+ send(@method, 0).floor(-1).should.eql?(0)
+ send(@method, 0).floor(-10).should.eql?(0)
+ end
+
+ it "returns largest integer less than self with at least precision.abs trailing zeros" do
+ send(@method, 123).floor(-1).should.eql?(120)
+ send(@method, 123).floor(-2).should.eql?(100)
+ send(@method, 123).floor(-3).should.eql?(0)
+
+ send(@method, -123).floor(-1).should.eql?(-130)
+ send(@method, -123).floor(-2).should.eql?(-200)
+ send(@method, -123).floor(-3).should.eql?(-1000)
+ end
+
+ # Bug #20654
+ it "returns -(10**precision.abs) when self is negative and precision.abs is larger than the number digits of self" do
+ send(@method, -123).floor(-20).should.eql?(-100000000000000000000)
+ send(@method, -123).floor(-50).should.eql?(-100000000000000000000000000000000000000000000000000)
+ end
+ end
+end
diff --git a/spec/ruby/core/integer/shared/integer_rounding.rb b/spec/ruby/core/integer/shared/integer_rounding.rb
index 56d1819f84..92f2a2327c 100644
--- a/spec/ruby/core/integer/shared/integer_rounding.rb
+++ b/spec/ruby/core/integer/shared/integer_rounding.rb
@@ -1,19 +1,19 @@
describe :integer_rounding_positive_precision, shared: true do
it "returns self if not passed a precision" do
[2, -4, 10**70, -10**100].each do |v|
- v.send(@method).should eql(v)
+ v.send(@method).should.eql?(v)
end
end
it "returns self if passed a precision of zero" do
[2, -4, 10**70, -10**100].each do |v|
- v.send(@method, 0).should eql(v)
+ v.send(@method, 0).should.eql?(v)
end
end
it "returns itself if passed a positive precision" do
[2, -4, 10**70, -10**100].each do |v|
- v.send(@method, 42).should eql(v)
+ v.send(@method, 42).should.eql?(v)
end
end
end
diff --git a/spec/ruby/core/integer/shared/modulo.rb b/spec/ruby/core/integer/shared/modulo.rb
index b06d81e17d..d0b5e26ed5 100644
--- a/spec/ruby/core/integer/shared/modulo.rb
+++ b/spec/ruby/core/integer/shared/modulo.rb
@@ -1,6 +1,12 @@
describe :integer_modulo, shared: true do
context "fixnum" do
it "returns the modulus obtained from dividing self by the given argument" do
+ # test all possible combinations:
+ # - integer/double/bignum argument
+ # - positive/negative argument
+ # - positive/negative self
+ # - self greater/smaller than argument
+
13.send(@method, 4).should == 1
4.send(@method, 13).should == 4
@@ -16,59 +22,93 @@ describe :integer_modulo, shared: true do
(200).send(@method, -256).should == -56
(1000).send(@method, -512).should == -24
+ 13.send(@method, -4.0).should == -3.0
+ 4.send(@method, -13.0).should == -9.0
+
+ -13.send(@method, -4.0).should == -1.0
+ -4.send(@method, -13.0).should == -4.0
+
+ -13.send(@method, 4.0).should == 3.0
+ -4.send(@method, 13.0).should == 9.0
+
1.send(@method, 2.0).should == 1.0
200.send(@method, bignum_value).should == 200
+
+ 4.send(@method, bignum_value(10)).should == 4
+ 4.send(@method, -bignum_value(10)).should == -18446744073709551622
+ -4.send(@method, bignum_value(10)).should == 18446744073709551622
+ -4.send(@method, -bignum_value(10)).should == -4
end
it "raises a ZeroDivisionError when the given argument is 0" do
- -> { 13.send(@method, 0) }.should raise_error(ZeroDivisionError)
- -> { 0.send(@method, 0) }.should raise_error(ZeroDivisionError)
- -> { -10.send(@method, 0) }.should raise_error(ZeroDivisionError)
+ -> { 13.send(@method, 0) }.should.raise(ZeroDivisionError)
+ -> { 0.send(@method, 0) }.should.raise(ZeroDivisionError)
+ -> { -10.send(@method, 0) }.should.raise(ZeroDivisionError)
end
it "raises a ZeroDivisionError when the given argument is 0 and a Float" do
- -> { 0.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
- -> { 10.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
- -> { -10.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
+ -> { 0.send(@method, 0.0) }.should.raise(ZeroDivisionError)
+ -> { 10.send(@method, 0.0) }.should.raise(ZeroDivisionError)
+ -> { -10.send(@method, 0.0) }.should.raise(ZeroDivisionError)
end
it "raises a TypeError when given a non-Integer" do
-> {
(obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10)
13.send(@method, obj)
- }.should raise_error(TypeError)
- -> { 13.send(@method, "10") }.should raise_error(TypeError)
- -> { 13.send(@method, :symbol) }.should raise_error(TypeError)
+ }.should.raise(TypeError)
+ -> { 13.send(@method, "10") }.should.raise(TypeError)
+ -> { 13.send(@method, :symbol) }.should.raise(TypeError)
end
end
context "bignum" do
before :each do
- @bignum = bignum_value
+ @bignum = bignum_value(10)
end
it "returns the modulus obtained from dividing self by the given argument" do
- @bignum.send(@method, 5).should == 3
- @bignum.send(@method, -5).should == -2
- @bignum.send(@method, -100).should == -92
- @bignum.send(@method, 2.22).should be_close(0.780180180180252, TOLERANCE)
- @bignum.send(@method, bignum_value(10)).should == 9223372036854775808
+ # test all possible combinations:
+ # - integer/double/bignum argument
+ # - positive/negative argument
+ # - positive/negative self
+ # - self greater/smaller than argument
+
+ @bignum.send(@method, 5).should == 1
+ @bignum.send(@method, -5).should == -4
+ (-@bignum).send(@method, 5).should == 4
+ (-@bignum).send(@method, -5).should == -1
+
+ @bignum.send(@method, 2.22).should be_close(1.5603603603605034, TOLERANCE)
+ @bignum.send(@method, -2.22).should be_close(-0.6596396396394968, TOLERANCE)
+ (-@bignum).send(@method, 2.22).should be_close(0.6596396396394968, TOLERANCE)
+ (-@bignum).send(@method, -2.22).should be_close(-1.5603603603605034, TOLERANCE)
+
+ @bignum.send(@method, @bignum + 10).should == 18446744073709551626
+ @bignum.send(@method, -(@bignum + 10)).should == -10
+ (-@bignum).send(@method, @bignum + 10).should == 10
+ (-@bignum).send(@method, -(@bignum + 10)).should == -18446744073709551626
+
+ (@bignum + 10).send(@method, @bignum).should == 10
+ (@bignum + 10).send(@method, -@bignum).should == -18446744073709551616
+ (-(@bignum + 10)).send(@method, @bignum).should == 18446744073709551616
+ (-(@bignum + 10)).send(@method, -@bignum).should == -10
end
it "raises a ZeroDivisionError when the given argument is 0" do
- -> { @bignum.send(@method, 0) }.should raise_error(ZeroDivisionError)
- -> { (-@bignum).send(@method, 0) }.should raise_error(ZeroDivisionError)
+ -> { @bignum.send(@method, 0) }.should.raise(ZeroDivisionError)
+ -> { (-@bignum).send(@method, 0) }.should.raise(ZeroDivisionError)
end
it "raises a ZeroDivisionError when the given argument is 0 and a Float" do
- -> { @bignum.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
- -> { -@bignum.send(@method, 0.0) }.should raise_error(ZeroDivisionError)
+ -> { @bignum.send(@method, 0.0) }.should.raise(ZeroDivisionError)
+ -> { -@bignum.send(@method, 0.0) }.should.raise(ZeroDivisionError)
end
it "raises a TypeError when given a non-Integer" do
- -> { @bignum.send(@method, mock('10')) }.should raise_error(TypeError)
- -> { @bignum.send(@method, "10") }.should raise_error(TypeError)
- -> { @bignum.send(@method, :symbol) }.should raise_error(TypeError)
+ -> { @bignum.send(@method, mock('10')) }.should.raise(TypeError)
+ -> { @bignum.send(@method, "10") }.should.raise(TypeError)
+ -> { @bignum.send(@method, :symbol) }.should.raise(TypeError)
end
end
end
diff --git a/spec/ruby/core/integer/shared/to_i.rb b/spec/ruby/core/integer/shared/to_i.rb
index 7b974cd3a7..2b6a50484a 100644
--- a/spec/ruby/core/integer/shared/to_i.rb
+++ b/spec/ruby/core/integer/shared/to_i.rb
@@ -1,8 +1,8 @@
describe :integer_to_i, shared: true do
it "returns self" do
- 10.send(@method).should eql(10)
- (-15).send(@method).should eql(-15)
- bignum_value.send(@method).should eql(bignum_value)
- (-bignum_value).send(@method).should eql(-bignum_value)
+ 10.send(@method).should.eql?(10)
+ (-15).send(@method).should.eql?(-15)
+ bignum_value.send(@method).should.eql?(bignum_value)
+ (-bignum_value).send(@method).should.eql?(-bignum_value)
end
end
diff --git a/spec/ruby/core/integer/size_spec.rb b/spec/ruby/core/integer/size_spec.rb
index a134e82384..725e9eb062 100644
--- a/spec/ruby/core/integer/size_spec.rb
+++ b/spec/ruby/core/integer/size_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "Integer#size" do
- platform_is wordsize: 32 do
+ platform_is c_long_size: 32 do
it "returns the number of bytes in the machine representation of self" do
-1.size.should == 4
0.size.should == 4
@@ -9,7 +9,7 @@ describe "Integer#size" do
end
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
it "returns the number of bytes in the machine representation of self" do
-1.size.should == 8
0.size.should == 8
diff --git a/spec/ruby/core/integer/sqrt_spec.rb b/spec/ruby/core/integer/sqrt_spec.rb
index 4149ca2cc9..bf90ea747e 100644
--- a/spec/ruby/core/integer/sqrt_spec.rb
+++ b/spec/ruby/core/integer/sqrt_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "Integer.sqrt" do
it "returns an integer" do
- Integer.sqrt(10).should be_kind_of(Integer)
+ Integer.sqrt(10).should.is_a?(Integer)
end
it "returns the integer square root of the argument" do
@@ -14,7 +14,7 @@ describe "Integer.sqrt" do
end
it "raises a Math::DomainError if the argument is negative" do
- -> { Integer.sqrt(-4) }.should raise_error(Math::DomainError)
+ -> { Integer.sqrt(-4) }.should.raise(Math::DomainError)
end
it "accepts any argument that can be coerced to Integer" do
@@ -26,6 +26,6 @@ describe "Integer.sqrt" do
end
it "raises a TypeError if the argument cannot be coerced to Integer" do
- -> { Integer.sqrt("test") }.should raise_error(TypeError)
+ -> { Integer.sqrt("test") }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/integer/to_f_spec.rb b/spec/ruby/core/integer/to_f_spec.rb
index 06092eaa92..0681236095 100644
--- a/spec/ruby/core/integer/to_f_spec.rb
+++ b/spec/ruby/core/integer/to_f_spec.rb
@@ -11,9 +11,9 @@ describe "Integer#to_f" do
context "bignum" do
it "returns self converted to a Float" do
- bignum_value(0x4000_0aa0_0bb0_0000).to_f.should eql(13_835_069_737_789_292_544.00)
- bignum_value(0x8000_0000_0000_0ccc).to_f.should eql(18_446_744_073_709_555_712.00)
- (-bignum_value(99)).to_f.should eql(-9_223_372_036_854_775_808.00)
+ bignum_value(0x4000_0aa0_0bb0_0000).to_f.should.eql?(23_058_441_774_644_068_352.0)
+ bignum_value(0x8000_0000_0000_0ccc).to_f.should.eql?(27_670_116_110_564_330_700.0)
+ (-bignum_value(99)).to_f.should.eql?(-18_446_744_073_709_551_715.0)
end
it "converts number close to Float::MAX without exceeding MAX or producing NaN" do
diff --git a/spec/ruby/core/integer/to_r_spec.rb b/spec/ruby/core/integer/to_r_spec.rb
index 4a40575b7f..2649c7c78d 100644
--- a/spec/ruby/core/integer/to_r_spec.rb
+++ b/spec/ruby/core/integer/to_r_spec.rb
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
describe "Integer#to_r" do
it "returns a Rational object" do
- 309.to_r.should be_an_instance_of(Rational)
+ 309.to_r.should.instance_of?(Rational)
end
it "constructs a rational number with self as the numerator" do
@@ -15,12 +15,12 @@ describe "Integer#to_r" do
it "works even if self is a Bignum" do
bignum = 99999**999
- bignum.should be_an_instance_of(Bignum)
+ bignum.should.instance_of?(Integer)
bignum.to_r.should == Rational(bignum, 1)
end
it "raises an ArgumentError if given any arguments" do
- -> { 287.to_r(2) }.should raise_error(ArgumentError)
- -> { 9102826.to_r(309, [], 71) }.should raise_error(ArgumentError)
+ -> { 287.to_r(2) }.should.raise(ArgumentError)
+ -> { 9102826.to_r(309, [], 71) }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/integer/to_s_spec.rb b/spec/ruby/core/integer/to_s_spec.rb
index 7988bfde7a..4970a2a12f 100644
--- a/spec/ruby/core/integer/to_s_spec.rb
+++ b/spec/ruby/core/integer/to_s_spec.rb
@@ -13,10 +13,10 @@ describe "Integer#to_s" do
end
it "raises an ArgumentError if the base is less than 2 or higher than 36" do
- -> { 123.to_s(-1) }.should raise_error(ArgumentError)
- -> { 123.to_s(0) }.should raise_error(ArgumentError)
- -> { 123.to_s(1) }.should raise_error(ArgumentError)
- -> { 123.to_s(37) }.should raise_error(ArgumentError)
+ -> { 123.to_s(-1) }.should.raise(ArgumentError)
+ -> { 123.to_s(0) }.should.raise(ArgumentError)
+ -> { 123.to_s(1) }.should.raise(ArgumentError)
+ -> { 123.to_s(37) }.should.raise(ArgumentError)
end
end
@@ -39,12 +39,12 @@ describe "Integer#to_s" do
it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do
Encoding.default_internal = nil
- 1.to_s.encoding.should equal(Encoding::US_ASCII)
+ 1.to_s.encoding.should.equal?(Encoding::US_ASCII)
end
it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do
Encoding.default_internal = Encoding::IBM437
- 1.to_s.encoding.should equal(Encoding::US_ASCII)
+ 1.to_s.encoding.should.equal?(Encoding::US_ASCII)
end
end
@@ -59,18 +59,18 @@ describe "Integer#to_s" do
end
it "raises an ArgumentError if the base is less than 2 or higher than 36" do
- -> { 123.to_s(-1) }.should raise_error(ArgumentError)
- -> { 123.to_s(0) }.should raise_error(ArgumentError)
- -> { 123.to_s(1) }.should raise_error(ArgumentError)
- -> { 123.to_s(37) }.should raise_error(ArgumentError)
+ -> { 123.to_s(-1) }.should.raise(ArgumentError)
+ -> { 123.to_s(0) }.should.raise(ArgumentError)
+ -> { 123.to_s(1) }.should.raise(ArgumentError)
+ -> { 123.to_s(37) }.should.raise(ArgumentError)
end
end
describe "when given no base" do
it "returns self converted to a String using base 10" do
- bignum_value(9).to_s.should == "9223372036854775817"
- bignum_value.to_s.should == "9223372036854775808"
- (-bignum_value(675)).to_s.should == "-9223372036854776483"
+ bignum_value(9).to_s.should == "18446744073709551625"
+ bignum_value.to_s.should == "18446744073709551616"
+ (-bignum_value(675)).to_s.should == "-18446744073709552291"
end
end
@@ -84,12 +84,12 @@ describe "Integer#to_s" do
it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do
Encoding.default_internal = nil
- bignum_value.to_s.encoding.should equal(Encoding::US_ASCII)
+ bignum_value.to_s.encoding.should.equal?(Encoding::US_ASCII)
end
it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do
Encoding.default_internal = Encoding::IBM437
- bignum_value.to_s.encoding.should equal(Encoding::US_ASCII)
+ bignum_value.to_s.encoding.should.equal?(Encoding::US_ASCII)
end
end
end
diff --git a/spec/ruby/core/integer/truncate_spec.rb b/spec/ruby/core/integer/truncate_spec.rb
index db16e74be4..b95c183cf3 100644
--- a/spec/ruby/core/integer/truncate_spec.rb
+++ b/spec/ruby/core/integer/truncate_spec.rb
@@ -8,12 +8,12 @@ describe "Integer#truncate" do
context "precision argument specified as part of the truncate method is negative" do
it "returns an integer with at least precision.abs trailing zeros" do
- 1832.truncate(-1).should eql(1830)
- 1832.truncate(-2).should eql(1800)
- 1832.truncate(-3).should eql(1000)
- -1832.truncate(-1).should eql(-1830)
- -1832.truncate(-2).should eql(-1800)
- -1832.truncate(-3).should eql(-1000)
+ 1832.truncate(-1).should.eql?(1830)
+ 1832.truncate(-2).should.eql?(1800)
+ 1832.truncate(-3).should.eql?(1000)
+ -1832.truncate(-1).should.eql?(-1830)
+ -1832.truncate(-2).should.eql?(-1800)
+ -1832.truncate(-3).should.eql?(-1000)
end
end
end
diff --git a/spec/ruby/core/integer/try_convert_spec.rb b/spec/ruby/core/integer/try_convert_spec.rb
new file mode 100644
index 0000000000..8c9b4276b9
--- /dev/null
+++ b/spec/ruby/core/integer/try_convert_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Integer.try_convert" do
+ it "returns the argument if it's an Integer" do
+ x = 42
+ Integer.try_convert(x).should.equal?(x)
+ end
+
+ it "returns nil when the argument does not respond to #to_int" do
+ Integer.try_convert(Object.new).should == nil
+ end
+
+ it "sends #to_int to the argument and returns the result if it's nil" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return(nil)
+ Integer.try_convert(obj).should == nil
+ end
+
+ it "sends #to_int to the argument and returns the result if it's an Integer" do
+ x = 234
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return(x)
+ Integer.try_convert(obj).should.equal?(x)
+ end
+
+ it "sends #to_int to the argument and raises TypeError if it's not a kind of Integer" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return(Object.new)
+ -> {
+ Integer.try_convert obj
+ }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_int gives Object)")
+ end
+
+ it "responds with a different error message when it raises a TypeError, depending on the type of the non-Integer object :to_int returns" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_return("A String")
+ -> {
+ Integer.try_convert obj
+ }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_int gives String)")
+ end
+
+ it "does not rescue exceptions raised by #to_int" do
+ obj = mock("to_int")
+ obj.should_receive(:to_int).and_raise(RuntimeError)
+ -> { Integer.try_convert obj }.should.raise(RuntimeError)
+ end
+end
diff --git a/spec/ruby/core/integer/uminus_spec.rb b/spec/ruby/core/integer/uminus_spec.rb
index b6b110dec4..7a9cfe89bf 100644
--- a/spec/ruby/core/integer/uminus_spec.rb
+++ b/spec/ruby/core/integer/uminus_spec.rb
@@ -20,11 +20,11 @@ describe "Integer#-@" do
context "bignum" do
it "returns self as a negative value" do
- bignum_value.send(:-@).should == -9223372036854775808
- (-bignum_value).send(:-@).should == 9223372036854775808
+ bignum_value.send(:-@).should == -18446744073709551616
+ (-bignum_value).send(:-@).should == 18446744073709551616
- bignum_value(921).send(:-@).should == -9223372036854776729
- (-bignum_value(921).send(:-@)).should == 9223372036854776729
+ bignum_value(921).send(:-@).should == -18446744073709552537
+ (-bignum_value(921).send(:-@)).should == 18446744073709552537
end
end
end
diff --git a/spec/ruby/core/integer/upto_spec.rb b/spec/ruby/core/integer/upto_spec.rb
index 6049eb173e..ba63dcc9ea 100644
--- a/spec/ruby/core/integer/upto_spec.rb
+++ b/spec/ruby/core/integer/upto_spec.rb
@@ -1,6 +1,6 @@
require_relative '../../spec_helper'
-describe "Integer#upto [stop] when self and stop are Fixnums" do
+describe "Integer#upto [stop] when self and stop are Integers" do
it "does not yield when stop is less than self" do
result = []
5.upto(4) { |x| result << x }
@@ -27,8 +27,8 @@ describe "Integer#upto [stop] when self and stop are Fixnums" do
end
it "raises an ArgumentError for non-numeric endpoints" do
- -> { 1.upto("A") {|x| p x} }.should raise_error(ArgumentError)
- -> { 1.upto(nil) {|x| p x} }.should raise_error(ArgumentError)
+ -> { 1.upto("A") {|x| p x} }.should.raise(ArgumentError)
+ -> { 1.upto(nil) {|x| p x} }.should.raise(ArgumentError)
end
describe "when no block is given" do
@@ -45,9 +45,9 @@ describe "Integer#upto [stop] when self and stop are Fixnums" do
describe "size" do
it "raises an ArgumentError for non-numeric endpoints" do
enum = 1.upto("A")
- -> { enum.size }.should raise_error(ArgumentError)
+ -> { enum.size }.should.raise(ArgumentError)
enum = 1.upto(nil)
- -> { enum.size }.should raise_error(ArgumentError)
+ -> { enum.size }.should.raise(ArgumentError)
end
it "returns stop - self + 1" do
diff --git a/spec/ruby/core/integer/zero_spec.rb b/spec/ruby/core/integer/zero_spec.rb
new file mode 100644
index 0000000000..bd362c4181
--- /dev/null
+++ b/spec/ruby/core/integer/zero_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+
+describe "Integer#zero?" do
+ it "returns true if self is 0" do
+ 0.should.zero?
+ 1.should_not.zero?
+ -1.should_not.zero?
+ end
+
+ it "Integer#zero? overrides Numeric#zero?" do
+ 42.method(:zero?).owner.should == Integer
+ end
+end
diff --git a/spec/ruby/core/io/advise_spec.rb b/spec/ruby/core/io/advise_spec.rb
index 0a845487e2..b77ed53ad4 100644
--- a/spec/ruby/core/io/advise_spec.rb
+++ b/spec/ruby/core/io/advise_spec.rb
@@ -14,83 +14,73 @@ describe "IO#advise" do
it "raises a TypeError if advise is not a Symbol" do
-> {
@io.advise("normal")
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
it "raises a TypeError if offset cannot be coerced to an Integer" do
-> {
@io.advise(:normal, "wat")
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
it "raises a TypeError if len cannot be coerced to an Integer" do
-> {
@io.advise(:normal, 0, "wat")
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
it "raises a RangeError if offset is too big" do
-> {
@io.advise(:normal, 10 ** 32)
- }.should raise_error(RangeError)
+ }.should.raise(RangeError)
end
it "raises a RangeError if len is too big" do
-> {
@io.advise(:normal, 0, 10 ** 32)
- }.should raise_error(RangeError)
+ }.should.raise(RangeError)
end
it "raises a NotImplementedError if advise is not recognized" do
->{
@io.advise(:foo)
- }.should raise_error(NotImplementedError)
+ }.should.raise(NotImplementedError)
end
it "supports the normal advice type" do
- @io.advise(:normal).should be_nil
+ @io.advise(:normal).should == nil
end
it "supports the sequential advice type" do
- @io.advise(:sequential).should be_nil
+ @io.advise(:sequential).should == nil
end
it "supports the random advice type" do
- @io.advise(:random).should be_nil
+ @io.advise(:random).should == nil
end
it "supports the dontneed advice type" do
- @io.advise(:dontneed).should be_nil
+ @io.advise(:dontneed).should == nil
end
it "supports the noreuse advice type" do
- @io.advise(:noreuse).should be_nil
+ @io.advise(:noreuse).should == nil
end
platform_is_not :linux do
it "supports the willneed advice type" do
- @io.advise(:willneed).should be_nil
+ @io.advise(:willneed).should == nil
end
end
- platform_is :linux do
+ guard -> { platform_is :linux and kernel_version_is '3.6' } do # [ruby-core:65355] tmpfs is not supported
it "supports the willneed advice type" do
- require 'etc'
- uname = if Etc.respond_to?(:uname)
- Etc.uname[:release]
- else
- `uname -r`.chomp
- end
- if (uname.split('.').map(&:to_i) <=> [3,6]) < 0
- skip "[ruby-core:65355] tmpfs is not supported"
- else
- @io.advise(:willneed).should be_nil
- end
+ @io.advise(:willneed).should == nil
end
end
it "raises an IOError if the stream is closed" do
@io.close
- -> { @io.advise(:normal) }.should raise_error(IOError)
+ -> { @io.advise(:normal) }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/autoclose_spec.rb b/spec/ruby/core/io/autoclose_spec.rb
new file mode 100644
index 0000000000..596f3d6934
--- /dev/null
+++ b/spec/ruby/core/io/autoclose_spec.rb
@@ -0,0 +1,77 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "IO#autoclose?" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.autoclose = true unless @io.closed?
+ @io.close unless @io.closed?
+ end
+
+ it "is set to true by default" do
+ @io.should.autoclose?
+ end
+
+ it "cannot be queried on a closed IO object" do
+ @io.close
+ -> { @io.autoclose? }.should.raise(IOError, /closed stream/)
+ end
+end
+
+describe "IO#autoclose=" do
+ before :each do
+ @io = IOSpecs.io_fixture "lines.txt"
+ end
+
+ after :each do
+ @io.autoclose = true unless @io.closed?
+ @io.close unless @io.closed?
+ end
+
+ it "can be set to true" do
+ @io.autoclose = false
+ @io.autoclose = true
+ @io.should.autoclose?
+ end
+
+ it "can be set to false" do
+ @io.autoclose = true
+ @io.autoclose = false
+ @io.should_not.autoclose?
+ end
+
+ it "can be set to any truthy value" do
+ @io.autoclose = false
+ @io.autoclose = 42
+ @io.should.autoclose?
+
+ @io.autoclose = false
+ @io.autoclose = Object.new
+ @io.should.autoclose?
+ end
+
+ it "can be set to any falsy value" do
+ @io.autoclose = true
+ @io.autoclose = nil
+ @io.should_not.autoclose?
+ end
+
+ it "can be set multiple times" do
+ @io.autoclose = true
+ @io.should.autoclose?
+
+ @io.autoclose = false
+ @io.should_not.autoclose?
+
+ @io.autoclose = true
+ @io.should.autoclose?
+ end
+
+ it "cannot be set on a closed IO object" do
+ @io.close
+ -> { @io.autoclose = false }.should.raise(IOError, /closed stream/)
+ end
+end
diff --git a/spec/ruby/core/io/binmode_spec.rb b/spec/ruby/core/io/binmode_spec.rb
index b698777cad..8117229c91 100644
--- a/spec/ruby/core/io/binmode_spec.rb
+++ b/spec/ruby/core/io/binmode_spec.rb
@@ -13,11 +13,11 @@ describe "IO#binmode" do
it "returns self" do
@io = new_io(@name)
- @io.binmode.should equal(@io)
+ @io.binmode.should.equal?(@io)
end
it "raises an IOError on closed stream" do
- -> { IOSpecs.closed_io.binmode }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.binmode }.should.raise(IOError)
end
it "sets external encoding to binary" do
@@ -47,9 +47,9 @@ describe "IO#binmode?" do
end
it "is true after a call to IO#binmode" do
- @file.binmode?.should be_false
+ @file.binmode?.should == false
@file.binmode
- @file.binmode?.should be_true
+ @file.binmode?.should == true
end
it "propagates to dup'ed IO objects" do
@@ -57,4 +57,8 @@ describe "IO#binmode?" do
@duped = @file.dup
@duped.binmode?.should == @file.binmode?
end
+
+ it "raises an IOError on closed stream" do
+ -> { IOSpecs.closed_io.binmode? }.should.raise(IOError)
+ end
end
diff --git a/spec/ruby/core/io/binread_spec.rb b/spec/ruby/core/io/binread_spec.rb
index a3f752d8f9..3023c7f177 100644
--- a/spec/ruby/core/io/binread_spec.rb
+++ b/spec/ruby/core/io/binread_spec.rb
@@ -38,10 +38,20 @@ describe "IO.binread" do
end
it "raises an ArgumentError when not passed a valid length" do
- -> { IO.binread @fname, -1 }.should raise_error(ArgumentError)
+ -> { IO.binread @fname, -1 }.should.raise(ArgumentError)
end
it "raises an Errno::EINVAL when not passed a valid offset" do
- -> { IO.binread @fname, 0, -1 }.should raise_error(Errno::EINVAL)
+ -> { IO.binread @fname, 0, -1 }.should.raise(Errno::EINVAL)
+ end
+
+ ruby_version_is ""..."4.0" do
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ cmd = "|echo ok"
+ -> {
+ IO.binread(cmd)
+ }.should complain(/IO process creation with a leading '\|'/)
+ end
end
end
diff --git a/spec/ruby/core/io/buffer/and_spec.rb b/spec/ruby/core/io/buffer/and_spec.rb
new file mode 100644
index 0000000000..3cea163b0e
--- /dev/null
+++ b/spec/ruby/core/io/buffer/and_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../../spec_helper'
+
+describe :io_buffer_and, shared: true do
+ it "applies the argument buffer as an AND bit mask across the whole buffer" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F") do |mask|
+ result = buffer.send(@method, mask)
+ result.get_string.should == "\x30\x02\x30\x04\x30".b
+ result.free
+ end
+ end
+ end
+
+ it "ignores extra parts of mask if it is longer than source buffer" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F\x00\x00\x00\xFF\xFF") do |mask|
+ result = buffer.send(@method, mask)
+ result.get_string.should == "\x30\x02\x00\x00\x00".b
+ result.free
+ end
+ end
+ end
+
+ it "raises TypeError if mask is not an IO::Buffer" do
+ IO::Buffer.for(+"12345") do |buffer|
+ -> { buffer.send(@method, "\xF8\x8F") }.should.raise(TypeError, "wrong argument type String (expected IO::Buffer)")
+ -> { buffer.send(@method, 0xF8) }.should.raise(TypeError, "wrong argument type Integer (expected IO::Buffer)")
+ -> { buffer.send(@method, nil) }.should.raise(TypeError, "wrong argument type nil (expected IO::Buffer)")
+ end
+ end
+end
+
+describe "IO::Buffer#&" do
+ it_behaves_like :io_buffer_and, :&
+
+ it "creates a new internal buffer of the same size" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F") do |mask|
+ result = buffer & mask
+ result.should_not.equal? buffer
+ result.should.internal?
+ result.size.should == buffer.size
+ result.free
+ buffer.get_string.should == "12345".b
+ end
+ end
+ end
+end
+
+describe "IO::Buffer#and!" do
+ it_behaves_like :io_buffer_and, :and!
+
+ it "modifies the buffer in place" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F") do |mask|
+ result = buffer.and!(mask)
+ result.should.equal? buffer
+ result.should.external?
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/buffer/bit_count_spec.rb b/spec/ruby/core/io/buffer/bit_count_spec.rb
new file mode 100644
index 0000000000..62f56f5b98
--- /dev/null
+++ b/spec/ruby/core/io/buffer/bit_count_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../../spec_helper'
+
+ruby_version_is "4.1" do
+ describe "IO::Buffer#bit_count" do
+ it "counts all set bits in the whole buffer" do
+ IO::Buffer.for(+"\xFF\x00\x0F") do |buffer|
+ buffer.bit_count.should == 12
+ end
+ end
+
+ it "returns 0 for a buffer of all zero bytes" do
+ IO::Buffer.for(+"\x00\x00\x00") do |buffer|
+ buffer.bit_count.should == 0
+ end
+ end
+
+ it "returns 8 * size for a buffer of all 0xFF bytes" do
+ IO::Buffer.for(+"\xFF" * 9) do |buffer|
+ buffer.bit_count.should == 72
+ end
+ end
+
+ it "returns 0 for an empty buffer" do
+ IO::Buffer.new(0).bit_count.should == 0
+ end
+
+ it "accepts an offset to start counting from (length defaults to remaining bytes)" do
+ IO::Buffer.for(+"\xFF\x00\x0F") do |buffer|
+ buffer.bit_count(0).should == 12 # offset=0 => entire buffer
+ buffer.bit_count(1).should == 4 # offset=1 => 0x00 + 0x0F
+ buffer.bit_count(2).should == 4 # offset=2 => 0x0F only
+ end
+ end
+
+ it "accepts an offset and length to restrict the counted region" do
+ IO::Buffer.for(+"\xFF\x00\x0F") do |buffer|
+ buffer.bit_count(0, 1).should == 8 # just 0xFF
+ buffer.bit_count(1, 1).should == 0 # just 0x00
+ buffer.bit_count(2, 1).should == 4 # just 0x0F
+ buffer.bit_count(1, 2).should == 4 # 0x00 + 0x0F
+ end
+ end
+
+ it "handles 8-byte aligned buffers efficiently" do
+ IO::Buffer.for(+"\xAA" * 8) do |buffer|
+ # 0xAA = 10101010 => 4 bits per byte => 32 total
+ buffer.bit_count.should == 32
+ end
+ end
+
+ it "raises ArgumentError when offset + length exceeds buffer size" do
+ IO::Buffer.for(+"\xFF") do |buffer|
+ -> { buffer.bit_count(0, 2) }.should.raise(ArgumentError)
+ -> { buffer.bit_count(1, 1) }.should.raise(ArgumentError)
+ end
+ end
+
+ it "returns an Integer" do
+ IO::Buffer.for(+"\xFF") do |buffer|
+ buffer.bit_count.should.is_a?(Integer)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/buffer/empty_spec.rb b/spec/ruby/core/io/buffer/empty_spec.rb
new file mode 100644
index 0000000000..4cceb4dc0d
--- /dev/null
+++ b/spec/ruby/core/io/buffer/empty_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/null_and_empty'
+
+describe "IO::Buffer#empty?" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ it_behaves_like :io_buffer_null_and_empty, :empty?
+
+ it "is true for a 0-length String-backed buffer created with .for" do
+ @buffer = IO::Buffer.for("")
+ @buffer.empty?.should == true
+ end
+
+ it "is true for a 0-length String-backed buffer created with .string" do
+ IO::Buffer.string(0) do |buffer|
+ buffer.empty?.should == true
+ end
+ end
+
+ it "is true for a 0-length slice of a buffer with size > 0" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.slice(3, 0).empty?.should == true
+ end
+end
diff --git a/spec/ruby/core/io/buffer/external_spec.rb b/spec/ruby/core/io/buffer/external_spec.rb
new file mode 100644
index 0000000000..18b2ee0d06
--- /dev/null
+++ b/spec/ruby/core/io/buffer/external_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#external?" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ it "is true for a buffer with externally-managed memory" do
+ @buffer = IO::Buffer.for("string")
+ @buffer.external?.should == true
+ end
+
+ it "is false for a buffer with self-managed memory" do
+ @buffer = IO::Buffer.new(12, IO::Buffer::MAPPED)
+ @buffer.external?.should == false
+ end
+
+ it "is false for a null buffer" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.external?.should == false
+ end
+end
diff --git a/spec/ruby/core/io/buffer/for_spec.rb b/spec/ruby/core/io/buffer/for_spec.rb
new file mode 100644
index 0000000000..4c614f74b0
--- /dev/null
+++ b/spec/ruby/core/io/buffer/for_spec.rb
@@ -0,0 +1,95 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer.for" do
+ before :each do
+ @string = +"för striñg"
+ end
+
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ context "without a block" do
+ it "copies string's contents, creating a separate read-only buffer" do
+ @buffer = IO::Buffer.for(@string)
+
+ @buffer.size.should == @string.bytesize
+ @buffer.get_string.should == @string.b
+
+ @string[0] = "d"
+ @buffer.get_string(0, 1).should == "f".b
+
+ -> { @buffer.set_string("d") }.should.raise(IO::Buffer::AccessError, "Buffer is not writable!")
+ end
+
+ it "creates an external, read-only buffer" do
+ @buffer = IO::Buffer.for(@string)
+
+ @buffer.should_not.internal?
+ @buffer.should_not.mapped?
+ @buffer.should.external?
+
+ @buffer.should_not.empty?
+ @buffer.should_not.null?
+
+ @buffer.should_not.shared?
+ @buffer.should_not.private?
+ @buffer.should.readonly?
+
+ @buffer.should_not.locked?
+ @buffer.should.valid?
+ end
+ end
+
+ context "with a block" do
+ it "returns the last value in the block" do
+ value =
+ IO::Buffer.for(@string) do |buffer|
+ buffer.size * 3
+ end
+ value.should == @string.bytesize * 3
+ end
+
+ it "frees the buffer at the end of the block" do
+ IO::Buffer.for(@string) do |buffer|
+ @buffer = buffer
+ @buffer.should_not.null?
+ end
+ @buffer.should.null?
+ end
+
+ context "if string is not frozen" do
+ it "creates a modifiable string-backed buffer" do
+ IO::Buffer.for(@string) do |buffer|
+ buffer.size.should == @string.bytesize
+ buffer.get_string.should == @string.b
+
+ buffer.should_not.readonly?
+ buffer.should_not.locked?
+
+ buffer.set_string("ghost shell")
+ @string.should == "ghost shellg"
+ end
+ end
+
+ it "locks the original string to prevent modification" do
+ IO::Buffer.for(@string) do |_buffer|
+ -> { @string[0] = "t" }.should.raise(RuntimeError, "can't modify string; temporarily locked")
+ end
+ @string[1] = "u"
+ @string.should == "fur striñg"
+ end
+ end
+
+ context "if string is frozen" do
+ it "creates a read-only string-backed buffer" do
+ IO::Buffer.for(@string.freeze) do |buffer|
+ buffer.should.readonly?
+
+ -> { buffer.set_string("ghost shell") }.should.raise(IO::Buffer::AccessError, "Buffer is not writable!")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/buffer/free_spec.rb b/spec/ruby/core/io/buffer/free_spec.rb
new file mode 100644
index 0000000000..20fb7b901f
--- /dev/null
+++ b/spec/ruby/core/io/buffer/free_spec.rb
@@ -0,0 +1,102 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#free" do
+ context "with a buffer created with .new" do
+ it "frees internal memory and nullifies the buffer" do
+ buffer = IO::Buffer.new(4)
+ buffer.free
+ buffer.null?.should == true
+ end
+
+ it "frees mapped memory and nullifies the buffer" do
+ buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
+ buffer.free
+ buffer.null?.should == true
+ end
+ end
+
+ context "with a file-backed buffer created with .map" do
+ it "frees mapped memory and nullifies the buffer" do
+ File.open(__FILE__, "r") do |file|
+ buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
+ buffer.free
+ buffer.null?.should == true
+ end
+ end
+ end
+
+ context "with a String-backed buffer created with .for" do
+ context "without a block" do
+ it "disassociates the buffer from the string and nullifies the buffer" do
+ string = +"test"
+ buffer = IO::Buffer.for(string)
+ # Read-only buffer, can't modify the string.
+ buffer.free
+ buffer.null?.should == true
+ end
+ end
+
+ context "with a block" do
+ it "disassociates the buffer from the string and nullifies the buffer" do
+ string = +"test"
+ IO::Buffer.for(string) do |buffer|
+ buffer.set_string("meat")
+ buffer.free
+ buffer.null?.should == true
+ end
+ string.should == "meat"
+ end
+ end
+ end
+
+ context "with a String-backed buffer created with .string" do
+ it "disassociates the buffer from the string and nullifies the buffer" do
+ string =
+ IO::Buffer.string(4) do |buffer|
+ buffer.set_string("meat")
+ buffer.free
+ buffer.null?.should == true
+ end
+ string.should == "meat"
+ end
+ end
+
+ it "can be called repeatedly without an error" do
+ buffer = IO::Buffer.new(4)
+ buffer.free
+ buffer.null?.should == true
+ buffer.free
+ buffer.null?.should == true
+ end
+
+ it "is disallowed while locked, raising IO::Buffer::LockedError" do
+ buffer = IO::Buffer.new(4)
+ buffer.locked do
+ -> { buffer.free }.should.raise(IO::Buffer::LockedError, "Buffer is locked!")
+ end
+ buffer.free
+ buffer.null?.should == true
+ end
+
+ context "with a slice of a buffer" do
+ it "nullifies the slice, not touching the buffer" do
+ buffer = IO::Buffer.new(4)
+ slice = buffer.slice(0, 2)
+
+ slice.free
+ slice.null?.should == true
+ buffer.null?.should == false
+
+ buffer.free
+ end
+
+ it "nullifies buffer, invalidating the slice" do
+ buffer = IO::Buffer.new(4)
+ slice = buffer.slice(0, 2)
+
+ buffer.free
+ slice.null?.should == false
+ slice.valid?.should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/io/buffer/initialize_spec.rb b/spec/ruby/core/io/buffer/initialize_spec.rb
new file mode 100644
index 0000000000..dba6fddc79
--- /dev/null
+++ b/spec/ruby/core/io/buffer/initialize_spec.rb
@@ -0,0 +1,119 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#initialize" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ it "creates a new zero-filled buffer with default size" do
+ @buffer = IO::Buffer.new
+ @buffer.size.should == IO::Buffer::DEFAULT_SIZE
+ @buffer.each(:U8).should.all? { |_offset, value| value.eql?(0) }
+ end
+
+ it "creates a buffer with default state" do
+ @buffer = IO::Buffer.new
+
+ @buffer.should_not.external?
+
+ @buffer.should_not.shared?
+ @buffer.should_not.private?
+ @buffer.should_not.readonly?
+
+ @buffer.should_not.empty?
+ @buffer.should_not.null?
+
+ @buffer.should_not.locked?
+ @buffer.should.valid?
+ end
+
+ context "with size argument" do
+ it "creates a new internal buffer if size is less than IO::Buffer::PAGE_SIZE" do
+ size = IO::Buffer::PAGE_SIZE - 1
+ @buffer = IO::Buffer.new(size)
+ @buffer.size.should == size
+ @buffer.should_not.empty?
+
+ @buffer.should.internal?
+ @buffer.should_not.mapped?
+ end
+
+ it "creates a new mapped buffer if size is greater than or equal to IO::Buffer::PAGE_SIZE" do
+ size = IO::Buffer::PAGE_SIZE
+ @buffer = IO::Buffer.new(size)
+ @buffer.size.should == size
+ @buffer.should_not.empty?
+
+ @buffer.should_not.internal?
+ @buffer.should.mapped?
+ end
+
+ it "creates a null buffer if size is 0" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.should.null?
+ @buffer.should.empty?
+ end
+
+ it "raises TypeError if size is not an Integer" do
+ -> { IO::Buffer.new(nil) }.should.raise(TypeError, "not an Integer")
+ -> { IO::Buffer.new(10.0) }.should.raise(TypeError, "not an Integer")
+ end
+
+ it "raises ArgumentError if size is negative" do
+ -> { IO::Buffer.new(-1) }.should.raise(ArgumentError, "Size can't be negative!")
+ end
+ end
+
+ context "with size and flags arguments" do
+ it "forces mapped buffer with IO::Buffer::MAPPED flag" do
+ @buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE - 1, IO::Buffer::MAPPED)
+ @buffer.should.mapped?
+ @buffer.should_not.internal?
+ @buffer.should_not.empty?
+ end
+
+ it "forces internal buffer with IO::Buffer::INTERNAL flag" do
+ @buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::INTERNAL)
+ @buffer.should.internal?
+ @buffer.should_not.mapped?
+ @buffer.should_not.empty?
+ end
+
+ it "allows extra flags" do
+ @buffer = IO::Buffer.new(10, IO::Buffer::INTERNAL | IO::Buffer::SHARED | IO::Buffer::READONLY)
+ @buffer.should.internal?
+ @buffer.should.shared?
+ @buffer.should.readonly?
+ end
+
+ it "ignores flags if size is 0" do
+ @buffer = IO::Buffer.new(0, 0xffff)
+ @buffer.should.null?
+ @buffer.should.empty?
+
+ @buffer.should_not.internal?
+ @buffer.should_not.mapped?
+ @buffer.should_not.external?
+
+ @buffer.should_not.shared?
+ @buffer.should_not.readonly?
+
+ @buffer.should_not.locked?
+ @buffer.should.valid?
+ end
+
+ it "raises IO::Buffer::AllocationError if neither IO::Buffer::MAPPED nor IO::Buffer::INTERNAL is given" do
+ -> { IO::Buffer.new(10, IO::Buffer::READONLY) }.should.raise(IO::Buffer::AllocationError, "Could not allocate buffer!")
+ -> { IO::Buffer.new(10, 0) }.should.raise(IO::Buffer::AllocationError, "Could not allocate buffer!")
+ end
+
+ it "raises ArgumentError if flags is negative" do
+ -> { IO::Buffer.new(10, -1) }.should.raise(ArgumentError, "Flags can't be negative!")
+ end
+
+ it "raises TypeError with non-Integer flags" do
+ -> { IO::Buffer.new(10, 0.0) }.should.raise(TypeError, "not an Integer")
+ end
+ end
+end
diff --git a/spec/ruby/core/io/buffer/internal_spec.rb b/spec/ruby/core/io/buffer/internal_spec.rb
new file mode 100644
index 0000000000..73e19eb85e
--- /dev/null
+++ b/spec/ruby/core/io/buffer/internal_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#internal?" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ it "is true for an internally-allocated buffer" do
+ @buffer = IO::Buffer.new(12)
+ @buffer.internal?.should == true
+ end
+
+ it "is false for an externally-allocated buffer" do
+ @buffer = IO::Buffer.new(12, IO::Buffer::MAPPED)
+ @buffer.internal?.should == false
+ end
+
+ it "is false for a null buffer" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.internal?.should == false
+ end
+end
diff --git a/spec/ruby/core/io/buffer/locked_spec.rb b/spec/ruby/core/io/buffer/locked_spec.rb
new file mode 100644
index 0000000000..249026aa8a
--- /dev/null
+++ b/spec/ruby/core/io/buffer/locked_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#locked" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ context "when buffer is locked" do
+ it "allows reading and writing operations on the buffer" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.set_string("test")
+ @buffer.locked do
+ @buffer.get_string.should == "test"
+ @buffer.set_string("meat")
+ end
+ @buffer.get_string.should == "meat"
+ end
+
+ it "disallows operations changing buffer itself, raising IO::Buffer::LockedError" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.locked do
+ # Just an example, each method is responsible for checking the lock state.
+ -> { @buffer.resize(8) }.should.raise(IO::Buffer::LockedError)
+ end
+ end
+ end
+
+ it "disallows reentrant locking, raising IO::Buffer::LockedError" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.locked do
+ -> { @buffer.locked {} }.should.raise(IO::Buffer::LockedError, "Buffer already locked!")
+ end
+ end
+
+ it "does not propagate to buffer's slices" do
+ @buffer = IO::Buffer.new(4)
+ slice = @buffer.slice(0, 2)
+ @buffer.locked do
+ @buffer.locked?.should == true
+ slice.locked?.should == false
+ slice.locked { slice.locked?.should == true }
+ end
+ end
+
+ it "does not propagate backwards from buffer's slices" do
+ @buffer = IO::Buffer.new(4)
+ slice = @buffer.slice(0, 2)
+ slice.locked do
+ slice.locked?.should == true
+ @buffer.locked?.should == false
+ @buffer.locked { @buffer.locked?.should == true }
+ end
+ end
+end
+
+describe "IO::Buffer#locked?" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ it "is false by default" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.locked?.should == false
+ end
+
+ it "is true only inside of #locked block" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.locked do
+ @buffer.locked?.should == true
+ end
+ @buffer.locked?.should == false
+ end
+end
diff --git a/spec/ruby/core/io/buffer/map_spec.rb b/spec/ruby/core/io/buffer/map_spec.rb
new file mode 100644
index 0000000000..97764c2dd7
--- /dev/null
+++ b/spec/ruby/core/io/buffer/map_spec.rb
@@ -0,0 +1,347 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer.map" do
+ before :all do
+ @tmp_files = []
+
+ @big_file_name = nil
+ @small_file_name = nil
+ end
+
+ after :all do
+ @tmp_files.each {|file| File.delete(file)}
+ end
+
+ def open_fixture
+ unless @small_file_name
+ @small_file_name = tmp("read_text.txt")
+ File.copy_stream(fixture(__dir__, "read_text.txt"), @small_file_name)
+ @tmp_files << @small_file_name
+ end
+ File.open(@small_file_name, "rb+")
+ end
+
+ def open_big_file_fixture
+ unless @big_file_name
+ @big_file_name = tmp("big_file")
+ # Usually 4 kibibytes + 16 bytes
+ File.write(@big_file_name, "12345678" * (IO::Buffer::PAGE_SIZE / 8 + 2))
+ @tmp_files << @big_file_name
+ end
+ File.open(@big_file_name, "rb+")
+ end
+
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ @file&.close
+ @file = nil
+ end
+
+ it "creates a new buffer mapped from a file" do
+ @file = open_fixture
+ @buffer = IO::Buffer.map(@file)
+
+ @buffer.size.should == 9
+ @buffer.get_string.should == "abcâdef\n".b
+ end
+
+ it "allows to close the file after creating buffer, retaining mapping" do
+ file = open_fixture
+ @buffer = IO::Buffer.map(file)
+ file.close
+
+ @buffer.get_string.should == "abcâdef\n".b
+ end
+
+ it "creates a mapped, external, shared buffer" do
+ @file = open_fixture
+ @buffer = IO::Buffer.map(@file)
+
+ @buffer.should_not.internal?
+ @buffer.should.mapped?
+ @buffer.should.external?
+
+ @buffer.should_not.empty?
+ @buffer.should_not.null?
+
+ @buffer.should.shared?
+ @buffer.should_not.private?
+ @buffer.should_not.readonly?
+
+ @buffer.should_not.locked?
+ @buffer.should.valid?
+ end
+
+ # IO::Buffer.map seems not shareable across processes on OpenBSD.
+ # See https://rubyci.s3.amazonaws.com/openbsd-current/ruby-master/log/20260129T163005Z.fail.html.gz
+ platform_is_not :openbsd do
+ guard -> { Process.respond_to?(:fork) } do
+ it "is shareable across processes" do
+ file_name = tmp("shared_buffer")
+ @file = File.open(file_name, "w+")
+ @file << "I'm private"
+ @file.rewind
+ @buffer = IO::Buffer.map(@file)
+
+ IO.popen("-") do |child_pipe|
+ if child_pipe
+ # Synchronize on child's output.
+ child_pipe.readlines.first.chomp.should == @buffer.to_s
+ @buffer.get_string.should == "I'm shared!"
+
+ @file.read.should == "I'm shared!"
+ else
+ @buffer.set_string("I'm shared!")
+ puts @buffer
+ end
+ ensure
+ child_pipe&.close
+ end
+ ensure
+ File.unlink(file_name)
+ end
+ end
+ end
+
+ context "with an empty file" do
+ ruby_version_is "4.0" do
+ it "raises ArgumentError" do
+ file_name = tmp("empty.txt")
+ @file = File.open(file_name, "wb+")
+ @tmp_files << file_name
+ -> { IO::Buffer.map(@file) }.should.raise(ArgumentError, "Invalid negative or zero file size!")
+ end
+ end
+ end
+
+ context "with a file opened only for reading" do
+ it "raises a SystemCallError unless read-only" do
+ @file = File.open(fixture(__dir__, "read_text.txt"), "rb")
+ -> { IO::Buffer.map(@file) }.should.raise(SystemCallError)
+ end
+ end
+
+ context "with size argument" do
+ it "limits the buffer to the specified size in bytes, starting from the start of the file" do
+ @file = open_fixture
+ @buffer = IO::Buffer.map(@file, 4)
+
+ @buffer.size.should == 4
+ @buffer.get_string.should == "abc\xC3".b
+ end
+
+ it "maps the whole file if size is nil" do
+ @file = open_fixture
+ @buffer = IO::Buffer.map(@file, nil)
+
+ @buffer.size.should == 9
+ end
+
+ context "if size is 0" do
+ ruby_version_is "4.0" do
+ it "raises ArgumentError" do
+ @file = open_fixture
+ -> { IO::Buffer.map(@file, 0) }.should.raise(ArgumentError, "Size can't be zero!")
+ end
+ end
+ end
+
+ it "raises TypeError if size is not an Integer or nil" do
+ @file = open_fixture
+ -> { IO::Buffer.map(@file, "10") }.should.raise(TypeError, "not an Integer")
+ -> { IO::Buffer.map(@file, 10.0) }.should.raise(TypeError, "not an Integer")
+ end
+
+ it "raises ArgumentError if size is negative" do
+ @file = open_fixture
+ -> { IO::Buffer.map(@file, -1) }.should.raise(ArgumentError, "Size can't be negative!")
+ end
+
+ ruby_version_is ""..."4.0" do
+ # May or may not cause a crash on access.
+ it "is undefined behavior if size is larger than file size"
+ end
+
+ ruby_version_is "4.0" do
+ it "raises ArgumentError if size is larger than file size" do
+ @file = open_fixture
+ -> { IO::Buffer.map(@file, 8192) }.should.raise(ArgumentError, "Size can't be larger than file size!")
+ end
+ end
+ end
+
+ context "with size and offset arguments" do
+ # Neither Windows nor macOS have clear, stable behavior with non-zero offset.
+ # https://bugs.ruby-lang.org/issues/21700
+ platform_is :linux do
+ context "if offset is an allowed value for system call" do
+ it "maps the span specified by size starting from the offset" do
+ @file = open_big_file_fixture
+ @buffer = IO::Buffer.map(@file, 14, IO::Buffer::PAGE_SIZE)
+
+ @buffer.size.should == 14
+ @buffer.get_string(0, 14).should == "12345678123456"
+ end
+
+ context "if size is nil" do
+ ruby_version_is ""..."4.0" do
+ it "maps the rest of the file" do
+ @file = open_big_file_fixture
+ @buffer = IO::Buffer.map(@file, nil, IO::Buffer::PAGE_SIZE)
+
+ @buffer.get_string(0, 1).should == "1"
+ end
+
+ it "incorrectly sets buffer's size to file's full size" do
+ @file = open_big_file_fixture
+ @buffer = IO::Buffer.map(@file, nil, IO::Buffer::PAGE_SIZE)
+
+ @buffer.size.should == @file.size
+ end
+ end
+
+ ruby_version_is "4.0" do
+ it "maps the rest of the file" do
+ @file = open_big_file_fixture
+ @buffer = IO::Buffer.map(@file, nil, IO::Buffer::PAGE_SIZE)
+
+ @buffer.get_string(0, 1).should == "1"
+ end
+
+ it "sets buffer's size to file's remaining size" do
+ @file = open_big_file_fixture
+ @buffer = IO::Buffer.map(@file, nil, IO::Buffer::PAGE_SIZE)
+
+ @buffer.size.should == (@file.size - IO::Buffer::PAGE_SIZE)
+ end
+ end
+ end
+ end
+ end
+
+ it "maps the file from the start if offset is 0" do
+ @file = open_fixture
+ @buffer = IO::Buffer.map(@file, 4, 0)
+
+ @buffer.size.should == 4
+ @buffer.get_string.should == "abc\xC3".b
+ end
+
+ ruby_version_is ""..."4.0" do
+ # May or may not cause a crash on access.
+ it "is undefined behavior if offset+size is larger than file size"
+ end
+
+ ruby_version_is "4.0" do
+ it "raises ArgumentError if offset+size is larger than file size" do
+ @file = open_big_file_fixture
+ -> { IO::Buffer.map(@file, 17, IO::Buffer::PAGE_SIZE) }.should.raise(ArgumentError, "Offset too large!")
+ ensure
+ # Windows requires the file to be closed before deletion.
+ @file.close unless @file.closed?
+ end
+ end
+
+ it "raises TypeError if offset is not convertible to Integer" do
+ @file = open_fixture
+ -> { IO::Buffer.map(@file, 4, "4096") }.should.raise(TypeError, /no implicit conversion/)
+ -> { IO::Buffer.map(@file, 4, nil) }.should.raise(TypeError, /no implicit conversion/)
+ end
+
+ ruby_version_is "4.0" do
+ it "raises ArgumentError if offset is negative" do
+ @file = open_fixture
+ -> { IO::Buffer.map(@file, 4, -1) }.should.raise(ArgumentError, "Offset can't be negative!")
+ end
+ end
+ end
+
+ context "with flags argument" do
+ context "when READONLY flag is specified" do
+ it "sets readonly flag on the buffer, allowing only reads" do
+ @file = open_fixture
+ @buffer = IO::Buffer.map(@file, nil, 0, IO::Buffer::READONLY)
+
+ @buffer.should.readonly?
+
+ @buffer.get_string.should == "abc\xC3\xA2def\n".b
+ end
+
+ it "allows mapping read-only files" do
+ @file = File.open(fixture(__dir__, "read_text.txt"), "rb")
+ @buffer = IO::Buffer.map(@file, nil, 0, IO::Buffer::READONLY)
+
+ @buffer.should.readonly?
+
+ @buffer.get_string.should == "abc\xC3\xA2def\n".b
+ end
+
+ it "causes IO::Buffer::AccessError on write" do
+ @file = open_fixture
+ @buffer = IO::Buffer.map(@file, nil, 0, IO::Buffer::READONLY)
+
+ -> { @buffer.set_string("test") }.should.raise(IO::Buffer::AccessError, "Buffer is not writable!")
+ end
+ end
+
+ context "when PRIVATE is specified" do
+ it "sets private flag on the buffer, making it freely modifiable" do
+ @file = open_fixture
+ @buffer = IO::Buffer.map(@file, nil, 0, IO::Buffer::PRIVATE)
+
+ @buffer.should.private?
+ @buffer.should_not.shared?
+ @buffer.should_not.external?
+
+ @buffer.get_string.should == "abc\xC3\xA2def\n".b
+ @buffer.set_string("test12345")
+ @buffer.get_string.should == "test12345".b
+
+ @file.read.should == "abcâdef\n".b
+ end
+
+ it "allows mapping read-only files and modifying the buffer" do
+ @file = File.open(fixture(__dir__, "read_text.txt"), "rb")
+ @buffer = IO::Buffer.map(@file, nil, 0, IO::Buffer::PRIVATE)
+
+ @buffer.should.private?
+ @buffer.should_not.shared?
+ @buffer.should_not.external?
+
+ @buffer.get_string.should == "abc\xC3\xA2def\n".b
+ @buffer.set_string("test12345")
+ @buffer.get_string.should == "test12345".b
+
+ @file.read.should == "abcâdef\n".b
+ end
+
+ guard -> { Process.respond_to?(:fork) } do
+ it "is not shared across processes" do
+ file_name = tmp("shared_buffer")
+ @file = File.open(file_name, "w+")
+ @file << "I'm private"
+ @file.rewind
+ @buffer = IO::Buffer.map(@file, nil, 0, IO::Buffer::PRIVATE)
+
+ IO.popen("-") do |child_pipe|
+ if child_pipe
+ # Synchronize on child's output.
+ child_pipe.readlines.first.chomp.should == @buffer.to_s
+ @buffer.get_string.should == "I'm private"
+
+ @file.read.should == "I'm private"
+ else
+ @buffer.set_string("I'm shared!")
+ puts @buffer
+ end
+ ensure
+ child_pipe&.close
+ end
+ ensure
+ File.unlink(file_name)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/buffer/mapped_spec.rb b/spec/ruby/core/io/buffer/mapped_spec.rb
new file mode 100644
index 0000000000..0decb97704
--- /dev/null
+++ b/spec/ruby/core/io/buffer/mapped_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#mapped?" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ it "is true for a buffer with mapped memory" do
+ @buffer = IO::Buffer.new(12, IO::Buffer::MAPPED)
+ @buffer.mapped?.should == true
+ end
+
+ it "is false for a buffer with non-mapped memory" do
+ @buffer = IO::Buffer.for("string")
+ @buffer.mapped?.should == false
+ end
+
+ it "is false for a null buffer" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.mapped?.should == false
+ end
+end
diff --git a/spec/ruby/core/io/buffer/not_spec.rb b/spec/ruby/core/io/buffer/not_spec.rb
new file mode 100644
index 0000000000..4737a30bde
--- /dev/null
+++ b/spec/ruby/core/io/buffer/not_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../../spec_helper'
+
+describe :io_buffer_not, shared: true do
+ it "inverts every bit of the buffer" do
+ IO::Buffer.for(+"12345") do |buffer|
+ result = buffer.send(@method)
+ result.get_string.should == "\xCE\xCD\xCC\xCB\xCA".b
+ result.free
+ end
+ end
+end
+
+describe "IO::Buffer#~" do
+ it_behaves_like :io_buffer_not, :~
+
+ it "creates a new internal buffer of the same size" do
+ IO::Buffer.for(+"12345") do |buffer|
+ result = ~buffer
+ result.should_not.equal? buffer
+ result.should.internal?
+ result.size.should == buffer.size
+ result.free
+ end
+ end
+end
+
+describe "IO::Buffer#not!" do
+ it_behaves_like :io_buffer_not, :not!
+
+ it "modifies the buffer in place" do
+ IO::Buffer.for(+"12345") do |buffer|
+ result = buffer.not!
+ result.should.equal? buffer
+ result.should.external?
+ end
+ end
+end
diff --git a/spec/ruby/core/io/buffer/null_spec.rb b/spec/ruby/core/io/buffer/null_spec.rb
new file mode 100644
index 0000000000..380a98bde1
--- /dev/null
+++ b/spec/ruby/core/io/buffer/null_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../../spec_helper'
+require_relative 'shared/null_and_empty'
+
+describe "IO::Buffer#null?" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ it_behaves_like :io_buffer_null_and_empty, :null?
+
+ it "is false for a 0-length String-backed buffer created with .for" do
+ @buffer = IO::Buffer.for("")
+ @buffer.null?.should == false
+ end
+
+ it "is false for a 0-length String-backed buffer created with .string" do
+ IO::Buffer.string(0) do |buffer|
+ buffer.null?.should == false
+ end
+ end
+
+ it "is false for a 0-length slice of a buffer with size > 0" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.slice(3, 0).null?.should == false
+ end
+end
diff --git a/spec/ruby/core/io/buffer/or_spec.rb b/spec/ruby/core/io/buffer/or_spec.rb
new file mode 100644
index 0000000000..3e627c216c
--- /dev/null
+++ b/spec/ruby/core/io/buffer/or_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../../spec_helper'
+
+describe :io_buffer_or, shared: true do
+ it "applies the argument buffer as an OR bit mask across the whole buffer" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F") do |mask|
+ result = buffer.send(@method, mask)
+ result.get_string.should == "\xF9\xBF\xFB\xBF\xFD".b
+ result.free
+ end
+ end
+ end
+
+ it "ignores extra parts of mask if it is longer than source buffer" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F\x00\x00\x00\xFF\xFF") do |mask|
+ result = buffer.send(@method, mask)
+ result.get_string.should == "\xF9\xBF345".b
+ result.free
+ end
+ end
+ end
+
+ it "raises TypeError if mask is not an IO::Buffer" do
+ IO::Buffer.for(+"12345") do |buffer|
+ -> { buffer.send(@method, "\xF8\x8F") }.should.raise(TypeError, "wrong argument type String (expected IO::Buffer)")
+ -> { buffer.send(@method, 0xF8) }.should.raise(TypeError, "wrong argument type Integer (expected IO::Buffer)")
+ -> { buffer.send(@method, nil) }.should.raise(TypeError, "wrong argument type nil (expected IO::Buffer)")
+ end
+ end
+end
+
+describe "IO::Buffer#|" do
+ it_behaves_like :io_buffer_or, :|
+
+ it "creates a new internal buffer of the same size" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F") do |mask|
+ result = buffer | mask
+ result.should_not.equal? buffer
+ result.should.internal?
+ result.size.should == buffer.size
+ result.free
+ buffer.get_string.should == "12345".b
+ end
+ end
+ end
+end
+
+describe "IO::Buffer#or!" do
+ it_behaves_like :io_buffer_or, :or!
+
+ it "modifies the buffer in place" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F") do |mask|
+ result = buffer.or!(mask)
+ result.should.equal? buffer
+ result.should.external?
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/buffer/private_spec.rb b/spec/ruby/core/io/buffer/private_spec.rb
new file mode 100644
index 0000000000..6e6afee34c
--- /dev/null
+++ b/spec/ruby/core/io/buffer/private_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#private?" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ it "is true for a buffer created with PRIVATE flag" do
+ @buffer = IO::Buffer.new(12, IO::Buffer::INTERNAL | IO::Buffer::PRIVATE)
+ @buffer.private?.should == true
+ end
+
+ it "is false for a buffer created without PRIVATE flag" do
+ @buffer = IO::Buffer.new(12, IO::Buffer::INTERNAL)
+ @buffer.private?.should == false
+ end
+
+ it "is false for a null buffer" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.private?.should == false
+ end
+end
diff --git a/spec/ruby/core/io/buffer/readonly_spec.rb b/spec/ruby/core/io/buffer/readonly_spec.rb
new file mode 100644
index 0000000000..4eefc9f29f
--- /dev/null
+++ b/spec/ruby/core/io/buffer/readonly_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#readonly?" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ it "is true for a buffer created with READONLY flag" do
+ @buffer = IO::Buffer.new(12, IO::Buffer::INTERNAL | IO::Buffer::READONLY)
+ @buffer.readonly?.should == true
+ end
+
+ it "is true for a buffer that is non-writable" do
+ @buffer = IO::Buffer.for("string")
+ @buffer.readonly?.should == true
+ end
+
+ it "is false for a modifiable buffer" do
+ @buffer = IO::Buffer.new(12)
+ @buffer.readonly?.should == false
+ end
+
+ it "is false for a null buffer" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.readonly?.should == false
+ end
+end
diff --git a/spec/ruby/core/io/buffer/resize_spec.rb b/spec/ruby/core/io/buffer/resize_spec.rb
new file mode 100644
index 0000000000..6e684475f3
--- /dev/null
+++ b/spec/ruby/core/io/buffer/resize_spec.rb
@@ -0,0 +1,151 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#resize" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ context "with a buffer created with .new" do
+ it "resizes internal buffer, preserving type" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.resize(IO::Buffer::PAGE_SIZE)
+ @buffer.size.should == IO::Buffer::PAGE_SIZE
+ @buffer.internal?.should == true
+ @buffer.mapped?.should == false
+ end
+
+ platform_is :linux do
+ it "resizes mapped buffer, preserving type" do
+ @buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::MAPPED)
+ @buffer.resize(4)
+ @buffer.size.should == 4
+ @buffer.internal?.should == false
+ @buffer.mapped?.should == true
+ end
+ end
+
+ platform_is_not :linux do
+ it "resizes mapped buffer, changing type to internal" do
+ @buffer = IO::Buffer.new(IO::Buffer::PAGE_SIZE, IO::Buffer::MAPPED)
+ @buffer.resize(4)
+ @buffer.size.should == 4
+ @buffer.internal?.should == true
+ @buffer.mapped?.should == false
+ end
+ end
+ end
+
+ context "with a file-backed buffer created with .map" do
+ it "disallows resizing shared buffer, raising IO::Buffer::AccessError" do
+ File.open(__FILE__, "r+") do |file|
+ @buffer = IO::Buffer.map(file)
+ -> { @buffer.resize(10) }.should.raise(IO::Buffer::AccessError, "Cannot resize external buffer!")
+ end
+ end
+
+ it "resizes private buffer, discarding excess contents" do
+ File.open(__FILE__, "r") do |file|
+ @buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::PRIVATE)
+ @buffer.resize(10)
+ @buffer.size.should == 10
+ @buffer.get_string.should == "require_re"
+ @buffer.resize(12)
+ @buffer.size.should == 12
+ @buffer.get_string.should == "require_re\0\0"
+ end
+ end
+ end
+
+ context "with a String-backed buffer created with .for" do
+ context "without a block" do
+ it "disallows resizing, raising IO::Buffer::AccessError" do
+ @buffer = IO::Buffer.for(+"test")
+ -> { @buffer.resize(10) }.should.raise(IO::Buffer::AccessError, "Cannot resize external buffer!")
+ end
+ end
+
+ context "with a block" do
+ it "disallows resizing, raising IO::Buffer::AccessError" do
+ IO::Buffer.for(+'test') do |buffer|
+ -> { buffer.resize(10) }.should.raise(IO::Buffer::AccessError, "Cannot resize external buffer!")
+ end
+ end
+ end
+ end
+
+ context "with a String-backed buffer created with .string" do
+ it "disallows resizing, raising IO::Buffer::AccessError" do
+ IO::Buffer.string(4) do |buffer|
+ -> { buffer.resize(10) }.should.raise(IO::Buffer::AccessError, "Cannot resize external buffer!")
+ end
+ end
+ end
+
+ context "with a null buffer" do
+ it "allows resizing a 0-sized buffer, creating a regular buffer according to new size" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.resize(IO::Buffer::PAGE_SIZE)
+ @buffer.size.should == IO::Buffer::PAGE_SIZE
+ @buffer.internal?.should == false
+ @buffer.mapped?.should == true
+ end
+
+ it "allows resizing after a free, creating a regular buffer according to new size" do
+ @buffer = IO::Buffer.for("test")
+ @buffer.free
+ @buffer.resize(10)
+ @buffer.size.should == 10
+ @buffer.internal?.should == true
+ @buffer.mapped?.should == false
+ end
+ end
+
+ it "allows resizing to 0, freeing memory" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.resize(0)
+ @buffer.null?.should == true
+ end
+
+ it "can be called repeatedly" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.resize(10)
+ @buffer.resize(27)
+ @buffer.resize(1)
+ @buffer.size.should == 1
+ end
+
+ it "always clears extra memory" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.set_string("test")
+ # This should not cause a re-allocation, just a technical resizing,
+ # even with very aggressive memory allocation.
+ @buffer.resize(2)
+ @buffer.resize(4)
+ @buffer.get_string.should == "te\0\0"
+ end
+
+ it "is disallowed while locked, raising IO::Buffer::LockedError" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.locked do
+ -> { @buffer.resize(10) }.should.raise(IO::Buffer::LockedError, "Cannot resize locked buffer!")
+ end
+ end
+
+ it "raises ArgumentError if size is negative" do
+ @buffer = IO::Buffer.new(4)
+ -> { @buffer.resize(-1) }.should.raise(ArgumentError, "Size can't be negative!")
+ end
+
+ it "raises TypeError if size is not an Integer" do
+ @buffer = IO::Buffer.new(4)
+ -> { @buffer.resize(nil) }.should.raise(TypeError, "not an Integer")
+ -> { @buffer.resize(10.0) }.should.raise(TypeError, "not an Integer")
+ end
+
+ context "with a slice of a buffer" do
+ # Current behavior of slice resizing seems unintended (it's undocumented, too).
+ # It either creates a completely new buffer, or breaks the slice on size 0.
+ it "needs to be reviewed for spec completeness"
+ end
+end
diff --git a/spec/ruby/core/io/buffer/shared/null_and_empty.rb b/spec/ruby/core/io/buffer/shared/null_and_empty.rb
new file mode 100644
index 0000000000..f8abc5f0fc
--- /dev/null
+++ b/spec/ruby/core/io/buffer/shared/null_and_empty.rb
@@ -0,0 +1,57 @@
+describe :io_buffer_null_and_empty, shared: true do
+ it "is false for a buffer with size > 0" do
+ @buffer = IO::Buffer.new(1)
+ @buffer.send(@method).should == false
+ end
+
+ it "is false for a slice with length > 0" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.slice(1, 2).send(@method).should == false
+ end
+
+ it "is false for a file-mapped buffer" do
+ File.open(__FILE__, "rb") do |file|
+ @buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
+ @buffer.send(@method).should == false
+ end
+ end
+
+ it "is false for a non-empty String-backed buffer created with .for" do
+ @buffer = IO::Buffer.for("test")
+ @buffer.send(@method).should == false
+ end
+
+ it "is false for a non-empty String-backed buffer created with .string" do
+ IO::Buffer.string(4) do |buffer|
+ buffer.send(@method).should == false
+ end
+ end
+
+ it "is true for a 0-sized buffer" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.send(@method).should == true
+ end
+
+ it "is true for a slice of a 0-sized buffer" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.slice(0, 0).send(@method).should == true
+ end
+
+ it "is true for a freed buffer" do
+ @buffer = IO::Buffer.new(1)
+ @buffer.free
+ @buffer.send(@method).should == true
+ end
+
+ it "is true for a buffer resized to 0" do
+ @buffer = IO::Buffer.new(1)
+ @buffer.resize(0)
+ @buffer.send(@method).should == true
+ end
+
+ it "is true for a buffer whose memory was transferred" do
+ buffer = IO::Buffer.new(1)
+ @buffer = buffer.transfer
+ buffer.send(@method).should == true
+ end
+end
diff --git a/spec/ruby/core/io/buffer/shared_spec.rb b/spec/ruby/core/io/buffer/shared_spec.rb
new file mode 100644
index 0000000000..2cc93e6d08
--- /dev/null
+++ b/spec/ruby/core/io/buffer/shared_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#shared?" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ it "is true for a buffer created with SHARED flag" do
+ @buffer = IO::Buffer.new(12, IO::Buffer::INTERNAL | IO::Buffer::SHARED)
+ @buffer.shared?.should == true
+ end
+
+ it "is true for a non-private buffer created with .map" do
+ path = fixture(__dir__, "read_text.txt")
+ file = File.open(path, "r+")
+ @buffer = IO::Buffer.map(file)
+ @buffer.shared?.should == true
+ ensure
+ @buffer.free
+ file.close
+ end
+
+ it "is false for an unshared buffer" do
+ @buffer = IO::Buffer.new(12)
+ @buffer.shared?.should == false
+ end
+
+ it "is false for a null buffer" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.shared?.should == false
+ end
+end
diff --git a/spec/ruby/core/io/buffer/string_spec.rb b/spec/ruby/core/io/buffer/string_spec.rb
new file mode 100644
index 0000000000..4c73ba5e1e
--- /dev/null
+++ b/spec/ruby/core/io/buffer/string_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer.string" do
+ it "creates a modifiable buffer for the duration of the block" do
+ IO::Buffer.string(7) do |buffer|
+ @buffer = buffer
+
+ buffer.size.should == 7
+ buffer.get_string.should == "\0\0\0\0\0\0\0".b
+
+ buffer.set_string("test")
+ buffer.get_string.should == "test\0\0\0"
+ end
+ @buffer.should.null?
+ end
+
+ it "returns contents of the buffer as a binary string" do
+ string =
+ IO::Buffer.string(7) do |buffer|
+ buffer.set_string("ä test")
+ end
+ string.should == "\xC3\xA4 test".b
+ end
+
+ it "creates an external buffer" do
+ IO::Buffer.string(8) do |buffer|
+ buffer.should_not.internal?
+ buffer.should_not.mapped?
+ buffer.should.external?
+
+ buffer.should_not.empty?
+ buffer.should_not.null?
+
+ buffer.should_not.shared?
+ buffer.should_not.private?
+ buffer.should_not.readonly?
+
+ buffer.should_not.locked?
+ buffer.should.valid?
+ end
+ end
+
+ it "returns an empty string if size is 0" do
+ string =
+ IO::Buffer.string(0) do |buffer|
+ buffer.size.should == 0
+ end
+ string.should == ""
+ end
+
+ it "raises ArgumentError if size is negative" do
+ -> { IO::Buffer.string(-1) {} }.should.raise(ArgumentError, "negative string size (or size too big)")
+ end
+
+ it "raises RangeError if size is too large" do
+ -> { IO::Buffer.string(2 ** 232) {} }.should.raise(RangeError, /\Abignum too big to convert into [`']long'\z/)
+ end
+
+ it "raises LocalJumpError if no block is given" do
+ -> { IO::Buffer.string(7) }.should.raise(LocalJumpError, "no block given")
+ end
+end
diff --git a/spec/ruby/core/io/buffer/transfer_spec.rb b/spec/ruby/core/io/buffer/transfer_spec.rb
new file mode 100644
index 0000000000..3bc08998dd
--- /dev/null
+++ b/spec/ruby/core/io/buffer/transfer_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#transfer" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ context "with a buffer created with .new" do
+ it "transfers internal memory to a new buffer, nullifying the original" do
+ buffer = IO::Buffer.new(4)
+ info = buffer.to_s
+ @buffer = buffer.transfer
+ @buffer.to_s.should == info
+ buffer.null?.should == true
+ end
+
+ it "transfers mapped memory to a new buffer, nullifying the original" do
+ buffer = IO::Buffer.new(4, IO::Buffer::MAPPED)
+ info = buffer.to_s
+ @buffer = buffer.transfer
+ @buffer.to_s.should == info
+ buffer.null?.should == true
+ end
+ end
+
+ context "with a file-backed buffer created with .map" do
+ it "transfers mapped memory to a new buffer, nullifying the original" do
+ File.open(__FILE__, "r") do |file|
+ buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
+ info = buffer.to_s
+ @buffer = buffer.transfer
+ @buffer.to_s.should == info
+ buffer.null?.should == true
+ end
+ end
+ end
+
+ context "with a String-backed buffer created with .for" do
+ context "without a block" do
+ it "transfers memory to a new buffer, nullifying the original" do
+ buffer = IO::Buffer.for("test")
+ info = buffer.to_s
+ @buffer = buffer.transfer
+ @buffer.to_s.should == info
+ buffer.null?.should == true
+ end
+ end
+
+ context "with a block" do
+ it "transfers memory to a new buffer, breaking the transaction by nullifying the original" do
+ IO::Buffer.for(+"test") do |buffer|
+ info = buffer.to_s
+ @buffer = buffer.transfer
+ @buffer.to_s.should == info
+ buffer.null?.should == true
+ end
+ @buffer.null?.should == false
+ end
+ end
+ end
+
+ context "with a String-backed buffer created with .string" do
+ it "transfers memory to a new buffer, breaking the transaction by nullifying the original" do
+ IO::Buffer.string(4) do |buffer|
+ info = buffer.to_s
+ @buffer = buffer.transfer
+ @buffer.to_s.should == info
+ buffer.null?.should == true
+ end
+ @buffer.null?.should == false
+ end
+ end
+
+ it "allows multiple transfers" do
+ buffer_1 = IO::Buffer.new(4)
+ buffer_2 = buffer_1.transfer
+ @buffer = buffer_2.transfer
+ buffer_1.null?.should == true
+ buffer_2.null?.should == true
+ @buffer.null?.should == false
+ end
+
+ it "is disallowed while locked, raising IO::Buffer::LockedError" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.locked do
+ -> { @buffer.transfer }.should.raise(IO::Buffer::LockedError, "Cannot transfer ownership of locked buffer!")
+ end
+ end
+
+ context "with a slice of a buffer" do
+ it "transfers source to a new slice, not touching the buffer" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.set_string("test")
+ slice = @buffer.slice(0, 2)
+ slice.get_string.should == "te"
+
+ new_slice = slice.transfer
+ slice.null?.should == true
+ new_slice.null?.should == false
+ @buffer.null?.should == false
+
+ new_slice.set_string("ea")
+ @buffer.get_string.should == "east"
+ end
+
+ it "nullifies buffer, invalidating the slice" do
+ buffer = IO::Buffer.new(4)
+ slice = buffer.slice(0, 2)
+ @buffer = buffer.transfer
+
+ slice.null?.should == false
+ slice.valid?.should == false
+ -> { slice.get_string }.should.raise(IO::Buffer::InvalidatedError, "Buffer has been invalidated!")
+ end
+ end
+end
diff --git a/spec/ruby/core/io/buffer/valid_spec.rb b/spec/ruby/core/io/buffer/valid_spec.rb
new file mode 100644
index 0000000000..b84bdd0cfd
--- /dev/null
+++ b/spec/ruby/core/io/buffer/valid_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../../../spec_helper'
+
+describe "IO::Buffer#valid?" do
+ after :each do
+ @buffer&.free
+ @buffer = nil
+ end
+
+ # Non-slices are always valid
+ context "with a non-slice buffer" do
+ it "is true for a regular buffer" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.valid?.should == true
+ end
+
+ it "is true for a 0-size buffer" do
+ @buffer = IO::Buffer.new(0)
+ @buffer.valid?.should == true
+ end
+
+ it "is true for a freed buffer" do
+ @buffer = IO::Buffer.new(4)
+ @buffer.free
+ @buffer.valid?.should == true
+ end
+
+ it "is true for a freed file-backed buffer" do
+ File.open(__FILE__, "r") do |file|
+ @buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
+ @buffer.valid?.should == true
+ @buffer.free
+ @buffer.valid?.should == true
+ end
+ end
+
+ it "is true for a freed string-backed buffer" do
+ @buffer = IO::Buffer.for("hello")
+ @buffer.valid?.should == true
+ @buffer.free
+ @buffer.valid?.should == true
+ end
+ end
+
+ # "A buffer becomes invalid if it is a slice of another buffer (or string)
+ # which has been freed or re-allocated at a different address."
+ context "with a slice" do
+ it "is true for a slice of a live buffer" do
+ @buffer = IO::Buffer.new(4)
+ slice = @buffer.slice(0, 2)
+ slice.valid?.should == true
+ end
+
+ context "when buffer is resized" do
+ it "is false when slice becomes outside the buffer" do
+ @buffer = IO::Buffer.new(4)
+ slice = @buffer.slice(2, 2)
+ @buffer.resize(3)
+ slice.valid?.should == false
+ end
+ end
+
+ it "is false for a slice of a transferred buffer" do
+ buffer = IO::Buffer.new(4)
+ slice = buffer.slice(0, 2)
+ @buffer = buffer.transfer
+ slice.valid?.should == false
+ end
+
+ it "is false for a slice of a freed buffer" do
+ @buffer = IO::Buffer.new(4)
+ slice = @buffer.slice(0, 2)
+ @buffer.free
+ slice.valid?.should == false
+ end
+
+ it "is false for a slice of a freed file-backed buffer" do
+ File.open(__FILE__, "r") do |file|
+ @buffer = IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)
+ slice = @buffer.slice(0, 2)
+ slice.valid?.should == true
+ @buffer.free
+ slice.valid?.should == false
+ end
+ end
+
+ it "is true for a slice of a freed string-backed buffer while string is alive" do
+ @buffer = IO::Buffer.for("alive")
+ slice = @buffer.slice(0, 2)
+ slice.valid?.should == true
+ @buffer.free
+ slice.valid?.should == true
+ end
+
+ # There probably should be a test with a garbage-collected string,
+ # but it's not clear how to force that.
+
+ it "needs to be reviewed for spec completeness"
+ end
+end
diff --git a/spec/ruby/core/io/buffer/xor_spec.rb b/spec/ruby/core/io/buffer/xor_spec.rb
new file mode 100644
index 0000000000..9287611f7f
--- /dev/null
+++ b/spec/ruby/core/io/buffer/xor_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../../../spec_helper'
+
+describe :io_buffer_xor, shared: true do
+ it "applies the argument buffer as an XOR bit mask across the whole buffer" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F") do |mask|
+ result = buffer.send(@method, mask)
+ result.get_string.should == "\xC9\xBD\xCB\xBB\xCD".b
+ result.free
+ end
+ end
+ end
+
+ it "ignores extra parts of mask if it is longer than source buffer" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F\x00\x00\x00\xFF\xFF") do |mask|
+ result = buffer.send(@method, mask)
+ result.get_string.should == "\xC9\xBD345".b
+ result.free
+ end
+ end
+ end
+
+ it "raises TypeError if mask is not an IO::Buffer" do
+ IO::Buffer.for(+"12345") do |buffer|
+ -> { buffer.send(@method, "\xF8\x8F") }.should.raise(TypeError, "wrong argument type String (expected IO::Buffer)")
+ -> { buffer.send(@method, 0xF8) }.should.raise(TypeError, "wrong argument type Integer (expected IO::Buffer)")
+ -> { buffer.send(@method, nil) }.should.raise(TypeError, "wrong argument type nil (expected IO::Buffer)")
+ end
+ end
+end
+
+describe "IO::Buffer#^" do
+ it_behaves_like :io_buffer_xor, :^
+
+ it "creates a new internal buffer of the same size" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F") do |mask|
+ result = buffer ^ mask
+ result.should_not.equal? buffer
+ result.should.internal?
+ result.size.should == buffer.size
+ result.free
+ buffer.get_string.should == "12345".b
+ end
+ end
+ end
+end
+
+describe "IO::Buffer#xor!" do
+ it_behaves_like :io_buffer_xor, :xor!
+
+ it "modifies the buffer in place" do
+ IO::Buffer.for(+"12345") do |buffer|
+ IO::Buffer.for(+"\xF8\x8F") do |mask|
+ result = buffer.xor!(mask)
+ result.should.equal? buffer
+ result.should.external?
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/bytes_spec.rb b/spec/ruby/core/io/bytes_spec.rb
deleted file mode 100644
index feeb493566..0000000000
--- a/spec/ruby/core/io/bytes_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- encoding: utf-8 -*-
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-
-describe "IO#bytes" do
- before :each do
- @io = IOSpecs.io_fixture "lines.txt"
- end
-
- after :each do
- @io.close unless @io.closed?
- end
-
- it "returns an enumerator of the next bytes from the stream" do
- enum = @io.bytes
- enum.should be_an_instance_of(Enumerator)
- @io.readline.should == "Voici la ligne une.\n"
- enum.first(5).should == [81, 117, 105, 32, 195]
- end
-
- it "yields each byte" do
- count = 0
- ScratchPad.record []
- @io.each_byte do |byte|
- ScratchPad << byte
- break if 4 < count += 1
- end
-
- ScratchPad.recorded.should == [86, 111, 105, 99, 105]
- end
-
- it "raises an IOError on closed stream" do
- enum = IOSpecs.closed_io.bytes
- -> { enum.first }.should raise_error(IOError)
- end
-
- it "raises an IOError on an enumerator for a stream that has been closed" do
- enum = @io.bytes
- enum.first.should == 86
- @io.close
- -> { enum.first }.should raise_error(IOError)
- end
-end
diff --git a/spec/ruby/core/io/chars_spec.rb b/spec/ruby/core/io/chars_spec.rb
deleted file mode 100644
index cd5dbbce4f..0000000000
--- a/spec/ruby/core/io/chars_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- encoding: utf-8 -*-
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/chars'
-
-describe "IO#chars" do
- it_behaves_like :io_chars, :chars
-end
-
-describe "IO#chars" do
- it_behaves_like :io_chars_empty, :chars
-end
diff --git a/spec/ruby/core/io/close_on_exec_spec.rb b/spec/ruby/core/io/close_on_exec_spec.rb
index 91bd3c8c40..28cdb967b9 100644
--- a/spec/ruby/core/io/close_on_exec_spec.rb
+++ b/spec/ruby/core/io/close_on_exec_spec.rb
@@ -42,11 +42,7 @@ describe "IO#close_on_exec=" do
it "raises IOError if called on a closed IO" do
@io.close
- -> { @io.close_on_exec = true }.should raise_error(IOError)
- end
-
- it "returns nil" do
- @io.send(:close_on_exec=, true).should be_nil
+ -> { @io.close_on_exec = true }.should.raise(IOError)
end
end
end
@@ -74,7 +70,7 @@ describe "IO#close_on_exec?" do
it "raises IOError if called on a closed IO" do
@io.close
- -> { @io.close_on_exec? }.should raise_error(IOError)
+ -> { @io.close_on_exec? }.should.raise(IOError)
end
end
end
diff --git a/spec/ruby/core/io/close_read_spec.rb b/spec/ruby/core/io/close_read_spec.rb
index 26454bfddd..c505289d72 100644
--- a/spec/ruby/core/io/close_read_spec.rb
+++ b/spec/ruby/core/io/close_read_spec.rb
@@ -4,7 +4,8 @@ require_relative 'fixtures/classes'
describe "IO#close_read" do
before :each do
- @io = IO.popen 'cat', "r+"
+ cmd = platform_is(:windows) ? 'rem' : 'cat'
+ @io = IO.popen cmd, "r+"
@path = tmp('io.close.txt')
end
@@ -16,26 +17,26 @@ describe "IO#close_read" do
it "closes the read end of a duplex I/O stream" do
@io.close_read
- -> { @io.read }.should raise_error(IOError)
+ -> { @io.read }.should.raise(IOError)
end
it "does nothing on subsequent invocations" do
@io.close_read
- @io.close_read.should be_nil
+ @io.close_read.should == nil
end
it "allows subsequent invocation of close" do
@io.close_read
- -> { @io.close }.should_not raise_error
+ -> { @io.close }.should_not.raise
end
it "raises an IOError if the stream is writable and not duplexed" do
io = File.open @path, 'w'
begin
- -> { io.close_read }.should raise_error(IOError)
+ -> { io.close_read }.should.raise(IOError)
ensure
io.close unless io.closed?
end
@@ -55,6 +56,6 @@ describe "IO#close_read" do
it "does nothing on closed stream" do
@io.close
- @io.close_read.should be_nil
+ @io.close_read.should == nil
end
end
diff --git a/spec/ruby/core/io/close_spec.rb b/spec/ruby/core/io/close_spec.rb
index dda82b34de..afd84ba101 100644
--- a/spec/ruby/core/io/close_spec.rb
+++ b/spec/ruby/core/io/close_spec.rb
@@ -23,41 +23,64 @@ describe "IO#close" do
it "raises an IOError reading from a closed IO" do
@io.close
- -> { @io.read }.should raise_error(IOError)
+ -> { @io.read }.should.raise(IOError)
end
it "raises an IOError writing to a closed IO" do
@io.close
- -> { @io.write "data" }.should raise_error(IOError)
+ -> { @io.write "data" }.should.raise(IOError)
end
it 'does not close the stream if autoclose is false' do
other_io = IO.new(@io.fileno)
other_io.autoclose = false
other_io.close
- -> { @io.write "data" }.should_not raise_error(IOError)
+ -> { @io.write "data" }.should_not.raise(IOError)
end
it "does nothing if already closed" do
@io.close
- @io.close.should be_nil
+ @io.close.should == nil
+ end
+
+ it "does not call the #flush method but flushes the stream internally" do
+ @io.should_not_receive(:flush)
+ @io.close
+ @io.should.closed?
end
it 'raises an IOError with a clear message' do
- read_io, write_io = IO.pipe
- going_to_read = false
- thread = Thread.new do
- -> do
- going_to_read = true
- read_io.read
- end.should raise_error(IOError, 'stream closed in another thread')
- end
-
- Thread.pass until going_to_read && thread.stop?
- read_io.close
- thread.join
- write_io.close
+ matching_exception = nil
+
+ -> do
+ IOSpecs::THREAD_CLOSE_RETRIES.times do
+ read_io, write_io = IO.pipe
+ going_to_read = false
+
+ thread = Thread.new do
+ begin
+ going_to_read = true
+ read_io.read
+ rescue IOError => ioe
+ if ioe.message == IOSpecs::THREAD_CLOSE_ERROR_MESSAGE
+ matching_exception = ioe
+ end
+ # try again
+ end
+ end
+
+ # best attempt to ensure the thread is actually blocked on read
+ Thread.pass until going_to_read && thread.stop?
+ sleep(0.001)
+
+ read_io.close
+ thread.join
+ write_io.close
+
+ matching_exception&.tap {|ex| raise ex}
+ end
+ end.should.raise(IOError, IOSpecs::THREAD_CLOSE_ERROR_MESSAGE)
end
end
@@ -70,7 +93,7 @@ describe "IO#close on an IO.popen stream" do
io.close
- -> { io.pid }.should raise_error(IOError)
+ -> { io.pid }.should.raise(IOError)
end
it "sets $?" do
diff --git a/spec/ruby/core/io/close_write_spec.rb b/spec/ruby/core/io/close_write_spec.rb
index 14835e4e2c..60b41505c3 100644
--- a/spec/ruby/core/io/close_write_spec.rb
+++ b/spec/ruby/core/io/close_write_spec.rb
@@ -3,7 +3,8 @@ require_relative 'fixtures/classes'
describe "IO#close_write" do
before :each do
- @io = IO.popen 'cat', 'r+'
+ cmd = platform_is(:windows) ? 'rem' : 'cat'
+ @io = IO.popen cmd, 'r+'
@path = tmp('io.close.txt')
end
@@ -15,26 +16,26 @@ describe "IO#close_write" do
it "closes the write end of a duplex I/O stream" do
@io.close_write
- -> { @io.write "attempt to write" }.should raise_error(IOError)
+ -> { @io.write "attempt to write" }.should.raise(IOError)
end
it "does nothing on subsequent invocations" do
@io.close_write
- @io.close_write.should be_nil
+ @io.close_write.should == nil
end
it "allows subsequent invocation of close" do
@io.close_write
- -> { @io.close }.should_not raise_error
+ -> { @io.close }.should_not.raise
end
it "raises an IOError if the stream is readable and not duplexed" do
io = File.open @path, 'w+'
begin
- -> { io.close_write }.should raise_error(IOError)
+ -> { io.close_write }.should.raise(IOError)
ensure
io.close unless io.closed?
end
@@ -48,17 +49,20 @@ describe "IO#close_write" do
io.should.closed?
end
- it "flushes and closes the write stream" do
- @io.puts '12345'
+ # Windows didn't have command like cat
+ platform_is_not :windows do
+ it "flushes and closes the write stream" do
+ @io.puts '12345'
- @io.close_write
+ @io.close_write
- @io.read.should == "12345\n"
+ @io.read.should == "12345\n"
+ end
end
it "does nothing on closed stream" do
@io.close
- @io.close_write.should be_nil
+ @io.close_write.should == nil
end
end
diff --git a/spec/ruby/core/io/closed_spec.rb b/spec/ruby/core/io/closed_spec.rb
index 7316546a0d..1f10858e28 100644
--- a/spec/ruby/core/io/closed_spec.rb
+++ b/spec/ruby/core/io/closed_spec.rb
@@ -11,10 +11,10 @@ describe "IO#closed?" do
end
it "returns true on closed stream" do
- IOSpecs.closed_io.closed?.should be_true
+ IOSpecs.closed_io.closed?.should == true
end
it "returns false on open stream" do
- @io.closed?.should be_false
+ @io.closed?.should == false
end
end
diff --git a/spec/ruby/core/io/codepoints_spec.rb b/spec/ruby/core/io/codepoints_spec.rb
deleted file mode 100644
index 915d99c027..0000000000
--- a/spec/ruby/core/io/codepoints_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-require_relative 'shared/codepoints'
-
-# See redmine #1667
-describe "IO#codepoints" do
- it_behaves_like :io_codepoints, :codepoints
-end
-
-describe "IO#codepoints" do
- before :each do
- @io = IOSpecs.io_fixture "lines.txt"
- end
-
- after :each do
- @io.close unless @io.closed?
- end
-
- it "calls the given block" do
- r = []
- @io.codepoints { |c| r << c }
- r[24].should == 232
- r.last.should == 10
- end
-end
diff --git a/spec/ruby/core/io/copy_stream_spec.rb b/spec/ruby/core/io/copy_stream_spec.rb
index df9c5c7390..31383f9b0f 100644
--- a/spec/ruby/core/io/copy_stream_spec.rb
+++ b/spec/ruby/core/io/copy_stream_spec.rb
@@ -31,7 +31,7 @@ describe :io_copy_stream_to_file, shared: true do
obj = mock("io_copy_stream_to")
obj.should_receive(:to_path).and_return(1)
- -> { IO.copy_stream(@object.from, obj) }.should raise_error(TypeError)
+ -> { IO.copy_stream(@object.from, obj) }.should.raise(TypeError)
end
end
@@ -69,14 +69,17 @@ describe :io_copy_stream_to_io, shared: true do
end
it "raises an IOError if the destination IO is not open for writing" do
- @to_io.close
- @to_io = new_io @to_name, "r"
- -> { IO.copy_stream @object.from, @to_io }.should raise_error(IOError)
+ to_io = new_io __FILE__, "r"
+ begin
+ -> { IO.copy_stream @object.from, to_io }.should.raise(IOError)
+ ensure
+ to_io.close
+ end
end
it "does not close the destination IO" do
IO.copy_stream(@object.from, @to_io)
- @to_io.closed?.should be_false
+ @to_io.closed?.should == false
end
it "copies only length bytes when specified" do
@@ -109,7 +112,8 @@ describe "IO.copy_stream" do
end
after :each do
- rm_r @to_name, @from_bigfile
+ rm_r @to_name if @to_name
+ rm_r @from_bigfile
end
describe "from an IO" do
@@ -125,12 +129,12 @@ describe "IO.copy_stream" do
it "raises an IOError if the source IO is not open for reading" do
@from_io.close
@from_io = new_io @from_bigfile, "a"
- -> { IO.copy_stream @from_io, @to_name }.should raise_error(IOError)
+ -> { IO.copy_stream @from_io, @to_name }.should.raise(IOError)
end
it "does not close the source IO" do
IO.copy_stream(@from_io, @to_name)
- @from_io.closed?.should be_false
+ @from_io.closed?.should == false
end
platform_is_not :windows do
@@ -164,6 +168,25 @@ describe "IO.copy_stream" do
it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream
end
+
+ describe "to a Tempfile" do
+ before :all do
+ require 'tempfile'
+ end
+
+ before :each do
+ @to_io = Tempfile.new("rubyspec_copy_stream", encoding: Encoding::BINARY, mode: File::RDONLY)
+ @to_name = @to_io.path
+ end
+
+ after :each do
+ @to_io.close!
+ @to_name = nil # do not rm_r it, already done by Tempfile#close!
+ end
+
+ it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream
+ it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream
+ end
end
describe "from a file name" do
@@ -183,7 +206,7 @@ describe "IO.copy_stream" do
obj = mock("io_copy_stream_from")
obj.should_receive(:to_path).and_return(1)
- -> { IO.copy_stream(obj, @to_name) }.should raise_error(TypeError)
+ -> { IO.copy_stream(obj, @to_name) }.should.raise(TypeError)
end
describe "to a file name" do
@@ -217,12 +240,12 @@ describe "IO.copy_stream" do
it "does not close the source IO" do
IO.copy_stream(@from_io, @to_name)
- @from_io.closed?.should be_false
+ @from_io.closed?.should == false
end
platform_is_not :windows do
it "raises an error when an offset is specified" do
- -> { IO.copy_stream(@from_io, @to_name, 8, 4) }.should raise_error(Errno::ESPIPE)
+ -> { IO.copy_stream(@from_io, @to_name, 8, 4) }.should.raise(Errno::ESPIPE)
end
end
@@ -278,9 +301,15 @@ describe "IO.copy_stream" do
IO.copy_stream(@io, @to_name)
end
+ it "does not call #read on the source or #write on the destination if zero length is given" do
+ from = mock("io_copy_stream_to_object_zero_length_read")
+ to = mock("io_copy_stream_to_object_zero_length_write")
+ from.should_not_receive(:read)
+ to.should_not_receive(:write)
+ IO.copy_stream(from, to, 0)
+ end
end
-
describe "with a destination that does partial reads" do
before do
@from_out, @from_in = IO.pipe
diff --git a/spec/ruby/core/io/dup_spec.rb b/spec/ruby/core/io/dup_spec.rb
index 8cadaee118..db4e9fe641 100644
--- a/spec/ruby/core/io/dup_spec.rb
+++ b/spec/ruby/core/io/dup_spec.rb
@@ -25,31 +25,31 @@ describe "IO#dup" do
@i.fileno.should_not == @f.fileno
end
-quarantine! do # This does not appear to be consistent across platforms
- it "shares the original stream between the two IOs" do
- start = @f.pos
- @i.pos.should == start
+ quarantine! do # This does not appear to be consistent across platforms
+ it "shares the original stream between the two IOs" do
+ start = @f.pos
+ @i.pos.should == start
- s = "Hello, wo.. wait, where am I?\n"
- s2 = "<evil voice> Muhahahaa!"
+ s = "Hello, wo.. wait, where am I?\n"
+ s2 = "<evil voice> Muhahahaa!"
- @f.write s
- @i.pos.should == @f.pos
+ @f.write s
+ @i.pos.should == @f.pos
- @i.rewind
- @i.gets.should == s
+ @i.rewind
+ @i.gets.should == s
- @i.rewind
- @i.write s2
+ @i.rewind
+ @i.write s2
- @f.rewind
- @f.gets.should == "#{s2}\n"
+ @f.rewind
+ @f.gets.should == "#{s2}\n"
+ end
end
-end
it "allows closing the new IO without affecting the original" do
@i.close
- -> { @f.gets }.should_not raise_error(Exception)
+ -> { @f.gets }.should_not.raise(Exception)
@i.should.closed?
@f.should_not.closed?
@@ -57,14 +57,14 @@ end
it "allows closing the original IO without affecting the new one" do
@f.close
- -> { @i.gets }.should_not raise_error(Exception)
+ -> { @i.gets }.should_not.raise(Exception)
@i.should_not.closed?
@f.should.closed?
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.dup }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.dup }.should.raise(IOError)
end
it "always sets the close-on-exec flag for the new IO object" do
@@ -84,4 +84,23 @@ end
dup.close
end
end
+
+ it "always sets the autoclose flag for the new IO object" do
+ @f.autoclose = true
+ dup = @f.dup
+ begin
+ dup.should.autoclose?
+ ensure
+ dup.close
+ end
+
+ @f.autoclose = false
+ dup = @f.dup
+ begin
+ dup.should.autoclose?
+ ensure
+ dup.close
+ @f.autoclose = true
+ end
+ end
end
diff --git a/spec/ruby/core/io/each_byte_spec.rb b/spec/ruby/core/io/each_byte_spec.rb
index ea618e8c0c..fe299f0fba 100644
--- a/spec/ruby/core/io/each_byte_spec.rb
+++ b/spec/ruby/core/io/each_byte_spec.rb
@@ -12,7 +12,7 @@ describe "IO#each_byte" do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.each_byte {} }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.each_byte {} }.should.raise(IOError)
end
it "yields each byte" do
@@ -28,7 +28,7 @@ describe "IO#each_byte" do
describe "when no block is given" do
it "returns an Enumerator" do
enum = @io.each_byte
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.first(5).should == [86, 111, 105, 99, 105]
end
@@ -52,6 +52,6 @@ describe "IO#each_byte" do
end
it "returns self on an empty stream" do
- @io.each_byte { |b| }.should equal(@io)
+ @io.each_byte { |b| }.should.equal?(@io)
end
end
diff --git a/spec/ruby/core/io/each_codepoint_spec.rb b/spec/ruby/core/io/each_codepoint_spec.rb
index cddc6b4662..26cc87fc0e 100644
--- a/spec/ruby/core/io/each_codepoint_spec.rb
+++ b/spec/ruby/core/io/each_codepoint_spec.rb
@@ -4,7 +4,7 @@ require_relative 'shared/codepoints'
# See redmine #1667
describe "IO#each_codepoint" do
- it_behaves_like :io_codepoints, :codepoints
+ it_behaves_like :io_codepoints, :each_codepoint
end
describe "IO#each_codepoint" do
@@ -24,7 +24,7 @@ describe "IO#each_codepoint" do
end
it "returns self" do
- @io.each_codepoint { |l| l }.should equal(@io)
+ @io.each_codepoint { |l| l }.should.equal?(@io)
end
end
@@ -38,6 +38,6 @@ describe "IO#each_codepoint" do
end
it "raises an exception at incomplete character before EOF when conversion takes place" do
- -> { @io.each_codepoint {} }.should raise_error(ArgumentError)
+ -> { @io.each_codepoint {} }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/io/eof_spec.rb b/spec/ruby/core/io/eof_spec.rb
index 315345d942..c8955abde0 100644
--- a/spec/ruby/core/io/eof_spec.rb
+++ b/spec/ruby/core/io/eof_spec.rb
@@ -18,7 +18,7 @@ describe "IO#eof?" do
it "raises IOError on stream not opened for reading" do
-> do
File.open(@name, "w") { |f| f.eof? }
- end.should raise_error(IOError)
+ end.should.raise(IOError)
end
end
@@ -67,16 +67,16 @@ describe "IO#eof?" do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.eof? }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.eof? }.should.raise(IOError)
end
it "raises IOError on stream closed for reading by close_read" do
@io.close_read
- -> { @io.eof? }.should raise_error(IOError)
+ -> { @io.eof? }.should.raise(IOError)
end
it "returns true on one-byte stream after single-byte read" do
- File.open(File.dirname(__FILE__) + '/fixtures/one_byte.txt') { |one_byte|
+ File.open(__dir__ + '/fixtures/one_byte.txt') { |one_byte|
one_byte.read(1)
one_byte.should.eof?
}
diff --git a/spec/ruby/core/io/external_encoding_spec.rb b/spec/ruby/core/io/external_encoding_spec.rb
index 9666974647..72d246cc2b 100644
--- a/spec/ruby/core/io/external_encoding_spec.rb
+++ b/spec/ruby/core/io/external_encoding_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe :io_external_encoding_write, shared: true do
describe "when Encoding.default_internal is nil" do
@@ -9,19 +10,19 @@ describe :io_external_encoding_write, shared: true do
it "returns nil" do
@io = new_io @name, @object
Encoding.default_external = Encoding::IBM437
- @io.external_encoding.should be_nil
+ @io.external_encoding.should == nil
end
it "returns the external encoding specified when the instance was created" do
@io = new_io @name, "#{@object}:ibm866"
Encoding.default_external = Encoding::IBM437
- @io.external_encoding.should equal(Encoding::IBM866)
+ @io.external_encoding.should.equal?(Encoding::IBM866)
end
it "returns the encoding set by #set_encoding" do
@io = new_io @name, "#{@object}:ibm866"
@io.set_encoding Encoding::EUC_JP, nil
- @io.external_encoding.should equal(Encoding::EUC_JP)
+ @io.external_encoding.should.equal?(Encoding::EUC_JP)
end
end
@@ -34,19 +35,19 @@ describe :io_external_encoding_write, shared: true do
it "returns the value of Encoding.default_external when the instance was created" do
@io = new_io @name, @object
Encoding.default_external = Encoding::UTF_8
- @io.external_encoding.should equal(Encoding::IBM437)
+ @io.external_encoding.should.equal?(Encoding::IBM437)
end
it "returns the external encoding specified when the instance was created" do
@io = new_io @name, "#{@object}:ibm866"
Encoding.default_external = Encoding::IBM437
- @io.external_encoding.should equal(Encoding::IBM866)
+ @io.external_encoding.should.equal?(Encoding::IBM866)
end
it "returns the encoding set by #set_encoding" do
@io = new_io @name, "#{@object}:ibm866"
@io.set_encoding Encoding::EUC_JP, nil
- @io.external_encoding.should equal(Encoding::EUC_JP)
+ @io.external_encoding.should.equal?(Encoding::EUC_JP)
end
end
@@ -59,19 +60,19 @@ describe :io_external_encoding_write, shared: true do
it "returns the value of Encoding.default_external when the instance was created" do
@io = new_io @name, @object
Encoding.default_external = Encoding::UTF_8
- @io.external_encoding.should equal(Encoding::IBM866)
+ @io.external_encoding.should.equal?(Encoding::IBM866)
end
it "returns the external encoding specified when the instance was created" do
@io = new_io @name, "#{@object}:ibm866"
Encoding.default_external = Encoding::IBM437
- @io.external_encoding.should equal(Encoding::IBM866)
+ @io.external_encoding.should.equal?(Encoding::IBM866)
end
it "returns the encoding set by #set_encoding" do
@io = new_io @name, "#{@object}:ibm866"
@io.set_encoding Encoding::EUC_JP, nil
- @io.external_encoding.should equal(Encoding::EUC_JP)
+ @io.external_encoding.should.equal?(Encoding::EUC_JP)
end
end
end
@@ -93,6 +94,12 @@ describe "IO#external_encoding" do
rm_r @name
end
+ it "can be retrieved from a closed stream" do
+ io = IOSpecs.io_fixture("lines.txt", "r")
+ io.close
+ io.external_encoding.should.equal?(Encoding.default_external)
+ end
+
describe "with 'r' mode" do
describe "when Encoding.default_internal is nil" do
before :each do
@@ -102,25 +109,25 @@ describe "IO#external_encoding" do
it "returns Encoding.default_external if the external encoding is not set" do
@io = new_io @name, "r"
- @io.external_encoding.should equal(Encoding::IBM866)
+ @io.external_encoding.should.equal?(Encoding::IBM866)
end
it "returns Encoding.default_external when that encoding is changed after the instance is created" do
@io = new_io @name, "r"
Encoding.default_external = Encoding::IBM437
- @io.external_encoding.should equal(Encoding::IBM437)
+ @io.external_encoding.should.equal?(Encoding::IBM437)
end
it "returns the external encoding specified when the instance was created" do
@io = new_io @name, "r:utf-8"
Encoding.default_external = Encoding::IBM437
- @io.external_encoding.should equal(Encoding::UTF_8)
+ @io.external_encoding.should.equal?(Encoding::UTF_8)
end
it "returns the encoding set by #set_encoding" do
@io = new_io @name, "r:utf-8"
@io.set_encoding Encoding::EUC_JP, nil
- @io.external_encoding.should equal(Encoding::EUC_JP)
+ @io.external_encoding.should.equal?(Encoding::EUC_JP)
end
end
@@ -133,19 +140,19 @@ describe "IO#external_encoding" do
it "returns the value of Encoding.default_external when the instance was created" do
@io = new_io @name, "r"
Encoding.default_external = Encoding::IBM437
- @io.external_encoding.should equal(Encoding::IBM866)
+ @io.external_encoding.should.equal?(Encoding::IBM866)
end
it "returns the external encoding specified when the instance was created" do
@io = new_io @name, "r:utf-8"
Encoding.default_external = Encoding::IBM437
- @io.external_encoding.should equal(Encoding::UTF_8)
+ @io.external_encoding.should.equal?(Encoding::UTF_8)
end
it "returns the encoding set by #set_encoding" do
@io = new_io @name, "r:utf-8"
@io.set_encoding Encoding::EUC_JP, nil
- @io.external_encoding.should equal(Encoding::EUC_JP)
+ @io.external_encoding.should.equal?(Encoding::EUC_JP)
end
end
@@ -159,13 +166,13 @@ describe "IO#external_encoding" do
it "returns the external encoding specified when the instance was created" do
@io = new_io @name, "r:utf-8"
Encoding.default_external = Encoding::IBM437
- @io.external_encoding.should equal(Encoding::UTF_8)
+ @io.external_encoding.should.equal?(Encoding::UTF_8)
end
it "returns the encoding set by #set_encoding" do
@io = new_io @name, "r:utf-8"
@io.set_encoding Encoding::EUC_JP, nil
- @io.external_encoding.should equal(Encoding::EUC_JP)
+ @io.external_encoding.should.equal?(Encoding::EUC_JP)
end
end
end
@@ -173,12 +180,12 @@ describe "IO#external_encoding" do
describe "with 'rb' mode" do
it "returns Encoding::BINARY" do
@io = new_io @name, "rb"
- @io.external_encoding.should equal(Encoding::BINARY)
+ @io.external_encoding.should.equal?(Encoding::BINARY)
end
it "returns the external encoding specified by the mode argument" do
@io = new_io @name, "rb:ibm437"
- @io.external_encoding.should equal(Encoding::IBM437)
+ @io.external_encoding.should.equal?(Encoding::IBM437)
end
end
@@ -193,12 +200,12 @@ describe "IO#external_encoding" do
describe "with 'wb' mode" do
it "returns Encoding::BINARY" do
@io = new_io @name, "wb"
- @io.external_encoding.should equal(Encoding::BINARY)
+ @io.external_encoding.should.equal?(Encoding::BINARY)
end
it "returns the external encoding specified by the mode argument" do
@io = new_io @name, "wb:ibm437"
- @io.external_encoding.should equal(Encoding::IBM437)
+ @io.external_encoding.should.equal?(Encoding::IBM437)
end
end
diff --git a/spec/ruby/core/io/fcntl_spec.rb b/spec/ruby/core/io/fcntl_spec.rb
index 30b4876fe3..be6d06c672 100644
--- a/spec/ruby/core/io/fcntl_spec.rb
+++ b/spec/ruby/core/io/fcntl_spec.rb
@@ -3,6 +3,6 @@ require_relative 'fixtures/classes'
describe "IO#fcntl" do
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.fcntl(5, 5) }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.fcntl(5, 5) }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/fileno_spec.rb b/spec/ruby/core/io/fileno_spec.rb
index 647609bf42..22fd68d166 100644
--- a/spec/ruby/core/io/fileno_spec.rb
+++ b/spec/ruby/core/io/fileno_spec.rb
@@ -7,6 +7,6 @@ describe "IO#fileno" do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.fileno }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.fileno }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/fixtures/classes.rb b/spec/ruby/core/io/fixtures/classes.rb
index 5cc42c9b44..204a2a101b 100644
--- a/spec/ruby/core/io/fixtures/classes.rb
+++ b/spec/ruby/core/io/fixtures/classes.rb
@@ -1,9 +1,24 @@
# -*- encoding: utf-8 -*-
module IOSpecs
+ THREAD_CLOSE_RETRIES = 10
+ THREAD_CLOSE_ERROR_MESSAGE = 'stream closed in another thread'
+
class SubIO < IO
end
+ class SubIOWithRedefinedNew < IO
+ def self.new(...)
+ ScratchPad << :redefined_new_called
+ super
+ end
+
+ def initialize(...)
+ ScratchPad << :call_original_initialize
+ super
+ end
+ end
+
def self.collector
Proc.new { |x| ScratchPad << x }
end
@@ -105,6 +120,14 @@ module IOSpecs
"linha ", "cinco.\nHere ", "is ", "line ", "six.\n" ]
end
+ def self.lines_space_separator_without_trailing_spaces
+ [ "Voici", "la", "ligne", "une.\nQui",
+ "\303\250", "la", "linea", "due.\n\n\nAqu\303\255",
+ "est\303\241", "la", "l\303\255nea", "tres.\nHier",
+ "ist", "Zeile", "vier.\n\nEst\303\241", "aqui", "a",
+ "linha", "cinco.\nHere", "is", "line", "six.\n" ]
+ end
+
def self.lines_arbitrary_separator
[ "Voici la ligne une.\nQui \303\250",
" la linea due.\n\n\nAqu\303\255 est\303\241 la l\303\255nea tres.\nHier ist Zeile vier.\n\nEst\303\241 aqui a linha cinco.\nHere is line six.\n" ]
@@ -116,6 +139,12 @@ module IOSpecs
"Est\303\241 aqui a linha cinco.\nHere is line six.\n" ]
end
+ def self.paragraphs_without_trailing_new_line_characters
+ [ "Voici la ligne une.\nQui \303\250 la linea due.",
+ "Aqu\303\255 est\303\241 la l\303\255nea tres.\nHier ist Zeile vier.",
+ "Est\303\241 aqui a linha cinco.\nHere is line six.\n" ]
+ end
+
# Creates an IO instance for an existing fixture file. The
# file should obviously not be deleted.
def self.io_fixture(name, mode = "r:utf-8")
diff --git a/spec/ruby/core/io/flush_spec.rb b/spec/ruby/core/io/flush_spec.rb
index 34cf42c425..4c3e8d12af 100644
--- a/spec/ruby/core/io/flush_spec.rb
+++ b/spec/ruby/core/io/flush_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "IO#flush" do
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.flush }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.flush }.should.raise(IOError)
end
describe "on a pipe" do
@@ -19,18 +19,18 @@ describe "IO#flush" do
end
end
- # [ruby-core:90895] MJIT worker may leave fd open in a forked child.
- # For instance, MJIT creates a worker before @r.close with fork(), @r.close happens,
- # and the MJIT worker keeps the pipe open until the worker execve().
- # TODO: consider acquiring GVL from MJIT worker.
- guard_not -> { defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? } do
+ # [ruby-core:90895] RJIT worker may leave fd open in a forked child.
+ # For instance, RJIT creates a worker before @r.close with fork(), @r.close happens,
+ # and the RJIT worker keeps the pipe open until the worker execve().
+ # TODO: consider acquiring GVL from RJIT worker.
+ guard_not -> { defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? } do
it "raises Errno::EPIPE if sync=false and the read end is closed" do
@w.sync = false
@w.write "foo"
@r.close
- -> { @w.flush }.should raise_error(Errno::EPIPE, /Broken pipe/)
- -> { @w.close }.should raise_error(Errno::EPIPE, /Broken pipe/)
+ -> { @w.flush }.should.raise(Errno::EPIPE, /Broken pipe/)
+ -> { @w.close }.should.raise(Errno::EPIPE, /Broken pipe/)
end
end
end
diff --git a/spec/ruby/core/io/foreach_spec.rb b/spec/ruby/core/io/foreach_spec.rb
index c2276cf544..015988f9fb 100644
--- a/spec/ruby/core/io/foreach_spec.rb
+++ b/spec/ruby/core/io/foreach_spec.rb
@@ -14,31 +14,46 @@ describe "IO.foreach" do
IO.foreach(@name) { $..should == @count += 1 }
end
- describe "when the filename starts with |" do
- it "gets data from the standard out of the subprocess" do
- cmd = "|sh -c 'echo hello;echo line2'"
- platform_is :windows do
- cmd = "|cmd.exe /C echo hello&echo line2"
+ ruby_version_is ""..."4.0" do
+ describe "when the filename starts with |" do
+ it "gets data from the standard out of the subprocess" do
+ cmd = "|sh -c 'echo hello;echo line2'"
+ platform_is :windows do
+ cmd = "|cmd.exe /C echo hello&echo line2"
+ end
+
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.foreach(cmd) { |l| ScratchPad << l }
+ end
+ ScratchPad.recorded.should == ["hello\n", "line2\n"]
end
- IO.foreach(cmd) { |l| ScratchPad << l }
- ScratchPad.recorded.should == ["hello\n", "line2\n"]
- end
- platform_is_not :windows do
- it "gets data from a fork when passed -" do
- parent_pid = $$
+ platform_is_not :windows do
+ it "gets data from a fork when passed -" do
+ parent_pid = $$
- IO.foreach("|-") { |l| ScratchPad << l }
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.foreach("|-") { |l| ScratchPad << l }
+ end
- if $$ == parent_pid
- ScratchPad.recorded.should == ["hello\n", "from a fork\n"]
- else # child
- puts "hello"
- puts "from a fork"
- exit!
+ if $$ == parent_pid
+ ScratchPad.recorded.should == ["hello\n", "from a fork\n"]
+ else # child
+ puts "hello"
+ puts "from a fork"
+ exit!
+ end
end
end
end
+
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ cmd = "|echo ok"
+ -> {
+ IO.foreach(cmd).to_a
+ }.should complain(/IO process creation with a leading '\|'/)
+ end
end
end
@@ -58,12 +73,12 @@ describe "IO.foreach" do
it "sets $_ to nil" do
$_ = "test"
IO.foreach(@name) { }
- $_.should be_nil
+ $_.should == nil
end
describe "when no block is given" do
it "returns an Enumerator" do
- IO.foreach(@name).should be_an_instance_of(Enumerator)
+ IO.foreach(@name).should.instance_of?(Enumerator)
IO.foreach(@name).to_a.should == IOSpecs.lines
end
diff --git a/spec/ruby/core/io/fsync_spec.rb b/spec/ruby/core/io/fsync_spec.rb
index 6e6123de94..0317cbc805 100644
--- a/spec/ruby/core/io/fsync_spec.rb
+++ b/spec/ruby/core/io/fsync_spec.rb
@@ -12,7 +12,7 @@ describe "IO#fsync" do
end
it "raises an IOError on closed stream" do
- -> { IOSpecs.closed_io.fsync }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.fsync }.should.raise(IOError)
end
it "writes the buffered data to permanent storage" do
diff --git a/spec/ruby/core/io/getbyte_spec.rb b/spec/ruby/core/io/getbyte_spec.rb
index 6ba8f0a3e0..668d81519c 100644
--- a/spec/ruby/core/io/getbyte_spec.rb
+++ b/spec/ruby/core/io/getbyte_spec.rb
@@ -23,7 +23,7 @@ describe "IO#getbyte" do
end
it "raises an IOError on closed stream" do
- -> { IOSpecs.closed_io.getbyte }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.getbyte }.should.raise(IOError)
end
end
@@ -40,3 +40,19 @@ describe "IO#getbyte" do
@io.getbyte.should == nil
end
end
+
+describe "IO#getbyte" do
+ before :each do
+ @name = tmp("io_getbyte.txt")
+ @io = new_io(@name, 'w')
+ end
+
+ after :each do
+ @io.close if @io
+ rm_r @name if @name
+ end
+
+ it "raises an IOError if the stream is not readable" do
+ -> { @io.getbyte }.should.raise(IOError)
+ end
+end
diff --git a/spec/ruby/core/io/getc_spec.rb b/spec/ruby/core/io/getc_spec.rb
index 3949b5cb28..3be86203c0 100644
--- a/spec/ruby/core/io/getc_spec.rb
+++ b/spec/ruby/core/io/getc_spec.rb
@@ -19,11 +19,11 @@ describe "IO#getc" do
it "returns nil when invoked at the end of the stream" do
@io.read
- @io.getc.should be_nil
+ @io.getc.should == nil
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.getc }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.getc }.should.raise(IOError)
end
end
@@ -37,6 +37,6 @@ describe "IO#getc" do
end
it "returns nil on empty stream" do
- @io.getc.should be_nil
+ @io.getc.should == nil
end
end
diff --git a/spec/ruby/core/io/gets_spec.rb b/spec/ruby/core/io/gets_spec.rb
index a3cd180b66..ce3ee73b94 100644
--- a/spec/ruby/core/io/gets_spec.rb
+++ b/spec/ruby/core/io/gets_spec.rb
@@ -24,13 +24,19 @@ describe "IO#gets" do
end
end
+ it "sets $_ to nil after the last line has been read" do
+ while @io.gets
+ end
+ $_.should == nil
+ end
+
it "returns nil if called at the end of the stream" do
IOSpecs.lines.length.times { @io.gets }
@io.gets.should == nil
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.gets }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.gets }.should.raise(IOError)
end
describe "with no separator" do
@@ -38,14 +44,6 @@ describe "IO#gets" do
IOSpecs.lines.each { |line| line.should == @io.gets }
end
- ruby_version_is ''...'2.7' do
- it "returns tainted strings" do
- while line = @io.gets
- line.should.tainted?
- end
- end
- end
-
it "updates lineno with each invocation" do
while @io.gets
@io.lineno.should == @count += 1
@@ -64,14 +62,6 @@ describe "IO#gets" do
@io.gets(nil).should == IOSpecs.lines.join("")
end
- ruby_version_is ''...'2.7' do
- it "returns tainted strings" do
- while line = @io.gets(nil)
- line.should.tainted?
- end
- end
- end
-
it "updates lineno with each invocation" do
while @io.gets(nil)
@io.lineno.should == @count += 1
@@ -100,14 +90,6 @@ describe "IO#gets" do
@io.gets.should == IOSpecs.lines[4]
end
- ruby_version_is ''...'2.7' do
- it "returns tainted strings" do
- while line = @io.gets("")
- line.should.tainted?
- end
- end
- end
-
it "updates lineno with each invocation" do
while @io.gets("")
@io.lineno.should == @count += 1
@@ -126,14 +108,6 @@ describe "IO#gets" do
@io.gets("la linea").should == "Voici la ligne une.\nQui \303\250 la linea"
end
- ruby_version_is ''...'2.7' do
- it "returns tainted strings" do
- while line = @io.gets("la")
- line.should.tainted?
- end
- end
- end
-
it "updates lineno with each invocation" do
while (@io.gets("la"))
@io.lineno.should == @count += 1
@@ -145,12 +119,49 @@ describe "IO#gets" do
$..should == @count += 1
end
end
+
+ describe "that consists of multiple bytes" do
+ platform_is_not :windows do
+ it "should match the separator even if the buffer is filled over successive reads" do
+ IO.pipe do |read, write|
+
+ # Write part of the string with the separator split between two write calls. We want
+ # the read to intertwine such that when the read starts the full data isn't yet
+ # available in the buffer.
+ write.write("Aquí está la línea tres\r\n")
+
+ t = Thread.new do
+ # Continue reading until the separator is encountered or the pipe is closed.
+ read.gets("\r\n\r\n")
+ end
+
+ # Write the other half of the separator, which should cause the `gets` call to now
+ # match. Explicitly close the pipe for good measure so a bug in `gets` doesn't block forever.
+ Thread.pass until t.stop?
+
+ write.write("\r\nelse\r\n\r\n")
+ write.close
+
+ t.value.bytes.should == "Aquí está la línea tres\r\n\r\n".bytes
+ read.read(8).bytes.should == "else\r\n\r\n".bytes
+ end
+ end
+ end
+ end
end
describe "when passed chomp" do
it "returns the first line without a trailing newline character" do
@io.gets(chomp: true).should == IOSpecs.lines_without_newline_characters[0]
end
+
+ it "raises exception when options passed as Hash" do
+ -> { @io.gets({ chomp: true }) }.should.raise(TypeError)
+
+ -> {
+ @io.gets("\n", 1, { chomp: true })
+ }.should.raise(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
+ end
end
end
@@ -164,11 +175,11 @@ describe "IO#gets" do
end
it "raises an IOError if the stream is opened for append only" do
- -> { File.open(@name, "a:utf-8") { |f| f.gets } }.should raise_error(IOError)
+ -> { File.open(@name, "a:utf-8") { |f| f.gets } }.should.raise(IOError)
end
it "raises an IOError if the stream is opened for writing only" do
- -> { File.open(@name, "w:utf-8") { |f| f.gets } }.should raise_error(IOError)
+ -> { File.open(@name, "w:utf-8") { |f| f.gets } }.should.raise(IOError)
end
end
@@ -232,6 +243,16 @@ describe "IO#gets" do
it "reads all bytes when pass a separator and reading more than all bytes" do
@io.gets("\t", 100).should == "one\n\ntwo\n\nthree\nfour\n"
end
+
+ it "returns empty string when 0 passed as a limit" do
+ @io.gets(0).should == ""
+ @io.gets(nil, 0).should == ""
+ @io.gets("", 0).should == ""
+ end
+
+ it "does not accept limit that doesn't fit in a C off_t" do
+ -> { @io.gets(2**128) }.should.raise(RangeError)
+ end
end
describe "IO#gets" do
@@ -317,11 +338,11 @@ describe "IO#gets" do
@io.gets.encoding.should == Encoding::BINARY
end
- it "transcodes to internal encoding if the IO object's external encoding is BINARY" do
+ it "ignores the internal encoding if the IO object's external encoding is BINARY" do
Encoding.default_external = Encoding::BINARY
Encoding.default_internal = Encoding::UTF_8
@io = new_io @name, 'r'
@io.set_encoding Encoding::BINARY, Encoding::UTF_8
- @io.gets.encoding.should == Encoding::UTF_8
+ @io.gets.encoding.should == Encoding::BINARY
end
end
diff --git a/spec/ruby/core/io/initialize_spec.rb b/spec/ruby/core/io/initialize_spec.rb
index c0d84765a8..3425e5ac37 100644
--- a/spec/ruby/core/io/initialize_spec.rb
+++ b/spec/ruby/core/io/initialize_spec.rb
@@ -13,7 +13,7 @@ describe "IO#initialize" do
rm_r @name
end
- it "reassociates the IO instance with the new descriptor when passed a Fixnum" do
+ it "reassociates the IO instance with the new descriptor when passed an Integer" do
fd = new_fd @name, "r:utf-8"
@io.send :initialize, fd, 'r'
@io.fileno.should == fd
@@ -27,23 +27,34 @@ describe "IO#initialize" do
@io.fileno.should == fd
end
+ it "accepts options as keyword arguments" do
+ fd = new_fd @name, "w:utf-8"
+
+ @io.send(:initialize, fd, "w", flags: File::CREAT)
+ @io.fileno.should == fd
+
+ -> {
+ @io.send(:initialize, fd, "w", {flags: File::CREAT})
+ }.should.raise(ArgumentError, "wrong number of arguments (given 3, expected 1..2)")
+ end
+
it "raises a TypeError when passed an IO" do
- -> { @io.send :initialize, STDOUT, 'w' }.should raise_error(TypeError)
+ -> { @io.send :initialize, STDOUT, 'w' }.should.raise(TypeError)
end
it "raises a TypeError when passed nil" do
- -> { @io.send :initialize, nil, 'w' }.should raise_error(TypeError)
+ -> { @io.send :initialize, nil, 'w' }.should.raise(TypeError)
end
it "raises a TypeError when passed a String" do
- -> { @io.send :initialize, "4", 'w' }.should raise_error(TypeError)
+ -> { @io.send :initialize, "4", 'w' }.should.raise(TypeError)
end
it "raises IOError on closed stream" do
- -> { @io.send :initialize, IOSpecs.closed_io.fileno }.should raise_error(IOError)
+ -> { @io.send :initialize, IOSpecs.closed_io.fileno }.should.raise(IOError)
end
it "raises an Errno::EBADF when given an invalid file descriptor" do
- -> { @io.send :initialize, -1, 'w' }.should raise_error(Errno::EBADF)
+ -> { @io.send :initialize, -1, 'w' }.should.raise(Errno::EBADF)
end
end
diff --git a/spec/ruby/core/io/inspect_spec.rb b/spec/ruby/core/io/inspect_spec.rb
index c653c307c4..37dc459f22 100644
--- a/spec/ruby/core/io/inspect_spec.rb
+++ b/spec/ruby/core/io/inspect_spec.rb
@@ -8,13 +8,13 @@ describe "IO#inspect" do
it "contains the file descriptor number" do
@r, @w = IO.pipe
- @r.inspect.should include("fd #{@r.fileno}")
+ @r.inspect.should.include?("fd #{@r.fileno}")
end
it "contains \"(closed)\" if the stream is closed" do
@r, @w = IO.pipe
@r.close
- @r.inspect.should include("(closed)")
+ @r.inspect.should.include?("(closed)")
end
it "reports IO as its Method object's owner" do
diff --git a/spec/ruby/core/io/internal_encoding_spec.rb b/spec/ruby/core/io/internal_encoding_spec.rb
index 10ebf28707..9963a93f33 100644
--- a/spec/ruby/core/io/internal_encoding_spec.rb
+++ b/spec/ruby/core/io/internal_encoding_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe :io_internal_encoding, shared: true do
describe "when Encoding.default_internal is not set" do
@@ -8,25 +9,25 @@ describe :io_internal_encoding, shared: true do
it "returns nil if the internal encoding is not set" do
@io = new_io @name, @object
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
it "returns nil if Encoding.default_internal is changed after the instance is created" do
@io = new_io @name, @object
Encoding.default_internal = Encoding::IBM437
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
it "returns the value set when the instance was created" do
@io = new_io @name, "#{@object}:utf-8:euc-jp"
Encoding.default_internal = Encoding::IBM437
- @io.internal_encoding.should equal(Encoding::EUC_JP)
+ @io.internal_encoding.should.equal?(Encoding::EUC_JP)
end
it "returns the value set by #set_encoding" do
@io = new_io @name, @object
@io.set_encoding(Encoding::US_ASCII, Encoding::IBM437)
- @io.internal_encoding.should equal(Encoding::IBM437)
+ @io.internal_encoding.should.equal?(Encoding::IBM437)
end
end
@@ -38,13 +39,13 @@ describe :io_internal_encoding, shared: true do
it "returns nil" do
@io = new_io @name, @object
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
it "returns nil regardless of Encoding.default_internal changes" do
@io = new_io @name, @object
Encoding.default_internal = Encoding::IBM437
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
end
@@ -56,41 +57,41 @@ describe :io_internal_encoding, shared: true do
it "returns the value of Encoding.default_internal when the instance was created if the internal encoding is not set" do
@io = new_io @name, @object
- @io.internal_encoding.should equal(Encoding::IBM866)
+ @io.internal_encoding.should.equal?(Encoding::IBM866)
end
it "does not change when Encoding.default_internal is changed" do
@io = new_io @name, @object
Encoding.default_internal = Encoding::IBM437
- @io.internal_encoding.should equal(Encoding::IBM866)
+ @io.internal_encoding.should.equal?(Encoding::IBM866)
end
it "returns the internal encoding set when the instance was created" do
@io = new_io @name, "#{@object}:utf-8:euc-jp"
- @io.internal_encoding.should equal(Encoding::EUC_JP)
+ @io.internal_encoding.should.equal?(Encoding::EUC_JP)
end
it "does not change when set and Encoding.default_internal is changed" do
@io = new_io @name, "#{@object}:utf-8:euc-jp"
Encoding.default_internal = Encoding::IBM437
- @io.internal_encoding.should equal(Encoding::EUC_JP)
+ @io.internal_encoding.should.equal?(Encoding::EUC_JP)
end
it "returns the value set by #set_encoding" do
@io = new_io @name, @object
@io.set_encoding(Encoding::US_ASCII, Encoding::IBM437)
- @io.internal_encoding.should equal(Encoding::IBM437)
+ @io.internal_encoding.should.equal?(Encoding::IBM437)
end
it "returns nil when Encoding.default_external is BINARY and the internal encoding is not set" do
Encoding.default_external = Encoding::BINARY
@io = new_io @name, @object
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
it "returns nil when the external encoding is BINARY and the internal encoding is not set" do
@io = new_io @name, "#{@object}:binary"
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
end
end
@@ -112,6 +113,12 @@ describe "IO#internal_encoding" do
Encoding.default_internal = @internal
end
+ it "can be retrieved from a closed stream" do
+ io = IOSpecs.io_fixture("lines.txt", "r")
+ io.close
+ io.internal_encoding.should.equal?(Encoding.default_internal)
+ end
+
describe "with 'r' mode" do
it_behaves_like :io_internal_encoding, nil, "r"
end
diff --git a/spec/ruby/core/io/ioctl_spec.rb b/spec/ruby/core/io/ioctl_spec.rb
index 8dcd9eb2c6..15a5d6ec22 100644
--- a/spec/ruby/core/io/ioctl_spec.rb
+++ b/spec/ruby/core/io/ioctl_spec.rb
@@ -4,7 +4,7 @@ require_relative 'fixtures/classes'
describe "IO#ioctl" do
platform_is_not :windows do
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.ioctl(5, 5) }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.ioctl(5, 5) }.should.raise(IOError)
end
end
@@ -12,10 +12,10 @@ describe "IO#ioctl" do
guard -> { RUBY_PLATFORM.include?("86") } do # x86 / x86_64
it "resizes an empty String to match the output size" do
File.open(__FILE__, 'r') do |f|
- buffer = ''
+ buffer = +''
# FIONREAD in /usr/include/asm-generic/ioctls.h
f.ioctl 0x541B, buffer
- buffer.unpack('I').first.should be_kind_of(Integer)
+ buffer.unpack('I').first.should.is_a?(Integer)
end
end
end
@@ -25,7 +25,7 @@ describe "IO#ioctl" do
-> {
# TIOCGWINSZ in /usr/include/asm-generic/ioctls.h
f.ioctl 0x5413, nil
- }.should raise_error(SystemCallError)
+ }.should.raise(SystemCallError)
end
end
end
diff --git a/spec/ruby/core/io/lineno_spec.rb b/spec/ruby/core/io/lineno_spec.rb
index 3d1b2275cc..93b505652a 100644
--- a/spec/ruby/core/io/lineno_spec.rb
+++ b/spec/ruby/core/io/lineno_spec.rb
@@ -11,7 +11,26 @@ describe "IO#lineno" do
end
it "raises an IOError on a closed stream" do
- -> { IOSpecs.closed_io.lineno }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.lineno }.should.raise(IOError)
+ end
+
+ it "raises an IOError on a write-only stream" do
+ name = tmp("io_lineno.txt")
+ begin
+ File.open(name, 'w') do |f|
+ -> { f.lineno }.should.raise(IOError)
+ end
+ ensure
+ rm_r name
+ end
+ end
+
+ it "raises an IOError on a duplexed stream with the read side closed" do
+ cmd = platform_is(:windows) ? 'rem' : 'cat'
+ IO.popen(cmd, 'r+') do |p|
+ p.close_read
+ -> { p.lineno }.should.raise(IOError)
+ end
end
it "returns the current line number" do
@@ -37,7 +56,26 @@ describe "IO#lineno=" do
end
it "raises an IOError on a closed stream" do
- -> { IOSpecs.closed_io.lineno = 5 }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.lineno = 5 }.should.raise(IOError)
+ end
+
+ it "raises an IOError on a write-only stream" do
+ name = tmp("io_lineno.txt")
+ begin
+ File.open(name, 'w') do |f|
+ -> { f.lineno = 0 }.should.raise(IOError)
+ end
+ ensure
+ rm_r name
+ end
+ end
+
+ it "raises an IOError on a duplexed stream with the read side closed" do
+ cmd = platform_is(:windows) ? 'rem' : 'cat'
+ IO.popen(cmd, 'r+') do |p|
+ p.close_read
+ -> { p.lineno = 0 }.should.raise(IOError)
+ end
end
it "calls #to_int on a non-numeric argument" do
@@ -56,8 +94,13 @@ describe "IO#lineno=" do
@io.lineno.should == 92233
end
- it "raises TypeError on nil argument" do
- -> { @io.lineno = nil }.should raise_error(TypeError)
+ it "raises TypeError if cannot convert argument to Integer implicitly" do
+ -> { @io.lineno = "1" }.should.raise(TypeError, 'no implicit conversion of String into Integer')
+ -> { @io.lineno = nil }.should raise_consistent_error(TypeError, 'no implicit conversion of nil into Integer')
+ end
+
+ it "does not accept Integers that don't fit in a C int" do
+ -> { @io.lineno = 2**32 }.should.raise(RangeError)
end
it "sets the current line number to the given value" do
diff --git a/spec/ruby/core/io/lines_spec.rb b/spec/ruby/core/io/lines_spec.rb
deleted file mode 100644
index a8b8023a2a..0000000000
--- a/spec/ruby/core/io/lines_spec.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- encoding: utf-8 -*-
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-
-describe "IO#lines" do
- before :each do
- @io = IOSpecs.io_fixture "lines.txt"
- end
-
- after :each do
- @io.close if @io
- end
-
- it "returns an Enumerator" do
- @io.lines.should be_an_instance_of(Enumerator)
- end
-
- describe "when no block is given" do
- it "returns an Enumerator" do
- @io.lines.should be_an_instance_of(Enumerator)
- end
-
- describe "returned Enumerator" do
- describe "size" do
- it "should return nil" do
- @io.lines.size.should == nil
- end
- end
- end
- end
-
- it "returns a line when accessed" do
- enum = @io.lines
- enum.first.should == IOSpecs.lines[0]
- end
-
- it "yields each line to the passed block" do
- ScratchPad.record []
- @io.lines { |s| ScratchPad << s }
- ScratchPad.recorded.should == IOSpecs.lines
- end
-end
diff --git a/spec/ruby/core/io/new_spec.rb b/spec/ruby/core/io/new_spec.rb
index 3597098caf..979ac0efcb 100644
--- a/spec/ruby/core/io/new_spec.rb
+++ b/spec/ruby/core/io/new_spec.rb
@@ -1,8 +1,16 @@
require_relative '../../spec_helper'
require_relative 'shared/new'
+# NOTE: should be synchronized with library/stringio/initialize_spec.rb
+
describe "IO.new" do
it_behaves_like :io_new, :new
+
+ it "does not use the given block and warns to use IO::open" do
+ -> {
+ @io = IO.send(@method, @fd) { raise }
+ }.should complain(/warning: IO::new\(\) does not take block; use IO::open\(\) instead/)
+ end
end
describe "IO.new" do
diff --git a/spec/ruby/core/io/nonblock_spec.rb b/spec/ruby/core/io/nonblock_spec.rb
new file mode 100644
index 0000000000..99dc0cafd0
--- /dev/null
+++ b/spec/ruby/core/io/nonblock_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+
+platform_is_not :windows do
+ describe "IO#nonblock?" do
+ before :all do
+ require 'io/nonblock'
+ end
+
+ it "returns false for a file by default" do
+ File.open(__FILE__) do |f|
+ f.nonblock?.should == false
+ end
+ end
+
+ it "returns true for pipe by default" do
+ r, w = IO.pipe
+ begin
+ r.nonblock?.should == true
+ w.nonblock?.should == true
+ ensure
+ r.close
+ w.close
+ end
+ end
+
+ it "returns true for socket by default" do
+ require 'socket'
+ TCPServer.open(0) do |socket|
+ socket.nonblock?.should == true
+ end
+ end
+ end
+
+ describe "IO#nonblock=" do
+ before :all do
+ require 'io/nonblock'
+ end
+
+ it "changes the IO to non-blocking mode" do
+ File.open(__FILE__) do |f|
+ f.nonblock = true
+ f.nonblock?.should == true
+ f.nonblock = false
+ f.nonblock?.should == false
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/io/open_spec.rb b/spec/ruby/core/io/open_spec.rb
index 94df5a5ef6..ff22d14685 100644
--- a/spec/ruby/core/io/open_spec.rb
+++ b/spec/ruby/core/io/open_spec.rb
@@ -32,11 +32,24 @@ describe "IO.open" do
super()
ScratchPad.record :called
end
- io.closed?.should be_false
+ io.closed?.should == false
end
ScratchPad.recorded.should == :called
end
+ it "propagate an exception in the block after calling #close" do
+ -> do
+ IO.open(@fd, "w") do |io|
+ IOSpecs.io_mock(io, :close) do
+ super()
+ ScratchPad.record :called
+ end
+ raise Exception
+ end
+ end.should.raise(Exception)
+ ScratchPad.recorded.should == :called
+ end
+
it "propagates an exception raised by #close that is not a StandardError" do
-> do
IO.open(@fd, "w") do |io|
@@ -46,7 +59,7 @@ describe "IO.open" do
raise Exception
end
end
- end.should raise_error(Exception)
+ end.should.raise(Exception)
ScratchPad.recorded.should == :called
end
@@ -59,11 +72,11 @@ describe "IO.open" do
raise StandardError
end
end
- end.should raise_error(StandardError)
+ end.should.raise(StandardError)
ScratchPad.recorded.should == :called
end
- it "does not propagate a IOError with 'closed stream' message raised by #close" do
+ it "does not propagate an IOError with 'closed stream' message raised by #close" do
IO.open(@fd, "w") do |io|
IOSpecs.io_mock(io, :close) do
super()
@@ -74,7 +87,7 @@ describe "IO.open" do
ScratchPad.recorded.should == :called
end
- it "does not set last error when a IOError with 'closed stream' raised by #close" do
+ it "does not set last error when an IOError with 'closed stream' raised by #close" do
IO.open(@fd, "w") do |io|
IOSpecs.io_mock(io, :close) do
super()
diff --git a/spec/ruby/core/io/output_spec.rb b/spec/ruby/core/io/output_spec.rb
index 2aafb305f4..0decf8c95b 100644
--- a/spec/ruby/core/io/output_spec.rb
+++ b/spec/ruby/core/io/output_spec.rb
@@ -16,7 +16,7 @@ describe "IO#<<" do
it "raises an error if the stream is closed" do
io = IOSpecs.closed_io
- -> { io << "test" }.should raise_error(IOError)
+ -> { io << "test" }.should.raise(IOError)
end
it "returns self" do
diff --git a/spec/ruby/core/io/path_spec.rb b/spec/ruby/core/io/path_spec.rb
new file mode 100644
index 0000000000..798adb2163
--- /dev/null
+++ b/spec/ruby/core/io/path_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+
+describe "IO#path" do
+ it "returns the path of the file associated with the IO object" do
+ path = tmp("io_path.txt")
+ File.open(path, "w") do |file|
+ IO.new(file.fileno, path: file.path, autoclose: false).path.should == file.path
+ end
+ ensure
+ File.unlink(path)
+ end
+end
diff --git a/spec/ruby/core/io/pid_spec.rb b/spec/ruby/core/io/pid_spec.rb
index bc09fe7c3b..04956887ff 100644
--- a/spec/ruby/core/io/pid_spec.rb
+++ b/spec/ruby/core/io/pid_spec.rb
@@ -25,11 +25,11 @@ describe "IO#pid" do
end
it "returns the ID of a process associated with stream" do
- @io.pid.should_not be_nil
+ @io.pid.should_not == nil
end
it "raises an IOError on closed stream" do
@io.close
- -> { @io.pid }.should raise_error(IOError)
+ -> { @io.pid }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/pipe_spec.rb b/spec/ruby/core/io/pipe_spec.rb
index 2f2cf06f4d..9f1b01a5cd 100644
--- a/spec/ruby/core/io/pipe_spec.rb
+++ b/spec/ruby/core/io/pipe_spec.rb
@@ -16,14 +16,25 @@ describe "IO.pipe" do
it "returns two IO objects" do
@r, @w = IO.pipe
- @r.should be_kind_of(IO)
- @w.should be_kind_of(IO)
+ @r.should.is_a?(IO)
+ @w.should.is_a?(IO)
end
it "returns instances of a subclass when called on a subclass" do
@r, @w = IOSpecs::SubIO.pipe
- @r.should be_an_instance_of(IOSpecs::SubIO)
- @w.should be_an_instance_of(IOSpecs::SubIO)
+ @r.should.instance_of?(IOSpecs::SubIO)
+ @w.should.instance_of?(IOSpecs::SubIO)
+ end
+
+ it "does not use IO.new method to create pipes and allows its overriding" do
+ ScratchPad.record []
+
+ # so redefined .new is not called, but original #initialize is
+ @r, @w = IOSpecs::SubIOWithRedefinedNew.pipe
+ ScratchPad.recorded.should == [:call_original_initialize, :call_original_initialize] # called 2 times - for each pipe (r and w)
+
+ @r.should.instance_of?(IOSpecs::SubIOWithRedefinedNew)
+ @w.should.instance_of?(IOSpecs::SubIOWithRedefinedNew)
end
end
@@ -31,8 +42,8 @@ describe "IO.pipe" do
describe "passed a block" do
it "yields two IO objects" do
IO.pipe do |r, w|
- r.should be_kind_of(IO)
- w.should be_kind_of(IO)
+ r.should.is_a?(IO)
+ w.should.is_a?(IO)
end
end
@@ -56,7 +67,7 @@ describe "IO.pipe" do
w = _w
raise RuntimeError
end
- end.should raise_error(RuntimeError)
+ end.should.raise(RuntimeError)
r.should.closed?
w.should.closed?
end
@@ -89,7 +100,7 @@ describe "IO.pipe" do
IO.pipe do |r, w|
r.external_encoding.should == Encoding::ISO_8859_1
- r.internal_encoding.should be_nil
+ r.internal_encoding.should == nil
end
end
@@ -109,14 +120,14 @@ describe "IO.pipe" do
IO.pipe do |r, w|
r.external_encoding.should == Encoding::UTF_8
- r.internal_encoding.should be_nil
+ r.internal_encoding.should == nil
end
end
it "sets the external encoding of the read end when passed an Encoding argument" do
IO.pipe(Encoding::UTF_8) do |r, w|
r.external_encoding.should == Encoding::UTF_8
- r.internal_encoding.should be_nil
+ r.internal_encoding.should == nil
end
end
@@ -130,14 +141,14 @@ describe "IO.pipe" do
it "sets the external encoding of the read end when passed the name of an Encoding" do
IO.pipe("UTF-8") do |r, w|
r.external_encoding.should == Encoding::UTF_8
- r.internal_encoding.should be_nil
+ r.internal_encoding.should == nil
end
end
it "accepts 'bom|' prefix for external encoding" do
IO.pipe("BOM|UTF-8") do |r, w|
r.external_encoding.should == Encoding::UTF_8
- r.internal_encoding.should be_nil
+ r.internal_encoding.should == nil
end
end
@@ -202,13 +213,13 @@ describe "IO.pipe" do
it "sets no external encoding for the write end" do
IO.pipe(Encoding::UTF_8) do |r, w|
- w.external_encoding.should be_nil
+ w.external_encoding.should == nil
end
end
it "sets no internal encoding for the write end" do
IO.pipe(Encoding::UTF_8) do |r, w|
- w.external_encoding.should be_nil
+ w.external_encoding.should == nil
end
end
end
diff --git a/spec/ruby/core/io/popen_spec.rb b/spec/ruby/core/io/popen_spec.rb
index e9d32c5c7d..b5747bf255 100644
--- a/spec/ruby/core/io/popen_spec.rb
+++ b/spec/ruby/core/io/popen_spec.rb
@@ -21,7 +21,7 @@ describe "IO.popen" do
it "returns an open IO" do
@io = IO.popen(ruby_cmd('exit'), "r")
- @io.closed?.should be_false
+ @io.closed?.should == false
end
it "reads a read-only pipe" do
@@ -31,7 +31,7 @@ describe "IO.popen" do
it "raises IOError when writing a read-only pipe" do
@io = IO.popen('echo foo', "r")
- -> { @io.write('bar') }.should raise_error(IOError)
+ -> { @io.write('bar') }.should.raise(IOError)
@io.read.should == "foo\n"
end
@@ -52,7 +52,7 @@ describe "IO.popen" do
it "raises IOError when reading a write-only pipe" do
@io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)'), "w")
- -> { @io.read }.should raise_error(IOError)
+ -> { @io.read }.should.raise(IOError)
end
it "reads and writes a read/write pipe" do
@@ -86,7 +86,7 @@ describe "IO.popen" do
it "returns an instance of a subclass when called on a subclass" do
@io = IOSpecs::SubIO.popen(ruby_cmd('exit'), "r")
- @io.should be_an_instance_of(IOSpecs::SubIO)
+ @io.should.instance_of?(IOSpecs::SubIO)
end
it "coerces mode argument with #to_str" do
@@ -95,27 +95,43 @@ describe "IO.popen" do
@io = IO.popen(ruby_cmd('exit 0'), mode)
end
+ it "accepts a path using the chdir: keyword argument" do
+ path = File.dirname(@fname)
+
+ @io = IO.popen(ruby_cmd("puts Dir.pwd"), "r", chdir: path)
+ @io.read.chomp.should == path
+ end
+
+ it "accepts a path using the chdir: keyword argument and a coercible path" do
+ path = File.dirname(@fname)
+ object = mock("path")
+ object.should_receive(:to_path).and_return(path)
+
+ @io = IO.popen(ruby_cmd("puts Dir.pwd"), "r", chdir: object)
+ @io.read.chomp.should == path
+ end
+
describe "with a block" do
it "yields an open IO to the block" do
IO.popen(ruby_cmd('exit'), "r") do |io|
- io.closed?.should be_false
+ io.closed?.should == false
end
end
it "yields an instance of a subclass when called on a subclass" do
IOSpecs::SubIO.popen(ruby_cmd('exit'), "r") do |io|
- io.should be_an_instance_of(IOSpecs::SubIO)
+ io.should.instance_of?(IOSpecs::SubIO)
end
end
it "closes the IO after yielding" do
io = IO.popen(ruby_cmd('exit'), "r") { |_io| _io }
- io.closed?.should be_true
+ io.closed?.should == true
end
it "allows the IO to be closed inside the block" do
io = IO.popen(ruby_cmd('exit'), 'r') { |_io| _io.close; _io }
- io.closed?.should be_true
+ io.closed?.should == true
end
it "returns the value of the block" do
@@ -153,7 +169,7 @@ describe "IO.popen" do
it "sets the internal encoding to nil if it's the same as the external encoding" do
@io = IO.popen(ruby_cmd('exit'), external_encoding: Encoding::EUC_JP,
internal_encoding: Encoding::EUC_JP)
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
context "with a leading ENV Hash" do
diff --git a/spec/ruby/core/io/pread_spec.rb b/spec/ruby/core/io/pread_spec.rb
index 43071d6a31..cfb8dc4c68 100644
--- a/spec/ruby/core/io/pread_spec.rb
+++ b/spec/ruby/core/io/pread_spec.rb
@@ -1,50 +1,138 @@
# -*- encoding: utf-8 -*-
require_relative '../../spec_helper'
-platform_is_not :windows do
- describe "IO#pread" do
- before :each do
- @fname = tmp("io_pread.txt")
- @contents = "1234567890"
- touch(@fname) { |f| f.write @contents }
- @file = File.open(@fname, "r+")
- end
+describe "IO#pread" do
+ before :each do
+ @fname = tmp("io_pread.txt")
+ @contents = "1234567890"
+ touch(@fname) { |f| f.write @contents }
+ @file = File.open(@fname, "r+")
+ end
- after :each do
- @file.close
- rm_r @fname
- end
+ after :each do
+ @file.close
+ rm_r @fname
+ end
- it "accepts a length, and an offset" do
- @file.pread(4, 0).should == "1234"
- @file.pread(3, 4).should == "567"
- end
+ it "accepts a length, and an offset" do
+ @file.pread(4, 0).should == "1234"
+ @file.pread(3, 4).should == "567"
+ end
- it "accepts a length, an offset, and an output buffer" do
- buffer = "foo"
- @file.pread(3, 4, buffer)
- buffer.should == "567"
- end
+ it "accepts a length, an offset, and an output buffer" do
+ buffer = +"foo"
+ @file.pread(3, 4, buffer).should.equal?(buffer)
+ buffer.should == "567"
+ end
- it "does not advance the file pointer" do
- @file.pread(4, 0).should == "1234"
- @file.read.should == "1234567890"
- end
+ it "shrinks the buffer in case of less bytes read" do
+ buffer = +"foo"
+ @file.pread(1, 0, buffer)
+ buffer.should == "1"
+ end
- it "raises EOFError if end-of-file is reached" do
- -> { @file.pread(1, 10) }.should raise_error(EOFError)
- end
+ it "grows the buffer in case of more bytes read" do
+ buffer = +"foo"
+ @file.pread(5, 0, buffer)
+ buffer.should == "12345"
+ end
- it "raises IOError when file is not open in read mode" do
- File.open(@fname, "w") do |file|
- -> { file.pread(1, 1) }.should raise_error(IOError)
- end
- end
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @file.pread(10, 0, buffer)
+
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "does not advance the file pointer" do
+ @file.pread(4, 0).should == "1234"
+ @file.read.should == "1234567890"
+ end
+
+ it "ignores the current offset" do
+ @file.pos = 3
+ @file.pread(4, 0).should == "1234"
+ end
+
+ it "returns an empty string for maxlen = 0" do
+ @file.pread(0, 4).should == ""
+ end
+
+ it "returns a buffer for maxlen = 0 when buffer specified" do
+ buffer = +"foo"
+ @file.pread(0, 4, buffer).should.equal?(buffer)
+ buffer.should == "foo"
+ end
+
+ it "ignores the offset for maxlen = 0, even if it is out of file bounds" do
+ @file.pread(0, 400).should == ""
+ end
+
+ it "does not reset the buffer when reading with maxlen = 0" do
+ buffer = +"foo"
+ @file.pread(0, 4, buffer)
+ buffer.should == "foo"
+
+ @file.pread(0, 400, buffer)
+ buffer.should == "foo"
+ end
+
+ it "converts maxlen to Integer using #to_int" do
+ maxlen = mock('maxlen')
+ maxlen.should_receive(:to_int).and_return(4)
+ @file.pread(maxlen, 0).should == "1234"
+ end
+
+ it "converts offset to Integer using #to_int" do
+ offset = mock('offset')
+ offset.should_receive(:to_int).and_return(0)
+ @file.pread(4, offset).should == "1234"
+ end
+
+ it "converts a buffer to String using to_str" do
+ buffer = mock('buffer')
+ buffer.should_receive(:to_str).at_least(1).and_return(+"foo")
+ @file.pread(4, 0, buffer)
+ buffer.should_not.is_a?(String)
+ buffer.to_str.should == "1234"
+ end
+
+ it "raises TypeError if maxlen is not an Integer and cannot be coerced into Integer" do
+ maxlen = Object.new
+ -> { @file.pread(maxlen, 0) }.should.raise(TypeError, 'no implicit conversion of Object into Integer')
+ end
- it "raises IOError when file is closed" do
- file = File.open(@fname, "r+")
- file.close
- -> { file.pread(1, 1) }.should raise_error(IOError)
+ it "raises TypeError if offset is not an Integer and cannot be coerced into Integer" do
+ offset = Object.new
+ -> { @file.pread(4, offset) }.should.raise(TypeError, 'no implicit conversion of Object into Integer')
+ end
+
+ it "raises ArgumentError for negative values of maxlen" do
+ -> { @file.pread(-4, 0) }.should.raise(ArgumentError, 'negative string size (or size too big)')
+ end
+
+ it "raised Errno::EINVAL for negative values of offset" do
+ -> { @file.pread(4, -1) }.should.raise(Errno::EINVAL, /Invalid argument/)
+ end
+
+ it "raises TypeError if the buffer is not a String and cannot be coerced into String" do
+ buffer = Object.new
+ -> { @file.pread(4, 0, buffer) }.should.raise(TypeError, 'no implicit conversion of Object into String')
+ end
+
+ it "raises EOFError if end-of-file is reached" do
+ -> { @file.pread(1, 10) }.should.raise(EOFError)
+ end
+
+ it "raises IOError when file is not open in read mode" do
+ File.open(@fname, "w") do |file|
+ -> { file.pread(1, 1) }.should.raise(IOError)
end
end
+
+ it "raises IOError when file is closed" do
+ file = File.open(@fname, "r+")
+ file.close
+ -> { file.pread(1, 1) }.should.raise(IOError)
+ end
end
diff --git a/spec/ruby/core/io/print_spec.rb b/spec/ruby/core/io/print_spec.rb
index 04e971ef6d..065cb3b8cb 100644
--- a/spec/ruby/core/io/print_spec.rb
+++ b/spec/ruby/core/io/print_spec.rb
@@ -3,16 +3,27 @@ require_relative 'fixtures/classes'
describe "IO#print" do
before :each do
- @old_separator = $\
- suppress_warning {$\ = '->'}
+ @old_record_separator = $\
+ @old_field_separator = $,
+ suppress_warning {
+ $\ = '->'
+ $, = '^^'
+ }
@name = tmp("io_print")
end
after :each do
- suppress_warning {$\ = @old_separator}
+ suppress_warning {
+ $\ = @old_record_separator
+ $, = @old_field_separator
+ }
rm_r @name
end
+ it "returns nil" do
+ touch(@name) { |f| f.print.should == nil }
+ end
+
it "writes $_.to_s followed by $\\ (if any) to the stream if no arguments given" do
o = mock('o')
o.should_receive(:to_s).and_return("mockmockmock")
@@ -38,16 +49,18 @@ describe "IO#print" do
IO.read(@name).should == "hello#{$\}"
end
- it "writes each obj.to_s to the stream and appends $\\ (if any) given multiple objects" do
+ it "writes each obj.to_s to the stream separated by $, (if any) and appends $\\ (if any) given multiple objects" do
o, o2 = Object.new, Object.new
def o.to_s(); 'o'; end
def o2.to_s(); 'o2'; end
- touch(@name) { |f| f.print(o, o2) }
- IO.read(@name).should == "#{o.to_s}#{o2.to_s}#{$\}"
+ suppress_warning {
+ touch(@name) { |f| f.print(o, o2) }
+ }
+ IO.read(@name).should == "#{o.to_s}#{$,}#{o2.to_s}#{$\}"
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.print("stuff") }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.print("stuff") }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/printf_spec.rb b/spec/ruby/core/io/printf_spec.rb
index baa00f14ce..d5519bdaa3 100644
--- a/spec/ruby/core/io/printf_spec.rb
+++ b/spec/ruby/core/io/printf_spec.rb
@@ -27,6 +27,6 @@ describe "IO#printf" do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.printf("stuff") }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.printf("stuff") }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/puts_spec.rb b/spec/ruby/core/io/puts_spec.rb
index be220c4035..df526ea56a 100644
--- a/spec/ruby/core/io/puts_spec.rb
+++ b/spec/ruby/core/io/puts_spec.rb
@@ -6,7 +6,7 @@ describe "IO#puts" do
@before_separator = $/
@name = tmp("io_puts.txt")
@io = new_io @name
- ScratchPad.record ""
+ ScratchPad.record(+"")
def @io.write(str)
ScratchPad << str
end
@@ -25,7 +25,7 @@ describe "IO#puts" do
end
it "writes just a newline when given just a newline" do
- -> { $stdout.puts "\n" }.should output_to_fd("\n", STDOUT)
+ -> { $stdout.puts "\n" }.should output_to_fd("\n", $stdout)
end
it "writes empty string with a newline when given nil as an arg" do
@@ -33,7 +33,7 @@ describe "IO#puts" do
ScratchPad.recorded.should == "\n"
end
- it "writes empty string with a newline when when given nil as multiple args" do
+ it "writes empty string with a newline when given nil as multiple args" do
@io.puts(nil, nil).should == nil
ScratchPad.recorded.should == "\n\n"
end
@@ -111,7 +111,7 @@ describe "IO#puts" do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.puts("stuff") }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.puts("stuff") }.should.raise(IOError)
end
it "writes crlf when IO is opened with newline: :crlf" do
diff --git a/spec/ruby/core/io/pwrite_spec.rb b/spec/ruby/core/io/pwrite_spec.rb
index fe29d1e1f6..c318d551bc 100644
--- a/spec/ruby/core/io/pwrite_spec.rb
+++ b/spec/ruby/core/io/pwrite_spec.rb
@@ -1,43 +1,67 @@
# -*- encoding: utf-8 -*-
require_relative '../../spec_helper'
-platform_is_not :windows do
- describe "IO#pwrite" do
- before :each do
- @fname = tmp("io_pwrite.txt")
- @file = File.open(@fname, "w+")
- end
+describe "IO#pwrite" do
+ before :each do
+ @fname = tmp("io_pwrite.txt")
+ @file = File.open(@fname, "w+")
+ end
- after :each do
- @file.close
- rm_r @fname
- end
+ after :each do
+ @file.close
+ rm_r @fname
+ end
- it "returns the number of bytes written" do
- @file.pwrite("foo", 0).should == 3
- end
+ it "returns the number of bytes written" do
+ @file.pwrite("foo", 0).should == 3
+ end
- it "accepts a string and an offset" do
- @file.pwrite("foo", 2)
- @file.pread(3, 2).should == "foo"
- end
+ it "accepts a string and an offset" do
+ @file.pwrite("foo", 2)
+ @file.pread(3, 2).should == "foo"
+ end
- it "does not advance the pointer in the file" do
- @file.pwrite("bar", 3)
- @file.write("foo")
- @file.pread(6, 0).should == "foobar"
- end
+ it "does not advance the pointer in the file" do
+ @file.pwrite("bar", 3)
+ @file.write("foo")
+ @file.pread(6, 0).should == "foobar"
+ end
- it "raises IOError when file is not open in write mode" do
- File.open(@fname, "r") do |file|
- -> { file.pwrite("foo", 1) }.should raise_error(IOError)
- end
- end
+ it "calls #to_s on the object to be written" do
+ object = mock("to_s")
+ object.should_receive(:to_s).and_return("foo")
+ @file.pwrite(object, 0)
+ @file.pread(3, 0).should == "foo"
+ end
- it "raises IOError when file is closed" do
- file = File.open(@fname, "w+")
- file.close
- -> { file.pwrite("foo", 1) }.should raise_error(IOError)
+ it "calls #to_int on the offset" do
+ offset = mock("to_int")
+ offset.should_receive(:to_int).and_return(2)
+ @file.pwrite("foo", offset)
+ @file.pread(3, 2).should == "foo"
+ end
+
+ it "raises IOError when file is not open in write mode" do
+ File.open(@fname, "r") do |file|
+ -> { file.pwrite("foo", 1) }.should.raise(IOError, "not opened for writing")
end
end
+
+ it "raises IOError when file is closed" do
+ file = File.open(@fname, "w+")
+ file.close
+ -> { file.pwrite("foo", 1) }.should.raise(IOError, "closed stream")
+ end
+
+ it "raises a NoMethodError if object does not respond to #to_s" do
+ -> {
+ @file.pwrite(BasicObject.new, 0)
+ }.should.raise(NoMethodError, /undefined method [`']to_s'/)
+ end
+
+ it "raises a TypeError if the offset cannot be converted to an Integer" do
+ -> {
+ @file.pwrite("foo", Object.new)
+ }.should.raise(TypeError, "no implicit conversion of Object into Integer")
+ end
end
diff --git a/spec/ruby/core/io/read_nonblock_spec.rb b/spec/ruby/core/io/read_nonblock_spec.rb
index e50531d336..511cf03263 100644
--- a/spec/ruby/core/io/read_nonblock_spec.rb
+++ b/spec/ruby/core/io/read_nonblock_spec.rb
@@ -12,12 +12,12 @@ describe "IO#read_nonblock" do
end
it "raises an exception extending IO::WaitReadable when there is no data" do
- -> { @read.read_nonblock(5) }.should raise_error(IO::WaitReadable) { |e|
+ -> { @read.read_nonblock(5) }.should.raise(IO::WaitReadable) { |e|
platform_is_not :windows do
- e.should be_kind_of(Errno::EAGAIN)
+ e.should.is_a?(Errno::EAGAIN)
end
platform_is :windows do
- e.should be_kind_of(Errno::EWOULDBLOCK)
+ e.should.is_a?(Errno::EWOULDBLOCK)
end
}
end
@@ -36,7 +36,7 @@ describe "IO#read_nonblock" do
@read.read_nonblock(5)
- @read.read_nonblock(5, exception: false).should be_nil
+ @read.read_nonblock(5, exception: false).should == nil
end
end
end
@@ -55,6 +55,27 @@ describe "IO#read_nonblock" do
@read.read_nonblock(4).should == "hell"
end
+ it "reads after ungetc with data in the buffer" do
+ @write.write("foobar")
+ @read.set_encoding(
+ 'utf-8', universal_newline: false
+ )
+ c = @read.getc
+ @read.ungetc(c)
+ @read.read_nonblock(3).should == "foo"
+ @read.read_nonblock(3).should == "bar"
+ end
+
+ it "raises an exception after ungetc with data in the buffer and character conversion enabled" do
+ @write.write("foobar")
+ @read.set_encoding(
+ 'utf-8', universal_newline: true
+ )
+ c = @read.getc
+ @read.ungetc(c)
+ -> { @read.read_nonblock(3).should == "foo" }.should.raise(IOError)
+ end
+
it "returns less data if that is all that is available" do
@write << "hello"
@read.read_nonblock(10).should == "hello"
@@ -70,22 +91,41 @@ describe "IO#read_nonblock" do
@read.read_nonblock(1).should == "1"
end
+ it "raises ArgumentError when length is less than 0" do
+ -> { @read.read_nonblock(-1) }.should.raise(ArgumentError)
+ end
+
it "reads into the passed buffer" do
- buffer = ""
+ buffer = +""
@write.write("1")
@read.read_nonblock(1, buffer)
buffer.should == "1"
end
it "returns the passed buffer" do
- buffer = ""
+ buffer = +""
@write.write("1")
output = @read.read_nonblock(1, buffer)
- output.should equal(buffer)
+ output.should.equal?(buffer)
+ end
+
+ it "discards the existing buffer content upon successful read" do
+ buffer = +"existing content"
+ @write.write("hello world")
+ @write.close
+ @read.read_nonblock(11, buffer)
+ buffer.should == "hello world"
+ end
+
+ it "discards the existing buffer content upon error" do
+ buffer = +"existing content"
+ @write.close
+ -> { @read.read_nonblock(1, buffer) }.should.raise(EOFError)
+ buffer.should.empty?
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.read_nonblock(5) }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.read_nonblock(5) }.should.raise(IOError)
end
it "raises EOFError when the end is reached" do
@@ -94,6 +134,15 @@ describe "IO#read_nonblock" do
@read.read_nonblock(5)
- -> { @read.read_nonblock(5) }.should raise_error(EOFError)
+ -> { @read.read_nonblock(5) }.should.raise(EOFError)
+ end
+
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @write.write("abc")
+ @write.close
+ @read.read_nonblock(10, buffer)
+
+ buffer.encoding.should == Encoding::ISO_8859_1
end
end
diff --git a/spec/ruby/core/io/read_spec.rb b/spec/ruby/core/io/read_spec.rb
index 841e693f37..dd787c9b60 100644
--- a/spec/ruby/core/io/read_spec.rb
+++ b/spec/ruby/core/io/read_spec.rb
@@ -23,6 +23,15 @@ describe "IO.read" do
IO.read(p)
end
+ # https://bugs.ruby-lang.org/issues/19354
+ it "accepts options as keyword arguments" do
+ IO.read(@fname, 3, 0, mode: "r+").should == @contents[0, 3]
+
+ -> {
+ IO.read(@fname, 3, 0, {mode: "r+"})
+ }.should.raise(ArgumentError, /wrong number of arguments/)
+ end
+
it "accepts an empty options Hash" do
IO.read(@fname, **{}).should == @contents
end
@@ -36,11 +45,11 @@ describe "IO.read" do
end
it "raises an IOError if the options Hash specifies write mode" do
- -> { IO.read(@fname, 3, 0, mode: "w") }.should raise_error(IOError)
+ -> { IO.read(@fname, 3, 0, mode: "w") }.should.raise(IOError)
end
it "raises an IOError if the options Hash specifies append only mode" do
- -> { IO.read(@fname, mode: "a") }.should raise_error(IOError)
+ -> { IO.read(@fname, mode: "a") }.should.raise(IOError)
end
it "reads the file if the options Hash includes read mode" do
@@ -55,6 +64,24 @@ describe "IO.read" do
IO.read(@fname, mode: "a+").should == @contents
end
+ platform_is_not :windows do
+ end
+
+ it "disregards other options if :open_args is given" do
+ string = IO.read(@fname,mode: "w", encoding: Encoding::UTF_32LE, open_args: ["r", encoding: Encoding::UTF_8])
+ string.encoding.should == Encoding::UTF_8
+ end
+
+ it "doesn't require mode to be specified in :open_args" do
+ string = IO.read(@fname, nil, 0, open_args: [{encoding: Encoding::US_ASCII}])
+ string.encoding.should == Encoding::US_ASCII
+ end
+
+ it "doesn't require mode to be specified in :open_args even if flags option passed" do
+ string = IO.read(@fname, nil, 0, open_args: [{encoding: Encoding::US_ASCII, flags: File::CREAT}])
+ string.encoding.should == Encoding::US_ASCII
+ end
+
it "treats second nil argument as no length limit" do
IO.read(@fname, nil).should == @contents
IO.read(@fname, nil, 5).should == IO.read(@fname, @contents.length, 5)
@@ -77,22 +104,31 @@ describe "IO.read" do
IO.read(@fname, 1, 10).should == nil
end
+ it "returns an empty string when reading zero bytes" do
+ IO.read(@fname, 0).should == ''
+ end
+
+ it "returns a String in BINARY when passed a size" do
+ IO.read(@fname, 1).encoding.should == Encoding::BINARY
+ IO.read(@fname, 0).encoding.should == Encoding::BINARY
+ end
+
it "raises an Errno::ENOENT when the requested file does not exist" do
rm_r @fname
- -> { IO.read @fname }.should raise_error(Errno::ENOENT)
+ -> { IO.read @fname }.should.raise(Errno::ENOENT)
end
it "raises a TypeError when not passed a String type" do
- -> { IO.read nil }.should raise_error(TypeError)
+ -> { IO.read nil }.should.raise(TypeError)
end
it "raises an ArgumentError when not passed a valid length" do
- -> { IO.read @fname, -1 }.should raise_error(ArgumentError)
+ -> { IO.read @fname, -1 }.should.raise(ArgumentError)
end
- it "raises an Errno::EINVAL when not passed a valid offset" do
- -> { IO.read @fname, 0, -1 }.should raise_error(Errno::EINVAL)
- -> { IO.read @fname, -1, -1 }.should raise_error(Errno::EINVAL)
+ it "raises an ArgumentError when not passed a valid offset" do
+ -> { IO.read @fname, 0, -1 }.should.raise(ArgumentError)
+ -> { IO.read @fname, -1, -1 }.should.raise(ArgumentError)
end
it "uses the external encoding specified via the :external_encoding option" do
@@ -104,57 +140,89 @@ describe "IO.read" do
str = IO.read(@fname, encoding: Encoding::ISO_8859_1)
str.encoding.should == Encoding::ISO_8859_1
end
-end
-describe "IO.read from a pipe" do
- it "runs the rest as a subprocess and returns the standard output" do
- cmd = "|sh -c 'echo hello'"
- platform_is :windows do
- cmd = "|cmd.exe /C echo hello"
+ platform_is :windows do
+ it "reads the file in text mode" do
+ # 0x1A is CTRL+Z and is EOF in Windows text mode.
+ File.binwrite(@fname, "\x1Abbb")
+ IO.read(@fname).should.empty?
end
- IO.read(cmd).should == "hello\n"
end
+end
- platform_is_not :windows do
- it "opens a pipe to a fork if the rest is -" do
- str = IO.read("|-")
- if str # parent
- str.should == "hello from child\n"
- else #child
- puts "hello from child"
- exit!
+ruby_version_is ""..."4.0" do
+ describe "IO.read from a pipe" do
+ it "runs the rest as a subprocess and returns the standard output" do
+ cmd = "|sh -c 'echo hello'"
+ platform_is :windows do
+ cmd = "|cmd.exe /C echo hello"
+ end
+
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.read(cmd).should == "hello\n"
end
end
- end
- it "reads only the specified number of bytes requested" do
- cmd = "|sh -c 'echo hello'"
- platform_is :windows do
- cmd = "|cmd.exe /C echo hello"
+ platform_is_not :windows do
+ it "opens a pipe to a fork if the rest is -" do
+ str = nil
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ str = IO.read("|-")
+ end
+
+ if str # parent
+ str.should == "hello from child\n"
+ else #child
+ puts "hello from child"
+ exit!
+ end
+ end
end
- IO.read(cmd, 1).should == "h"
- end
- platform_is_not :windows do
- it "raises Errno::ESPIPE if passed an offset" do
- -> {
- IO.read("|sh -c 'echo hello'", 1, 1)
- }.should raise_error(Errno::ESPIPE)
+ it "reads only the specified number of bytes requested" do
+ cmd = "|sh -c 'echo hello'"
+ platform_is :windows do
+ cmd = "|cmd.exe /C echo hello"
+ end
+
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.read(cmd, 1).should == "h"
+ end
end
- end
-quarantine! do # The process tried to write to a nonexistent pipe.
- platform_is :windows do
- # TODO: It should raise Errno::ESPIPE on Windows as well
- # once https://bugs.ruby-lang.org/issues/12230 is fixed.
- it "raises Errno::EINVAL if passed an offset" do
+ platform_is_not :windows do
+ it "raises Errno::ESPIPE if passed an offset" do
+ -> {
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.read("|sh -c 'echo hello'", 1, 1)
+ end
+ }.should.raise(Errno::ESPIPE)
+ end
+ end
+
+ quarantine! do # The process tried to write to a nonexistent pipe.
+ platform_is :windows do
+ # TODO: It should raise Errno::ESPIPE on Windows as well
+ # once https://bugs.ruby-lang.org/issues/12230 is fixed.
+ it "raises Errno::EINVAL if passed an offset" do
+ -> {
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ IO.read("|cmd.exe /C echo hello", 1, 1)
+ end
+ }.should.raise(Errno::EINVAL)
+ end
+ end
+ end
+
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation" do
+ cmd = "|echo ok"
-> {
- IO.read("|cmd.exe /C echo hello", 1, 1)
- }.should raise_error(Errno::EINVAL)
+ IO.read(cmd)
+ }.should complain(/IO process creation with a leading '\|'/)
end
end
end
-end
describe "IO.read on an empty file" do
before :each do
@@ -197,21 +265,53 @@ describe "IO#read" do
@io.read(4).should == '7890'
end
+ it "treats first nil argument as no length limit" do
+ @io.read(nil).should == @contents
+ end
+
+ it "raises an ArgumentError when not passed a valid length" do
+ -> { @io.read(-1) }.should.raise(ArgumentError)
+ end
+
it "clears the output buffer if there is nothing to read" do
@io.pos = 10
- buf = 'non-empty string'
+ buf = +'non-empty string'
@io.read(10, buf).should == nil
buf.should == ''
+
+ buf = +'non-empty string'
+
+ @io.read(nil, buf).should == ""
+
+ buf.should == ''
+
+ buf = +'non-empty string'
+
+ @io.read(0, buf).should == ""
+
+ buf.should == ''
+ end
+
+ it "raise FrozenError if the output buffer is frozen" do
+ @io.read
+ -> { @io.read(0, 'frozen-string'.freeze) }.should.raise(FrozenError)
+ -> { @io.read(1, 'frozen-string'.freeze) }.should.raise(FrozenError)
+ -> { @io.read(nil, 'frozen-string'.freeze) }.should.raise(FrozenError)
+ end
+
+ it "raise FrozenError if the output buffer is frozen (2)" do
+ @io.read
+ -> { @io.read(1, ''.freeze) }.should.raise(FrozenError)
end
it "consumes zero bytes when reading zero bytes" do
@io.read(0).should == ''
@io.pos.should == 0
- @io.getc.chr.should == '1'
+ @io.getc.should == '1'
end
it "is at end-of-file when everything has been read" do
@@ -224,50 +324,72 @@ describe "IO#read" do
end
it "places the specified number of bytes in the buffer" do
- buf = ""
+ buf = +""
@io.read 5, buf
buf.should == "12345"
end
it "expands the buffer when too small" do
- buf = "ABCDE"
+ buf = +"ABCDE"
@io.read nil, buf
buf.should == @contents
end
it "overwrites the buffer" do
- buf = "ABCDEFGHIJ"
+ buf = +"ABCDEFGHIJ"
@io.read nil, buf
buf.should == @contents
end
it "truncates the buffer when too big" do
- buf = "ABCDEFGHIJKLMNO"
+ buf = +"ABCDEFGHIJKLMNO"
@io.read nil, buf
buf.should == @contents
@io.rewind
- buf = "ABCDEFGHIJKLMNO"
+ buf = +"ABCDEFGHIJKLMNO"
@io.read 5, buf
buf.should == @contents[0..4]
end
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @io.read(10, buffer)
+
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
+
+ # https://bugs.ruby-lang.org/issues/20416
+ it "does not preserve the encoding of the given buffer when max length is not provided" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @io.read(nil, buffer)
+
+ buffer.encoding.should_not == Encoding::ISO_8859_1
+ end
+
it "returns the given buffer" do
- buf = ""
+ buf = +""
- @io.read(nil, buf).should equal buf
+ @io.read(nil, buf).should.equal? buf
+ end
+
+ it "returns the given buffer when there is nothing to read" do
+ buf = +""
+
+ @io.read
+ @io.read(nil, buf).should.equal? buf
end
it "coerces the second argument to string and uses it as a buffer" do
- buf = "ABCDE"
+ buf = +"ABCDE"
obj = mock("buff")
obj.should_receive(:to_str).any_number_of_times.and_return(buf)
- @io.read(15, obj).should_not equal obj
+ @io.read(15, obj).should_not.equal? obj
buf.should == @contents
end
@@ -301,9 +423,12 @@ describe "IO#read" do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.read }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.read }.should.raise(IOError)
end
+ it "raises ArgumentError when length is less than 0" do
+ -> { @io.read(-1) }.should.raise(ArgumentError)
+ end
platform_is_not :windows do
it "raises IOError when stream is closed by another thread" do
@@ -319,7 +444,7 @@ describe "IO#read" do
Thread.pass until t.stop?
r.close
t.join
- t.value.should be_kind_of(IOError)
+ t.value.should.is_a?(IOError)
w.close
end
end
@@ -384,13 +509,6 @@ describe "IO#read in binary mode" do
xE2 = [226].pack('C*')
result.should == ("abc" + xE2 + "def").force_encoding(Encoding::BINARY)
end
-
- it "does not transcode file contents when an internal encoding is specified" do
- result = File.open(@name, "r:binary:utf-8") { |f| f.read }.chomp
- result.encoding.should == Encoding::BINARY
- xE2 = [226].pack('C*')
- result.should == ("abc" + xE2 + "def").force_encoding(Encoding::BINARY)
- end
end
describe "IO#read in text mode" do
@@ -460,20 +578,20 @@ describe :io_read_internal_encoding, shared: true do
end
it "sets the String encoding to the internal encoding" do
- @io.read.encoding.should equal(Encoding::UTF_8)
+ @io.read.encoding.should.equal?(Encoding::UTF_8)
end
describe "when passed nil for limit" do
it "sets the buffer to a transcoded String" do
- result = @io.read(nil, buf = "")
- buf.should equal(result)
+ result = @io.read(nil, buf = +"")
+ buf.should.equal?(result)
buf.should == "ありがとう\n"
end
it "sets the buffer's encoding to the internal encoding" do
- buf = "".force_encoding Encoding::ISO_8859_1
+ buf = "".dup.force_encoding Encoding::ISO_8859_1
@io.read(nil, buf)
- buf.encoding.should equal(Encoding::UTF_8)
+ buf.encoding.should.equal?(Encoding::UTF_8)
end
end
end
@@ -484,23 +602,24 @@ describe :io_read_size_internal_encoding, shared: true do
end
it "returns a String in BINARY when passed a size" do
- @io.read(4).encoding.should equal(Encoding::BINARY)
+ @io.read(4).encoding.should.equal?(Encoding::BINARY)
+ @io.read(0).encoding.should.equal?(Encoding::BINARY)
end
it "does not change the buffer's encoding when passed a limit" do
- buf = "".force_encoding Encoding::ISO_8859_1
+ buf = "".dup.force_encoding Encoding::ISO_8859_1
@io.read(4, buf)
buf.should == [164, 162, 164, 234].pack('C*').force_encoding(Encoding::ISO_8859_1)
- buf.encoding.should equal(Encoding::ISO_8859_1)
+ buf.encoding.should.equal?(Encoding::ISO_8859_1)
end
it "truncates the buffer but does not change the buffer's encoding when no data remains" do
- buf = "abc".force_encoding Encoding::ISO_8859_1
+ buf = "abc".dup.force_encoding Encoding::ISO_8859_1
@io.read
- @io.read(1, buf).should be_nil
+ @io.read(1, buf).should == nil
buf.size.should == 0
- buf.encoding.should equal(Encoding::ISO_8859_1)
+ buf.encoding.should.equal?(Encoding::ISO_8859_1)
end
end
@@ -518,7 +637,7 @@ describe "IO#read" do
end
it "sets the String encoding to Encoding.default_external" do
- @io.read.encoding.should equal(Encoding.default_external)
+ @io.read.encoding.should.equal?(Encoding.default_external)
end
end
@@ -537,7 +656,7 @@ describe "IO#read" do
end
it "sets the String encoding to the external encoding" do
- @io.read.encoding.should equal(Encoding::EUC_JP)
+ @io.read.encoding.should.equal?(Encoding::EUC_JP)
end
it_behaves_like :io_read_size_internal_encoding, nil
diff --git a/spec/ruby/core/io/readbyte_spec.rb b/spec/ruby/core/io/readbyte_spec.rb
index 14426c28ac..07da1da919 100644
--- a/spec/ruby/core/io/readbyte_spec.rb
+++ b/spec/ruby/core/io/readbyte_spec.rb
@@ -19,6 +19,6 @@ describe "IO#readbyte" do
@io.seek(999999)
-> do
@io.readbyte
- end.should raise_error EOFError
+ end.should.raise EOFError
end
end
diff --git a/spec/ruby/core/io/readchar_spec.rb b/spec/ruby/core/io/readchar_spec.rb
index b5f762a846..29d14880ff 100644
--- a/spec/ruby/core/io/readchar_spec.rb
+++ b/spec/ruby/core/io/readchar_spec.rb
@@ -1,6 +1,16 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+describe :io_readchar_internal_encoding, shared: true do
+ it "returns a transcoded String" do
+ @io.readchar.should == "あ"
+ end
+
+ it "sets the String encoding to the internal encoding" do
+ @io.readchar.encoding.should.equal?(Encoding::UTF_8)
+ end
+end
+
describe "IO#readchar" do
before :each do
@io = IOSpecs.io_fixture "lines.txt"
@@ -21,11 +31,67 @@ describe "IO#readchar" do
it "raises an EOFError when invoked at the end of the stream" do
@io.read
- -> { @io.readchar }.should raise_error(EOFError)
+ -> { @io.readchar }.should.raise(EOFError)
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.readchar }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.readchar }.should.raise(IOError)
+ end
+end
+
+describe "IO#readchar with internal encoding" do
+ after :each do
+ @io.close if @io
+ end
+
+ describe "not specified" do
+ before :each do
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", "r:euc-jp"
+ end
+
+ it "does not transcode the String" do
+ @io.readchar.should == ("あ").encode(Encoding::EUC_JP)
+ end
+
+ it "sets the String encoding to the external encoding" do
+ @io.readchar.encoding.should.equal?(Encoding::EUC_JP)
+ end
+ end
+
+ describe "specified by open mode" do
+ before :each do
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", "r:euc-jp:utf-8"
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+
+ describe "specified by mode: option" do
+ before :each do
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", mode: "r:euc-jp:utf-8"
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+
+ describe "specified by internal_encoding: option" do
+ before :each do
+ options = { mode: "r",
+ internal_encoding: "utf-8",
+ external_encoding: "euc-jp" }
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", options
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
+ end
+
+ describe "specified by encoding: option" do
+ before :each do
+ options = { mode: "r", encoding: "euc-jp:utf-8" }
+ @io = IOSpecs.io_fixture "read_euc_jp.txt", options
+ end
+
+ it_behaves_like :io_readchar_internal_encoding, nil
end
end
@@ -39,6 +105,6 @@ describe "IO#readchar" do
end
it "raises EOFError on empty stream" do
- -> { @io.readchar }.should raise_error(EOFError)
+ -> { @io.readchar }.should.raise(EOFError)
end
end
diff --git a/spec/ruby/core/io/readline_spec.rb b/spec/ruby/core/io/readline_spec.rb
index 7cb1601816..009687710a 100644
--- a/spec/ruby/core/io/readline_spec.rb
+++ b/spec/ruby/core/io/readline_spec.rb
@@ -29,11 +29,11 @@ describe "IO#readline" do
it "raises EOFError on end of stream" do
IOSpecs.lines.length.times { @io.readline }
- -> { @io.readline }.should raise_error(EOFError)
+ -> { @io.readline }.should.raise(EOFError)
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.readline }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.readline }.should.raise(IOError)
end
it "assigns the returned line to $_" do
@@ -43,9 +43,42 @@ describe "IO#readline" do
end
end
+ describe "when passed limit" do
+ it "reads limit bytes" do
+ @io.readline(3).should == "Voi"
+ end
+
+ it "returns an empty string when passed 0 as a limit" do
+ @io.readline(0).should == ""
+ end
+
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { @io.readline(2**128) }.should.raise(RangeError)
+ end
+ end
+
+ describe "when passed separator and limit" do
+ it "reads limit bytes till the separator" do
+ # Voici la ligne une.\
+ @io.readline(" ", 4).should == "Voic"
+ @io.readline(" ", 4).should == "i "
+ @io.readline(" ", 4).should == "la "
+ @io.readline(" ", 4).should == "lign"
+ @io.readline(" ", 4).should == "e "
+ end
+ end
+
describe "when passed chomp" do
it "returns the first line without a trailing newline character" do
@io.readline(chomp: true).should == IOSpecs.lines_without_newline_characters[0]
end
+
+ it "raises exception when options passed as Hash" do
+ -> { @io.readline({ chomp: true }) }.should.raise(TypeError)
+
+ -> {
+ @io.readline("\n", 1, { chomp: true })
+ }.should.raise(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
+ end
end
end
diff --git a/spec/ruby/core/io/readlines_spec.rb b/spec/ruby/core/io/readlines_spec.rb
index eb99eb8d3a..640e253200 100644
--- a/spec/ruby/core/io/readlines_spec.rb
+++ b/spec/ruby/core/io/readlines_spec.rb
@@ -17,7 +17,7 @@ describe "IO#readlines" do
it "raises an IOError if the stream is closed" do
@io.close
- -> { @io.readlines }.should raise_error(IOError)
+ -> { @io.readlines }.should.raise(IOError)
end
describe "when passed no arguments" do
@@ -37,12 +37,12 @@ describe "IO#readlines" do
describe "when passed no arguments" do
it "updates self's position" do
@io.readlines
- @io.pos.should eql(137)
+ @io.pos.should.eql?(137)
end
it "updates self's lineno based on the number of lines read" do
@io.readlines
- @io.lineno.should eql(9)
+ @io.lineno.should.eql?(9)
end
it "does not change $_" do
@@ -81,12 +81,12 @@ describe "IO#readlines" do
it "updates self's lineno based on the number of lines read" do
@io.readlines("r")
- @io.lineno.should eql(5)
+ @io.lineno.should.eql?(5)
end
it "updates self's position based on the number of characters read" do
@io.readlines("r")
- @io.pos.should eql(137)
+ @io.pos.should.eql?(137)
end
it "does not change $_" do
@@ -102,28 +102,33 @@ describe "IO#readlines" do
end
end
- describe "when passed a string that starts with a |" do
- it "gets data from the standard out of the subprocess" do
- cmd = "|sh -c 'echo hello;echo line2'"
- platform_is :windows do
- cmd = "|cmd.exe /C echo hello&echo line2"
- end
- lines = IO.readlines(cmd)
- lines.should == ["hello\n", "line2\n"]
+ describe "when passed limit" do
+ it "raises ArgumentError when passed 0 as a limit" do
+ -> { @io.readlines(0) }.should.raise(ArgumentError)
end
- platform_is_not :windows do
- it "gets data from a fork when passed -" do
- lines = IO.readlines("|-")
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { @io.readlines(2**128) }.should.raise(RangeError)
+ end
+ end
- if lines # parent
- lines.should == ["hello\n", "from a fork\n"]
- else
- puts "hello"
- puts "from a fork"
- exit!
- end
- end
+ describe "when passed chomp" do
+ it "returns the first line without a trailing newline character" do
+ @io.readlines(chomp: true).should == IOSpecs.lines_without_newline_characters
+ end
+
+ it "raises exception when options passed as Hash" do
+ -> { @io.readlines({ chomp: true }) }.should.raise(TypeError)
+
+ -> {
+ @io.readlines("\n", 1, { chomp: true })
+ }.should.raise(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
+ end
+ end
+
+ describe "when passed arbitrary keyword argument" do
+ it "tolerates it" do
+ @io.readlines(chomp: true, foo: :bar).should == IOSpecs.lines_without_newline_characters
end
end
end
@@ -140,13 +145,13 @@ describe "IO#readlines" do
it "raises an IOError if the stream is opened for append only" do
-> do
File.open(@name, "a:utf-8") { |f| f.readlines }
- end.should raise_error(IOError)
+ end.should.raise(IOError)
end
it "raises an IOError if the stream is opened for write only" do
-> do
File.open(@name, "w:utf-8") { |f| f.readlines }
- end.should raise_error(IOError)
+ end.should.raise(IOError)
end
end
@@ -169,6 +174,48 @@ describe "IO.readlines" do
$_.should == "test"
end
+ ruby_version_is ""..."4.0" do
+ describe "when passed a string that starts with a |" do
+ it "gets data from the standard out of the subprocess" do
+ cmd = "|sh -c 'echo hello;echo line2'"
+ platform_is :windows do
+ cmd = "|cmd.exe /C echo hello&echo line2"
+ end
+
+ lines = nil
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ lines = IO.readlines(cmd)
+ end
+ lines.should == ["hello\n", "line2\n"]
+ end
+
+ platform_is_not :windows do
+ it "gets data from a fork when passed -" do
+ lines = nil
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ lines = IO.readlines("|-")
+ end
+
+ if lines # parent
+ lines.should == ["hello\n", "from a fork\n"]
+ else
+ puts "hello"
+ puts "from a fork"
+ exit!
+ end
+ end
+ end
+ end
+
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ cmd = "|echo ok"
+ -> {
+ IO.readlines(cmd)
+ }.should complain(/IO process creation with a leading '\|'/)
+ end
+ end
+
it_behaves_like :io_readlines, :readlines
it_behaves_like :io_readlines_options_19, :readlines
end
@@ -190,7 +237,7 @@ describe "IO.readlines" do
it "encodes lines using the default external encoding" do
Encoding.default_external = Encoding::UTF_8
lines = IO.readlines(@name)
- lines.all? { |s| s.encoding == Encoding::UTF_8 }.should be_true
+ lines.all? { |s| s.encoding == Encoding::UTF_8 }.should == true
end
it "encodes lines using the default internal encoding, when set" do
@@ -198,13 +245,13 @@ describe "IO.readlines" do
Encoding.default_internal = Encoding::UTF_16
suppress_warning {$/ = $/.encode Encoding::UTF_16}
lines = IO.readlines(@name)
- lines.all? { |s| s.encoding == Encoding::UTF_16 }.should be_true
+ lines.all? { |s| s.encoding == Encoding::UTF_16 }.should == true
end
it "ignores the default internal encoding if the external encoding is BINARY" do
Encoding.default_external = Encoding::BINARY
Encoding.default_internal = Encoding::UTF_8
lines = IO.readlines(@name)
- lines.all? { |s| s.encoding == Encoding::BINARY }.should be_true
+ lines.all? { |s| s.encoding == Encoding::BINARY }.should == true
end
end
diff --git a/spec/ruby/core/io/readpartial_spec.rb b/spec/ruby/core/io/readpartial_spec.rb
index 2b33a0d5b1..d3f5545c8f 100644
--- a/spec/ruby/core/io/readpartial_spec.rb
+++ b/spec/ruby/core/io/readpartial_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
@@ -15,10 +15,10 @@ describe "IO#readpartial" do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.readpartial(10) }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.readpartial(10) }.should.raise(IOError)
@rd.close
- -> { @rd.readpartial(10) }.should raise_error(IOError)
+ -> { @rd.readpartial(10) }.should.raise(IOError)
end
it "reads at most the specified number of bytes" do
@@ -59,10 +59,10 @@ describe "IO#readpartial" do
end
it "discards the existing buffer content upon successful read" do
- buffer = "existing"
+ buffer = +"existing content"
@wr.write("hello world")
@wr.close
- @rd.readpartial(11, buffer)
+ @rd.readpartial(11, buffer).should.equal?(buffer)
buffer.should == "hello world"
end
@@ -70,27 +70,46 @@ describe "IO#readpartial" do
@wr.write("abc")
@wr.close
@rd.readpartial(10).should == 'abc'
- -> { @rd.readpartial(10) }.should raise_error(EOFError)
+ -> { @rd.readpartial(10) }.should.raise(EOFError)
end
it "discards the existing buffer content upon error" do
- buffer = 'hello'
+ buffer = +'hello'
@wr.close
- -> { @rd.readpartial(1, buffer) }.should raise_error(EOFError)
- buffer.should be_empty
+ -> { @rd.readpartial(1, buffer) }.should.raise(EOFError)
+ buffer.should.empty?
end
it "raises IOError if the stream is closed" do
@wr.close
- -> { @rd.readpartial(1) }.should raise_error(IOError)
+ -> { @rd.readpartial(1) }.should.raise(IOError)
end
it "raises ArgumentError if the negative argument is provided" do
- -> { @rd.readpartial(-1) }.should raise_error(ArgumentError)
+ -> { @rd.readpartial(-1) }.should.raise(ArgumentError)
end
it "immediately returns an empty string if the length argument is 0" do
@rd.readpartial(0).should == ""
end
+ it "raises IOError if the stream is closed and the length argument is 0" do
+ @rd.close
+ -> { @rd.readpartial(0) }.should.raise(IOError, "closed stream")
+ end
+
+ it "clears and returns the given buffer if the length argument is 0" do
+ buffer = +"existing content"
+ @rd.readpartial(0, buffer).should == buffer
+ buffer.should == ""
+ end
+
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @wr.write("abc")
+ @wr.close
+ @rd.readpartial(10, buffer)
+
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
end
diff --git a/spec/ruby/core/io/reopen_spec.rb b/spec/ruby/core/io/reopen_spec.rb
index 8ff0f217f4..3b972d8978 100644
--- a/spec/ruby/core/io/reopen_spec.rb
+++ b/spec/ruby/core/io/reopen_spec.rb
@@ -27,35 +27,35 @@ describe "IO#reopen" do
it "changes the class of the instance to the class of the object returned by #to_io" do
obj = mock("io")
obj.should_receive(:to_io).and_return(@other_io)
- @io.reopen(obj).should be_an_instance_of(File)
+ @io.reopen(obj).should.instance_of?(File)
end
it "raises an IOError if the object returned by #to_io is closed" do
obj = mock("io")
obj.should_receive(:to_io).and_return(IOSpecs.closed_io)
- -> { @io.reopen obj }.should raise_error(IOError)
+ -> { @io.reopen obj }.should.raise(IOError)
end
it "raises a TypeError if #to_io does not return an IO instance" do
obj = mock("io")
obj.should_receive(:to_io).and_return("something else")
- -> { @io.reopen obj }.should raise_error(TypeError)
+ -> { @io.reopen obj }.should.raise(TypeError)
end
it "raises an IOError when called on a closed stream with an object" do
@io.close
obj = mock("io")
obj.should_not_receive(:to_io)
- -> { @io.reopen(STDOUT) }.should raise_error(IOError)
+ -> { @io.reopen(STDOUT) }.should.raise(IOError)
end
it "raises an IOError if the IO argument is closed" do
- -> { @io.reopen(IOSpecs.closed_io) }.should raise_error(IOError)
+ -> { @io.reopen(IOSpecs.closed_io) }.should.raise(IOError)
end
it "raises an IOError when called on a closed stream with an IO" do
@io.close
- -> { @io.reopen(STDOUT) }.should raise_error(IOError)
+ -> { @io.reopen(STDOUT) }.should.raise(IOError)
end
end
@@ -77,12 +77,12 @@ describe "IO#reopen with a String" do
it "does not raise an exception when called on a closed stream with a path" do
@io.close
@io.reopen @name, "r"
- @io.closed?.should be_false
+ @io.closed?.should == false
@io.gets.should == "Line 1: One\n"
end
it "returns self" do
- @io.reopen(@name).should equal(@io)
+ @io.reopen(@name).should.equal?(@io)
end
it "positions a newly created instance at the beginning of the new stream" do
@@ -188,7 +188,7 @@ describe "IO#reopen with a String" do
it "raises an Errno::ENOENT if the file does not exist and the IO is not opened in write mode" do
@io = new_io @name, "r"
- -> { @io.reopen(@other_name) }.should raise_error(Errno::ENOENT)
+ -> { @io.reopen(@other_name) }.should.raise(Errno::ENOENT)
end
end
@@ -214,9 +214,9 @@ describe "IO#reopen with an IO at EOF" do
end
it "resets the EOF status to false" do
- @io.eof?.should be_true
+ @io.eof?.should == true
@io.reopen @other_io
- @io.eof?.should be_false
+ @io.eof?.should == false
end
end
@@ -244,7 +244,7 @@ describe "IO#reopen with an IO" do
# MRI actually changes the class of @io in the call to #reopen
# but does not preserve the existing singleton class of @io.
def @io.to_io; flunk; end
- @io.reopen(@other_io).should be_an_instance_of(IO)
+ @io.reopen(@other_io).should.instance_of?(IO)
end
it "does not change the object_id" do
@@ -303,7 +303,7 @@ describe "IO#reopen with an IO" do
it "may change the class of the instance" do
@io.reopen @other_io
- @io.should be_an_instance_of(File)
+ @io.should.instance_of?(File)
end
it "sets path equals to the other IO's path if other IO is File" do
diff --git a/spec/ruby/core/io/rewind_spec.rb b/spec/ruby/core/io/rewind_spec.rb
index 649041afaf..43834ef307 100644
--- a/spec/ruby/core/io/rewind_spec.rb
+++ b/spec/ruby/core/io/rewind_spec.rb
@@ -18,6 +18,17 @@ describe "IO#rewind" do
@io.readline.should == "Voici la ligne une.\n"
end
+ it "positions the instance to the beginning of output for write-only IO" do
+ name = tmp("io_rewind_spec")
+ io = File.open(name, "w")
+ io.write("Voici la ligne une.\n")
+ io.rewind
+ io.pos.should == 0
+ ensure
+ io.close
+ rm_r name
+ end
+
it "positions the instance to the beginning of input and clears EOF" do
value = @io.read
@io.rewind
@@ -32,7 +43,11 @@ describe "IO#rewind" do
@io.lineno.should == 0
end
+ it "returns 0" do
+ @io.rewind.should == 0
+ end
+
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.rewind }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.rewind }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/seek_spec.rb b/spec/ruby/core/io/seek_spec.rb
index 2fa4a73ac9..d26629fb89 100644
--- a/spec/ruby/core/io/seek_spec.rb
+++ b/spec/ruby/core/io/seek_spec.rb
@@ -17,7 +17,7 @@ describe "IO#seek" do
end
it "moves the read position relative to the current position with SEEK_CUR" do
- -> { @io.seek(-1) }.should raise_error(Errno::EINVAL)
+ -> { @io.seek(-1) }.should.raise(Errno::EINVAL)
@io.seek(10, IO::SEEK_CUR)
@io.readline.should == "igne une.\n"
@io.seek(-5, IO::SEEK_CUR)
diff --git a/spec/ruby/core/io/select_spec.rb b/spec/ruby/core/io/select_spec.rb
index 4603c1fbbc..0a43fc6f5f 100644
--- a/spec/ruby/core/io/select_spec.rb
+++ b/spec/ruby/core/io/select_spec.rb
@@ -18,6 +18,10 @@ describe "IO.select" do
@wr.syswrite("be ready")
IO.pipe do |_, wr|
result = IO.select [@rd], [wr], nil, 0
+ unless result
+ # On some platforms (e.g., Windows), pipe readiness may not be immediate
+ result = IO.select [@rd], [wr], nil, 2
+ end
result.should == [[@rd], [wr], []]
end
end
@@ -55,8 +59,8 @@ describe "IO.select" do
end
end
- it "returns supplied objects correctly even when monitoring the same object in different arrays" do
- filename = tmp("IO_select_pipe_file") + $$.to_s
+ it "returns supplied objects correctly when monitoring the same object in different arrays" do
+ filename = tmp("IO_select_pipe_file")
io = File.open(filename, 'w+')
result = IO.select [io], [io], nil, 0
result.should == [[io], [io], []]
@@ -64,6 +68,17 @@ describe "IO.select" do
rm_r filename
end
+ it "returns the pipe read end in read set if the pipe write end is closed concurrently" do
+ main = Thread.current
+ t = Thread.new {
+ Thread.pass until main.stop?
+ @wr.close
+ }
+ IO.select([@rd]).should == [[@rd], [], []]
+ ensure
+ t.join
+ end
+
it "invokes to_io on supplied objects that are not IO and returns the supplied objects" do
# make some data available
@wr.write("foobar")
@@ -80,41 +95,96 @@ describe "IO.select" do
end
it "raises TypeError if supplied objects are not IO" do
- -> { IO.select([Object.new]) }.should raise_error(TypeError)
- -> { IO.select(nil, [Object.new]) }.should raise_error(TypeError)
+ -> { IO.select([Object.new]) }.should.raise(TypeError)
+ -> { IO.select(nil, [Object.new]) }.should.raise(TypeError)
obj = mock("io")
obj.should_receive(:to_io).any_number_of_times.and_return(nil)
- -> { IO.select([obj]) }.should raise_error(TypeError)
- -> { IO.select(nil, [obj]) }.should raise_error(TypeError)
+ -> { IO.select([obj]) }.should.raise(TypeError)
+ -> { IO.select(nil, [obj]) }.should.raise(TypeError)
end
it "raises a TypeError if the specified timeout value is not Numeric" do
- -> { IO.select([@rd], nil, nil, Object.new) }.should raise_error(TypeError)
+ -> { IO.select([@rd], nil, nil, Object.new) }.should.raise(TypeError)
end
it "raises TypeError if the first three arguments are not Arrays" do
- -> { IO.select(Object.new)}.should raise_error(TypeError)
- -> { IO.select(nil, Object.new)}.should raise_error(TypeError)
- -> { IO.select(nil, nil, Object.new)}.should raise_error(TypeError)
+ -> { IO.select(Object.new)}.should.raise(TypeError)
+ -> { IO.select(nil, Object.new)}.should.raise(TypeError)
+ -> { IO.select(nil, nil, Object.new)}.should.raise(TypeError)
end
it "raises an ArgumentError when passed a negative timeout" do
- -> { IO.select(nil, nil, nil, -5)}.should raise_error(ArgumentError)
+ -> { IO.select(nil, nil, nil, -5)}.should.raise(ArgumentError, "time interval must not be negative")
+ end
+
+ ruby_version_is "4.0" do
+ it "raises an ArgumentError when passed negative infinity as timeout" do
+ -> { IO.select(nil, nil, nil, -Float::INFINITY)}.should.raise(ArgumentError, "time interval must not be negative")
+ end
+ end
+
+ it "raises an RangeError when passed NaN as timeout" do
+ -> { IO.select(nil, nil, nil, Float::NAN)}.should.raise(RangeError, "NaN out of Time range")
+ end
+
+ describe "returns the available descriptors when the file descriptor" do
+ it "is in both read and error arrays" do
+ @wr.write("foobar")
+ result = IO.select([@rd], nil, [@rd])
+ result.should == [[@rd], [], []]
+ end
+
+ it "is in both write and error arrays" do
+ result = IO.select(nil, [@wr], [@wr])
+ result.should == [[], [@wr], []]
+ end
+
+ it "is in both read and write arrays" do
+ filename = tmp("IO_select_read_write_file")
+ w = File.open(filename, 'w+')
+ begin
+ IO.select([w], [w], []).should == [[w], [w], []]
+ ensure
+ w.close
+ rm_r filename
+ end
+
+ IO.select([@wr], [@wr], []).should == [[], [@wr], []]
+
+ @wr.write("foobar")
+ # CRuby on macOS returns [[@rd], [@rd], []], weird but we accept it here, probably only for pipe read-end
+ [
+ [[@rd], [], []],
+ [[@rd], [@rd], []]
+ ].should.include? IO.select([@rd], [@rd], [])
+ end
end
end
-describe "IO.select when passed nil for timeout" do
- it "sleeps forever and sets the thread status to 'sleep'" do
- t = Thread.new do
- IO.select(nil, nil, nil, nil)
+describe "IO.select with infinite timeout" do
+ describe :io_select_infinite_timeout, shared: true do
+ it "sleeps forever and sets the thread status to 'sleep'" do
+ t = Thread.new do
+ IO.select(nil, nil, nil, @method)
+ end
+
+ Thread.pass while t.status && t.status != "sleep"
+ t.join unless t.status
+ t.status.should == "sleep"
+ t.kill
+ t.join
end
+ end
- Thread.pass while t.status && t.status != "sleep"
- t.join unless t.status
- t.status.should == "sleep"
- t.kill
- t.join
+ describe "IO.select when passed nil for timeout" do
+ it_behaves_like :io_select_infinite_timeout, nil
+ end
+
+ ruby_version_is "4.0" do
+ describe "IO.select when passed Float::INFINITY for timeout" do
+ it_behaves_like :io_select_infinite_timeout, Float::INFINITY
+ end
end
end
diff --git a/spec/ruby/core/io/set_encoding_by_bom_spec.rb b/spec/ruby/core/io/set_encoding_by_bom_spec.rb
new file mode 100644
index 0000000000..5436879f11
--- /dev/null
+++ b/spec/ruby/core/io/set_encoding_by_bom_spec.rb
@@ -0,0 +1,262 @@
+require_relative '../../spec_helper'
+
+describe "IO#set_encoding_by_bom" do
+ before :each do
+ @name = tmp('io_set_encoding_by_bom.txt')
+ touch(@name)
+ @io = new_io(@name, 'rb')
+ end
+
+ after :each do
+ @io.close unless @io.closed?
+ rm_r @name
+ end
+
+ it "returns nil if not readable" do
+ not_readable_io = new_io(@name, 'wb')
+
+ not_readable_io.set_encoding_by_bom.should == nil
+ not_readable_io.external_encoding.should == Encoding::ASCII_8BIT
+ ensure
+ not_readable_io.close
+ end
+
+ it "returns the result encoding if found BOM UTF-8 sequence" do
+ File.binwrite(@name, "\u{FEFF}")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_8
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.binwrite(@name, "\u{FEFF}abc")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_8
+ @io.external_encoding.should == Encoding::UTF_8
+ @io.read.b.should == "abc".b
+ end
+
+ it "returns the result encoding if found BOM UTF_16LE sequence" do
+ File.binwrite(@name, "\xFF\xFE")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.binwrite(@name, "\xFF\xFEabc")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "abc".b
+ end
+
+ it "returns the result encoding if found BOM UTF_16BE sequence" do
+ File.binwrite(@name, "\xFE\xFF")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16BE
+ @io.external_encoding.should == Encoding::UTF_16BE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.binwrite(@name, "\xFE\xFFabcd")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16BE
+ @io.external_encoding.should == Encoding::UTF_16BE
+ @io.read.b.should == "abcd".b
+ end
+
+ it "returns the result encoding if found BOM UTF_32LE sequence" do
+ File.binwrite(@name, "\xFF\xFE\x00\x00")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_32LE
+ @io.external_encoding.should == Encoding::UTF_32LE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.binwrite(@name, "\xFF\xFE\x00\x00abc")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_32LE
+ @io.external_encoding.should == Encoding::UTF_32LE
+ @io.read.b.should == "abc".b
+ end
+
+ it "returns the result encoding if found BOM UTF_32BE sequence" do
+ File.binwrite(@name, "\x00\x00\xFE\xFF")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_32BE
+ @io.external_encoding.should == Encoding::UTF_32BE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.binwrite(@name, "\x00\x00\xFE\xFFabcd")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_32BE
+ @io.external_encoding.should == Encoding::UTF_32BE
+ @io.read.b.should == "abcd".b
+ end
+
+ it "returns nil if io is empty" do
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "returns nil if UTF-8 BOM sequence is incomplete" do
+ File.write(@name, "\xEF")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEF".b
+ @io.rewind
+
+ File.write(@name, "\xEFa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEFa".b
+ @io.rewind
+
+ File.write(@name, "\xEF\xBB")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEF\xBB".b
+ @io.rewind
+
+ File.write(@name, "\xEF\xBBa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xEF\xBBa".b
+ end
+
+ it "returns nil if UTF-16BE BOM sequence is incomplete" do
+ File.write(@name, "\xFE")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFE".b
+ @io.rewind
+
+ File.write(@name, "\xFEa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFEa".b
+ end
+
+ it "returns nil if UTF-16LE/UTF-32LE BOM sequence is incomplete" do
+ File.write(@name, "\xFF")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFF".b
+ @io.rewind
+
+ File.write(@name, "\xFFa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\xFFa".b
+ end
+
+ it "returns UTF-16LE if UTF-32LE BOM sequence is incomplete" do
+ File.write(@name, "\xFF\xFE")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.write(@name, "\xFF\xFE\x00")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "\x00".b
+ @io.rewind
+ @io.set_encoding(Encoding::ASCII_8BIT)
+
+ File.write(@name, "\xFF\xFE\x00a")
+
+ @io.set_encoding_by_bom.should == Encoding::UTF_16LE
+ @io.external_encoding.should == Encoding::UTF_16LE
+ @io.read.b.should == "\x00a".b
+ end
+
+ it "returns nil if UTF-32BE BOM sequence is incomplete" do
+ File.write(@name, "\x00")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00".b
+ @io.rewind
+
+ File.write(@name, "\x00a")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00a".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00a")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00a".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00\xFE")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00\xFE".b
+ @io.rewind
+
+ File.write(@name, "\x00\x00\xFEa")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read.b.should == "\x00\x00\xFEa".b
+ end
+
+ it "returns nil if found BOM sequence not provided" do
+ File.write(@name, "abc")
+
+ @io.set_encoding_by_bom.should == nil
+ @io.external_encoding.should == Encoding::ASCII_8BIT
+ @io.read(3).should == "abc".b
+ end
+
+ it 'returns exception if io not in binary mode' do
+ not_binary_io = new_io(@name, 'r')
+
+ -> { not_binary_io.set_encoding_by_bom }.should.raise(ArgumentError, 'ASCII incompatible encoding needs binmode')
+ ensure
+ not_binary_io.close
+ end
+
+ it 'returns exception if encoding already set' do
+ @io.set_encoding("utf-8")
+
+ -> { @io.set_encoding_by_bom }.should.raise(ArgumentError, 'encoding is set to UTF-8 already')
+ end
+
+ it 'returns exception if encoding conversion is already set' do
+ @io.set_encoding(Encoding::UTF_8, Encoding::UTF_16BE)
+
+ -> { @io.set_encoding_by_bom }.should.raise(ArgumentError, 'encoding conversion is set')
+ end
+end
diff --git a/spec/ruby/core/io/set_encoding_spec.rb b/spec/ruby/core/io/set_encoding_spec.rb
index 5aec6a96c3..237251de5b 100644
--- a/spec/ruby/core/io/set_encoding_spec.rb
+++ b/spec/ruby/core/io/set_encoding_spec.rb
@@ -1,12 +1,25 @@
require_relative '../../spec_helper'
describe :io_set_encoding_write, shared: true do
- it "sets the encodings to nil" do
+ it "sets the encodings to nil when they were set previously" do
@io = new_io @name, "#{@object}:ibm437:ibm866"
@io.set_encoding nil, nil
- @io.external_encoding.should be_nil
- @io.internal_encoding.should be_nil
+ @io.external_encoding.should == nil
+ @io.internal_encoding.should == nil
+ end
+
+ it "sets the encodings to nil when the IO is built with no explicit encoding" do
+ @io = new_io @name, @object
+
+ # Checking our assumptions first
+ @io.external_encoding.should == nil
+ @io.internal_encoding.should == nil
+
+ @io.set_encoding nil, nil
+
+ @io.external_encoding.should == nil
+ @io.internal_encoding.should == nil
end
it "prevents the encodings from changing when Encoding defaults are changed" do
@@ -16,8 +29,8 @@ describe :io_set_encoding_write, shared: true do
Encoding.default_external = Encoding::IBM437
Encoding.default_internal = Encoding::IBM866
- @io.external_encoding.should be_nil
- @io.internal_encoding.should be_nil
+ @io.external_encoding.should == nil
+ @io.internal_encoding.should == nil
end
it "sets the encodings to the current Encoding defaults" do
@@ -38,6 +51,7 @@ describe "IO#set_encoding when passed nil, nil" do
@external = Encoding.default_external
@internal = Encoding.default_internal
+ # The defaults
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = nil
@@ -61,8 +75,8 @@ describe "IO#set_encoding when passed nil, nil" do
Encoding.default_internal = Encoding::IBM866
@io.set_encoding nil, nil
- @io.external_encoding.should equal(Encoding::IBM437)
- @io.internal_encoding.should equal(Encoding::IBM866)
+ @io.external_encoding.should.equal?(Encoding::IBM437)
+ @io.internal_encoding.should.equal?(Encoding::IBM866)
end
it "prevents the #internal_encoding from changing when Encoding.default_internal is changed" do
@@ -71,7 +85,7 @@ describe "IO#set_encoding when passed nil, nil" do
Encoding.default_internal = Encoding::IBM437
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
it "allows the #external_encoding to change when Encoding.default_external is changed" do
@@ -80,17 +94,17 @@ describe "IO#set_encoding when passed nil, nil" do
Encoding.default_external = Encoding::IBM437
- @io.external_encoding.should equal(Encoding::IBM437)
+ @io.external_encoding.should.equal?(Encoding::IBM437)
end
end
describe "with 'rb' mode" do
it "returns Encoding.default_external" do
@io = new_io @name, "rb"
- @io.external_encoding.should equal(Encoding::BINARY)
+ @io.external_encoding.should.equal?(Encoding::BINARY)
@io.set_encoding nil, nil
- @io.external_encoding.should equal(Encoding.default_external)
+ @io.external_encoding.should.equal?(Encoding.default_external)
end
end
@@ -113,6 +127,22 @@ describe "IO#set_encoding when passed nil, nil" do
describe "with 'a+' mode" do
it_behaves_like :io_set_encoding_write, nil, "a+"
end
+
+ describe "with standard IOs" do
+ it "correctly resets them" do
+ STDOUT.external_encoding.should == nil
+ STDOUT.internal_encoding.should == nil
+
+ begin
+ STDOUT.set_encoding(Encoding::US_ASCII, Encoding::ISO_8859_1)
+ ensure
+ STDOUT.set_encoding(nil, nil)
+ end
+
+ STDOUT.external_encoding.should == nil
+ STDOUT.internal_encoding.should == nil
+ end
+ end
end
describe "IO#set_encoding" do
@@ -128,13 +158,13 @@ describe "IO#set_encoding" do
end
it "returns self" do
- @io.set_encoding(Encoding::UTF_8).should equal(@io)
+ @io.set_encoding(Encoding::UTF_8).should.equal?(@io)
end
it "sets the external encoding when passed an Encoding argument" do
@io.set_encoding(Encoding::UTF_8)
@io.external_encoding.should == Encoding::UTF_8
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
it "sets the external and internal encoding when passed two Encoding arguments" do
@@ -146,19 +176,19 @@ describe "IO#set_encoding" do
it "sets the external encoding when passed the name of an Encoding" do
@io.set_encoding("utf-8")
@io.external_encoding.should == Encoding::UTF_8
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
it "ignores the internal encoding if the same as external when passed Encoding objects" do
@io.set_encoding(Encoding::UTF_8, Encoding::UTF_8)
@io.external_encoding.should == Encoding::UTF_8
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
it "ignores the internal encoding if the same as external when passed encoding names separated by ':'" do
@io.set_encoding("utf-8:utf-8")
@io.external_encoding.should == Encoding::UTF_8
- @io.internal_encoding.should be_nil
+ @io.internal_encoding.should == nil
end
it "sets the external and internal encoding when passed the names of Encodings separated by ':'" do
@@ -188,4 +218,21 @@ describe "IO#set_encoding" do
@io.external_encoding.should == Encoding::UTF_8
@io.internal_encoding.should == Encoding::UTF_16BE
end
+
+ it "saves encoding options passed as a hash in the last argument" do
+ File.write(@name, "\xff")
+ io = File.open(@name)
+ io.set_encoding(Encoding::EUC_JP, Encoding::SHIFT_JIS, invalid: :replace, replace: ".")
+ io.read.should == "."
+ ensure
+ io.close
+ end
+
+ it "raises ArgumentError when no arguments are given" do
+ -> { @io.set_encoding() }.should.raise(ArgumentError)
+ end
+
+ it "raises ArgumentError when too many arguments are given" do
+ -> { @io.set_encoding(1, 2, 3) }.should.raise(ArgumentError)
+ end
end
diff --git a/spec/ruby/core/io/shared/binwrite.rb b/spec/ruby/core/io/shared/binwrite.rb
index 3649bb47ff..64793b1936 100644
--- a/spec/ruby/core/io/shared/binwrite.rb
+++ b/spec/ruby/core/io/shared/binwrite.rb
@@ -21,6 +21,14 @@ describe :io_binwrite, shared: true do
IO.send(@method, @filename, "abcde").should == 5
end
+ it "accepts options as a keyword argument" do
+ IO.send(@method, @filename, "hi", 0, flags: File::CREAT).should == 2
+
+ -> {
+ IO.send(@method, @filename, "hi", 0, {flags: File::CREAT})
+ }.should.raise(ArgumentError, "wrong number of arguments (given 4, expected 2..3)")
+ end
+
it "creates a file if missing" do
fn = @filename + "xxx"
begin
@@ -67,8 +75,13 @@ describe :io_binwrite, shared: true do
File.read(@filename).should == "\0\0foo"
end
+ it "accepts a :flags option without :mode one" do
+ IO.send(@method, @filename, "hello, world!", flags: File::CREAT)
+ File.read(@filename).should == "hello, world!"
+ end
+
it "raises an error if readonly mode is specified" do
- -> { IO.send(@method, @filename, "abcde", mode: "r") }.should raise_error(IOError)
+ -> { IO.send(@method, @filename, "abcde", mode: "r") }.should.raise(IOError)
end
it "truncates if empty :opts provided and offset skipped" do
diff --git a/spec/ruby/core/io/shared/chars.rb b/spec/ruby/core/io/shared/chars.rb
index 266566f221..efd4d5ee10 100644
--- a/spec/ruby/core/io/shared/chars.rb
+++ b/spec/ruby/core/io/shared/chars.rb
@@ -24,7 +24,7 @@ describe :io_chars, shared: true do
describe "when no block is given" do
it "returns an Enumerator" do
enum = @io.send(@method)
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.first(5).should == ["V", "o", "i", "c", "i"]
end
@@ -38,19 +38,19 @@ describe :io_chars, shared: true do
end
it "returns itself" do
- @io.send(@method) { |c| }.should equal(@io)
+ @io.send(@method) { |c| }.should.equal?(@io)
end
it "returns an enumerator for a closed stream" do
- IOSpecs.closed_io.send(@method).should be_an_instance_of(Enumerator)
+ IOSpecs.closed_io.send(@method).should.instance_of?(Enumerator)
end
it "raises an IOError when an enumerator created on a closed stream is accessed" do
- -> { IOSpecs.closed_io.send(@method).first }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.send(@method).first }.should.raise(IOError)
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.send(@method) {} }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.send(@method) {} }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/shared/codepoints.rb b/spec/ruby/core/io/shared/codepoints.rb
index 6872846c1a..21c756986f 100644
--- a/spec/ruby/core/io/shared/codepoints.rb
+++ b/spec/ruby/core/io/shared/codepoints.rb
@@ -13,7 +13,7 @@ describe :io_codepoints, shared: true do
describe "when no block is given" do
it "returns an Enumerator" do
- @enum.should be_an_instance_of(Enumerator)
+ @enum.should.instance_of?(Enumerator)
end
describe "returned Enumerator" do
@@ -39,7 +39,7 @@ describe :io_codepoints, shared: true do
it "raises an error if reading invalid sequence" do
@io.pos = 60 # inside of a multibyte sequence
- -> { @enum.first }.should raise_error(ArgumentError)
+ -> { @enum.first }.should.raise(ArgumentError)
end
it "does not change $_" do
@@ -49,6 +49,6 @@ describe :io_codepoints, shared: true do
end
it "raises an IOError when self is not readable" do
- -> { IOSpecs.closed_io.send(@method).to_a }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.send(@method).to_a }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/shared/each.rb b/spec/ruby/core/io/shared/each.rb
index 91766fbe03..ae60c3506a 100644
--- a/spec/ruby/core/io/shared/each.rb
+++ b/spec/ruby/core/io/shared/each.rb
@@ -24,7 +24,7 @@ describe :io_each, shared: true do
end
it "returns self" do
- @io.send(@method) { |l| l }.should equal(@io)
+ @io.send(@method) { |l| l }.should.equal?(@io)
end
it "does not change $_" do
@@ -33,12 +33,8 @@ describe :io_each, shared: true do
$_.should == "test"
end
- it "returns self" do
- @io.send(@method) { |l| l }.should equal(@io)
- end
-
it "raises an IOError when self is not readable" do
- -> { IOSpecs.closed_io.send(@method) {} }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.send(@method) {} }.should.raise(IOError)
end
it "makes line count accessible via lineno" do
@@ -54,7 +50,7 @@ describe :io_each, shared: true do
describe "when no block is given" do
it "returns an Enumerator" do
enum = @io.send(@method)
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.each { |l| ScratchPad << l }
ScratchPad.recorded.should == IOSpecs.lines
@@ -74,9 +70,13 @@ describe :io_each, shared: true do
describe "when limit is 0" do
it "raises an ArgumentError" do
# must pass block so Enumerator is evaluated and raises
- -> { @io.send(@method, 0){} }.should raise_error(ArgumentError)
+ -> { @io.send(@method, 0){} }.should.raise(ArgumentError)
end
end
+
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { @io.send(@method, 2**128){} }.should.raise(RangeError)
+ end
end
describe "when passed a String containing one space as a separator" do
@@ -113,13 +113,20 @@ describe :io_each, shared: true do
@io.send(@method, "") { |s| ScratchPad << s }
ScratchPad.recorded.should == IOSpecs.paragraphs
end
+
+ it "discards leading newlines" do
+ @io.readline
+ @io.readline
+ @io.send(@method, "") { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.paragraphs[1..-1]
+ end
end
describe "with both separator and limit" do
describe "when no block is given" do
it "returns an Enumerator" do
enum = @io.send(@method, nil, 1024)
- enum.should be_an_instance_of(Enumerator)
+ enum.should.instance_of?(Enumerator)
enum.each { |l| ScratchPad << l }
ScratchPad.recorded.should == [IOSpecs.lines.join]
@@ -136,7 +143,7 @@ describe :io_each, shared: true do
describe "when a block is given" do
it "accepts an empty block" do
- @io.send(@method, nil, 1024) {}.should equal(@io)
+ @io.send(@method, nil, 1024) {}.should.equal?(@io)
end
describe "when passed nil as a separator" do
@@ -152,6 +159,13 @@ describe :io_each, shared: true do
@io.send(@method, "", 1024) { |s| ScratchPad << s }
ScratchPad.recorded.should == IOSpecs.paragraphs
end
+
+ it "discards leading newlines" do
+ @io.readline
+ @io.readline
+ @io.send(@method, "", 1024) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.paragraphs[1..-1]
+ end
end
end
end
@@ -161,6 +175,60 @@ describe :io_each, shared: true do
@io.send(@method, chomp: true) { |s| ScratchPad << s }
ScratchPad.recorded.should == IOSpecs.lines_without_newline_characters
end
+
+ it "raises exception when options passed as Hash" do
+ -> {
+ @io.send(@method, { chomp: true }) { |s| }
+ }.should.raise(TypeError)
+
+ -> {
+ @io.send(@method, "\n", 1, { chomp: true }) { |s| }
+ }.should.raise(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
+ end
+ end
+
+ describe "when passed chomp and a separator" do
+ it "yields each line without separator to the passed block" do
+ @io.send(@method, " ", chomp: true) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.lines_space_separator_without_trailing_spaces
+ end
+ end
+
+ describe "when passed chomp and empty line as a separator" do
+ it "yields each paragraph without trailing new line characters" do
+ @io.send(@method, "", 1024, chomp: true) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == IOSpecs.paragraphs_without_trailing_new_line_characters
+ end
+ end
+
+ describe "when passed chomp and nil as a separator" do
+ it "yields self's content" do
+ @io.pos = 100
+ @io.send(@method, nil, chomp: true) { |s| ScratchPad << s }
+ ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"]
+ end
+ end
+
+ describe "when passed chomp, nil as a separator, and a limit" do
+ it "yields each line of limit size without truncating trailing new line character" do
+ # 43 - is a size of the 1st paragraph in the file
+ @io.send(@method, nil, 43, chomp: true) { |s| ScratchPad << s }
+
+ ScratchPad.recorded.should == [
+ "Voici la ligne une.\nQui è la linea due.\n\n\n",
+ "Aquí está la línea tres.\n" + "Hier ist Zeile ",
+ "vier.\n\nEstá aqui a linha cinco.\nHere is li",
+ "ne six.\n"
+ ]
+ end
+ end
+
+ describe "when passed too many arguments" do
+ it "raises ArgumentError" do
+ -> {
+ @io.send(@method, "", 1, "excess argument", chomp: true) {}
+ }.should.raise(ArgumentError)
+ end
end
end
diff --git a/spec/ruby/core/io/shared/gets_ascii.rb b/spec/ruby/core/io/shared/gets_ascii.rb
index 2a8fe3c9a5..2bd5470d99 100644
--- a/spec/ruby/core/io/shared/gets_ascii.rb
+++ b/spec/ruby/core/io/shared/gets_ascii.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
describe :io_gets_ascii, shared: true do
describe "with ASCII separator" do
before :each do
diff --git a/spec/ruby/core/io/shared/new.rb b/spec/ruby/core/io/shared/new.rb
index 87f3001862..6f318ddee5 100644
--- a/spec/ruby/core/io/shared/new.rb
+++ b/spec/ruby/core/io/shared/new.rb
@@ -1,5 +1,7 @@
require_relative '../fixtures/classes'
+# NOTE: should be synchronized with library/stringio/initialize_spec.rb
+
# This group of specs may ONLY contain specs that do successfully create
# an IO instance from the file descriptor returned by #new_fd helper.
describe :io_new, shared: true do
@@ -18,9 +20,9 @@ describe :io_new, shared: true do
rm_r @name
end
- it "creates an IO instance from a Fixnum argument" do
+ it "creates an IO instance from an Integer argument" do
@io = IO.send(@method, @fd, "w")
- @io.should be_an_instance_of(IO)
+ @io.should.instance_of?(IO)
end
it "creates an IO instance when STDOUT is closed" do
@@ -30,7 +32,7 @@ describe :io_new, shared: true do
begin
@io = IO.send(@method, @fd, "w")
- @io.should be_an_instance_of(IO)
+ @io.should.instance_of?(IO)
ensure
STDOUT = stdout
rm_r stdout_file
@@ -47,7 +49,7 @@ describe :io_new, shared: true do
begin
@io = IO.send(@method, @fd, "w")
- @io.should be_an_instance_of(IO)
+ @io.should.instance_of?(IO)
ensure
STDERR = stderr
rm_r stderr_file
@@ -55,11 +57,20 @@ describe :io_new, shared: true do
end
end
- it "calls #to_int on an object to convert to a Fixnum" do
+ it "calls #to_int on an object to convert to an Integer" do
obj = mock("file descriptor")
obj.should_receive(:to_int).and_return(@fd)
@io = IO.send(@method, obj, "w")
- @io.should be_an_instance_of(IO)
+ @io.should.instance_of?(IO)
+ end
+
+ it "accepts options as keyword arguments" do
+ @io = IO.send(@method, @fd, "w", flags: File::CREAT)
+ @io.write("foo").should == 3
+
+ -> {
+ IO.send(@method, @fd, "w", {flags: File::CREAT})
+ }.should.raise(ArgumentError, "wrong number of arguments (given 3, expected 1..2)")
end
it "accepts a :mode option" do
@@ -197,21 +208,30 @@ describe :io_new, shared: true do
@io.internal_encoding.to_s.should == 'IBM866'
end
- ruby_version_is ''...'2.8' do
- it "accepts nil options" do
- @io = suppress_keyword_warning do
- IO.send(@method, @fd, 'w', nil)
- end
- @io.write("foo").should == 3
- end
+ it "does not use binary encoding when mode encoding is specified along with binmode: true option" do
+ @io = IO.send(@method, @fd, 'w:iso-8859-1', binmode: true)
+ @io.external_encoding.to_s.should == 'ISO-8859-1'
end
- ruby_version_is '2.8' do
- it "raises ArgumentError for nil options" do
- -> {
- IO.send(@method, @fd, 'w', nil)
- }.should raise_error(ArgumentError)
- end
+ it "does not use textmode argument when mode encoding is specified" do
+ @io = IO.send(@method, @fd, 'w:ascii-8bit', textmode: true)
+ @io.external_encoding.to_s.should == 'ASCII-8BIT'
+ end
+
+ it "does not use binmode argument when external encoding is specified via the :external_encoding option" do
+ @io = IO.send(@method, @fd, 'w', binmode: true, external_encoding: 'iso-8859-1')
+ @io.external_encoding.to_s.should == 'ISO-8859-1'
+ end
+
+ it "does not use textmode argument when external encoding is specified via the :external_encoding option" do
+ @io = IO.send(@method, @fd, 'w', textmode: true, external_encoding: 'ascii-8bit')
+ @io.external_encoding.to_s.should == 'ASCII-8BIT'
+ end
+
+ it "raises ArgumentError for nil options" do
+ -> {
+ IO.send(@method, @fd, 'w', nil)
+ }.should.raise(ArgumentError)
end
it "coerces mode with #to_str" do
@@ -294,109 +314,100 @@ describe :io_new_errors, shared: true do
end
it "raises an Errno::EBADF if the file descriptor is not valid" do
- -> { IO.send(@method, -1, "w") }.should raise_error(Errno::EBADF)
+ -> { IO.send(@method, -1, "w") }.should.raise(Errno::EBADF)
end
it "raises an IOError if passed a closed stream" do
- -> { IO.send(@method, IOSpecs.closed_io.fileno, 'w') }.should raise_error(IOError)
+ -> { IO.send(@method, IOSpecs.closed_io.fileno, 'w') }.should.raise(IOError)
end
platform_is_not :windows do
it "raises an Errno::EINVAL if the new mode is not compatible with the descriptor's current mode" do
- -> { IO.send(@method, @fd, "r") }.should raise_error(Errno::EINVAL)
+ -> { IO.send(@method, @fd, "r") }.should.raise(Errno::EINVAL)
end
end
it "raises ArgumentError if passed an empty mode string" do
- -> { IO.send(@method, @fd, "") }.should raise_error(ArgumentError)
+ -> { IO.send(@method, @fd, "") }.should.raise(ArgumentError)
end
it "raises an error if passed modes two ways" do
-> {
IO.send(@method, @fd, "w", mode: "w")
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "raises an error if passed encodings two ways" do
-> {
@io = IO.send(@method, @fd, 'w:ISO-8859-1', encoding: 'ISO-8859-1')
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, 'w:ISO-8859-1', external_encoding: 'ISO-8859-1')
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
+ -> {
+ @io = IO.send(@method, @fd, 'w:ISO-8859-1', internal_encoding: 'ISO-8859-1')
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, 'w:ISO-8859-1:UTF-8', internal_encoding: 'ISO-8859-1')
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "raises an error if passed matching binary/text mode two ways" do
-> {
@io = IO.send(@method, @fd, "wb", binmode: true)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, "wt", textmode: true)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, "wb", textmode: false)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, "wt", binmode: false)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "raises an error if passed conflicting binary/text mode two ways" do
-> {
@io = IO.send(@method, @fd, "wb", binmode: false)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, "wt", textmode: false)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, "wb", textmode: true)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, "wt", binmode: true)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "raises an error when trying to set both binmode and textmode" do
-> {
@io = IO.send(@method, @fd, "w", textmode: true, binmode: true)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, File::Constants::WRONLY, textmode: true, binmode: true)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "raises ArgumentError if not passed a hash or nil for options" do
-> {
@io = IO.send(@method, @fd, 'w', false)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, false, false)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
@io = IO.send(@method, @fd, nil, false)
- }.should raise_error(ArgumentError)
- end
-
- ruby_version_is ''...'2.8' do
- it "raises TypeError if passed a hash for mode and nil for options" do
- -> {
- suppress_keyword_warning do
- @io = IO.send(@method, @fd, {mode: 'w'}, nil)
- end
- }.should raise_error(TypeError)
- end
+ }.should.raise(ArgumentError)
end
- ruby_version_is '2.8' do
- it "raises ArgumentError if passed a hash for mode and nil for options" do
- -> {
- @io = IO.send(@method, @fd, {mode: 'w'}, nil)
- }.should raise_error(ArgumentError)
- end
+ it "raises ArgumentError if passed a hash for mode and nil for options" do
+ -> {
+ @io = IO.send(@method, @fd, {mode: 'w'}, nil)
+ }.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/io/shared/pos.rb b/spec/ruby/core/io/shared/pos.rb
index 0b4d1e13b3..f4d0405863 100644
--- a/spec/ruby/core/io/shared/pos.rb
+++ b/spec/ruby/core/io/shared/pos.rb
@@ -19,7 +19,7 @@ describe :io_pos, shared: true do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.send(@method) }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.send(@method) }.should.raise(IOError)
end
it "resets #eof?" do
@@ -60,13 +60,19 @@ describe :io_set_pos, shared: true do
end
end
- it "does not accept Bignums that don't fit in a C long" do
+ it "raises TypeError when cannot convert implicitly argument to Integer" do
File.open @fname do |io|
- -> { io.send @method, 2**128 }.should raise_error(RangeError)
+ -> { io.send @method, Object.new }.should.raise(TypeError, "no implicit conversion of Object into Integer")
+ end
+ end
+
+ it "does not accept Integers that don't fit in a C off_t" do
+ File.open @fname do |io|
+ -> { io.send @method, 2**128 }.should.raise(RangeError)
end
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.send @method, 0 }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.send @method, 0 }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/shared/readlines.rb b/spec/ruby/core/io/shared/readlines.rb
index 339684a5d4..f54fccc2e3 100644
--- a/spec/ruby/core/io/shared/readlines.rb
+++ b/spec/ruby/core/io/shared/readlines.rb
@@ -1,11 +1,11 @@
describe :io_readlines, shared: true do
it "raises TypeError if the first parameter is nil" do
- -> { IO.send(@method, nil, &@object) }.should raise_error(TypeError)
+ -> { IO.send(@method, nil, &@object) }.should.raise(TypeError)
end
it "raises an Errno::ENOENT if the file does not exist" do
name = tmp("nonexistent.txt")
- -> { IO.send(@method, name, &@object) }.should raise_error(Errno::ENOENT)
+ -> { IO.send(@method, name, &@object) }.should.raise(Errno::ENOENT)
end
it "yields a single string with entire content when the separator is nil" do
@@ -54,7 +54,7 @@ describe :io_readlines_options_19, shared: true do
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator
end
- describe "when the object is a Fixnum" do
+ describe "when the object is an Integer" do
before :each do
@sep = $/
end
@@ -69,10 +69,25 @@ describe :io_readlines_options_19, shared: true do
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end
- it "uses the object as a limit if it is a Fixnum" do
+ it "uses the object as a limit if it is an Integer" do
result = IO.send(@method, @name, 10, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_limit
end
+
+ it "ignores the object as a limit if it is negative" do
+ result = IO.send(@method, @name, -2, &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines
+ end
+
+ it "does not accept Integers that don't fit in a C off_t" do
+ -> { IO.send(@method, @name, 2**128, &@object) }.should.raise(RangeError)
+ end
+
+ describe "when passed limit" do
+ it "raises ArgumentError when passed 0 as a limit" do
+ -> { IO.send(@method, @name, 0, &@object) }.should.raise(ArgumentError)
+ end
+ end
end
describe "when the object is a String" do
@@ -82,38 +97,40 @@ describe :io_readlines_options_19, shared: true do
end
it "accepts non-ASCII data as separator" do
- result = IO.send(@method, @name, "\303\250".force_encoding("utf-8"), &@object)
+ result = IO.send(@method, @name, "\303\250".dup.force_encoding("utf-8"), &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_arbitrary_separator
end
end
- describe "when the object is a Hash" do
- it "uses the value as the options hash" do
- result = IO.send(@method, @name, mode: "r", &@object)
- (result ? result : ScratchPad.recorded).should == IOSpecs.lines
+ describe "when the object is an options Hash" do
+ it "raises TypeError exception" do
+ -> {
+ IO.send(@method, @name, { chomp: true }, &@object)
+ }.should.raise(TypeError)
end
end
- end
- describe "when passed name, object, object" do
- describe "when the first object is a Fixnum" do
- it "uses the second object as an options Hash" do
- -> do
- IO.send(@method, @filename, 10, mode: "w", &@object)
- end.should raise_error(IOError)
- end
+ describe "when the object is neither Integer nor String" do
+ it "raises TypeError exception" do
+ obj = mock("not io readlines limit")
- it "calls #to_hash to convert the second object to a Hash" do
- options = mock("io readlines options Hash")
- options.should_receive(:to_hash).and_return({ mode: "w" })
- -> do
- IO.send(@method, @filename, 10, **options, &@object)
- end.should raise_error(IOError)
+ -> {
+ IO.send(@method, @name, obj, &@object)
+ }.should.raise(TypeError)
end
end
+ end
+ describe "when passed name, keyword arguments" do
+ it "uses the keyword arguments as options" do
+ result = IO.send(@method, @name, mode: "r", &@object)
+ (result ? result : ScratchPad.recorded).should == IOSpecs.lines
+ end
+ end
+
+ describe "when passed name, object, object" do
describe "when the first object is a String" do
- it "uses the second object as a limit if it is a Fixnum" do
+ it "uses the second object as a limit if it is an Integer" do
result = IO.send(@method, @name, " ", 10, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end
@@ -124,32 +141,18 @@ describe :io_readlines_options_19, shared: true do
result = IO.send(@method, @name, " ", limit, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end
-
- it "uses the second object as an options Hash" do
- -> do
- IO.send(@method, @filename, " ", mode: "w", &@object)
- end.should raise_error(IOError)
- end
-
- it "calls #to_hash to convert the second object to a Hash" do
- options = mock("io readlines options Hash")
- options.should_receive(:to_hash).and_return({ mode: "w" })
- -> do
- IO.send(@method, @filename, " ", **options, &@object)
- end.should raise_error(IOError)
- end
end
- describe "when the first object is not a String or Fixnum" do
+ describe "when the first object is not a String or Integer" do
it "calls #to_str to convert the object to a String" do
sep = mock("io readlines separator")
sep.should_receive(:to_str).at_least(1).and_return(" ")
- result = IO.send(@method, @name, sep, 10, mode: "r", &@object)
+ result = IO.send(@method, @name, sep, 10, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end
- it "uses the second object as a limit if it is a Fixnum" do
- result = IO.send(@method, @name, " ", 10, mode: "r", &@object)
+ it "uses the second object as a limit if it is an Integer" do
+ result = IO.send(@method, @name, " ", 10, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end
@@ -159,24 +162,57 @@ describe :io_readlines_options_19, shared: true do
result = IO.send(@method, @name, " ", limit, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end
+ end
+
+ describe "when the second object is neither Integer nor String" do
+ it "raises TypeError exception" do
+ obj = mock("not io readlines limit")
+
+ -> {
+ IO.send(@method, @name, " ", obj, &@object)
+ }.should.raise(TypeError)
+ end
+ end
- it "uses the second object as an options Hash" do
+ describe "when the second object is an options Hash" do
+ it "raises TypeError exception" do
+ -> {
+ IO.send(@method, @name, "", { chomp: true }, &@object)
+ }.should.raise(TypeError)
+ end
+ end
+ end
+
+ describe "when passed name, object, keyword arguments" do
+ describe "when the first object is an Integer" do
+ it "uses the keyword arguments as options" do
+ -> do
+ IO.send(@method, @filename, 10, mode: "w", &@object)
+ end.should.raise(IOError)
+ end
+ end
+
+ describe "when the first object is a String" do
+ it "uses the keyword arguments as options" do
-> do
IO.send(@method, @filename, " ", mode: "w", &@object)
- end.should raise_error(IOError)
+ end.should.raise(IOError)
end
+ end
+
+ describe "when the first object is not a String or Integer" do
+ it "uses the keyword arguments as options" do
+ sep = mock("io readlines separator")
+ sep.should_receive(:to_str).at_least(1).and_return(" ")
- it "calls #to_hash to convert the second object to a Hash" do
- options = mock("io readlines options Hash")
- options.should_receive(:to_hash).and_return({ mode: "w" })
-> do
- IO.send(@method, @filename, " ", **options, &@object)
- end.should raise_error(IOError)
+ IO.send(@method, @filename, sep, mode: "w", &@object)
+ end.should.raise(IOError)
end
end
end
- describe "when passed name, separator, limit, options" do
+ describe "when passed name, separator, limit, keyword arguments" do
it "calls #to_path to convert the name object" do
name = mock("io name to_path")
name.should_receive(:to_path).and_return(@name)
@@ -198,12 +234,24 @@ describe :io_readlines_options_19, shared: true do
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end
- it "calls #to_hash to convert the options object" do
- options = mock("io readlines options Hash")
- options.should_receive(:to_hash).and_return({ mode: "w" })
+ it "uses the keyword arguments as options" do
-> do
- IO.send(@method, @filename, " ", 10, **options, &@object)
- end.should raise_error(IOError)
+ IO.send(@method, @filename, " ", 10, mode: "w", &@object)
+ end.should.raise(IOError)
+ end
+
+ describe "when passed chomp, nil as a separator, and a limit" do
+ it "yields each line of limit size without truncating trailing new line character" do
+ # 43 - is a size of the 1st paragraph in the file
+ result = IO.send(@method, @name, nil, 43, chomp: true, &@object)
+
+ (result ? result : ScratchPad.recorded).should == [
+ "Voici la ligne une.\nQui è la linea due.\n\n\n",
+ "Aquí está la línea tres.\n" + "Hier ist Zeile ",
+ "vier.\n\nEstá aqui a linha cinco.\nHere is li",
+ "ne six.\n"
+ ]
+ end
end
end
end
diff --git a/spec/ruby/core/io/shared/tty.rb b/spec/ruby/core/io/shared/tty.rb
index 89ac08ec86..1dc0e95739 100644
--- a/spec/ruby/core/io/shared/tty.rb
+++ b/spec/ruby/core/io/shared/tty.rb
@@ -19,6 +19,6 @@ describe :io_tty, shared: true do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.send @method }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.send @method }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/shared/write.rb b/spec/ruby/core/io/shared/write.rb
index 270b84ac39..5de9fe4335 100644
--- a/spec/ruby/core/io/shared/write.rb
+++ b/spec/ruby/core/io/shared/write.rb
@@ -23,7 +23,7 @@ describe :io_write, shared: true do
end
it "checks if the file is writable if writing more than zero bytes" do
- -> { @readonly_file.send(@method, "abcde") }.should raise_error(IOError)
+ -> { @readonly_file.send(@method, "abcde") }.should.raise(IOError)
end
it "returns the number of bytes written" do
@@ -66,17 +66,7 @@ describe :io_write, shared: true do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.send(@method, "hello") }.should raise_error(IOError)
- end
-
- it "does not modify the passed argument" do
- File.open(@filename, "w") do |f|
- f.set_encoding(Encoding::IBM437)
- # A character whose codepoint differs between UTF-8 and IBM437
- f.write "ƒ".freeze
- end
-
- File.binread(@filename).bytes.should == [159]
+ -> { IOSpecs.closed_io.send(@method, "hello") }.should.raise(IOError)
end
describe "on a pipe" do
@@ -95,15 +85,70 @@ describe :io_write, shared: true do
@r.read.should == "foo"
end
- # [ruby-core:90895] MJIT worker may leave fd open in a forked child.
- # For instance, MJIT creates a worker before @r.close with fork(), @r.close happens,
- # and the MJIT worker keeps the pipe open until the worker execve().
- # TODO: consider acquiring GVL from MJIT worker.
- guard_not -> { defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? } do
+ # [ruby-core:90895] RJIT worker may leave fd open in a forked child.
+ # For instance, RJIT creates a worker before @r.close with fork(), @r.close happens,
+ # and the RJIT worker keeps the pipe open until the worker execve().
+ # TODO: consider acquiring GVL from RJIT worker.
+ guard_not -> { defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? } do
it "raises Errno::EPIPE if the read end is closed and does not die from SIGPIPE" do
@r.close
- -> { @w.send(@method, "foo") }.should raise_error(Errno::EPIPE, /Broken pipe/)
+ -> { @w.send(@method, "foo") }.should.raise(Errno::EPIPE, /Broken pipe/)
end
end
end
end
+
+describe :io_write_transcode, shared: true do
+ before :each do
+ @transcode_filename = tmp("io_write_transcode")
+ end
+
+ after :each do
+ rm_r @transcode_filename
+ end
+
+ it "transcodes the given string when the external encoding is set and neither is BINARY" do
+ utf8_str = "hello"
+
+ File.open(@transcode_filename, "w", external_encoding: Encoding::UTF_16BE) do |file|
+ file.external_encoding.should == Encoding::UTF_16BE
+ file.send(@method, utf8_str)
+ end
+
+ result = File.binread(@transcode_filename)
+ expected = [0, 104, 0, 101, 0, 108, 0, 108, 0, 111] # UTF-16BE bytes for "hello"
+
+ result.bytes.should == expected
+ end
+
+ it "transcodes the given string when the external encoding is set and the string encoding is BINARY" do
+ str = "été".b
+
+ File.open(@transcode_filename, "w", external_encoding: Encoding::UTF_16BE) do |file|
+ file.external_encoding.should == Encoding::UTF_16BE
+ -> { file.send(@method, str) }.should.raise(Encoding::UndefinedConversionError)
+ end
+ end
+end
+
+describe :io_write_no_transcode, shared: true do
+ before :each do
+ @transcode_filename = tmp("io_write_no_transcode")
+ end
+
+ after :each do
+ rm_r @transcode_filename
+ end
+
+ it "does not transcode the given string even when the external encoding is set" do
+ utf8_str = "hello"
+
+ File.open(@transcode_filename, "w", external_encoding: Encoding::UTF_16BE) do |file|
+ file.external_encoding.should == Encoding::UTF_16BE
+ file.send(@method, utf8_str)
+ end
+
+ result = File.binread(@transcode_filename)
+ result.bytes.should == utf8_str.bytes
+ end
+end
diff --git a/spec/ruby/core/io/stat_spec.rb b/spec/ruby/core/io/stat_spec.rb
index 58eba02b8f..f9fc232ee0 100644
--- a/spec/ruby/core/io/stat_spec.rb
+++ b/spec/ruby/core/io/stat_spec.rb
@@ -3,7 +3,8 @@ require_relative 'fixtures/classes'
describe "IO#stat" do
before :each do
- @io = IO.popen 'cat', "r+"
+ cmd = platform_is(:windows) ? 'rem' : 'cat'
+ @io = IO.popen cmd, "r+"
end
after :each do
@@ -11,14 +12,14 @@ describe "IO#stat" do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.stat }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.stat }.should.raise(IOError)
end
it "returns a File::Stat object for the stream" do
- STDOUT.stat.should be_an_instance_of(File::Stat)
+ STDOUT.stat.should.instance_of?(File::Stat)
end
it "can stat pipes" do
- @io.stat.should be_an_instance_of(File::Stat)
+ @io.stat.should.instance_of?(File::Stat)
end
end
diff --git a/spec/ruby/core/io/sync_spec.rb b/spec/ruby/core/io/sync_spec.rb
index 993b7ee244..b537db335b 100644
--- a/spec/ruby/core/io/sync_spec.rb
+++ b/spec/ruby/core/io/sync_spec.rb
@@ -27,7 +27,7 @@ describe "IO#sync=" do
end
it "raises an IOError on closed stream" do
- -> { IOSpecs.closed_io.sync = true }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.sync = true }.should.raise(IOError)
end
end
@@ -45,7 +45,7 @@ describe "IO#sync" do
end
it "raises an IOError on closed stream" do
- -> { IOSpecs.closed_io.sync }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.sync }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/sysopen_spec.rb b/spec/ruby/core/io/sysopen_spec.rb
index 4607d13687..325d51ed23 100644
--- a/spec/ruby/core/io/sysopen_spec.rb
+++ b/spec/ruby/core/io/sysopen_spec.rb
@@ -13,16 +13,16 @@ describe "IO.sysopen" do
it "returns the file descriptor for a given path" do
@fd = IO.sysopen(@filename, "w")
- @fd.should be_kind_of(Fixnum)
- @fd.should_not equal(0)
+ @fd.should.is_a?(Integer)
+ @fd.should_not.equal?(0)
end
# opening a directory is not supported on Windows
platform_is_not :windows do
it "works on directories" do
@fd = IO.sysopen(tmp("")) # /tmp
- @fd.should be_kind_of(Fixnum)
- @fd.should_not equal(0)
+ @fd.should.is_a?(Integer)
+ @fd.should_not.equal?(0)
end
end
@@ -33,18 +33,18 @@ describe "IO.sysopen" do
end
it "accepts a mode as second argument" do
- -> { @fd = IO.sysopen(@filename, "w") }.should_not raise_error
- @fd.should_not equal(0)
+ -> { @fd = IO.sysopen(@filename, "w") }.should_not.raise
+ @fd.should_not.equal?(0)
end
it "accepts permissions as third argument" do
@fd = IO.sysopen(@filename, "w", 777)
- @fd.should_not equal(0)
+ @fd.should_not.equal?(0)
end
it "accepts mode & permission that are nil" do
touch @filename # create the file
@fd = IO.sysopen(@filename, nil, nil)
- @fd.should_not equal(0)
+ @fd.should_not.equal?(0)
end
end
diff --git a/spec/ruby/core/io/sysread_spec.rb b/spec/ruby/core/io/sysread_spec.rb
index 024200efea..2d58db2250 100644
--- a/spec/ruby/core/io/sysread_spec.rb
+++ b/spec/ruby/core/io/sysread_spec.rb
@@ -6,7 +6,7 @@ describe "IO#sysread on a file" do
@file_name = tmp("IO_sysread_file") + $$.to_s
File.open(@file_name, "w") do |f|
# write some stuff
- f.write("012345678901234567890123456789")
+ f.write("012345678901234567890123456789\nabcdef")
end
@file = File.open(@file_name, "r+")
end
@@ -21,25 +21,25 @@ describe "IO#sysread on a file" do
end
it "reads the specified number of bytes from the file to the buffer" do
- buf = "" # empty buffer
+ buf = +"" # empty buffer
@file.sysread(15, buf).should == buf
buf.should == "012345678901234"
@file.rewind
- buf = "ABCDE" # small buffer
+ buf = +"ABCDE" # small buffer
@file.sysread(15, buf).should == buf
buf.should == "012345678901234"
@file.rewind
- buf = "ABCDE" * 5 # large buffer
+ buf = +"ABCDE" * 5 # large buffer
@file.sysread(15, buf).should == buf
buf.should == "012345678901234"
end
it "coerces the second argument to string and uses it as a buffer" do
- buf = "ABCDE"
+ buf = +"ABCDE"
(obj = mock("buff")).should_receive(:to_str).any_number_of_times.and_return(buf)
@file.sysread(15, obj).should == buf
buf.should == "012345678901234"
@@ -50,6 +50,11 @@ describe "IO#sysread on a file" do
@file.sysread(5).should == "56789"
end
+ it "raises an error when called after buffered reads" do
+ @file.readline
+ -> { @file.sysread(5) }.should.raise(IOError)
+ end
+
it "reads normally even when called immediately after a buffered IO#read" do
@file.read(15)
@file.sysread(5).should == "56789"
@@ -58,13 +63,13 @@ describe "IO#sysread on a file" do
it "does not raise error if called after IO#read followed by IO#write" do
@file.read(5)
@file.write("abcde")
- -> { @file.sysread(5) }.should_not raise_error(IOError)
+ -> { @file.sysread(5) }.should_not.raise(IOError)
end
it "does not raise error if called after IO#read followed by IO#syswrite" do
@file.read(5)
@file.syswrite("abcde")
- -> { @file.sysread(5) }.should_not raise_error(IOError)
+ -> { @file.sysread(5) }.should_not.raise(IOError)
end
it "reads updated content after the flushed buffered IO#write" do
@@ -77,7 +82,37 @@ describe "IO#sysread on a file" do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.sysread(5) }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.sysread(5) }.should.raise(IOError)
+ end
+
+ it "immediately returns an empty string if the length argument is 0" do
+ @file.sysread(0).should == ""
+ end
+
+ it "immediately returns the given buffer if the length argument is 0" do
+ buffer = +"existing content"
+ @file.sysread(0, buffer).should == buffer
+ buffer.should == "existing content"
+ end
+
+ it "discards the existing buffer content upon successful read" do
+ buffer = +"existing content"
+ @file.sysread(11, buffer).should.equal?(buffer)
+ buffer.should == "01234567890"
+ end
+
+ it "discards the existing buffer content upon error" do
+ buffer = +"existing content"
+ @file.seek(0, :END)
+ -> { @file.sysread(1, buffer) }.should.raise(EOFError)
+ buffer.should.empty?
+ end
+
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ string = @file.sysread(10, buffer)
+
+ buffer.encoding.should == Encoding::ISO_8859_1
end
end
@@ -95,4 +130,8 @@ describe "IO#sysread" do
@write.syswrite "ab"
@read.sysread(3).should == "ab"
end
+
+ it "raises ArgumentError when length is less than 0" do
+ -> { @read.sysread(-1) }.should.raise(ArgumentError)
+ end
end
diff --git a/spec/ruby/core/io/sysseek_spec.rb b/spec/ruby/core/io/sysseek_spec.rb
index df894734e3..2384895dc5 100644
--- a/spec/ruby/core/io/sysseek_spec.rb
+++ b/spec/ruby/core/io/sysseek_spec.rb
@@ -4,7 +4,7 @@ require_relative 'fixtures/classes'
require_relative 'shared/pos'
describe "IO#sysseek" do
- it_behaves_like :io_set_pos, :seek
+ it_behaves_like :io_set_pos, :sysseek
end
describe "IO#sysseek" do
@@ -23,7 +23,12 @@ describe "IO#sysseek" do
it "raises an error when called after buffered reads" do
@io.readline
- -> { @io.sysseek(-5, IO::SEEK_CUR) }.should raise_error(IOError)
+ -> { @io.sysseek(-5, IO::SEEK_CUR) }.should.raise(IOError)
+ end
+
+ it "seeks normally even when called immediately after a buffered IO#read" do
+ @io.read(15)
+ @io.sysseek(-5, IO::SEEK_CUR).should == 10
end
it "moves the read position relative to the start with SEEK_SET" do
@@ -36,7 +41,7 @@ describe "IO#sysseek" do
# this is the safest way of checking the EOF when
# sys-* methods are invoked
- -> { @io.sysread(1) }.should raise_error(EOFError)
+ -> { @io.sysread(1) }.should.raise(EOFError)
@io.sysseek(-25, IO::SEEK_END)
@io.sysread(7).should == "cinco.\n"
diff --git a/spec/ruby/core/io/syswrite_spec.rb b/spec/ruby/core/io/syswrite_spec.rb
index a28cc62174..8bf61a27c3 100644
--- a/spec/ruby/core/io/syswrite_spec.rb
+++ b/spec/ruby/core/io/syswrite_spec.rb
@@ -29,6 +29,16 @@ describe "IO#syswrite on a file" do
end
end
+ it "does not modify the passed argument" do
+ File.open(@filename, "w") do |f|
+ f.set_encoding(Encoding::IBM437)
+ # A character whose codepoint differs between UTF-8 and IBM437
+ f.syswrite("ƒ".freeze)
+ end
+
+ File.binread(@filename).bytes.should == [198, 146]
+ end
+
it "warns if called immediately after a buffered IO#write" do
@file.write("abcde")
-> { @file.syswrite("fghij") }.should complain(/syswrite/)
@@ -68,4 +78,5 @@ end
describe "IO#syswrite" do
it_behaves_like :io_write, :syswrite
+ it_behaves_like :io_write_no_transcode, :syswrite
end
diff --git a/spec/ruby/core/io/to_i_spec.rb b/spec/ruby/core/io/to_i_spec.rb
index acf138c663..1d0cf2a1f6 100644
--- a/spec/ruby/core/io/to_i_spec.rb
+++ b/spec/ruby/core/io/to_i_spec.rb
@@ -7,6 +7,6 @@ describe "IO#to_i" do
end
it "raises IOError on closed stream" do
- -> { IOSpecs.closed_io.to_i }.should raise_error(IOError)
+ -> { IOSpecs.closed_io.to_i }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/to_io_spec.rb b/spec/ruby/core/io/to_io_spec.rb
index 55a0977740..0b1809dffa 100644
--- a/spec/ruby/core/io/to_io_spec.rb
+++ b/spec/ruby/core/io/to_io_spec.rb
@@ -11,11 +11,11 @@ describe "IO#to_io" do
end
it "returns self for open stream" do
- @io.to_io.should equal(@io)
+ @io.to_io.should.equal?(@io)
end
it "returns self for closed stream" do
io = IOSpecs.closed_io
- io.to_io.should equal(io)
+ io.to_io.should.equal?(io)
end
end
diff --git a/spec/ruby/core/io/try_convert_spec.rb b/spec/ruby/core/io/try_convert_spec.rb
index 5fbd10b6fa..7600b01b75 100644
--- a/spec/ruby/core/io/try_convert_spec.rb
+++ b/spec/ruby/core/io/try_convert_spec.rb
@@ -13,7 +13,7 @@ describe "IO.try_convert" do
end
it "returns the passed IO object" do
- IO.try_convert(@io).should equal(@io)
+ IO.try_convert(@io).should.equal?(@io)
end
it "does not call #to_io on an IO instance" do
@@ -24,26 +24,26 @@ describe "IO.try_convert" do
it "calls #to_io to coerce an object" do
obj = mock("io")
obj.should_receive(:to_io).and_return(@io)
- IO.try_convert(obj).should equal(@io)
+ IO.try_convert(obj).should.equal?(@io)
end
it "returns nil when the passed object does not respond to #to_io" do
- IO.try_convert(mock("io")).should be_nil
+ IO.try_convert(mock("io")).should == nil
end
it "return nil when BasicObject is passed" do
- IO.try_convert(BasicObject.new).should be_nil
+ IO.try_convert(BasicObject.new).should == nil
end
it "raises a TypeError if the object does not return an IO from #to_io" do
obj = mock("io")
obj.should_receive(:to_io).and_return("io")
- -> { IO.try_convert(obj) }.should raise_error(TypeError)
+ -> { IO.try_convert(obj) }.should raise_consistent_error(TypeError, "can't convert MockObject into IO (MockObject#to_io gives String)")
end
it "propagates an exception raised by #to_io" do
obj = mock("io")
obj.should_receive(:to_io).and_raise(TypeError.new)
- ->{ IO.try_convert(obj) }.should raise_error(TypeError)
+ ->{ IO.try_convert(obj) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/io/ungetbyte_spec.rb b/spec/ruby/core/io/ungetbyte_spec.rb
index 9d189f8abb..e0615cd76c 100644
--- a/spec/ruby/core/io/ungetbyte_spec.rb
+++ b/spec/ruby/core/io/ungetbyte_spec.rb
@@ -13,12 +13,12 @@ describe "IO#ungetbyte" do
end
it "does nothing when passed nil" do
- @io.ungetbyte(nil).should be_nil
+ @io.ungetbyte(nil).should == nil
@io.getbyte.should == 97
end
it "puts back each byte in a String argument" do
- @io.ungetbyte("cat").should be_nil
+ @io.ungetbyte("cat").should == nil
@io.getbyte.should == 99
@io.getbyte.should == 97
@io.getbyte.should == 116
@@ -29,49 +29,26 @@ describe "IO#ungetbyte" do
str = mock("io ungetbyte")
str.should_receive(:to_str).and_return("dog")
- @io.ungetbyte(str).should be_nil
+ @io.ungetbyte(str).should == nil
@io.getbyte.should == 100
@io.getbyte.should == 111
@io.getbyte.should == 103
@io.getbyte.should == 97
end
- ruby_version_is ''...'2.6' do
- it "puts back one byte for a Fixnum argument..." do
- @io.ungetbyte(4095).should be_nil
+ it "never raises RangeError" do
+ for i in [4095, 0x4f7574206f6620636861722072616e67ff] do
+ @io.ungetbyte(i).should == nil
@io.getbyte.should == 255
end
-
- it "... but not for Bignum argument (eh?)" do
- -> {
- @io.ungetbyte(0x4f7574206f6620636861722072616e6765)
- }.should raise_error(TypeError)
- end
- end
-
- ruby_version_is '2.6'...'2.6.1' do
- it "is an RangeError if the integer is not in 8bit" do
- for i in [4095, 0x4f7574206f6620636861722072616e6765] do
- -> { @io.ungetbyte(i) }.should raise_error(RangeError)
- end
- end
- end
-
- ruby_version_is '2.6.1' do
- it "never raises RangeError" do
- for i in [4095, 0x4f7574206f6620636861722072616e67ff] do
- @io.ungetbyte(i).should be_nil
- @io.getbyte.should == 255
- end
- end
end
it "raises IOError on stream not opened for reading" do
- -> { STDOUT.ungetbyte(42) }.should raise_error(IOError, "not opened for reading")
+ -> { STDOUT.ungetbyte(42) }.should.raise(IOError, "not opened for reading")
end
it "raises an IOError if the IO is closed" do
@io.close
- -> { @io.ungetbyte(42) }.should raise_error(IOError)
+ -> { @io.ungetbyte(42) }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/ungetc_spec.rb b/spec/ruby/core/io/ungetc_spec.rb
index dc31c3743a..4a9e67f126 100644
--- a/spec/ruby/core/io/ungetc_spec.rb
+++ b/spec/ruby/core/io/ungetc_spec.rb
@@ -100,27 +100,17 @@ describe "IO#ungetc" do
it "makes subsequent unbuffered operations to raise IOError" do
@io.getc
@io.ungetc(100)
- -> { @io.sysread(1) }.should raise_error(IOError)
+ -> { @io.sysread(1) }.should.raise(IOError)
end
- ruby_version_is "0"..."2.8" do
- it "does not affect the stream and returns nil when passed nil" do
- @io.getc.should == ?V
- @io.ungetc(nil)
- @io.getc.should == ?o
- end
- end
-
- ruby_version_is "2.8" do
- it "raises TypeError if passed nil" do
- @io.getc.should == ?V
- proc{@io.ungetc(nil)}.should raise_error(TypeError)
- end
+ it "raises TypeError if passed nil" do
+ @io.getc.should == ?V
+ proc{@io.ungetc(nil)}.should.raise(TypeError)
end
it "puts one or more characters back in the stream" do
@io.gets
- @io.ungetc("Aquí ").should be_nil
+ @io.ungetc("Aquí ").should == nil
@io.gets.chomp.should == "Aquí Qui è la linea due."
end
@@ -128,21 +118,21 @@ describe "IO#ungetc" do
chars = mock("io ungetc")
chars.should_receive(:to_str).and_return("Aquí ")
- @io.ungetc(chars).should be_nil
+ @io.ungetc(chars).should == nil
@io.gets.chomp.should == "Aquí Voici la ligne une."
end
it "returns nil when invoked on stream that was not yet read" do
- @io.ungetc(100).should be_nil
+ @io.ungetc(100).should == nil
end
it "raises IOError on stream not opened for reading" do
- -> { STDOUT.ungetc(100) }.should raise_error(IOError, "not opened for reading")
+ -> { STDOUT.ungetc(100) }.should.raise(IOError, "not opened for reading")
end
it "raises IOError on closed stream" do
@io.getc
@io.close
- -> { @io.ungetc(100) }.should raise_error(IOError)
+ -> { @io.ungetc(100) }.should.raise(IOError)
end
end
diff --git a/spec/ruby/core/io/write_nonblock_spec.rb b/spec/ruby/core/io/write_nonblock_spec.rb
index a8b9ce0a36..a6bd43c058 100644
--- a/spec/ruby/core/io/write_nonblock_spec.rb
+++ b/spec/ruby/core/io/write_nonblock_spec.rb
@@ -31,15 +31,26 @@ platform_is_not :windows do
end
end
+ it "does not modify the passed argument" do
+ File.open(@filename, "w") do |f|
+ f.set_encoding(Encoding::IBM437)
+ # A character whose codepoint differs between UTF-8 and IBM437
+ f.write_nonblock("ƒ".freeze)
+ end
+
+ File.binread(@filename).bytes.should == [198, 146]
+ end
+
it "checks if the file is writable if writing zero bytes" do
-> {
- @readonly_file.write_nonblock("")
- }.should raise_error(IOError)
+ @readonly_file.write_nonblock("")
+ }.should.raise(IOError)
end
end
describe "IO#write_nonblock" do
it_behaves_like :io_write, :write_nonblock
+ it_behaves_like :io_write_no_transcode, :write_nonblock
end
end
@@ -56,12 +67,12 @@ describe 'IO#write_nonblock' do
it "raises an exception extending IO::WaitWritable when the write would block" do
-> {
loop { @write.write_nonblock('a' * 10_000) }
- }.should raise_error(IO::WaitWritable) { |e|
+ }.should.raise(IO::WaitWritable) { |e|
platform_is_not :windows do
- e.should be_kind_of(Errno::EAGAIN)
+ e.should.is_a?(Errno::EAGAIN)
end
platform_is :windows do
- e.should be_kind_of(Errno::EWOULDBLOCK)
+ e.should.is_a?(Errno::EWOULDBLOCK)
end
}
end
diff --git a/spec/ruby/core/io/write_spec.rb b/spec/ruby/core/io/write_spec.rb
index 9c9b8b254b..1a745ba012 100644
--- a/spec/ruby/core/io/write_spec.rb
+++ b/spec/ruby/core/io/write_spec.rb
@@ -21,11 +21,7 @@ describe "IO#write on a file" do
end
it "does not check if the file is writable if writing zero bytes" do
- -> { @readonly_file.write("") }.should_not raise_error
- end
-
- it "returns a length of 0 when writing a blank string" do
- @file.write('').should == 0
+ -> { @readonly_file.write("") }.should_not.raise
end
before :each do
@@ -40,10 +36,44 @@ describe "IO#write on a file" do
Encoding.default_internal = @internal
end
+ it "returns a length of 0 when writing a blank string" do
+ @file.write('').should == 0
+ end
+
+ it "returns a length of 0 when writing blank strings" do
+ @file.write('', '', '').should == 0
+ end
+
+ it "returns a length of 0 when passed no arguments" do
+ @file.write().should == 0
+ end
+
it "returns the number of bytes written" do
@file.write("hellø").should == 6
end
+ it "does not modify the passed argument" do
+ File.open(@filename, "w") do |f|
+ f.set_encoding(Encoding::IBM437)
+ # A character whose codepoint differs between UTF-8 and IBM437
+ f.write("ƒ".freeze)
+ end
+
+ File.binread(@filename).bytes.should == [159]
+ end
+
+ it "does not modify arguments when passed multiple arguments and external encoding not set" do
+ a, b = "a".freeze, "b".freeze
+
+ File.open(@filename, "w") do |f|
+ f.write(a, b)
+ end
+
+ File.binread(@filename).bytes.should == [97, 98]
+ a.encoding.should == Encoding::UTF_8
+ b.encoding.should == Encoding::UTF_8
+ end
+
it "uses the encoding from the given option for non-ascii encoding" do
File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file|
file.write("hi").should == 8
@@ -51,15 +81,39 @@ describe "IO#write on a file" do
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
end
- it "uses an :open_args option" do
- IO.write(@filename, 'hi', open_args: ["w", nil, {encoding: Encoding::UTF_32LE}]).should == 8
+ it "uses the encoding from the given option for non-ascii encoding even if in binary mode" do
+ File.open(@filename, "w", encoding: Encoding::UTF_32LE, binmode: true) do |file|
+ file.should.binmode?
+ file.write("hi").should == 8
+ end
+ File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
+
+ File.open(@filename, "wb", encoding: Encoding::UTF_32LE) do |file|
+ file.should.binmode?
+ file.write("hi").should == 8
+ end
+ File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
+ end
+
+ it "uses the encoding from the given option for non-ascii encoding when multiple arguments passes" do
+ File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file|
+ file.write("h", "i").should == 8
+ end
+ File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
+ end
+
+ it "ignores the 'bom|' prefix" do
+ File.open(@filename, "w", encoding: 'bom|utf-8') do |file|
+ file.write("hi")
+ end
+ File.binread(@filename).should == "hi"
end
it "raises a invalid byte sequence error if invalid bytes are being written" do
# pack "\xFEhi" to avoid utf-8 conflict
xFEhi = ([254].pack('C*') + 'hi').force_encoding('utf-8')
File.open(@filename, "w", encoding: Encoding::US_ASCII) do |file|
- -> { file.write(xFEhi) }.should raise_error(Encoding::InvalidByteSequenceError)
+ -> { file.write(xFEhi) }.should.raise(Encoding::InvalidByteSequenceError)
end
end
@@ -72,6 +126,20 @@ describe "IO#write on a file" do
res = "H#{ë}ll#{ö}"
File.binread(@filename).should == res.force_encoding(Encoding::BINARY)
end
+
+ platform_is_not :windows do
+ it "writes binary data if no encoding is given and multiple arguments passed" do
+ File.open(@filename, "w") do |file|
+ file.write("\x87".b, "ą") # 0x87 isn't a valid UTF-8 binary representation of a character
+ end
+ File.binread(@filename).bytes.should == [0x87, 0xC4, 0x85]
+
+ File.open(@filename, "w") do |file|
+ file.write("\x61".encode("utf-32le"), "ą")
+ end
+ File.binread(@filename).bytes.should == [0x61, 0x00, 0x00, 0x00, 0xC4, 0x85]
+ end
+ end
end
describe "IO.write" do
@@ -86,10 +154,44 @@ describe "IO.write" do
File.read(@filename).should == "\0\0hi"
end
+ it "requires mode to be specified in :open_args" do
+ -> {
+ IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true}])
+ }.should.raise(IOError, "not opened for writing")
+
+ IO.write(@filename, 'hi', open_args: ["w", {encoding: Encoding::UTF_32LE, binmode: true}]).should == 8
+ IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true, mode: "w"}]).should == 8
+ end
+
+ it "requires mode to be specified in :open_args even if flags option passed" do
+ -> {
+ IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true, flags: File::CREAT}])
+ }.should.raise(IOError, "not opened for writing")
+
+ IO.write(@filename, 'hi', open_args: ["w", {encoding: Encoding::UTF_32LE, binmode: true, flags: File::CREAT}]).should == 8
+ IO.write(@filename, 'hi', open_args: [{encoding: Encoding::UTF_32LE, binmode: true, flags: File::CREAT, mode: "w"}]).should == 8
+ end
+
it "uses the given encoding and returns the number of bytes written" do
IO.write(@filename, 'hi', mode: "w", encoding: Encoding::UTF_32LE).should == 8
end
+ it "raises ArgumentError if encoding is specified in mode parameter and is given as :encoding option" do
+ -> {
+ IO.write(@filename, 'hi', mode: "w:UTF-16LE:UTF-16BE", encoding: Encoding::UTF_32LE)
+ }.should.raise(ArgumentError, "encoding specified twice")
+
+ -> {
+ IO.write(@filename, 'hi', mode: "w:UTF-16BE", encoding: Encoding::UTF_32LE)
+ }.should.raise(ArgumentError, "encoding specified twice")
+ end
+
+ it "writes the file with the permissions in the :perm parameter" do
+ rm_r @filename
+ IO.write(@filename, 'write :perm spec', mode: "w", perm: 0o755).should == 16
+ (File.stat(@filename).mode & 0o777) == 0o755
+ end
+
it "writes binary data if no encoding is given" do
IO.write(@filename, 'Hëllö'.encode('ISO-8859-1'))
xEB = [235].pack('C*')
@@ -108,23 +210,39 @@ describe "IO.write" do
rm_r @fifo
end
- it "writes correctly" do
- thr = Thread.new do
- IO.read(@fifo)
- end
- begin
- string = "hi"
- IO.write(@fifo, string).should == string.length
- ensure
- thr.join
+ # rb_cloexec_open() is currently missing a retry on EINTR.
+ # @ioquatix is looking into fixing it. Quarantined until it's done.
+ quarantine! do
+ it "writes correctly" do
+ thr = Thread.new do
+ IO.read(@fifo)
+ end
+ begin
+ string = "hi"
+ IO.write(@fifo, string).should == string.length
+ ensure
+ thr.join
+ end
end
end
end
+
+ ruby_version_is ""..."4.0" do
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ -> {
+ -> {
+ IO.write("|cat", "xxx")
+ }.should output_to_fd("xxx")
+ }.should complain(/IO process creation with a leading '\|'/)
+ end
+ end
end
end
describe "IO#write" do
it_behaves_like :io_write, :write
+ it_behaves_like :io_write_transcode, :write
it "accepts multiple arguments" do
IO.pipe do |r, w|
@@ -162,3 +280,25 @@ platform_is :windows do
end
end
end
+
+describe "IO#write on STDOUT" do
+ # https://bugs.ruby-lang.org/issues/14413
+ platform_is_not :windows do
+ it "raises SignalException SIGPIPE if the stream is closed instead of Errno::EPIPE like other IOs" do
+ stderr_file = tmp("stderr")
+ begin
+ IO.popen([*ruby_exe, "-e", "loop { puts :ok }"], "r", err: stderr_file) do |io|
+ io.gets.should == "ok\n"
+ io.close
+ end
+ status = $?
+ status.should_not.success?
+ status.should.signaled?
+ Signal.signame(status.termsig).should == 'PIPE'
+ File.read(stderr_file).should.empty?
+ ensure
+ rm_r stderr_file
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/Array_spec.rb b/spec/ruby/core/kernel/Array_spec.rb
index b4a8bb7599..063faf7097 100644
--- a/spec/ruby/core/kernel/Array_spec.rb
+++ b/spec/ruby/core/kernel/Array_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "Kernel" do
it "has private instance method Array()" do
- Kernel.should have_private_instance_method(:Array)
+ Kernel.private_instance_methods(false).should.include?(:Array)
end
end
@@ -77,14 +77,14 @@ describe :kernel_Array, shared: true do
obj = mock("Array() string")
obj.should_receive(:to_ary).and_return("string")
- -> { @object.send(@method, obj) }.should raise_error(TypeError)
+ -> { @object.send(@method, obj) }.should.raise(TypeError)
end
it "raises a TypeError if #to_a does not return an Array" do
obj = mock("Array() string")
obj.should_receive(:to_a).and_return("string")
- -> { @object.send(@method, obj) }.should raise_error(TypeError)
+ -> { @object.send(@method, obj) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/kernel/Complex_spec.rb b/spec/ruby/core/kernel/Complex_spec.rb
index 37f9843931..92ce183cc8 100644
--- a/spec/ruby/core/kernel/Complex_spec.rb
+++ b/spec/ruby/core/kernel/Complex_spec.rb
@@ -1,4 +1,6 @@
require_relative '../../spec_helper'
+require_relative '../../shared/kernel/complex'
+require_relative 'fixtures/Complex'
describe "Kernel.Complex()" do
describe "when passed [Complex, Complex]" do
@@ -17,19 +19,19 @@ describe "Kernel.Complex()" do
describe "when passed [Integer, Integer]" do
it "returns a new Complex number" do
- Complex(1, 2).should be_an_instance_of(Complex)
+ Complex(1, 2).should.instance_of?(Complex)
Complex(1, 2).real.should == 1
Complex(1, 2).imag.should == 2
- Complex(-3, -5).should be_an_instance_of(Complex)
+ Complex(-3, -5).should.instance_of?(Complex)
Complex(-3, -5).real.should == -3
Complex(-3, -5).imag.should == -5
- Complex(3.5, -4.5).should be_an_instance_of(Complex)
+ Complex(3.5, -4.5).should.instance_of?(Complex)
Complex(3.5, -4.5).real.should == 3.5
Complex(3.5, -4.5).imag.should == -4.5
- Complex(bignum_value, 30).should be_an_instance_of(Complex)
+ Complex(bignum_value, 30).should.instance_of?(Complex)
Complex(bignum_value, 30).real.should == bignum_value
Complex(bignum_value, 30).imag.should == 30
end
@@ -39,26 +41,111 @@ describe "Kernel.Complex()" do
it "returns a new Complex number with 0 as the imaginary component" do
# Guard against the Mathn library
guard -> { !defined?(Math.rsqrt) } do
- Complex(1).should be_an_instance_of(Complex)
+ Complex(1).should.instance_of?(Complex)
Complex(1).imag.should == 0
Complex(1).real.should == 1
- Complex(-3).should be_an_instance_of(Complex)
+ Complex(-3).should.instance_of?(Complex)
Complex(-3).imag.should == 0
Complex(-3).real.should == -3
- Complex(-4.5).should be_an_instance_of(Complex)
+ Complex(-4.5).should.instance_of?(Complex)
Complex(-4.5).imag.should == 0
Complex(-4.5).real.should == -4.5
- Complex(bignum_value).should be_an_instance_of(Complex)
+ Complex(bignum_value).should.instance_of?(Complex)
Complex(bignum_value).imag.should == 0
Complex(bignum_value).real.should == bignum_value
end
end
end
- describe "when passed a String" do
+ describe "when passed [String]" do
+ it_behaves_like :kernel_complex, :Complex_method, KernelSpecs
+
+ context "invalid argument" do
+ it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do
+ -> {
+ Complex("79+4i".encode("UTF-16"))
+ }.should.raise(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16")
+ end
+
+ it "raises ArgumentError for unrecognised Strings" do
+ -> {
+ Complex("ruby")
+ }.should.raise(ArgumentError, 'invalid value for convert(): "ruby"')
+ end
+
+ it "raises ArgumentError for trailing garbage" do
+ -> {
+ Complex("79+4iruby")
+ }.should.raise(ArgumentError, 'invalid value for convert(): "79+4iruby"')
+ end
+
+ it "does not understand Float::INFINITY" do
+ -> {
+ Complex("Infinity")
+ }.should.raise(ArgumentError, 'invalid value for convert(): "Infinity"')
+
+ -> {
+ Complex("-Infinity")
+ }.should.raise(ArgumentError, 'invalid value for convert(): "-Infinity"')
+ end
+
+ it "does not understand Float::NAN" do
+ -> {
+ Complex("NaN")
+ }.should.raise(ArgumentError, 'invalid value for convert(): "NaN"')
+ end
+
+ it "does not understand a sequence of _" do
+ -> {
+ Complex("7__9+4__0i")
+ }.should.raise(ArgumentError, 'invalid value for convert(): "7__9+4__0i"')
+ end
+
+ it "does not allow null-byte" do
+ -> {
+ Complex("1-2i\0")
+ }.should.raise(ArgumentError, "string contains null byte")
+ end
+ end
+
+ context "invalid argument and exception: false passed" do
+ it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do
+ -> {
+ Complex("79+4i".encode("UTF-16"), exception: false)
+ }.should.raise(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16")
+ end
+
+ it "returns nil for unrecognised Strings" do
+ Complex("ruby", exception: false).should == nil
+ end
+
+ it "returns nil when trailing garbage" do
+ Complex("79+4iruby", exception: false).should == nil
+ end
+
+ it "returns nil for Float::INFINITY" do
+ Complex("Infinity", exception: false).should == nil
+ Complex("-Infinity", exception: false).should == nil
+ end
+
+ it "returns nil for Float::NAN" do
+ Complex("NaN", exception: false).should == nil
+ end
+
+ it "returns nil when there is a sequence of _" do
+ Complex("7__9+4__0i", exception: false).should == nil
+ end
+
+ it "returns nil when String contains null-byte" do
+ Complex("1-2i\0", exception: false).should == nil
+ end
+ end
+ end
+
+ describe "when passes [String, String]" do
it "needs to be reviewed for spec completeness"
end
@@ -73,7 +160,7 @@ describe "Kernel.Complex()" do
it "returns the passed argument" do
n = mock_numeric("unreal")
n.should_receive(:real?).any_number_of_times.and_return(false)
- Complex(n).should equal(n)
+ Complex(n).should.equal?(n)
end
end
@@ -82,8 +169,8 @@ describe "Kernel.Complex()" do
n = mock_numeric("real")
n.should_receive(:real?).any_number_of_times.and_return(true)
result = Complex(n)
- result.real.should equal(n)
- result.imag.should equal(0)
+ result.real.should.equal?(n)
+ result.imag.should.equal?(0)
end
end
@@ -98,7 +185,7 @@ describe "Kernel.Complex()" do
n2.should_receive(:real?).any_number_of_times.and_return(r2)
n2.should_receive(:*).with(Complex(0, 1)).and_return(n3)
n1.should_receive(:+).with(n3).and_return(n4)
- Complex(n1, n2).should equal(n4)
+ Complex(n1, n2).should.equal?(n4)
end
end
end
@@ -110,8 +197,8 @@ describe "Kernel.Complex()" do
n1.should_receive(:real?).any_number_of_times.and_return(true)
n2.should_receive(:real?).any_number_of_times.and_return(true)
result = Complex(n1, n2)
- result.real.should equal(n1)
- result.imag.should equal(n2)
+ result.real.should.equal?(n1)
+ result.imag.should.equal?(n2)
end
end
@@ -120,68 +207,70 @@ describe "Kernel.Complex()" do
n = mock("n")
c = Complex(0, 0)
n.should_receive(:to_c).and_return(c)
- Complex(n).should equal(c)
+ Complex(n).should.equal?(c)
end
end
describe "when passed a non-Numeric second argument" do
it "raises TypeError" do
- -> { Complex(:sym, :sym) }.should raise_error(TypeError)
- -> { Complex(0, :sym) }.should raise_error(TypeError)
+ -> { Complex(:sym, :sym) }.should.raise(TypeError)
+ -> { Complex(0, :sym) }.should.raise(TypeError)
end
end
describe "when passed nil" do
it "raises TypeError" do
- -> { Complex(nil) }.should raise_error(TypeError, "can't convert nil into Complex")
- -> { Complex(0, nil) }.should raise_error(TypeError, "can't convert nil into Complex")
- -> { Complex(nil, 0) }.should raise_error(TypeError, "can't convert nil into Complex")
+ -> { Complex(nil) }.should.raise(TypeError, "can't convert nil into Complex")
+ -> { Complex(0, nil) }.should.raise(TypeError, "can't convert nil into Complex")
+ -> { Complex(nil, 0) }.should.raise(TypeError, "can't convert nil into Complex")
end
end
- ruby_version_is "2.6" do
- describe "when passed exception: false" do
- describe "and [Numeric]" do
- it "returns a complex number" do
- Complex("123", exception: false).should == Complex(123)
- end
+ describe "when passed exception: false" do
+ describe "and [Numeric]" do
+ it "returns a complex number" do
+ Complex("123", exception: false).should == Complex(123)
end
+ end
- describe "and [non-Numeric]" do
- it "swallows an error" do
- Complex(:sym, exception: false).should == nil
- end
+ describe "and [non-Numeric]" do
+ it "swallows an error" do
+ Complex(:sym, exception: false).should == nil
end
+ end
- describe "and [non-Numeric, Numeric] argument" do
- it "throws a TypeError" do
- -> { Complex(:sym, 0, exception: false) }.should raise_error(TypeError, "not a real")
- end
+ describe "and [non-Numeric, Numeric] argument" do
+ it "throws a TypeError" do
+ -> { Complex(:sym, 0, exception: false) }.should.raise(TypeError, "not a real")
end
+ end
- describe "and [anything, non-Numeric] argument" do
- it "swallows an error" do
- Complex("a", :sym, exception: false).should == nil
- Complex(:sym, :sym, exception: false).should == nil
- Complex(0, :sym, exception: false).should == nil
- end
+ describe "and [anything, non-Numeric] argument" do
+ it "swallows an error" do
+ Complex("a", :sym, exception: false).should == nil
+ Complex(:sym, :sym, exception: false).should == nil
+ Complex(0, :sym, exception: false).should == nil
end
+ end
- describe "and non-numeric String arguments" do
- it "swallows an error" do
- Complex("a", "b", exception: false).should == nil
- Complex("a", 0, exception: false).should == nil
- Complex(0, "b", exception: false).should == nil
- end
+ describe "and non-numeric String arguments" do
+ it "swallows an error" do
+ Complex("a", "b", exception: false).should == nil
+ Complex("a", 0, exception: false).should == nil
+ Complex(0, "b", exception: false).should == nil
end
+ end
- describe "and nil arguments" do
- it "swallows an error" do
- Complex(nil, exception: false).should == nil
- Complex(0, nil, exception: false).should == nil
- Complex(nil, 0, exception: false).should == nil
- end
+ describe "and nil arguments" do
+ it "swallows an error" do
+ Complex(nil, exception: false).should == nil
+ Complex(0, nil, exception: false).should == nil
+ Complex(nil, 0, exception: false).should == nil
end
end
end
+
+ it "freezes its result" do
+ Complex(1).frozen?.should == true
+ end
end
diff --git a/spec/ruby/core/kernel/Float_spec.rb b/spec/ruby/core/kernel/Float_spec.rb
index af64fcbe86..f5566067ba 100644
--- a/spec/ruby/core/kernel/Float_spec.rb
+++ b/spec/ruby/core/kernel/Float_spec.rb
@@ -6,7 +6,7 @@ describe :kernel_float, shared: true do
float = 1.12
float2 = @object.send(:Float, float)
float2.should == float
- float2.should equal float
+ float2.should.equal? float
end
it "returns a Float for Fixnums" do
@@ -22,26 +22,26 @@ describe :kernel_float, shared: true do
end
it "raises an ArgumentError for nil" do
- -> { @object.send(:Float, nil) }.should raise_error(TypeError)
+ -> { @object.send(:Float, nil) }.should.raise(TypeError)
end
it "returns the identical NaN for NaN" do
nan = nan_value
- nan.nan?.should be_true
+ nan.nan?.should == true
nan2 = @object.send(:Float, nan)
- nan2.nan?.should be_true
- nan2.should equal(nan)
+ nan2.nan?.should == true
+ nan2.should.equal?(nan)
end
it "returns the same Infinity for Infinity" do
infinity = infinity_value
infinity2 = @object.send(:Float, infinity)
infinity2.should == infinity_value
- infinity.should equal(infinity2)
+ infinity.should.equal?(infinity2)
end
it "converts Strings to floats without calling #to_f" do
- string = "10"
+ string = +"10"
string.should_not_receive(:to_f)
@object.send(:Float, string).should == 10.0
end
@@ -51,30 +51,30 @@ describe :kernel_float, shared: true do
end
it "raises an ArgumentError for a String of word characters" do
- -> { @object.send(:Float, "float") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "float") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a String with string in error message" do
- -> { @object.send(:Float, "foo") }.should raise_error(ArgumentError) { |e|
+ -> { @object.send(:Float, "foo") }.should.raise(ArgumentError) { |e|
e.message.should == 'invalid value for Float(): "foo"'
}
end
it "raises an ArgumentError if there are two decimal points in the String" do
- -> { @object.send(:Float, "10.0.0") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "10.0.0") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a String of numbers followed by word characters" do
- -> { @object.send(:Float, "10D") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "10D") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a String of word characters followed by numbers" do
- -> { @object.send(:Float, "D10") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "D10") }.should.raise(ArgumentError)
end
it "is strict about the string form even across newlines" do
- -> { @object.send(:Float, "not a number\n10") }.should raise_error(ArgumentError)
- -> { @object.send(:Float, "10\nnot a number") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "not a number\n10") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "10\nnot a number") }.should.raise(ArgumentError)
end
it "converts String subclasses to floats without calling #to_f" do
@@ -96,17 +96,17 @@ describe :kernel_float, shared: true do
end
it "raises an ArgumentError if a + or - is embedded in a String" do
- -> { @object.send(:Float, "1+1") }.should raise_error(ArgumentError)
- -> { @object.send(:Float, "1-1") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "1+1") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "1-1") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if a String has a trailing + or -" do
- -> { @object.send(:Float, "11+") }.should raise_error(ArgumentError)
- -> { @object.send(:Float, "11-") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "11+") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "11-") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a String with a leading _" do
- -> { @object.send(:Float, "_1") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "_1") }.should.raise(ArgumentError)
end
it "returns a value for a String with an embedded _" do
@@ -114,31 +114,31 @@ describe :kernel_float, shared: true do
end
it "raises an ArgumentError for a String with a trailing _" do
- -> { @object.send(:Float, "10_") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "10_") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a String of \\0" do
- -> { @object.send(:Float, "\0") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "\0") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a String with a leading \\0" do
- -> { @object.send(:Float, "\01") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "\01") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a String with an embedded \\0" do
- -> { @object.send(:Float, "1\01") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "1\01") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a String with a trailing \\0" do
- -> { @object.send(:Float, "1\0") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "1\0") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a String that is just an empty space" do
- -> { @object.send(:Float, " ") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, " ") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a String that with an embedded space" do
- -> { @object.send(:Float, "1 2") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "1 2") }.should.raise(ArgumentError)
end
it "returns a value for a String with a leading space" do
@@ -157,13 +157,33 @@ describe :kernel_float, shared: true do
@object.send(:Float, "1\t\n").should == 1.0
end
+ ruby_version_is ""..."3.4" do
+ it "raises ArgumentError if a fractional part is missing" do
+ -> { @object.send(:Float, "1.") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "+1.") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "-1.") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "1.e+0") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "1.e-2") }.should.raise(ArgumentError)
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "allows String representation without a fractional part" do
+ @object.send(:Float, "1.").should == 1.0
+ @object.send(:Float, "+1.").should == 1.0
+ @object.send(:Float, "-1.").should == -1.0
+ @object.send(:Float, "1.e+0").should == 1.0
+ @object.send(:Float, "1.e-2").should be_close(0.01, TOLERANCE)
+ end
+ end
+
%w(e E).each do |e|
it "raises an ArgumentError if #{e} is the trailing character" do
- -> { @object.send(:Float, "2#{e}") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "2#{e}") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if #{e} is the leading character" do
- -> { @object.send(:Float, "#{e}2") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "#{e}2") }.should.raise(ArgumentError)
end
it "returns Infinity for '2#{e}1000'" do
@@ -181,18 +201,18 @@ describe :kernel_float, shared: true do
end
it "raises an exception if a space is embedded on either side of the '#{e}'" do
- -> { @object.send(:Float, "2 0#{e}100") }.should raise_error(ArgumentError)
- -> { @object.send(:Float, "20#{e}1 00") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "2 0#{e}100") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "20#{e}1 00") }.should.raise(ArgumentError)
end
it "raises an exception if there's a leading _ on either side of the '#{e}'" do
- -> { @object.send(:Float, "_20#{e}100") }.should raise_error(ArgumentError)
- -> { @object.send(:Float, "20#{e}_100") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "_20#{e}100") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "20#{e}_100") }.should.raise(ArgumentError)
end
it "raises an exception if there's a trailing _ on either side of the '#{e}'" do
- -> { @object.send(:Float, "20_#{e}100") }.should raise_error(ArgumentError)
- -> { @object.send(:Float, "20#{e}100_") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "20_#{e}100") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "20#{e}100_") }.should.raise(ArgumentError)
end
it "allows decimal points on the left side of the '#{e}'" do
@@ -200,63 +220,111 @@ describe :kernel_float, shared: true do
end
it "raises an ArgumentError if there's a decimal point on the right side of the '#{e}'" do
- -> { @object.send(:Float, "20#{e}2.0") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "20#{e}2.0") }.should.raise(ArgumentError)
end
end
- describe "for hexadecimal literals with binary exponent" do
- %w(p P).each do |p|
- it "interprets the fractional part (on the left side of '#{p}') in hexadecimal" do
- @object.send(:Float, "0x10#{p}0").should == 16.0
- end
+ context "for hexadecimal literals" do
+ it "interprets the 0x prefix as hexadecimal" do
+ @object.send(:Float, "0x10").should == 16.0
+ @object.send(:Float, "0x0F").should == 15.0
+ @object.send(:Float, "0x0f").should == 15.0
+ end
- it "interprets the exponent (on the right of '#{p}') in decimal" do
- @object.send(:Float, "0x1#{p}10").should == 1024.0
- end
+ it "interprets negative hex value" do
+ @object.send(:Float, "-0x10").should == -16.0
+ end
- it "raises an ArgumentError if #{p} is the trailing character" do
- -> { @object.send(:Float, "0x1#{p}") }.should raise_error(ArgumentError)
- end
+ it "accepts embedded _ if the number does not contain a-f" do
+ @object.send(:Float, "0x1_0").should == 16.0
+ end
- it "raises an ArgumentError if #{p} is the leading character" do
- -> { @object.send(:Float, "0x#{p}1") }.should raise_error(ArgumentError)
+ ruby_version_is ""..."3.4.3" do
+ it "does not accept embedded _ if the number contains a-f" do
+ -> { @object.send(:Float, "0x1_0a") }.should.raise(ArgumentError)
+ @object.send(:Float, "0x1_0a", exception: false).should == nil
end
+ end
- it "returns Infinity for '0x1#{p}10000'" do
- @object.send(:Float, "0x1#{p}10000").should == Float::INFINITY
+ ruby_version_is "3.4.3" do
+ it "accepts embedded _ if the number contains a-f" do
+ @object.send(:Float, "0x1_0a").should == 0x10a.to_f
end
+ end
- it "returns 0 for '0x1#{p}-10000'" do
- @object.send(:Float, "0x1#{p}-10000").should == 0
- end
+ it "does not accept _ before, after or inside the 0x prefix" do
+ -> { @object.send(:Float, "_0x10") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "0_x10") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "0x_10") }.should.raise(ArgumentError)
+ @object.send(:Float, "_0x10", exception: false).should == nil
+ @object.send(:Float, "0_x10", exception: false).should == nil
+ @object.send(:Float, "0x_10", exception: false).should == nil
+ end
- it "allows embedded _ in a number on either side of the #{p}" do
- @object.send(:Float, "0x1_0#{p}10").should == 16384.0
- @object.send(:Float, "0x10#{p}1_0").should == 16384.0
- @object.send(:Float, "0x1_0#{p}1_0").should == 16384.0
- end
+ it "parses negative hexadecimal string as negative float" do
+ @object.send(:Float, "-0x7b").should == -123.0
+ end
- it "raises an exception if a space is embedded on either side of the '#{p}'" do
- -> { @object.send(:Float, "0x1 0#{p}10") }.should raise_error(ArgumentError)
- -> { @object.send(:Float, "0x10#{p}1 0") }.should raise_error(ArgumentError)
+ ruby_version_is "3.4" do
+ it "accepts a fractional part" do
+ @object.send(:Float, "0x0.8").should == 0.5
end
+ end
- it "raises an exception if there's a leading _ on either side of the '#{p}'" do
- -> { @object.send(:Float, "0x_10#{p}10") }.should raise_error(ArgumentError)
- -> { @object.send(:Float, "0x10#{p}_10") }.should raise_error(ArgumentError)
- end
+ describe "with binary exponent" do
+ %w(p P).each do |p|
+ it "interprets the fractional part (on the left side of '#{p}') in hexadecimal" do
+ @object.send(:Float, "0x10#{p}0").should == 16.0
+ end
- it "raises an exception if there's a trailing _ on either side of the '#{p}'" do
- -> { @object.send(:Float, "0x10_#{p}10") }.should raise_error(ArgumentError)
- -> { @object.send(:Float, "0x10#{p}10_") }.should raise_error(ArgumentError)
- end
+ it "interprets the exponent (on the right of '#{p}') in decimal" do
+ @object.send(:Float, "0x1#{p}10").should == 1024.0
+ end
- it "allows hexadecimal points on the left side of the '#{p}'" do
- @object.send(:Float, "0x1.8#{p}0").should == 1.5
- end
+ it "raises an ArgumentError if #{p} is the trailing character" do
+ -> { @object.send(:Float, "0x1#{p}") }.should.raise(ArgumentError)
+ end
+
+ it "raises an ArgumentError if #{p} is the leading character" do
+ -> { @object.send(:Float, "0x#{p}1") }.should.raise(ArgumentError)
+ end
+
+ it "returns Infinity for '0x1#{p}10000'" do
+ @object.send(:Float, "0x1#{p}10000").should == Float::INFINITY
+ end
+
+ it "returns 0 for '0x1#{p}-10000'" do
+ @object.send(:Float, "0x1#{p}-10000").should == 0
+ end
+
+ it "allows embedded _ in a number on either side of the #{p}" do
+ @object.send(:Float, "0x1_0#{p}10").should == 16384.0
+ @object.send(:Float, "0x10#{p}1_0").should == 16384.0
+ @object.send(:Float, "0x1_0#{p}1_0").should == 16384.0
+ end
- it "raises an ArgumentError if there's a decimal point on the right side of the '#{p}'" do
- -> { @object.send(:Float, "0x1#{p}1.0") }.should raise_error(ArgumentError)
+ it "raises an exception if a space is embedded on either side of the '#{p}'" do
+ -> { @object.send(:Float, "0x1 0#{p}10") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "0x10#{p}1 0") }.should.raise(ArgumentError)
+ end
+
+ it "raises an exception if there's a leading _ on either side of the '#{p}'" do
+ -> { @object.send(:Float, "0x_10#{p}10") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "0x10#{p}_10") }.should.raise(ArgumentError)
+ end
+
+ it "raises an exception if there's a trailing _ on either side of the '#{p}'" do
+ -> { @object.send(:Float, "0x10_#{p}10") }.should.raise(ArgumentError)
+ -> { @object.send(:Float, "0x10#{p}10_") }.should.raise(ArgumentError)
+ end
+
+ it "allows hexadecimal points on the left side of the '#{p}'" do
+ @object.send(:Float, "0x1.8#{p}0").should == 1.5
+ end
+
+ it "raises an ArgumentError if there's a decimal point on the right side of the '#{p}'" do
+ -> { @object.send(:Float, "0x1#{p}1.0") }.should.raise(ArgumentError)
+ end
end
end
end
@@ -276,57 +344,55 @@ describe :kernel_float, shared: true do
nan = nan_value
(nan_to_f = mock('NaN')).should_receive(:to_f).once.and_return(nan)
nan2 = @object.send(:Float, nan_to_f)
- nan2.nan?.should be_true
- nan2.should equal(nan)
+ nan2.nan?.should == true
+ nan2.should.equal?(nan)
end
- it "returns the identical Infinity if to_f is called and it returns Infinity" do
+ it "returns the identical Infinity if #to_f is called and it returns Infinity" do
infinity = infinity_value
(infinity_to_f = mock('Infinity')).should_receive(:to_f).once.and_return(infinity)
infinity2 = @object.send(:Float, infinity_to_f)
- infinity2.should equal(infinity)
+ infinity2.should.equal?(infinity)
end
it "raises a TypeError if #to_f is not provided" do
- -> { @object.send(:Float, mock('x')) }.should raise_error(TypeError)
+ -> { @object.send(:Float, mock('x')) }.should.raise(TypeError)
end
it "raises a TypeError if #to_f returns a String" do
(obj = mock('ha!')).should_receive(:to_f).once.and_return('ha!')
- -> { @object.send(:Float, obj) }.should raise_error(TypeError)
+ -> { @object.send(:Float, obj) }.should.raise(TypeError)
end
it "raises a TypeError if #to_f returns an Integer" do
(obj = mock('123')).should_receive(:to_f).once.and_return(123)
- -> { @object.send(:Float, obj) }.should raise_error(TypeError)
+ -> { @object.send(:Float, obj) }.should.raise(TypeError)
end
it "raises a RangeError when passed a Complex argument" do
c = Complex(2, 3)
- -> { @object.send(:Float, c) }.should raise_error(RangeError)
+ -> { @object.send(:Float, c) }.should.raise(RangeError)
end
- ruby_version_is "2.6" do
- describe "when passed exception: false" do
- describe "and valid input" do
- it "returns a Float number" do
- @object.send(:Float, 1, exception: false).should == 1.0
- @object.send(:Float, "1", exception: false).should == 1.0
- @object.send(:Float, "1.23", exception: false).should == 1.23
- end
+ describe "when passed exception: false" do
+ describe "and valid input" do
+ it "returns a Float number" do
+ @object.send(:Float, 1, exception: false).should == 1.0
+ @object.send(:Float, "1", exception: false).should == 1.0
+ @object.send(:Float, "1.23", exception: false).should == 1.23
end
+ end
- describe "and invalid input" do
- it "swallows an error" do
- @object.send(:Float, "abc", exception: false).should == nil
- @object.send(:Float, :sym, exception: false).should == nil
- end
+ describe "and invalid input" do
+ it "swallows an error" do
+ @object.send(:Float, "abc", exception: false).should == nil
+ @object.send(:Float, :sym, exception: false).should == nil
end
+ end
- describe "and nil" do
- it "swallows it" do
- @object.send(:Float, nil, exception: false).should == nil
- end
+ describe "and nil" do
+ it "swallows it" do
+ @object.send(:Float, nil, exception: false).should == nil
end
end
end
@@ -342,6 +408,6 @@ end
describe "Kernel#Float" do
it "is a private method" do
- Kernel.should have_private_instance_method(:Float)
+ Kernel.private_instance_methods(false).should.include?(:Float)
end
end
diff --git a/spec/ruby/core/kernel/Hash_spec.rb b/spec/ruby/core/kernel/Hash_spec.rb
index cbe098a8ac..ee16f56a90 100644
--- a/spec/ruby/core/kernel/Hash_spec.rb
+++ b/spec/ruby/core/kernel/Hash_spec.rb
@@ -13,7 +13,7 @@ end
describe "Kernel" do
it "has private instance method Hash()" do
- Kernel.should have_private_instance_method(:Hash)
+ Kernel.private_instance_methods(false).should.include?(:Hash)
end
end
@@ -43,14 +43,14 @@ describe :kernel_Hash, shared: true do
end
it "raises a TypeError if it doesn't respond to #to_hash" do
- -> { @object.send(@method, mock("")) }.should raise_error(TypeError)
+ -> { @object.send(@method, mock("")) }.should.raise(TypeError)
end
it "raises a TypeError if #to_hash does not return an Hash" do
obj = mock("Hash() string")
obj.should_receive(:to_hash).and_return("string")
- -> { @object.send(@method, obj) }.should raise_error(TypeError)
+ -> { @object.send(@method, obj) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/kernel/Integer_spec.rb b/spec/ruby/core/kernel/Integer_spec.rb
index 59b41d37e6..978fd8ef08 100644
--- a/spec/ruby/core/kernel/Integer_spec.rb
+++ b/spec/ruby/core/kernel/Integer_spec.rb
@@ -10,38 +10,52 @@ describe :kernel_integer, shared: true do
Integer(100).should == 100
end
- ruby_version_is ""..."2.6" do
- it "uncritically return the value of to_int even if it is not an Integer" do
- obj = mock("object")
- obj.should_receive(:to_int).and_return("1")
- obj.should_not_receive(:to_i)
- Integer(obj).should == "1"
- end
+ it "raises a TypeError when to_int returns not-an-Integer object and to_i returns nil" do
+ obj = mock("object")
+ obj.should_receive(:to_int).and_return("1")
+ obj.should_receive(:to_i).and_return(nil)
+ -> {
+ Integer(obj)
+ }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_i gives nil)")
end
- ruby_version_is "2.6" do
- it "raises a TypeError when to_int returns not-an-Integer object and to_i returns nil" do
- obj = mock("object")
- obj.should_receive(:to_int).and_return("1")
- obj.should_receive(:to_i).and_return(nil)
- -> { Integer(obj) }.should raise_error(TypeError)
- end
+ it "return a result of to_i when to_int does not return an Integer" do
+ obj = mock("object")
+ obj.should_receive(:to_int).and_return("1")
+ obj.should_receive(:to_i).and_return(42)
+ Integer(obj).should == 42
+ end
- it "return a result of to_i when to_int does not return an Integer" do
- obj = mock("object")
- obj.should_receive(:to_int).and_return("1")
- obj.should_receive(:to_i).and_return(42)
- Integer(obj).should == 42
- end
+ it "returns a result of to_str" do
+ obj = mock("obj")
+ obj.should_receive(:to_str).and_return("1")
+
+ Integer(obj).should == 1
+ end
+
+ it "returns a result of to_int when both to_int and to_str are defined" do
+ obj = mock("obj")
+ obj.should_receive(:to_int).and_return(1)
+ obj.should_not_receive(:to_str)
+
+ Integer(obj).should == 1
+ end
+
+ it "returns a result of to_str when both to_str and to_i are defined" do
+ obj = mock("obj")
+ obj.should_receive(:to_str).and_return("1")
+ obj.should_not_receive(:to_i)
+
+ Integer(obj).should == 1
end
it "raises a TypeError when passed nil" do
- -> { Integer(nil) }.should raise_error(TypeError)
+ -> { Integer(nil) }.should.raise(TypeError, "can't convert nil into Integer")
end
- it "returns a Fixnum or Bignum object" do
- Integer(2).should be_an_instance_of(Fixnum)
- Integer(9**99).should be_an_instance_of(Bignum)
+ it "returns an Integer object" do
+ Integer(2).should.instance_of?(Integer)
+ Integer(9**99).should.instance_of?(Integer)
end
it "truncates Floats" do
@@ -78,101 +92,105 @@ describe :kernel_integer, shared: true do
it "raises a TypeError if to_i returns a value that is not an Integer" do
obj = mock("object")
obj.should_receive(:to_i).and_return("1")
- -> { Integer(obj) }.should raise_error(TypeError)
+ -> {
+ Integer(obj)
+ }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_i gives String)")
end
it "raises a TypeError if no to_int or to_i methods exist" do
obj = mock("object")
- -> { Integer(obj) }.should raise_error(TypeError)
+ -> {
+ Integer(obj)
+ }.should.raise(TypeError, "can't convert MockObject into Integer")
end
it "raises a TypeError if to_int returns nil and no to_i exists" do
obj = mock("object")
obj.should_receive(:to_i).and_return(nil)
- -> { Integer(obj) }.should raise_error(TypeError)
+ -> {
+ Integer(obj)
+ }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_i gives nil)")
end
it "raises a FloatDomainError when passed NaN" do
- -> { Integer(nan_value) }.should raise_error(FloatDomainError)
+ -> { Integer(nan_value) }.should.raise(FloatDomainError)
end
it "raises a FloatDomainError when passed Infinity" do
- -> { Integer(infinity_value) }.should raise_error(FloatDomainError)
+ -> { Integer(infinity_value) }.should.raise(FloatDomainError)
end
- ruby_version_is "2.6" do
- describe "when passed exception: false" do
- describe "and to_i returns a value that is not an Integer" do
- it "swallows an error" do
- obj = mock("object")
- obj.should_receive(:to_i).and_return("1")
- Integer(obj, exception: false).should == nil
- end
+ describe "when passed exception: false" do
+ describe "and to_i returns a value that is not an Integer" do
+ it "swallows an error" do
+ obj = mock("object")
+ obj.should_receive(:to_i).and_return("1")
+ Integer(obj, exception: false).should == nil
end
+ end
- describe "and no to_int or to_i methods exist" do
- it "swallows an error" do
- obj = mock("object")
- Integer(obj, exception: false).should == nil
- end
+ describe "and no to_int or to_i methods exist" do
+ it "swallows an error" do
+ obj = mock("object")
+ Integer(obj, exception: false).should == nil
end
+ end
- describe "and to_int returns nil and no to_i exists" do
- it "swallows an error" do
- obj = mock("object")
- obj.should_receive(:to_i).and_return(nil)
- Integer(obj, exception: false).should == nil
- end
+ describe "and to_int returns nil and no to_i exists" do
+ it "swallows an error" do
+ obj = mock("object")
+ obj.should_receive(:to_i).and_return(nil)
+ Integer(obj, exception: false).should == nil
end
+ end
- describe "and passed NaN" do
- it "swallows an error" do
- Integer(nan_value, exception: false).should == nil
- end
+ describe "and passed NaN" do
+ it "swallows an error" do
+ Integer(nan_value, exception: false).should == nil
end
+ end
- describe "and passed Infinity" do
- it "swallows an error" do
- Integer(infinity_value, exception: false).should == nil
- end
+ describe "and passed Infinity" do
+ it "swallows an error" do
+ Integer(infinity_value, exception: false).should == nil
end
+ end
- describe "and passed nil" do
- it "swallows an error" do
- Integer(nil, exception: false).should == nil
- end
+ describe "and passed nil" do
+ it "swallows an error" do
+ Integer(nil, exception: false).should == nil
end
+ end
- describe "and passed a String that contains numbers" do
- it "normally parses it and returns an Integer" do
- Integer("42", exception: false).should == 42
- end
+ describe "and passed a String that contains numbers" do
+ it "normally parses it and returns an Integer" do
+ Integer("42", exception: false).should == 42
end
+ end
- describe "and passed a String that can't be converted to an Integer" do
- it "swallows an error" do
- Integer("abc", exception: false).should == nil
- end
+ describe "and passed a String that can't be converted to an Integer" do
+ it "swallows an error" do
+ Integer("abc", exception: false).should == nil
end
end
end
end
-describe "Integer() given a String", shared: true do
+describe :kernel_integer_string, shared: true do
it "raises an ArgumentError if the String is a null byte" do
- -> { Integer("\0") }.should raise_error(ArgumentError)
+ -> { Integer("\0") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if the String starts with a null byte" do
- -> { Integer("\01") }.should raise_error(ArgumentError)
+ -> { Integer("\01") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if the String ends with a null byte" do
- -> { Integer("1\0") }.should raise_error(ArgumentError)
+ -> { Integer("1\0") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if the String contains a null byte" do
- -> { Integer("1\01") }.should raise_error(ArgumentError)
+ -> { Integer("1\01") }.should.raise(ArgumentError)
end
it "ignores leading whitespace" do
@@ -188,13 +206,13 @@ describe "Integer() given a String", shared: true do
end
it "raises an ArgumentError if there are leading _s" do
- -> { Integer("_1") }.should raise_error(ArgumentError)
- -> { Integer("___1") }.should raise_error(ArgumentError)
+ -> { Integer("_1") }.should.raise(ArgumentError)
+ -> { Integer("___1") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if there are trailing _s" do
- -> { Integer("1_") }.should raise_error(ArgumentError)
- -> { Integer("1___") }.should raise_error(ArgumentError)
+ -> { Integer("1_") }.should.raise(ArgumentError)
+ -> { Integer("1___") }.should.raise(ArgumentError)
end
it "ignores an embedded _" do
@@ -202,8 +220,8 @@ describe "Integer() given a String", shared: true do
end
it "raises an ArgumentError if there are multiple embedded _s" do
- -> { Integer("1__1") }.should raise_error(ArgumentError)
- -> { Integer("1___1") }.should raise_error(ArgumentError)
+ -> { Integer("1__1") }.should.raise(ArgumentError)
+ -> { Integer("1___1") }.should.raise(ArgumentError)
end
it "ignores a single leading +" do
@@ -211,17 +229,17 @@ describe "Integer() given a String", shared: true do
end
it "raises an ArgumentError if there is a space between the + and number" do
- -> { Integer("+ 1") }.should raise_error(ArgumentError)
+ -> { Integer("+ 1") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if there are multiple leading +s" do
- -> { Integer("++1") }.should raise_error(ArgumentError)
- -> { Integer("+++1") }.should raise_error(ArgumentError)
+ -> { Integer("++1") }.should.raise(ArgumentError)
+ -> { Integer("+++1") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if there are trailing +s" do
- -> { Integer("1+") }.should raise_error(ArgumentError)
- -> { Integer("1+++") }.should raise_error(ArgumentError)
+ -> { Integer("1+") }.should.raise(ArgumentError)
+ -> { Integer("1+++") }.should.raise(ArgumentError)
end
it "makes the number negative if there's a leading -" do
@@ -229,47 +247,45 @@ describe "Integer() given a String", shared: true do
end
it "raises an ArgumentError if there are multiple leading -s" do
- -> { Integer("--1") }.should raise_error(ArgumentError)
- -> { Integer("---1") }.should raise_error(ArgumentError)
+ -> { Integer("--1") }.should.raise(ArgumentError)
+ -> { Integer("---1") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if there are trailing -s" do
- -> { Integer("1-") }.should raise_error(ArgumentError)
- -> { Integer("1---") }.should raise_error(ArgumentError)
+ -> { Integer("1-") }.should.raise(ArgumentError)
+ -> { Integer("1---") }.should.raise(ArgumentError)
end
it "raises an ArgumentError if there is a period" do
- -> { Integer("0.0") }.should raise_error(ArgumentError)
+ -> { Integer("0.0") }.should.raise(ArgumentError)
end
it "raises an ArgumentError for an empty String" do
- -> { Integer("") }.should raise_error(ArgumentError)
+ -> { Integer("") }.should.raise(ArgumentError)
end
- ruby_version_is "2.6" do
- describe "when passed exception: false" do
- describe "and multiple leading -s" do
- it "swallows an error" do
- Integer("---1", exception: false).should == nil
- end
+ describe "when passed exception: false" do
+ describe "and multiple leading -s" do
+ it "swallows an error" do
+ Integer("---1", exception: false).should == nil
end
+ end
- describe "and multiple trailing -s" do
- it "swallows an error" do
- Integer("1---", exception: false).should == nil
- end
+ describe "and multiple trailing -s" do
+ it "swallows an error" do
+ Integer("1---", exception: false).should == nil
end
+ end
- describe "and an argument that contains a period" do
- it "swallows an error" do
- Integer("0.0", exception: false).should == nil
- end
+ describe "and an argument that contains a period" do
+ it "swallows an error" do
+ Integer("0.0", exception: false).should == nil
end
+ end
- describe "and an empty string" do
- it "swallows an error" do
- Integer("", exception: false).should == nil
- end
+ describe "and an empty string" do
+ it "swallows an error" do
+ Integer("", exception: false).should == nil
end
end
end
@@ -295,7 +311,7 @@ describe "Integer() given a String", shared: true do
end
it "raises an ArgumentError if the number cannot be parsed as hex" do
- -> { Integer("0#{x}g") }.should raise_error(ArgumentError)
+ -> { Integer("0#{x}g") }.should.raise(ArgumentError)
end
end
@@ -316,7 +332,7 @@ describe "Integer() given a String", shared: true do
end
it "raises an ArgumentError if the number cannot be parsed as binary" do
- -> { Integer("0#{b}2") }.should raise_error(ArgumentError)
+ -> { Integer("0#{b}2") }.should.raise(ArgumentError)
end
end
@@ -337,7 +353,7 @@ describe "Integer() given a String", shared: true do
end
it "raises an ArgumentError if the number cannot be parsed as octal" do
- -> { Integer("0#{o}9") }.should raise_error(ArgumentError)
+ -> { Integer("0#{o}9") }.should.raise(ArgumentError)
end
end
@@ -358,26 +374,26 @@ describe "Integer() given a String", shared: true do
end
it "raises an ArgumentError if the number cannot be parsed as decimal" do
- -> { Integer("0#{d}a") }.should raise_error(ArgumentError)
+ -> { Integer("0#{d}a") }.should.raise(ArgumentError)
end
end
end
-describe "Integer() given a String and base", shared: true do
+describe :kernel_integer_string_base, shared: true do
it "raises an ArgumentError if the String is a null byte" do
- -> { Integer("\0", 2) }.should raise_error(ArgumentError)
+ -> { Integer("\0", 2) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if the String starts with a null byte" do
- -> { Integer("\01", 3) }.should raise_error(ArgumentError)
+ -> { Integer("\01", 3) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if the String ends with a null byte" do
- -> { Integer("1\0", 4) }.should raise_error(ArgumentError)
+ -> { Integer("1\0", 4) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if the String contains a null byte" do
- -> { Integer("1\01", 5) }.should raise_error(ArgumentError)
+ -> { Integer("1\01", 5) }.should.raise(ArgumentError)
end
it "ignores leading whitespace" do
@@ -393,13 +409,13 @@ describe "Integer() given a String and base", shared: true do
end
it "raises an ArgumentError if there are leading _s" do
- -> { Integer("_1", 7) }.should raise_error(ArgumentError)
- -> { Integer("___1", 7) }.should raise_error(ArgumentError)
+ -> { Integer("_1", 7) }.should.raise(ArgumentError)
+ -> { Integer("___1", 7) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if there are trailing _s" do
- -> { Integer("1_", 12) }.should raise_error(ArgumentError)
- -> { Integer("1___", 12) }.should raise_error(ArgumentError)
+ -> { Integer("1_", 12) }.should.raise(ArgumentError)
+ -> { Integer("1___", 12) }.should.raise(ArgumentError)
end
it "ignores an embedded _" do
@@ -407,8 +423,8 @@ describe "Integer() given a String and base", shared: true do
end
it "raises an ArgumentError if there are multiple embedded _s" do
- -> { Integer("1__1", 4) }.should raise_error(ArgumentError)
- -> { Integer("1___1", 4) }.should raise_error(ArgumentError)
+ -> { Integer("1__1", 4) }.should.raise(ArgumentError)
+ -> { Integer("1___1", 4) }.should.raise(ArgumentError)
end
it "ignores a single leading +" do
@@ -416,17 +432,17 @@ describe "Integer() given a String and base", shared: true do
end
it "raises an ArgumentError if there is a space between the + and number" do
- -> { Integer("+ 1", 3) }.should raise_error(ArgumentError)
+ -> { Integer("+ 1", 3) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if there are multiple leading +s" do
- -> { Integer("++1", 3) }.should raise_error(ArgumentError)
- -> { Integer("+++1", 3) }.should raise_error(ArgumentError)
+ -> { Integer("++1", 3) }.should.raise(ArgumentError)
+ -> { Integer("+++1", 3) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if there are trailing +s" do
- -> { Integer("1+", 3) }.should raise_error(ArgumentError)
- -> { Integer("1+++", 12) }.should raise_error(ArgumentError)
+ -> { Integer("1+", 3) }.should.raise(ArgumentError)
+ -> { Integer("1+++", 12) }.should.raise(ArgumentError)
end
it "makes the number negative if there's a leading -" do
@@ -434,29 +450,29 @@ describe "Integer() given a String and base", shared: true do
end
it "raises an ArgumentError if there are multiple leading -s" do
- -> { Integer("--1", 9) }.should raise_error(ArgumentError)
- -> { Integer("---1", 9) }.should raise_error(ArgumentError)
+ -> { Integer("--1", 9) }.should.raise(ArgumentError)
+ -> { Integer("---1", 9) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if there are trailing -s" do
- -> { Integer("1-", 12) }.should raise_error(ArgumentError)
- -> { Integer("1---", 12) }.should raise_error(ArgumentError)
+ -> { Integer("1-", 12) }.should.raise(ArgumentError)
+ -> { Integer("1---", 12) }.should.raise(ArgumentError)
end
it "raises an ArgumentError if there is a period" do
- -> { Integer("0.0", 3) }.should raise_error(ArgumentError)
+ -> { Integer("0.0", 3) }.should.raise(ArgumentError)
end
it "raises an ArgumentError for an empty String" do
- -> { Integer("", 12) }.should raise_error(ArgumentError)
+ -> { Integer("", 12) }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a base of 1" do
- -> { Integer("1", 1) }.should raise_error(ArgumentError)
+ -> { Integer("1", 1) }.should.raise(ArgumentError)
end
it "raises an ArgumentError for a base of 37" do
- -> { Integer("1", 37) }.should raise_error(ArgumentError)
+ -> { Integer("1", 37) }.should.raise(ArgumentError)
end
it "accepts wholly lowercase alphabetic strings for bases > 10" do
@@ -484,8 +500,8 @@ describe "Integer() given a String and base", shared: true do
end
it "raises an ArgumentError for letters invalid in the given base" do
- -> { Integer('z',19) }.should raise_error(ArgumentError)
- -> { Integer('c00o',2) }.should raise_error(ArgumentError)
+ -> { Integer('z',19) }.should.raise(ArgumentError)
+ -> { Integer('c00o',2) }.should.raise(ArgumentError)
end
%w(x X).each do |x|
@@ -506,12 +522,12 @@ describe "Integer() given a String and base", shared: true do
2.upto(15) do |base|
it "raises an ArgumentError if the number begins with 0#{x} and the base is #{base}" do
- -> { Integer("0#{x}1", base) }.should raise_error(ArgumentError)
+ -> { Integer("0#{x}1", base) }.should.raise(ArgumentError)
end
end
it "raises an ArgumentError if the number cannot be parsed as hex and the base is 16" do
- -> { Integer("0#{x}g", 16) }.should raise_error(ArgumentError)
+ -> { Integer("0#{x}g", 16) }.should.raise(ArgumentError)
end
end
@@ -532,7 +548,7 @@ describe "Integer() given a String and base", shared: true do
end
it "raises an ArgumentError if the number cannot be parsed as binary and the base is 2" do
- -> { Integer("0#{b}2", 2) }.should raise_error(ArgumentError)
+ -> { Integer("0#{b}2", 2) }.should.raise(ArgumentError)
end
end
@@ -553,12 +569,12 @@ describe "Integer() given a String and base", shared: true do
end
it "raises an ArgumentError if the number cannot be parsed as octal and the base is 8" do
- -> { Integer("0#{o}9", 8) }.should raise_error(ArgumentError)
+ -> { Integer("0#{o}9", 8) }.should.raise(ArgumentError)
end
2.upto(7) do |base|
it "raises an ArgumentError if the number begins with 0#{o} and the base is #{base}" do
- -> { Integer("0#{o}1", base) }.should raise_error(ArgumentError)
+ -> { Integer("0#{o}1", base) }.should.raise(ArgumentError)
end
end
end
@@ -580,34 +596,45 @@ describe "Integer() given a String and base", shared: true do
end
it "raises an ArgumentError if the number cannot be parsed as decimal and the base is 10" do
- -> { Integer("0#{d}a", 10) }.should raise_error(ArgumentError)
+ -> { Integer("0#{d}a", 10) }.should.raise(ArgumentError)
end
2.upto(9) do |base|
it "raises an ArgumentError if the number begins with 0#{d} and the base is #{base}" do
- -> { Integer("0#{d}1", base) }.should raise_error(ArgumentError)
+ -> { Integer("0#{d}1", base) }.should.raise(ArgumentError)
end
end
+ end
- it "raises an ArgumentError if a base is given for a non-String value" do
- -> { Integer(98, 15) }.should raise_error(ArgumentError)
- end
+ it "raises an ArgumentError if a base is given for a non-String value" do
+ -> { Integer(98, 15) }.should.raise(ArgumentError, "base specified for non string value")
end
- ruby_version_is "2.6" do
- describe "when passed exception: false" do
- describe "and valid argument" do
- it "returns an Integer number" do
- Integer("100", 10, exception: false).should == 100
- Integer("100", 2, exception: false).should == 4
- end
+ it "tries to convert the base to an integer using to_int" do
+ obj = mock('8')
+ obj.should_receive(:to_int).and_return(8)
+
+ Integer("777", obj).should == 0777
+ end
+
+ it "raises a TypeError if it is not an integer and does not respond to #to_i" do
+ -> {
+ Integer("777", "8")
+ }.should.raise(TypeError, "no implicit conversion of String into Integer")
+ end
+
+ describe "when passed exception: false" do
+ describe "and valid argument" do
+ it "returns an Integer number" do
+ Integer("100", 10, exception: false).should == 100
+ Integer("100", 2, exception: false).should == 4
end
+ end
- describe "and invalid argument" do
- it "swallows an error" do
- Integer("999", 2, exception: false).should == nil
- Integer("abc", 10, exception: false).should == nil
- end
+ describe "and invalid argument" do
+ it "swallows an error" do
+ Integer("999", 2, exception: false).should == nil
+ Integer("abc", 10, exception: false).should == nil
end
end
end
@@ -616,176 +643,176 @@ end
describe :kernel_Integer, shared: true do
it "raises an ArgumentError when the String contains digits out of range of radix 2" do
str = "23456789abcdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 2) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 2) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 3" do
str = "3456789abcdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 3) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 3) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 4" do
str = "456789abcdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 4) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 4) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 5" do
str = "56789abcdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 5) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 5) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 6" do
str = "6789abcdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 6) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 6) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 7" do
str = "789abcdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 7) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 7) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 8" do
str = "89abcdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 8) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 8) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 9" do
str = "9abcdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 9) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 9) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 10" do
str = "abcdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 10) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 10) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 11" do
str = "bcdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 11) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 11) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 12" do
str = "cdefghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 12) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 12) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 13" do
str = "defghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 13) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 13) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 14" do
str = "efghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 14) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 14) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 15" do
str = "fghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 15) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 15) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 16" do
str = "ghijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 16) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 16) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 17" do
str = "hijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 17) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 17) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 18" do
str = "ijklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 18) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 18) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 19" do
str = "jklmnopqrstuvwxyz"
- -> { @object.send(@method, str, 19) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 19) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 20" do
str = "klmnopqrstuvwxyz"
- -> { @object.send(@method, str, 20) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 20) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 21" do
str = "lmnopqrstuvwxyz"
- -> { @object.send(@method, str, 21) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 21) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 22" do
str = "mnopqrstuvwxyz"
- -> { @object.send(@method, str, 22) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 22) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 23" do
str = "nopqrstuvwxyz"
- -> { @object.send(@method, str, 23) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 23) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 24" do
str = "opqrstuvwxyz"
- -> { @object.send(@method, str, 24) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 24) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 25" do
str = "pqrstuvwxyz"
- -> { @object.send(@method, str, 25) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 25) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 26" do
str = "qrstuvwxyz"
- -> { @object.send(@method, str, 26) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 26) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 27" do
str = "rstuvwxyz"
- -> { @object.send(@method, str, 27) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 27) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 28" do
str = "stuvwxyz"
- -> { @object.send(@method, str, 28) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 28) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 29" do
str = "tuvwxyz"
- -> { @object.send(@method, str, 29) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 29) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 30" do
str = "uvwxyz"
- -> { @object.send(@method, str, 30) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 30) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 31" do
str = "vwxyz"
- -> { @object.send(@method, str, 31) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 31) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 32" do
str = "wxyz"
- -> { @object.send(@method, str, 32) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 32) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 33" do
str = "xyz"
- -> { @object.send(@method, str, 33) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 33) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 34" do
str = "yz"
- -> { @object.send(@method, str, 34) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 34) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 35" do
str = "z"
- -> { @object.send(@method, str, 35) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, str, 35) }.should.raise(ArgumentError)
end
it "raises an ArgumentError when the String contains digits out of range of radix 36" do
- -> { @object.send(@method, "{", 36) }.should raise_error(ArgumentError)
+ -> { @object.send(@method, "{", 36) }.should.raise(ArgumentError)
end
end
@@ -794,9 +821,9 @@ describe "Kernel.Integer" do
# TODO: fix these specs
it_behaves_like :kernel_integer, :Integer, Kernel
- it_behaves_like "Integer() given a String", :Integer
+ it_behaves_like :kernel_integer_string, :Integer
- it_behaves_like "Integer() given a String and base", :Integer
+ it_behaves_like :kernel_integer_string_base, :Integer
it "is a public method" do
Kernel.Integer(10).should == 10
@@ -808,11 +835,11 @@ describe "Kernel#Integer" do
# TODO: fix these specs
it_behaves_like :kernel_integer, :Integer, Object.new
- it_behaves_like "Integer() given a String", :Integer
+ it_behaves_like :kernel_integer_string, :Integer
- it_behaves_like "Integer() given a String and base", :Integer
+ it_behaves_like :kernel_integer_string_base, :Integer
it "is a private method" do
- Kernel.should have_private_instance_method(:Integer)
+ Kernel.private_instance_methods(false).should.include?(:Integer)
end
end
diff --git a/spec/ruby/core/kernel/Rational_spec.rb b/spec/ruby/core/kernel/Rational_spec.rb
index 2d1051db7f..b13ab4cf55 100644
--- a/spec/ruby/core/kernel/Rational_spec.rb
+++ b/spec/ruby/core/kernel/Rational_spec.rb
@@ -1,6 +1,236 @@
require_relative '../../spec_helper'
-require_relative '../../shared/rational/Rational'
+require_relative '../rational/fixtures/rational'
describe "Kernel.Rational" do
- it_behaves_like :kernel_Rational, :Rational
+ describe "passed Integer" do
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ it "returns a new Rational number with 1 as the denominator" do
+ Rational(1).should.eql?(Rational(1, 1))
+ Rational(-3).should.eql?(Rational(-3, 1))
+ Rational(bignum_value).should.eql?(Rational(bignum_value, 1))
+ end
+ end
+ end
+
+ describe "passed two integers" do
+ it "returns a new Rational number" do
+ rat = Rational(1, 2)
+ rat.numerator.should == 1
+ rat.denominator.should == 2
+ rat.should.instance_of?(Rational)
+
+ rat = Rational(-3, -5)
+ rat.numerator.should == 3
+ rat.denominator.should == 5
+ rat.should.instance_of?(Rational)
+
+ rat = Rational(bignum_value, 3)
+ rat.numerator.should == bignum_value
+ rat.denominator.should == 3
+ rat.should.instance_of?(Rational)
+ end
+
+ it "reduces the Rational" do
+ rat = Rational(2, 4)
+ rat.numerator.should == 1
+ rat.denominator.should == 2
+
+ rat = Rational(3, 9)
+ rat.numerator.should == 1
+ rat.denominator.should == 3
+ end
+ end
+
+ describe "when passed a String" do
+ it "converts the String to a Rational using the same method as String#to_r" do
+ r = Rational(13, 25)
+ s_r = ".52".to_r
+ r_s = Rational(".52")
+
+ r_s.should == r
+ r_s.should == s_r
+ end
+
+ it "scales the Rational value of the first argument by the Rational value of the second" do
+ Rational(".52", ".6").should == Rational(13, 15)
+ Rational(".52", "1.6").should == Rational(13, 40)
+ end
+
+ it "does not use the same method as Float#to_r" do
+ r = Rational(3, 5)
+ f_r = 0.6.to_r
+ r_s = Rational("0.6")
+
+ r_s.should == r
+ r_s.should_not == f_r
+ end
+ end
+
+ describe "when passed a Numeric" do
+ it "calls #to_r to convert the first argument to a Rational" do
+ num = RationalSpecs::SubNumeric.new(2)
+
+ Rational(num).should == Rational(2)
+ end
+ end
+
+ describe "when passed a Complex" do
+ context "[Complex]" do
+ it "returns a Rational from the real part if the imaginary part is 0" do
+ Rational(Complex(1, 0)).should == Rational(1)
+ end
+
+ it "raises a RangeError if the imaginary part is not 0" do
+ -> { Rational(Complex(1, 2)) }.should.raise(RangeError, "can't convert 1+2i into Rational")
+ end
+ end
+
+ context "[Numeric, Complex]" do
+ it "uses the real part if the imaginary part is 0" do
+ Rational(1, Complex(2, 0)).should == Rational(1, 2)
+ end
+
+ it "divides a numerator by the Complex denominator if the imaginary part is not 0" do
+ Rational(1, Complex(2, 1)).should == Complex(2/5r, -1/5r)
+ end
+ end
+ end
+
+ context "when passed neither a Numeric nor a String" do
+ it "converts to Rational with #to_r method" do
+ obj = Object.new
+ def obj.to_r; 1/2r; end
+
+ Rational(obj).should == 1/2r
+ end
+
+ it "tries to convert to Integer with #to_int method if it does not respond to #to_r" do
+ obj = Object.new
+ def obj.to_int; 1; end
+
+ Rational(obj).should == 1r
+ end
+
+ it "raises TypeError if it neither responds to #to_r nor #to_int method" do
+ -> { Rational([]) }.should.raise(TypeError, "can't convert Array into Rational")
+ -> { Rational({}) }.should.raise(TypeError, "can't convert Hash into Rational")
+ -> { Rational(nil) }.should.raise(TypeError, "can't convert nil into Rational")
+ end
+
+ it "swallows exception raised in #to_int method" do
+ object = Object.new
+ def object.to_int() raise NoMethodError; end
+
+ -> { Rational(object) }.should.raise(TypeError)
+ -> { Rational(object, 1) }.should.raise(TypeError)
+ -> { Rational(1, object) }.should.raise(TypeError)
+ end
+
+ it "raises TypeError if #to_r does not return Rational" do
+ obj = Object.new
+ def obj.to_r; []; end
+
+ -> { Rational(obj) }.should raise_consistent_error(TypeError, "can't convert Object into Rational (Object#to_r gives Array)")
+ end
+ end
+
+ it "raises a ZeroDivisionError if the second argument is 0" do
+ -> { Rational(1, 0) }.should.raise(ZeroDivisionError, "divided by 0")
+ -> { Rational(1, 0.0) }.should.raise(ZeroDivisionError, "divided by 0")
+ end
+
+ it "raises a TypeError if the first argument is nil" do
+ -> { Rational(nil) }.should.raise(TypeError, "can't convert nil into Rational")
+ end
+
+ it "raises a TypeError if the second argument is nil" do
+ -> { Rational(1, nil) }.should.raise(TypeError, "can't convert nil into Rational")
+ end
+
+ it "raises a TypeError if the first argument is a Symbol" do
+ -> { Rational(:sym) }.should.raise(TypeError)
+ end
+
+ it "raises a TypeError if the second argument is a Symbol" do
+ -> { Rational(1, :sym) }.should.raise(TypeError)
+ end
+
+ describe "when passed exception: false" do
+ describe "and [non-Numeric]" do
+ it "swallows an error" do
+ Rational(:sym, exception: false).should == nil
+ Rational("abc", exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_r" do
+ obj = Object.new
+ def obj.to_r; raise; end
+ Rational(obj, exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_int" do
+ obj = Object.new
+ def obj.to_int; raise; end
+ Rational(obj, exception: false).should == nil
+ end
+ end
+
+ describe "and [non-Numeric, Numeric]" do
+ it "swallows an error" do
+ Rational(:sym, 1, exception: false).should == nil
+ Rational("abc", 1, exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_r" do
+ obj = Object.new
+ def obj.to_r; raise; end
+ Rational(obj, 1, exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_int" do
+ obj = Object.new
+ def obj.to_int; raise; end
+ Rational(obj, 1, exception: false).should == nil
+ end
+ end
+
+ describe "and [anything, non-Numeric]" do
+ it "swallows an error" do
+ Rational(:sym, :sym, exception: false).should == nil
+ Rational("abc", :sym, exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_r" do
+ obj = Object.new
+ def obj.to_r; raise; end
+ Rational(obj, obj, exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_int" do
+ obj = Object.new
+ def obj.to_int; raise; end
+ Rational(obj, obj, exception: false).should == nil
+ end
+ end
+
+ describe "and non-Numeric String arguments" do
+ it "swallows an error" do
+ Rational("a", "b", exception: false).should == nil
+ Rational("a", 0, exception: false).should == nil
+ Rational(0, "b", exception: false).should == nil
+ end
+ end
+
+ describe "and nil arguments" do
+ it "swallows an error" do
+ Rational(nil, exception: false).should == nil
+ Rational(nil, nil, exception: false).should == nil
+ end
+ end
+ end
+
+ it "freezes its result" do
+ Rational(1).frozen?.should == true
+ end
end
diff --git a/spec/ruby/core/kernel/String_spec.rb b/spec/ruby/core/kernel/String_spec.rb
index 47ee797be5..a9b0758ad7 100644
--- a/spec/ruby/core/kernel/String_spec.rb
+++ b/spec/ruby/core/kernel/String_spec.rb
@@ -32,7 +32,7 @@ describe :kernel_String, shared: true do
undef_method :to_s
end
- -> { @object.send(@method, obj) }.should raise_error(TypeError)
+ -> { @object.send(@method, obj) }.should.raise(TypeError)
end
# #5158
@@ -44,7 +44,7 @@ describe :kernel_String, shared: true do
end
end
- -> { @object.send(@method, obj) }.should raise_error(TypeError)
+ -> { @object.send(@method, obj) }.should.raise(TypeError)
end
it "raises a TypeError if #to_s is not defined, even though #respond_to?(:to_s) returns true" do
@@ -57,7 +57,7 @@ describe :kernel_String, shared: true do
end
end
- -> { @object.send(@method, obj) }.should raise_error(TypeError)
+ -> { @object.send(@method, obj) }.should.raise(TypeError)
end
it "calls #to_s if #respond_to?(:to_s) returns true" do
@@ -74,14 +74,14 @@ describe :kernel_String, shared: true do
it "raises a TypeError if #to_s does not return a String" do
(obj = mock('123')).should_receive(:to_s).and_return(123)
- -> { @object.send(@method, obj) }.should raise_error(TypeError)
+ -> { @object.send(@method, obj) }.should.raise(TypeError)
end
it "returns the same object if it is already a String" do
- string = "Hello"
+ string = +"Hello"
string.should_not_receive(:to_s)
string2 = @object.send(@method, string)
- string.should equal(string2)
+ string.should.equal?(string2)
end
it "returns the same object if it is an instance of a String subclass" do
@@ -89,7 +89,7 @@ describe :kernel_String, shared: true do
string = subklass.new("Hello")
string.should_not_receive(:to_s)
string2 = @object.send(@method, string)
- string.should equal(string2)
+ string.should.equal?(string2)
end
end
@@ -101,6 +101,6 @@ describe "Kernel#String" do
it_behaves_like :kernel_String, :String, Object.new
it "is a private method" do
- Kernel.should have_private_instance_method(:String)
+ Kernel.private_instance_methods(false).should.include?(:String)
end
end
diff --git a/spec/ruby/core/kernel/__dir___spec.rb b/spec/ruby/core/kernel/__dir___spec.rb
index 0686b31e97..242adbf48b 100644
--- a/spec/ruby/core/kernel/__dir___spec.rb
+++ b/spec/ruby/core/kernel/__dir___spec.rb
@@ -19,19 +19,9 @@ describe "Kernel#__dir__" do
end
end
- ruby_version_is ""..."2.8" do
- context "when used in eval with top level binding" do
- it "returns the real name of the directory containing the currently-executing file" do
- eval("__dir__", binding).should == File.realpath(File.dirname(__FILE__))
- end
- end
- end
-
- ruby_version_is "2.8" do
- context "when used in eval with top level binding" do
- it "returns nil" do
- eval("__dir__", binding).should == nil
- end
+ context "when used in eval with top level binding" do
+ it "returns nil" do
+ eval("__dir__", binding).should == nil
end
end
end
diff --git a/spec/ruby/core/kernel/abort_spec.rb b/spec/ruby/core/kernel/abort_spec.rb
index f8152718c5..b75138182d 100644
--- a/spec/ruby/core/kernel/abort_spec.rb
+++ b/spec/ruby/core/kernel/abort_spec.rb
@@ -4,7 +4,7 @@ require_relative '../../shared/process/abort'
describe "Kernel#abort" do
it "is a private method" do
- Kernel.should have_private_instance_method(:abort)
+ Kernel.private_instance_methods(false).should.include?(:abort)
end
it_behaves_like :process_abort, :abort, KernelSpecs::Method.new
diff --git a/spec/ruby/core/kernel/at_exit_spec.rb b/spec/ruby/core/kernel/at_exit_spec.rb
index 7bdb5391fe..fdb0eaf34c 100644
--- a/spec/ruby/core/kernel/at_exit_spec.rb
+++ b/spec/ruby/core/kernel/at_exit_spec.rb
@@ -1,66 +1,16 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+require_relative '../../shared/kernel/at_exit'
describe "Kernel.at_exit" do
- it "is a private method" do
- Kernel.should have_private_instance_method(:at_exit)
- end
-
- it "runs after all other code" do
- ruby_exe("at_exit {print 5}; print 6").should == "65"
- end
-
- it "runs in reverse order of registration" do
- code = "at_exit {print 4};at_exit {print 5}; print 6; at_exit {print 7}"
- ruby_exe(code).should == "6754"
- end
-
- it "allows calling exit inside at_exit handler" do
- code = "at_exit {print 3}; at_exit {print 4; exit; print 5}; at_exit {print 6}"
- ruby_exe(code).should == "643"
- end
-
- it "gives access to the last raised exception" do
- code = <<-EOC
- at_exit do
- puts "The exception matches: \#{$! == $exception} (message=\#{$!.message})"
- end
+ it_behaves_like :kernel_at_exit, :at_exit
- begin
- raise "foo"
- rescue => $exception
- raise
- end
- EOC
-
- result = ruby_exe(code, args: "2>&1")
- result.lines.should.include?("The exception matches: true (message=foo)\n")
- end
-
- it "both exceptions in at_exit and in the main script are printed" do
- result = ruby_exe('at_exit { raise "at_exit_error" }; raise "main_script_error"', args: "2>&1")
- result.should.include?('at_exit_error (RuntimeError)')
- result.should.include?('main_script_error (RuntimeError)')
- end
-
- it "decides the exit status if both at_exit and the main script raise SystemExit" do
- ruby_exe('at_exit { exit 43 }; exit 42', args: "2>&1")
- $?.exitstatus.should == 43
- end
-
- it "runs all at_exit even if some raise exceptions" do
- code = 'at_exit { STDERR.puts "last" }; at_exit { exit 43 }; at_exit { STDERR.puts "first" }; exit 42'
- result = ruby_exe(code, args: "2>&1")
- result.should == "first\nlast\n"
- $?.exitstatus.should == 43
+ it "is a private method" do
+ Kernel.private_instance_methods(false).should.include?(:at_exit)
end
- it "runs at_exit handlers even if the main script fails to parse" do
- script = fixture(__FILE__, "at_exit.rb")
- result = ruby_exe('{', options: "-r#{script}", args: "2>&1")
- $?.should_not.success?
- result.should.include?("at_exit ran\n")
- result.should.include?("syntax error")
+ it "raises ArgumentError if called without a block" do
+ -> { at_exit }.should.raise(ArgumentError, "called without a block")
end
end
diff --git a/spec/ruby/core/kernel/autoload_relative_spec.rb b/spec/ruby/core/kernel/autoload_relative_spec.rb
new file mode 100644
index 0000000000..7d03da8a40
--- /dev/null
+++ b/spec/ruby/core/kernel/autoload_relative_spec.rb
@@ -0,0 +1,114 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# Specs for Kernel#autoload_relative
+
+ruby_version_is "4.1" do
+ describe "Kernel#autoload_relative" do
+ before :each do
+ @loaded_features = $".dup
+ end
+
+ after :each do
+ $".replace @loaded_features
+ # Clean up constants defined by these tests
+ [:KSAutoloadRelativeA, :KSAutoloadRelativeB, :KSAutoloadRelativeC,
+ :KSAutoloadRelativeE, :KSAutoloadRelativeF, :KSAutoloadRelativeG,
+ :KSAutoloadRelativeH, :KSAutoloadRelativeI].each do |const|
+ KernelSpecs.send(:remove_const, const) if KernelSpecs.const_defined?(const, false)
+ end
+ [:KSAutoloadRelativeD, :NestedTest].each do |const|
+ Object.send(:remove_const, const) if Object.const_defined?(const, false)
+ end
+ end
+
+ it "is a private method" do
+ Kernel.private_instance_methods(false).should.include?(:autoload_relative)
+ end
+
+ it "registers a file to load relative to the current file" do
+ KernelSpecs.autoload_relative :KSAutoloadRelativeA, "fixtures/autoload_relative_b.rb"
+ path = KernelSpecs.autoload?(:KSAutoloadRelativeA)
+ path.should_not == nil
+ path.should.end_with?("autoload_relative_b.rb")
+ File.exist?(path).should == true
+ end
+
+ it "loads the file when the constant is accessed" do
+ KernelSpecs.autoload_relative :KSAutoloadRelativeB, "fixtures/autoload_relative_b.rb"
+ KernelSpecs::KSAutoloadRelativeB.loaded.should == :ksautoload_b
+ end
+
+ it "sets the autoload constant in the constant table" do
+ KernelSpecs.autoload_relative :KSAutoloadRelativeC, "fixtures/autoload_relative_b.rb"
+ KernelSpecs.should.const_defined?(:KSAutoloadRelativeC, false)
+ end
+
+ it "can autoload in instance_eval with a file context" do
+ result = Object.new.instance_eval(<<-CODE, __FILE__, __LINE__)
+ autoload_relative :KSAutoloadRelativeD, "fixtures/autoload_relative_d.rb"
+ KSAutoloadRelativeD.loaded
+ CODE
+ result.should == :ksautoload_d
+ end
+
+ it "raises LoadError if called from eval without file context" do
+ -> {
+ eval('autoload_relative :Foo, "foo.rb"')
+ }.should.raise(LoadError, /autoload_relative called without file context/)
+ end
+
+ it "accepts both string and symbol for constant name" do
+ KernelSpecs.autoload_relative :KSAutoloadRelativeE, "fixtures/autoload_relative_b.rb"
+ KernelSpecs.autoload_relative "KSAutoloadRelativeF", "fixtures/autoload_relative_b.rb"
+
+ KernelSpecs.should.const_defined?(:KSAutoloadRelativeE, false)
+ KernelSpecs.should.const_defined?(:KSAutoloadRelativeF, false)
+ end
+
+ it "returns nil" do
+ KernelSpecs.autoload_relative(:KSAutoloadRelativeG, "fixtures/autoload_relative_b.rb").should == nil
+ end
+
+ it "resolves nested directory paths correctly" do
+ -> {
+ autoload_relative :NestedTest, "../kernel/fixtures/autoload_relative_b.rb"
+ autoload?(:NestedTest)
+ }.should_not.raise
+ end
+
+ it "resolves paths starting with ./" do
+ KernelSpecs.autoload_relative :KSAutoloadRelativeH, "./fixtures/autoload_relative_b.rb"
+ path = KernelSpecs.autoload?(:KSAutoloadRelativeH)
+ path.should_not == nil
+ path.should.end_with?("autoload_relative_b.rb")
+ end
+
+ it "ignores $LOAD_PATH and uses only relative path resolution" do
+ original_load_path = $LOAD_PATH.dup
+ $LOAD_PATH.clear
+ begin
+ KernelSpecs.autoload_relative :KSAutoloadRelativeI, "fixtures/autoload_relative_b.rb"
+ path = KernelSpecs.autoload?(:KSAutoloadRelativeI)
+ path.should_not == nil
+ # Should still resolve even with empty $LOAD_PATH
+ File.exist?(path).should == true
+ ensure
+ $LOAD_PATH.replace(original_load_path)
+ end
+ end
+
+ describe "when Object is frozen" do
+ it "raises a FrozenError before defining the constant" do
+ ruby_exe(<<-RUBY).should.include?("FrozenError")
+ Object.freeze
+ begin
+ autoload_relative :Foo, "autoload_b.rb"
+ rescue => e
+ puts e.class
+ end
+ RUBY
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/autoload_spec.rb b/spec/ruby/core/kernel/autoload_spec.rb
index 8d56021fa8..46783734c4 100644
--- a/spec/ruby/core/kernel/autoload_spec.rb
+++ b/spec/ruby/core/kernel/autoload_spec.rb
@@ -7,7 +7,9 @@ require_relative 'fixtures/classes'
autoload :KSAutoloadA, "autoload_a.rb"
autoload :KSAutoloadB, fixture(__FILE__, "autoload_b.rb")
-autoload :KSAutoloadCallsRequire, "main_autoload_not_exist.rb"
+define_autoload_KSAutoloadCallsRequire = -> {
+ autoload :KSAutoloadCallsRequire, "main_autoload_not_exist.rb"
+}
def check_autoload(const)
autoload? const
@@ -23,7 +25,7 @@ describe "Kernel#autoload" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:autoload)
+ Kernel.private_instance_methods(false).should.include?(:autoload)
end
it "registers a file to load the first time the named constant is accessed" do
@@ -35,7 +37,7 @@ describe "Kernel#autoload" do
end
it "sets the autoload constant in Object's constant table" do
- Object.should have_constant(:KSAutoloadA)
+ Object.should.const_defined?(:KSAutoloadA, false)
end
it "loads the file when the constant is accessed" do
@@ -43,10 +45,11 @@ describe "Kernel#autoload" do
end
it "calls main.require(path) to load the file" do
+ define_autoload_KSAutoloadCallsRequire.call
main = TOPLEVEL_BINDING.eval("self")
main.should_receive(:require).with("main_autoload_not_exist.rb")
# The constant won't be defined since require is mocked to do nothing
- -> { KSAutoloadCallsRequire }.should raise_error(NameError)
+ -> { KSAutoloadCallsRequire }.should.raise(NameError)
end
it "can autoload in instance_eval" do
@@ -56,6 +59,21 @@ describe "Kernel#autoload" do
end
end
+ describe "inside a Class.new method body" do
+ # NOTE: this spec is being discussed in https://github.com/ruby/spec/pull/839
+ it "should define on the new anonymous class" do
+ cls = Class.new do
+ def go
+ autoload :Object, 'bogus'
+ autoload? :Object
+ end
+ end
+
+ cls.new.go.should == 'bogus'
+ cls.autoload?(:Object).should == 'bogus'
+ end
+ end
+
describe "when Object is frozen" do
it "raises a FrozenError before defining the constant" do
ruby_exe(fixture(__FILE__, "autoload_frozen.rb")).should == "FrozenError - nil"
@@ -84,7 +102,7 @@ end
describe "Kernel#autoload?" do
it "is a private method" do
- Kernel.should have_private_instance_method(:autoload?)
+ Kernel.private_instance_methods(false).should.include?(:autoload?)
end
it "returns the name of the file that will be autoloaded" do
@@ -92,7 +110,7 @@ describe "Kernel#autoload?" do
end
it "returns nil if no file has been registered for a constant" do
- check_autoload(:Manualload).should be_nil
+ check_autoload(:Manualload).should == nil
end
end
@@ -119,7 +137,7 @@ describe "Kernel.autoload" do
end
it "sets the autoload constant in Object's constant table" do
- Object.should have_constant(:KSAutoloadBB)
+ Object.should.const_defined?(:KSAutoloadBB, false)
end
it "calls #to_path on non-String filenames" do
@@ -155,6 +173,6 @@ describe "Kernel.autoload?" do
end
it "returns nil if no file has been registered for a constant" do
- Kernel.autoload?(:Manualload).should be_nil
+ Kernel.autoload?(:Manualload).should == nil
end
end
diff --git a/spec/ruby/core/kernel/backtick_spec.rb b/spec/ruby/core/kernel/backtick_spec.rb
index 391583b539..42e0f975f5 100644
--- a/spec/ruby/core/kernel/backtick_spec.rb
+++ b/spec/ruby/core/kernel/backtick_spec.rb
@@ -11,7 +11,7 @@ describe "Kernel#`" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:`)
+ Kernel.private_instance_methods(false).should.include?(:`)
end
it "returns the standard output of the executed sub-process" do
@@ -28,24 +28,28 @@ describe "Kernel#`" do
it "produces a String in the default external encoding" do
Encoding.default_external = Encoding::SHIFT_JIS
- `echo disc`.encoding.should equal(Encoding::SHIFT_JIS)
+ `echo disc`.encoding.should.equal?(Encoding::SHIFT_JIS)
end
it "raises an Errno::ENOENT if the command is not executable" do
- -> { `nonexistent_command` }.should raise_error(Errno::ENOENT)
+ -> { `nonexistent_command` }.should.raise(Errno::ENOENT)
end
platform_is_not :windows do
+ it "handles invalid UTF-8 bytes in command" do
+ `echo "testing\xC2 a non UTF-8 string"`.b.should == "testing\xC2 a non UTF-8 string\n".b
+ end
+
it "sets $? to the exit status of the executed sub-process" do
ip = 'world'
`echo disc #{ip}`
- $?.should be_kind_of(Process::Status)
+ $?.should.is_a?(Process::Status)
$?.should_not.stopped?
$?.should.exited?
$?.exitstatus.should == 0
$?.should.success?
`echo disc #{ip}; exit 99`
- $?.should be_kind_of(Process::Status)
+ $?.should.is_a?(Process::Status)
$?.should_not.stopped?
$?.should.exited?
$?.exitstatus.should == 99
@@ -57,13 +61,13 @@ describe "Kernel#`" do
it "sets $? to the exit status of the executed sub-process" do
ip = 'world'
`echo disc #{ip}`
- $?.should be_kind_of(Process::Status)
+ $?.should.is_a?(Process::Status)
$?.should_not.stopped?
$?.should.exited?
$?.exitstatus.should == 0
$?.should.success?
`echo disc #{ip}& exit 99`
- $?.should be_kind_of(Process::Status)
+ $?.should.is_a?(Process::Status)
$?.should_not.stopped?
$?.should.exited?
$?.exitstatus.should == 99
diff --git a/spec/ruby/core/kernel/binding_spec.rb b/spec/ruby/core/kernel/binding_spec.rb
index f1c9c6ec9f..6f27b26f37 100644
--- a/spec/ruby/core/kernel/binding_spec.rb
+++ b/spec/ruby/core/kernel/binding_spec.rb
@@ -9,7 +9,7 @@ end
describe "Kernel#binding" do
it "is a private method" do
- Kernel.should have_private_instance_method(:binding)
+ Kernel.private_instance_methods(false).should.include?(:binding)
end
before :each do
@@ -35,7 +35,7 @@ describe "Kernel#binding" do
end
it "raises a NameError on undefined variable" do
- -> { eval("a_fake_variable", @b1) }.should raise_error(NameError)
+ -> { eval("a_fake_variable", @b1) }.should.raise(NameError)
end
it "uses the closure's self as self in the binding" do
diff --git a/spec/ruby/core/kernel/block_given_spec.rb b/spec/ruby/core/kernel/block_given_spec.rb
index b00bfabfc3..20bb90ae96 100644
--- a/spec/ruby/core/kernel/block_given_spec.rb
+++ b/spec/ruby/core/kernel/block_given_spec.rb
@@ -5,15 +5,20 @@ describe :kernel_block_given, shared: true do
it "returns true if and only if a block is supplied" do
@object.accept_block {}.should == true
@object.accept_block_as_argument {}.should == true
+ @object.accept_block_inside_block {}.should == true
+ @object.accept_block_as_argument_inside_block {}.should == true
@object.accept_block.should == false
@object.accept_block_as_argument.should == false
+ @object.accept_block_inside_block.should == false
+ @object.accept_block_as_argument_inside_block.should == false
end
# Clarify: Based on http://www.ruby-forum.com/topic/137822 it appears
# that Matz wanted this to be true in 1.9.
it "returns false when a method defined by define_method is called with a block" do
@object.defined_block {}.should == false
+ @object.defined_block_inside_block {}.should == false
end
end
@@ -25,7 +30,7 @@ describe "Kernel#block_given?" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:block_given?)
+ Kernel.private_instance_methods(false).should.include?(:block_given?)
end
end
diff --git a/spec/ruby/core/kernel/caller_locations_spec.rb b/spec/ruby/core/kernel/caller_locations_spec.rb
index c4e0084085..04cf806ef4 100644
--- a/spec/ruby/core/kernel/caller_locations_spec.rb
+++ b/spec/ruby/core/kernel/caller_locations_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/caller_locations'
describe 'Kernel#caller_locations' do
it 'is a private method' do
- Kernel.should have_private_instance_method(:caller_locations)
+ Kernel.private_instance_methods(false).should.include?(:caller_locations)
end
it 'returns an Array of caller locations' do
@@ -28,12 +28,16 @@ describe 'Kernel#caller_locations' do
locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
end
- ruby_version_is "2.6" do
- it "works with endless ranges" do
- locations1 = caller_locations(0)
- locations2 = caller_locations(eval("(2..)"))
- locations2.map(&:to_s).should == locations1[2..-1].map(&:to_s)
- end
+ it "works with endless ranges" do
+ locations1 = caller_locations(0)
+ locations2 = caller_locations(eval("(2..)"))
+ locations2.map(&:to_s).should == locations1[2..-1].map(&:to_s)
+ end
+
+ it "works with beginless ranges" do
+ locations1 = caller_locations(0)
+ locations2 = caller_locations((...5))
+ locations2.map(&:to_s)[eval("(2..)")].should == locations1[(...5)].map(&:to_s)[eval("(2..)")]
end
it "can be called with a range whose end is negative" do
@@ -65,4 +69,45 @@ describe 'Kernel#caller_locations' do
it "must return the same locations when called with 1..-1 and when called with no arguments" do
caller_locations.map(&:to_s).should == caller_locations(1..-1).map(&:to_s)
end
+
+ guard -> { Kernel.instance_method(:tap).source_location } do
+ ruby_version_is ""..."3.4" do
+ it "includes core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller_locations(1, 1)[0] }
+ loc.label.should == "tap"
+ loc.path.should.start_with? "<internal:"
+ end
+ end
+
+ ruby_version_is "3.4"..."4.0" do
+ it "includes core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller_locations(1, 1)[0] }
+ loc.label.should == "Kernel#tap"
+ loc.path.should.start_with? "<internal:"
+ end
+ end
+
+ ruby_version_is "4.0" do
+ it "does not include core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller_locations(1, 1)[0] }
+ loc.label.should == "Kernel#tap"
+ # CRuby hides the file which defines the method: https://bugs.ruby-lang.org/issues/20968
+ unless loc.path == __FILE__
+ loc.path.should.start_with? "<internal:"
+ end
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/caller_spec.rb b/spec/ruby/core/kernel/caller_spec.rb
index b71060770c..225f6fc458 100644
--- a/spec/ruby/core/kernel/caller_spec.rb
+++ b/spec/ruby/core/kernel/caller_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/caller'
describe 'Kernel#caller' do
it 'is a private method' do
- Kernel.should have_private_instance_method(:caller)
+ Kernel.private_instance_methods(false).should.include?(:caller)
end
it 'returns an Array of caller locations' do
@@ -32,23 +32,90 @@ describe 'Kernel#caller' do
locations = KernelSpecs::CallerTest.locations
line = __LINE__ - 1
- locations[0].should include("#{__FILE__}:#{line}:in")
+ locations[0].should.include?("#{__FILE__}:#{line}:in")
end
it "returns an Array with the block given to #at_exit at the base of the stack" do
path = fixture(__FILE__, "caller_at_exit.rb")
lines = ruby_exe(path).lines
- lines.should == [
- "#{path}:6:in `foo'\n",
- "#{path}:2:in `block in <main>'\n"
- ]
+ lines.size.should == 2
+ lines[0].should =~ /\A#{path}:6:in [`'](?:Object#)?foo'\n\z/
+ lines[1].should =~ /\A#{path}:2:in [`']block in <main>'\n\z/
end
- ruby_version_is "2.6" do
- it "works with endless ranges" do
- locations1 = KernelSpecs::CallerTest.locations(0)
- locations2 = KernelSpecs::CallerTest.locations(eval("(2..)"))
- locations2.map(&:to_s).should == locations1[2..-1].map(&:to_s)
+ it "can be called with a range" do
+ locations1 = caller(0)
+ locations2 = caller(2..4)
+ locations1[2..4].should == locations2
+ end
+
+ it "works with endless ranges" do
+ locations1 = KernelSpecs::CallerTest.locations(0)
+ locations2 = KernelSpecs::CallerTest.locations(eval("(2..)"))
+ locations2.should == locations1[2..-1]
+ end
+
+ it "works with beginless ranges" do
+ locations1 = KernelSpecs::CallerTest.locations(0)
+ locations2 = KernelSpecs::CallerTest.locations((..5))
+ locations2[eval("(2..)")].should == locations1[(..5)][eval("(2..)")]
+ end
+
+ it "can be called with a range whose end is negative" do
+ locations1 = caller(0)
+ locations2 = caller(2..-1)
+ locations3 = caller(2..-2)
+ locations1[2..-1].should == locations2
+ locations1[2..-2].should == locations3
+ end
+
+ it "must return nil if omitting more locations than available" do
+ caller(100).should == nil
+ caller(100..-1).should == nil
+ end
+
+ it "must return [] if omitting exactly the number of locations available" do
+ omit = caller(0).length
+ caller(omit).should == []
+ end
+
+ it "must return the same locations when called with 1..-1 and when called with no arguments" do
+ caller.should == caller(1..-1)
+ end
+
+ guard -> { Kernel.instance_method(:tap).source_location } do
+ ruby_version_is ""..."3.4" do
+ it "includes core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller(1, 1)[0] }
+ loc.should =~ /\A<internal:.*in `tap'\z/
+ end
+ end
+
+ ruby_version_is "3.4"..."4.0" do
+ it "includes core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller(1, 1)[0] }
+ loc.should =~ /\A<internal:.*in 'Kernel#tap'\z/
+ end
+ end
+
+ ruby_version_is "4.0" do
+ it "does not include core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller(1, 1)[0] }
+ # CRuby hides the file which defines the method: https://bugs.ruby-lang.org/issues/20968
+ loc.should =~ /\A(<internal:|#{__FILE__}:).*in 'Kernel#tap'\z/
+ end
end
end
end
diff --git a/spec/ruby/core/kernel/case_compare_spec.rb b/spec/ruby/core/kernel/case_compare_spec.rb
index b8d30960e8..c74bbf63b9 100644
--- a/spec/ruby/core/kernel/case_compare_spec.rb
+++ b/spec/ruby/core/kernel/case_compare_spec.rb
@@ -58,18 +58,18 @@ describe "Kernel#=== for a class with #== overridden to consider other object's
end
it "returns true if #== returns true even if #equal? is false" do
- @o1.should_not equal(@o2)
+ @o1.should_not.equal?(@o2)
(@o1 == @o2).should == true
(@o1 === @o2).should == true
end
it "returns true if #equal? returns true" do
- @o1.should equal(@o1)
+ @o1.should.equal?(@o1)
(@o1 === @o1).should == true
end
it "returns false if neither #== nor #equal? returns true" do
- @o1.should_not equal(@o)
+ @o1.should_not.equal?(@o)
(@o1 == @o).should == false
(@o1 === @o).should == false
end
@@ -83,13 +83,13 @@ describe "Kernel#=== for a class with #equal? overridden to always be false" do
end
it "returns true if #== returns true even if #equal? is false" do
- @o1.should_not equal(@o1)
+ @o1.should_not.equal?(@o1)
(@o1 == @o1).should == true
(@o1 === @o1).should == true
end
it "returns false if neither #== nor #equal? returns true" do
- @o1.should_not equal(@o)
+ @o1.should_not.equal?(@o)
(@o1 == @o).should == false
(@o1 === @o).should == false
end
@@ -105,7 +105,7 @@ describe "Kernel#=== for a class with #== and #equal? overridden to always be fa
it "returns true if the object id is the same even if both #== and #equal? return false" do
@o1.object_id.should == @o1.object_id
- @o1.should_not equal(@o1)
+ @o1.should_not.equal?(@o1)
(@o1 == @o1).should == false
(@o1 === @o1).should == true
@@ -114,7 +114,7 @@ describe "Kernel#=== for a class with #== and #equal? overridden to always be fa
it "returns false if the object id is not the same and both #== and #equal? return false" do
@o1.object_id.should_not == @o2.object_id
- @o1.should_not equal(@o2)
+ @o1.should_not.equal?(@o2)
(@o1 == @o2).should == false
(@o1 === @o2).should == false
diff --git a/spec/ruby/core/kernel/catch_spec.rb b/spec/ruby/core/kernel/catch_spec.rb
index 4060172429..9f02303678 100644
--- a/spec/ruby/core/kernel/catch_spec.rb
+++ b/spec/ruby/core/kernel/catch_spec.rb
@@ -31,11 +31,11 @@ describe "Kernel.catch" do
end
it "raises an ArgumentError if a Symbol is thrown for a String catch value" do
- -> { catch("exit") { throw :exit } }.should raise_error(ArgumentError)
+ -> { catch("exit") { throw :exit } }.should.raise(ArgumentError)
end
it "raises an ArgumentError if a String with different identity is thrown" do
- -> { catch("exit") { throw "exit" } }.should raise_error(ArgumentError)
+ -> { catch("exit".dup) { throw "exit".dup } }.should.raise(ArgumentError)
end
it "catches a Symbol when thrown a matching Symbol" do
@@ -60,7 +60,7 @@ describe "Kernel.catch" do
end
it "yields an object when called without arguments" do
- catch { |tag| tag }.should be_an_instance_of(Object)
+ catch { |tag| tag }.should.instance_of?(Object)
end
it "can be used even in a method different from where throw is called" do
@@ -116,12 +116,12 @@ describe "Kernel.catch" do
end
it "raises LocalJumpError if no block is given" do
- -> { catch :blah }.should raise_error(LocalJumpError)
+ -> { catch :blah }.should.raise(LocalJumpError)
end
end
describe "Kernel#catch" do
it "is a private method" do
- Kernel.should have_private_instance_method(:catch)
+ Kernel.private_instance_methods(false).should.include?(:catch)
end
end
diff --git a/spec/ruby/core/kernel/chomp_spec.rb b/spec/ruby/core/kernel/chomp_spec.rb
index d30e77c35a..434bf652f9 100644
--- a/spec/ruby/core/kernel/chomp_spec.rb
+++ b/spec/ruby/core/kernel/chomp_spec.rb
@@ -26,7 +26,7 @@ end
describe :kernel_chomp_private, shared: true do
it "is a private method" do
- KernelSpecs.has_private_method(@method).should be_true
+ KernelSpecs.has_private_method(@method).should == true
end
end
diff --git a/spec/ruby/core/kernel/chop_spec.rb b/spec/ruby/core/kernel/chop_spec.rb
index 9b91c011bc..bbf3c3f724 100644
--- a/spec/ruby/core/kernel/chop_spec.rb
+++ b/spec/ruby/core/kernel/chop_spec.rb
@@ -14,7 +14,7 @@ end
describe :kernel_chop_private, shared: true do
it "is a private method" do
- KernelSpecs.has_private_method(@method).should be_true
+ KernelSpecs.has_private_method(@method).should == true
end
end
diff --git a/spec/ruby/core/kernel/class_spec.rb b/spec/ruby/core/kernel/class_spec.rb
index 4d3b4e549b..0a7f774d14 100644
--- a/spec/ruby/core/kernel/class_spec.rb
+++ b/spec/ruby/core/kernel/class_spec.rb
@@ -3,24 +3,24 @@ require_relative 'fixtures/classes'
describe "Kernel#class" do
it "returns the class of the object" do
- Object.new.class.should equal(Object)
+ Object.new.class.should.equal?(Object)
- 1.class.should equal(Fixnum)
- 3.14.class.should equal(Float)
- :hello.class.should equal(Symbol)
- "hello".class.should equal(String)
- [1, 2].class.should equal(Array)
- { 1 => 2 }.class.should equal(Hash)
+ 1.class.should.equal?(Integer)
+ 3.14.class.should.equal?(Float)
+ :hello.class.should.equal?(Symbol)
+ "hello".class.should.equal?(String)
+ [1, 2].class.should.equal?(Array)
+ { 1 => 2 }.class.should.equal?(Hash)
end
it "returns Class for a class" do
- BasicObject.class.should equal(Class)
- String.class.should equal(Class)
+ BasicObject.class.should.equal?(Class)
+ String.class.should.equal?(Class)
end
it "returns the first non-singleton class" do
- a = "hello"
+ a = +"hello"
def a.my_singleton_method; end
- a.class.should equal(String)
+ a.class.should.equal?(String)
end
end
diff --git a/spec/ruby/core/kernel/clone_spec.rb b/spec/ruby/core/kernel/clone_spec.rb
index 6aeb57f55b..4ddb23d6e6 100644
--- a/spec/ruby/core/kernel/clone_spec.rb
+++ b/spec/ruby/core/kernel/clone_spec.rb
@@ -25,30 +25,91 @@ describe "Kernel#clone" do
end
clone = instance.clone
- clone.class.should equal klass
+ clone.class.should.equal? klass
end
- it "copies frozen state from the original" do
- o2 = @obj.clone
- @obj.freeze
- o3 = @obj.clone
+ describe "with no arguments" do
+ it "copies frozen state from the original" do
+ o2 = @obj.clone
+ o2.should_not.frozen?
- o2.should_not.frozen?
- o3.should.frozen?
+ @obj.freeze
+ o3 = @obj.clone
+ o3.should.frozen?
+ end
+
+ it 'copies frozen?' do
+ o = ''.freeze.clone
+ o.frozen?.should == true
+ end
end
- ruby_version_is '2.8' do
- it 'takes an freeze: true option to frozen copy' do
- @obj.clone(freeze: true).should.frozen?
+ describe "with freeze: nil" do
+ it "copies frozen state from the original, like #clone without arguments" do
+ o2 = @obj.clone(freeze: nil)
+ o2.should_not.frozen?
+
+ @obj.freeze
+ o3 = @obj.clone(freeze: nil)
+ o3.should.frozen?
+ end
+
+ it "copies frozen?" do
+ o = "".freeze.clone(freeze: nil)
+ o.frozen?.should == true
+ end
+ end
+
+ describe "with freeze: true" do
+ it 'makes a frozen copy if the original is frozen' do
@obj.freeze
@obj.clone(freeze: true).should.frozen?
end
+
+ it 'freezes the copy even if the original was not frozen' do
+ @obj.clone(freeze: true).should.frozen?
+ end
+
+ it "calls #initialize_clone with kwargs freeze: true" do
+ obj = KernelSpecs::CloneFreeze.new
+ obj.clone(freeze: true)
+ ScratchPad.recorded.should == [obj, { freeze: true }]
+ end
+
+ it "calls #initialize_clone with kwargs freeze: true even if #initialize_clone only takes a single argument" do
+ obj = KernelSpecs::Clone.new
+ -> { obj.clone(freeze: true) }.should.raise(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
+ end
end
- it 'takes an freeze: false option to not return frozen copy' do
- @obj.clone(freeze: false).should_not.frozen?
- @obj.freeze
- @obj.clone(freeze: false).should_not.frozen?
+ describe "with freeze: false" do
+ it 'does not freeze the copy if the original is frozen' do
+ @obj.freeze
+ @obj.clone(freeze: false).should_not.frozen?
+ end
+
+ it 'does not freeze the copy if the original is not frozen' do
+ @obj.clone(freeze: false).should_not.frozen?
+ end
+
+ it "calls #initialize_clone with kwargs freeze: false" do
+ obj = KernelSpecs::CloneFreeze.new
+ obj.clone(freeze: false)
+ ScratchPad.recorded.should == [obj, { freeze: false }]
+ end
+
+ it "calls #initialize_clone with kwargs freeze: false even if #initialize_clone only takes a single argument" do
+ obj = KernelSpecs::Clone.new
+ -> { obj.clone(freeze: false) }.should.raise(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
+ end
+ end
+
+ describe "with freeze: anything else" do
+ it 'raises ArgumentError when passed not true/false/nil' do
+ -> { @obj.clone(freeze: 1) }.should.raise(ArgumentError, /unexpected value for freeze: Integer/)
+ -> { @obj.clone(freeze: "") }.should.raise(ArgumentError, /unexpected value for freeze: String/)
+ -> { @obj.clone(freeze: Object.new) }.should.raise(ArgumentError, /unexpected value for freeze: Object/)
+ end
end
it "copies instance variables" do
@@ -113,16 +174,4 @@ describe "Kernel#clone" do
cloned.bar.should == ['a']
end
-
- it 'copies frozen?' do
- o = ''.freeze.clone
- o.frozen?.should be_true
- end
-
- ruby_version_is ''...'2.7' do
- it 'copies tainted?' do
- o = ''.taint.clone
- o.tainted?.should be_true
- end
- end
end
diff --git a/spec/ruby/core/kernel/comparison_spec.rb b/spec/ruby/core/kernel/comparison_spec.rb
index affdc5c00d..48f17e1172 100644
--- a/spec/ruby/core/kernel/comparison_spec.rb
+++ b/spec/ruby/core/kernel/comparison_spec.rb
@@ -15,17 +15,17 @@ describe "Kernel#<=>" do
it "returns nil if self is eql? but not == to the argument" do
obj = mock('has eql?')
obj.should_not_receive(:eql?)
- obj.<=>(Object.new).should be_nil
+ obj.<=>(Object.new).should == nil
end
it "returns nil if self.==(arg) returns nil" do
obj = mock('wrong ==')
obj.should_receive(:==).and_return(nil)
- obj.<=>(Object.new).should be_nil
+ obj.<=>(Object.new).should == nil
end
it "returns nil if self is not == to the argument" do
obj = Object.new
- obj.<=>(3.14).should be_nil
+ obj.<=>(3.14).should == nil
end
end
diff --git a/spec/ruby/core/kernel/define_singleton_method_spec.rb b/spec/ruby/core/kernel/define_singleton_method_spec.rb
index dc77c3e6f8..ec48581db8 100644
--- a/spec/ruby/core/kernel/define_singleton_method_spec.rb
+++ b/spec/ruby/core/kernel/define_singleton_method_spec.rb
@@ -14,14 +14,14 @@ describe "Kernel#define_singleton_method" do
end
it "adds the new method to the methods list" do
- DefineSingletonMethodSpecClass.should have_method(:another_test_method)
+ DefineSingletonMethodSpecClass.should.respond_to?(:another_test_method)
end
it "defines any Child class method from any Parent's class methods" do
um = KernelSpecs::Parent.method(:parent_class_method).unbind
KernelSpecs::Child.send :define_singleton_method, :child_class_method, um
KernelSpecs::Child.child_class_method.should == :foo
- ->{KernelSpecs::Parent.child_class_method}.should raise_error(NoMethodError)
+ ->{KernelSpecs::Parent.child_class_method}.should.raise(NoMethodError)
end
it "will raise when attempting to define an object's singleton method from another object's singleton method" do
@@ -33,7 +33,7 @@ describe "Kernel#define_singleton_method" do
end
end
um = p.method(:singleton_method).unbind
- ->{ other.send :define_singleton_method, :other_singleton_method, um }.should raise_error(TypeError)
+ ->{ other.send :define_singleton_method, :other_singleton_method, um }.should.raise(TypeError)
end
end
@@ -52,11 +52,11 @@ describe "Kernel#define_singleton_method" do
it "raises a TypeError when the given method is no Method/Proc" do
-> {
Class.new { define_singleton_method(:test, "self") }
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
-> {
Class.new { define_singleton_method(:test, 1234) }
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
it "defines a new singleton method for objects" do
@@ -65,7 +65,7 @@ describe "Kernel#define_singleton_method" do
obj.test.should == "world!"
-> {
Object.new.test
- }.should raise_error(NoMethodError)
+ }.should.raise(NoMethodError)
end
it "maintains the Proc's scope" do
@@ -83,7 +83,7 @@ describe "Kernel#define_singleton_method" do
obj = Object.new
-> {
obj.define_singleton_method(:test)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "does not use the caller block when no block is given" do
@@ -94,6 +94,27 @@ describe "Kernel#define_singleton_method" do
-> {
o.define(:foo) { raise "not used" }
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
+ end
+
+ it "always defines the method with public visibility" do
+ cls = Class.new
+ def cls.define(name, &block)
+ private
+ define_singleton_method(name, &block)
+ end
+
+ -> {
+ suppress_warning do
+ cls.define(:foo) { :ok }
+ end
+ cls.foo.should == :ok
+ }.should_not.raise(NoMethodError)
+ end
+
+ it "cannot define a singleton method with a frozen singleton class" do
+ o = Object.new
+ o.freeze
+ -> { o.define_singleton_method(:foo) { 1 } }.should.raise(FrozenError)
end
end
diff --git a/spec/ruby/core/kernel/dup_spec.rb b/spec/ruby/core/kernel/dup_spec.rb
index 70198abdb7..99de5e3732 100644
--- a/spec/ruby/core/kernel/dup_spec.rb
+++ b/spec/ruby/core/kernel/dup_spec.rb
@@ -25,7 +25,7 @@ describe "Kernel#dup" do
end
dup = instance.dup
- dup.class.should equal klass
+ dup.class.should.equal? klass
end
it "does not copy frozen state from the original" do
@@ -44,7 +44,7 @@ describe "Kernel#dup" do
it "does not copy singleton methods" do
def @obj.special() :the_one end
dup = @obj.dup
- -> { dup.special }.should raise_error(NameError)
+ -> { dup.special }.should.raise(NameError)
end
it "does not copy modules included in the singleton class" do
@@ -53,7 +53,7 @@ describe "Kernel#dup" do
end
dup = @obj.dup
- -> { dup.repr }.should raise_error(NameError)
+ -> { dup.repr }.should.raise(NameError)
end
it "does not copy constants defined in the singleton class" do
@@ -62,6 +62,6 @@ describe "Kernel#dup" do
end
dup = @obj.dup
- -> { class << dup; CLONE; end }.should raise_error(NameError)
+ -> { class << dup; CLONE; end }.should.raise(NameError)
end
end
diff --git a/spec/ruby/core/kernel/eql_spec.rb b/spec/ruby/core/kernel/eql_spec.rb
index e62a601a79..b3289090e5 100644
--- a/spec/ruby/core/kernel/eql_spec.rb
+++ b/spec/ruby/core/kernel/eql_spec.rb
@@ -3,7 +3,7 @@ require_relative '../../shared/kernel/equal'
describe "Kernel#eql?" do
it "is a public instance method" do
- Kernel.should have_public_instance_method(:eql?)
+ Kernel.public_instance_methods(false).should.include?(:eql?)
end
it_behaves_like :object_equal, :eql?
diff --git a/spec/ruby/core/kernel/eval_spec.rb b/spec/ruby/core/kernel/eval_spec.rb
index 783009ac01..f9ca47866e 100644
--- a/spec/ruby/core/kernel/eval_spec.rb
+++ b/spec/ruby/core/kernel/eval_spec.rb
@@ -5,7 +5,7 @@ EvalSpecs::A.new.c
describe "Kernel#eval" do
it "is a private method" do
- Kernel.should have_private_instance_method(:eval)
+ Kernel.private_instance_methods(false).should.include?(:eval)
end
it "is a module function" do
@@ -76,12 +76,12 @@ describe "Kernel#eval" do
x = 1
bind = proc {}
- -> { eval("x", bind) }.should raise_error(TypeError)
+ -> { eval("x", bind) }.should.raise(TypeError)
end
it "does not make Proc locals visible to evaluated code" do
bind = proc { inner = 4 }
- -> { eval("inner", bind.binding) }.should raise_error(NameError)
+ -> { eval("inner", bind.binding) }.should.raise(NameError)
end
# REWRITE ME: This obscures the real behavior of where locals are stored
@@ -119,7 +119,7 @@ describe "Kernel#eval" do
outer_binding = binding
eval("if false; a = 1; end", outer_binding)
- eval("a", outer_binding).should be_nil
+ eval("a", outer_binding).should == nil
end
it "allows creating a new class in a binding" do
@@ -135,8 +135,8 @@ describe "Kernel#eval" do
it "includes file and line information in syntax error" do
expected = 'speccing.rb'
-> {
- eval('if true',TOPLEVEL_BINDING, expected)
- }.should raise_error(SyntaxError) { |e|
+ eval('if true', TOPLEVEL_BINDING, expected)
+ }.should.raise(SyntaxError) { |e|
e.message.should =~ /#{expected}:1:.+/
}
end
@@ -144,8 +144,8 @@ describe "Kernel#eval" do
it "evaluates string with given filename and negative linenumber" do
expected_file = 'speccing.rb'
-> {
- eval('if true',TOPLEVEL_BINDING, expected_file, -100)
- }.should raise_error(SyntaxError) { |e|
+ eval('if true', TOPLEVEL_BINDING, expected_file, -100)
+ }.should.raise(SyntaxError) { |e|
e.message.should =~ /#{expected_file}:-100:.+/
}
end
@@ -159,29 +159,88 @@ describe "Kernel#eval" do
end
end
- ruby_version_is ""..."2.8" do
- it "uses the filename of the binding if none is provided" do
- eval("__FILE__").should == "(eval)"
- suppress_warning {eval("__FILE__", binding)}.should == __FILE__
- eval("__FILE__", binding, "success").should == "success"
- suppress_warning {eval("eval '__FILE__', binding")}.should == "(eval)"
- suppress_warning {eval("eval '__FILE__', binding", binding)}.should == __FILE__
- suppress_warning {eval("eval '__FILE__', binding", binding, 'success')}.should == 'success'
+ context "parameter forwarding" do
+ it "allows anonymous rest parameter forwarding" do
+ object = Object.new
+ def object.foo(a, b, c)
+ [a, b, c]
+ end
+ def object.bar(*)
+ eval "foo(*)"
+ end
+
+ object.bar(1, 2, 3).should == [1, 2, 3]
end
- end
- ruby_version_is "2.8" do
- it "uses (eval) filename if none is provided" do
- eval("__FILE__").should == "(eval)"
- eval("__FILE__", binding).should == "(eval)"
- eval("__FILE__", binding, "success").should == "success"
- eval("eval '__FILE__', binding").should == "(eval)"
- eval("eval '__FILE__', binding", binding).should == "(eval)"
- eval("eval '__FILE__', binding", binding, 'success').should == '(eval)'
- eval("eval '__FILE__', binding, 'success'", binding).should == 'success'
+ it "allows anonymous keyword parameters forwarding" do
+ object = Object.new
+ def object.foo(a:, b:, c:)
+ [a, b, c]
+ end
+ def object.bar(**)
+ eval "foo(**)"
+ end
+
+ object.bar(a: 1, b: 2, c: 3).should == [1, 2, 3]
+ end
+
+ it "allows anonymous block parameter forwarding" do
+ object = Object.new
+ def object.foo(&block)
+ block.call
+ end
+ def object.bar(&)
+ eval "foo(&)"
+ end
+
+ object.bar { :foobar }.should == :foobar
end
+
+ it "allows ... forwarding" do
+ object = Object.new
+ def object.foo(a, b:, &block)
+ [a, b, block.call]
+ end
+ def object.bar(...)
+ eval "foo(...)"
+ end
+
+ object.bar(1, b: 2) { 3 }.should == [1, 2, 3]
+ end
+
+ it "allows parameter forwarding to super" do
+ m = Module.new do
+ def foo(a, b:, &block)
+ [a, b, block.call]
+ end
+ end
+
+ c = Class.new do
+ include m
+
+ def foo(a, b:, &block)
+ eval "super"
+ end
+ end
+
+ object = c.new
+ object.foo(1, b: 2) { 3 }.should == [1, 2, 3]
+ end
+ end
+
+ it "uses (eval at __FILE__:__LINE__) if none is provided" do
+ eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})"
+ eval("__FILE__", binding).should == "(eval at #{__FILE__}:#{__LINE__})"
+ eval("__FILE__", binding, "success").should == "success"
+ eval("eval '__FILE__', binding").should == "(eval at (eval at #{__FILE__}:#{__LINE__}):1)"
+ eval("eval '__FILE__', binding", binding).should == "(eval at (eval at #{__FILE__}:#{__LINE__}):1)"
+ eval("eval '__FILE__', binding", binding, 'success').should == "(eval at success:1)"
+ eval("eval '__FILE__', binding, 'success'", binding).should == 'success'
end
+ it 'uses (eval at __FILE__:__LINE__) for __FILE__ and 1 for __LINE__ with a binding argument' do
+ eval("[__FILE__, __LINE__]", binding).should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
# Found via Rubinius bug github:#149
it "does not alter the value of __FILE__ in the binding" do
first_time = EvalSpecs.call_eval
@@ -204,20 +263,34 @@ describe "Kernel#eval" do
# See http://jira.codehaus.org/browse/JRUBY-5163
it "uses the receiver as self inside the eval" do
- eval("self").should equal(self)
- Kernel.eval("self").should equal(Kernel)
+ eval("self").should.equal?(self)
+ Kernel.eval("self").should.equal?(Kernel)
end
it "does not pass the block to the method being eval'ed" do
-> {
eval('KernelSpecs::EvalTest.call_yield') { "content" }
- }.should raise_error(LocalJumpError)
+ }.should.raise(LocalJumpError)
end
it "returns from the scope calling #eval when evaluating 'return'" do
-> { eval("return :eval") }.call.should == :eval
end
+ it "returns from the method calling #eval when evaluating 'return'" do
+ def eval_return(n)
+ eval("return n*2")
+ end
+ -> { eval_return(3) }.call.should == 6
+ end
+
+ it "returns from the method calling #eval when evaluating 'return' in BEGIN" do
+ def eval_return(n)
+ eval("BEGIN {return n*3}")
+ end
+ -> { eval_return(4) }.call.should == 12
+ end
+
it "unwinds through a Proc-style closure and returns from a lambda-style closure in the closure chain" do
code = fixture __FILE__, "eval_return_with_lambda.rb"
ruby_exe(code).chomp.should == "a,b,c,eval,f"
@@ -228,6 +301,50 @@ describe "Kernel#eval" do
ruby_exe(code).chomp.should == "a,b,c,e,LocalJumpError,f"
end
+ it "can be called with Method#call" do
+ method(:eval).call("2 * 3").should == 6
+ end
+
+ it "has the correct default definee when called through Method#call" do
+ class EvalSpecs
+ method(:eval).call("def eval_spec_method_call; end")
+ EvalSpecs.should.method_defined?(:eval_spec_method_call, false)
+ end
+ end
+
+ it "makes flip-flop operator work correctly" do
+ ScratchPad.record []
+
+ eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 4) }"
+ ScratchPad.recorded.should == [4, 5, 6, 7, 8, 9]
+
+ ScratchPad.clear
+ end
+
+ it "returns nil if given an empty string" do
+ eval("").should == nil
+ end
+
+ context "with shebang" do
+ it "ignores shebang with ruby interpreter" do
+ pid = eval(<<~CODE.b)
+ #!/usr/bin/env ruby
+ Process.pid
+ CODE
+
+ pid.should == Process.pid
+ end
+
+ it "ignores shebang with non-ruby interpreter" do
+ pid = eval(<<~CODE.b)
+ #!/usr/bin/env puma
+ Process.pid
+ CODE
+
+ pid.should == Process.pid
+ end
+ end
+
# See language/magic_comment_spec.rb for more magic comments specs
describe "with a magic encoding comment" do
it "uses the magic comment encoding for the encoding of literal strings" do
@@ -245,8 +362,10 @@ end
CODE
code.encoding.should == Encoding::BINARY
eval(code)
- EvalSpecs.constants(false).should include(:"Vπ")
+ EvalSpecs.constants(false).should.include?(:"Vπ")
EvalSpecs::Vπ.should == 3.14
+ ensure
+ EvalSpecs.send(:remove_const, :Vπ)
end
it "allows an emacs-style magic comment encoding" do
@@ -258,8 +377,10 @@ end
CODE
code.encoding.should == Encoding::BINARY
eval(code)
- EvalSpecs.constants(false).should include(:"Vπemacs")
+ EvalSpecs.constants(false).should.include?(:"Vπemacs")
EvalSpecs::Vπemacs.should == 3.14
+ ensure
+ EvalSpecs.send(:remove_const, :Vπemacs)
end
it "allows spaces before the magic encoding comment" do
@@ -271,8 +392,10 @@ end
CODE
code.encoding.should == Encoding::BINARY
eval(code)
- EvalSpecs.constants(false).should include(:"Vπspaces")
+ EvalSpecs.constants(false).should.include?(:"Vπspaces")
EvalSpecs::Vπspaces.should == 3.14
+ ensure
+ EvalSpecs.send(:remove_const, :Vπspaces)
end
it "allows a shebang line before the magic encoding comment" do
@@ -285,8 +408,10 @@ end
CODE
code.encoding.should == Encoding::BINARY
eval(code)
- EvalSpecs.constants(false).should include(:"Vπshebang")
+ EvalSpecs.constants(false).should.include?(:"Vπshebang")
EvalSpecs::Vπshebang.should == 3.14
+ ensure
+ EvalSpecs.send(:remove_const, :Vπshebang)
end
it "allows a shebang line and some spaces before the magic encoding comment" do
@@ -299,27 +424,30 @@ end
CODE
code.encoding.should == Encoding::BINARY
eval(code)
- EvalSpecs.constants(false).should include(:"Vπshebang_spaces")
+ EvalSpecs.constants(false).should.include?(:"Vπshebang_spaces")
EvalSpecs::Vπshebang_spaces.should == 3.14
+ ensure
+ EvalSpecs.send(:remove_const, :Vπshebang_spaces)
end
it "allows a magic encoding comment and a subsequent frozen_string_literal magic comment" do
- # Make sure frozen_string_literal is not default true
- eval("'foo'".b).frozen?.should be_false
+ frozen_string_default = "test".frozen?
code = <<CODE.b
# encoding: UTF-8
-# frozen_string_literal: true
+# frozen_string_literal: #{!frozen_string_default}
class EvalSpecs
Vπstring = "frozen"
end
CODE
code.encoding.should == Encoding::BINARY
eval(code)
- EvalSpecs.constants(false).should include(:"Vπstring")
+ EvalSpecs.constants(false).should.include?(:"Vπstring")
EvalSpecs::Vπstring.should == "frozen"
EvalSpecs::Vπstring.encoding.should == Encoding::UTF_8
- EvalSpecs::Vπstring.frozen?.should be_true
+ EvalSpecs::Vπstring.frozen?.should == !frozen_string_default
+ ensure
+ EvalSpecs.send(:remove_const, :Vπstring)
end
it "allows a magic encoding comment and a frozen_string_literal magic comment on the same line in emacs style" do
@@ -331,15 +459,18 @@ end
CODE
code.encoding.should == Encoding::BINARY
eval(code)
- EvalSpecs.constants(false).should include(:"Vπsame_line")
+ EvalSpecs.constants(false).should.include?(:"Vπsame_line")
EvalSpecs::Vπsame_line.should == "frozen"
EvalSpecs::Vπsame_line.encoding.should == Encoding::UTF_8
- EvalSpecs::Vπsame_line.frozen?.should be_true
+ EvalSpecs::Vπsame_line.frozen?.should == true
+ ensure
+ EvalSpecs.send(:remove_const, :Vπsame_line)
end
it "ignores the magic encoding comment if it is after a frozen_string_literal magic comment" do
+ frozen_string_default = "test".frozen?
code = <<CODE.b
-# frozen_string_literal: true
+# frozen_string_literal: #{!frozen_string_default}
# encoding: UTF-8
class EvalSpecs
Vπfrozen_first = "frozen"
@@ -347,70 +478,75 @@ end
CODE
code.encoding.should == Encoding::BINARY
eval(code)
- EvalSpecs.constants(false).should_not include(:"Vπfrozen_first")
+ EvalSpecs.constants(false).should_not.include?(:"Vπfrozen_first")
binary_constant = "Vπfrozen_first".b.to_sym
- EvalSpecs.constants(false).should include(binary_constant)
+ EvalSpecs.constants(false).should.include?(binary_constant)
value = EvalSpecs.const_get(binary_constant)
value.should == "frozen"
value.encoding.should == Encoding::BINARY
- value.frozen?.should be_true
+ value.frozen?.should == !frozen_string_default
+ ensure
+ EvalSpecs.send(:remove_const, binary_constant)
end
it "ignores the frozen_string_literal magic comment if it appears after a token and warns if $VERBOSE is true" do
+ frozen_string_default = "test".frozen?
code = <<CODE
some_token_before_magic_comment = :anything
-# frozen_string_literal: true
+# frozen_string_literal: #{!frozen_string_default}
class EvalSpecs
Vπstring_not_frozen = "not frozen"
end
CODE
- -> { eval(code) }.should complain(/warning: `frozen_string_literal' is ignored after any tokens/, verbose: true)
- EvalSpecs::Vπstring_not_frozen.frozen?.should be_false
+ -> { eval(code) }.should complain(/warning: [`']frozen_string_literal' is ignored after any tokens/, verbose: true)
+ EvalSpecs::Vπstring_not_frozen.frozen?.should == frozen_string_default
EvalSpecs.send :remove_const, :Vπstring_not_frozen
-> { eval(code) }.should_not complain(verbose: false)
- EvalSpecs::Vπstring_not_frozen.frozen?.should be_false
+ EvalSpecs::Vπstring_not_frozen.frozen?.should == frozen_string_default
EvalSpecs.send :remove_const, :Vπstring_not_frozen
end
end
- it "activates refinements from the eval scope" do
- refinery = Module.new do
- refine EvalSpecs::A do
- def foo
- "bar"
+ describe 'with refinements' do
+ it "activates refinements from the eval scope" do
+ refinery = Module.new do
+ refine EvalSpecs::A do
+ def foo
+ "bar"
+ end
end
end
- end
- result = nil
+ result = nil
- Module.new do
- using refinery
+ Module.new do
+ using refinery
- result = eval "EvalSpecs::A.new.foo"
- end
+ result = eval "EvalSpecs::A.new.foo"
+ end
- result.should == "bar"
- end
+ result.should == "bar"
+ end
- it "activates refinements from the binding" do
- refinery = Module.new do
- refine EvalSpecs::A do
- def foo
- "bar"
+ it "activates refinements from the binding" do
+ refinery = Module.new do
+ refine EvalSpecs::A do
+ def foo
+ "bar"
+ end
end
end
- end
- b = nil
- m = Module.new do
- using refinery
- b = binding
- end
+ b = nil
+ m = Module.new do
+ using refinery
+ b = binding
+ end
- result = eval "EvalSpecs::A.new.foo", b
+ result = eval "EvalSpecs::A.new.foo", b
- result.should == "bar"
+ result.should == "bar"
+ end
end
end
diff --git a/spec/ruby/core/kernel/exec_spec.rb b/spec/ruby/core/kernel/exec_spec.rb
index 1b4a7ae6f4..fd8485791a 100644
--- a/spec/ruby/core/kernel/exec_spec.rb
+++ b/spec/ruby/core/kernel/exec_spec.rb
@@ -3,16 +3,16 @@ require_relative 'fixtures/classes'
describe "Kernel#exec" do
it "is a private method" do
- Kernel.should have_private_instance_method(:exec)
+ Kernel.private_instance_methods(false).should.include?(:exec)
end
it "runs the specified command, replacing current process" do
- ruby_exe('exec "echo hello"; puts "fail"', escape: true).should == "hello\n"
+ ruby_exe('exec "echo hello"; puts "fail"').should == "hello\n"
end
end
describe "Kernel.exec" do
it "runs the specified command, replacing current process" do
- ruby_exe('Kernel.exec "echo hello"; puts "fail"', escape: true).should == "hello\n"
+ ruby_exe('Kernel.exec "echo hello"; puts "fail"').should == "hello\n"
end
end
diff --git a/spec/ruby/core/kernel/exit_spec.rb b/spec/ruby/core/kernel/exit_spec.rb
index f168cb375e..864ff84449 100644
--- a/spec/ruby/core/kernel/exit_spec.rb
+++ b/spec/ruby/core/kernel/exit_spec.rb
@@ -4,24 +4,24 @@ require_relative '../../shared/process/exit'
describe "Kernel#exit" do
it "is a private method" do
- Kernel.should have_private_instance_method(:exit)
+ Kernel.private_instance_methods(false).should.include?(:exit)
end
it_behaves_like :process_exit, :exit, KernelSpecs::Method.new
end
+describe "Kernel.exit" do
+ it_behaves_like :process_exit, :exit, Kernel
+end
+
describe "Kernel#exit!" do
it "is a private method" do
- Kernel.should have_private_instance_method(:exit!)
+ Kernel.private_instance_methods(false).should.include?(:exit!)
end
it_behaves_like :process_exit!, :exit!, "self"
end
-describe "Kernel.exit" do
- it_behaves_like :process_exit, :exit, Kernel
-end
-
describe "Kernel.exit!" do
- it_behaves_like :process_exit!, :exit!, Kernel
+ it_behaves_like :process_exit!, :exit!, "Kernel"
end
diff --git a/spec/ruby/core/kernel/extend_spec.rb b/spec/ruby/core/kernel/extend_spec.rb
index 47b22f3a18..a344c7b5ca 100644
--- a/spec/ruby/core/kernel/extend_spec.rb
+++ b/spec/ruby/core/kernel/extend_spec.rb
@@ -53,13 +53,13 @@ describe "Kernel#extend" do
end
it "raises an ArgumentError when no arguments given" do
- -> { Object.new.extend }.should raise_error(ArgumentError)
+ -> { Object.new.extend }.should.raise(ArgumentError)
end
it "raises a TypeError when the argument is not a Module" do
o = mock('o')
klass = Class.new
- -> { o.extend(klass) }.should raise_error(TypeError)
+ -> { o.extend(klass) }.should.raise(TypeError)
end
describe "on frozen instance" do
@@ -69,11 +69,23 @@ describe "Kernel#extend" do
end
it "raises an ArgumentError when no arguments given" do
- -> { @frozen.extend }.should raise_error(ArgumentError)
+ -> { @frozen.extend }.should.raise(ArgumentError)
end
it "raises a FrozenError" do
- -> { @frozen.extend @module }.should raise_error(FrozenError)
+ -> { @frozen.extend @module }.should.raise(FrozenError)
end
end
+
+ it "updated class methods of a module when it extends self and includes another module" do
+ a = Module.new do
+ extend self
+ end
+ b = Module.new do
+ def foo; :foo; end
+ end
+
+ a.include b
+ a.foo.should == :foo
+ end
end
diff --git a/spec/ruby/core/kernel/fail_spec.rb b/spec/ruby/core/kernel/fail_spec.rb
index fab622037e..ac379b67d5 100644
--- a/spec/ruby/core/kernel/fail_spec.rb
+++ b/spec/ruby/core/kernel/fail_spec.rb
@@ -3,11 +3,11 @@ require_relative 'fixtures/classes'
describe "Kernel#fail" do
it "is a private method" do
- Kernel.should have_private_instance_method(:fail)
+ Kernel.private_instance_methods(false).should.include?(:fail)
end
it "raises a RuntimeError" do
- -> { fail }.should raise_error(RuntimeError)
+ -> { fail }.should.raise(RuntimeError)
end
it "accepts an Object with an exception method returning an Exception" do
@@ -15,12 +15,12 @@ describe "Kernel#fail" do
def obj.exception(msg)
StandardError.new msg
end
- -> { fail obj, "..." }.should raise_error(StandardError, "...")
+ -> { fail obj, "..." }.should.raise(StandardError, "...")
end
it "instantiates the specified exception class" do
error_class = Class.new(RuntimeError)
- -> { fail error_class }.should raise_error(error_class)
+ -> { fail error_class }.should.raise(error_class)
end
it "uses the specified message" do
@@ -33,7 +33,7 @@ describe "Kernel#fail" do
else
raise Exception
end
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
end
end
diff --git a/spec/ruby/core/kernel/fixtures/Complex.rb b/spec/ruby/core/kernel/fixtures/Complex.rb
new file mode 100644
index 0000000000..bf14d55ad5
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/Complex.rb
@@ -0,0 +1,5 @@
+module KernelSpecs
+ def self.Complex_method(string)
+ Complex(string)
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/at_exit.rb b/spec/ruby/core/kernel/fixtures/at_exit.rb
deleted file mode 100644
index 9c11a7ad6c..0000000000
--- a/spec/ruby/core/kernel/fixtures/at_exit.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-at_exit do
- STDERR.puts "at_exit ran"
-end
diff --git a/spec/ruby/core/kernel/fixtures/autoload_relative_b.rb b/spec/ruby/core/kernel/fixtures/autoload_relative_b.rb
new file mode 100644
index 0000000000..6de6f5091d
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/autoload_relative_b.rb
@@ -0,0 +1,7 @@
+module KernelSpecs
+ module KSAutoloadRelativeB
+ def self.loaded
+ :ksautoload_b
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/autoload_relative_d.rb b/spec/ruby/core/kernel/fixtures/autoload_relative_d.rb
new file mode 100644
index 0000000000..5b6b5e1fa2
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/autoload_relative_d.rb
@@ -0,0 +1,5 @@
+module KSAutoloadRelativeD
+ def self.loaded
+ :ksautoload_d
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/classes.rb b/spec/ruby/core/kernel/fixtures/classes.rb
index 8702c925c8..ad82f3cef5 100644
--- a/spec/ruby/core/kernel/fixtures/classes.rb
+++ b/spec/ruby/core/kernel/fixtures/classes.rb
@@ -180,6 +180,25 @@ module KernelSpecs
alias aliased_pub_method pub_method
end
+ class BasicA < BasicObject
+ define_method(:respond_to?, ::Kernel.instance_method(:respond_to?))
+
+ def pub_method; :public_method; end
+
+ def undefed_method; :undefed_method; end
+ undef_method :undefed_method
+
+ protected
+ def protected_method; :protected_method; end
+
+ private
+ def private_method; :private_method; end
+ end
+
+ class MissingA < A
+ undef :respond_to_missing?
+ end
+
class VisibilityChange
class << self
private :new
@@ -219,10 +238,28 @@ module KernelSpecs
block_given?
end
+ def self.accept_block_inside_block()
+ yield_self {
+ block_given?
+ }
+ end
+
+ def self.accept_block_as_argument_inside_block(&block)
+ yield_self {
+ block_given?
+ }
+ end
+
class << self
define_method(:defined_block) do
block_given?
end
+
+ define_method(:defined_block_inside_block) do
+ yield_self {
+ block_given?
+ }
+ end
end
end
@@ -235,10 +272,28 @@ module KernelSpecs
self.send(:block_given?)
end
+ def self.accept_block_inside_block
+ yield_self {
+ self.send(:block_given?)
+ }
+ end
+
+ def self.accept_block_as_argument_inside_block(&block)
+ yield_self {
+ self.send(:block_given?)
+ }
+ end
+
class << self
define_method(:defined_block) do
self.send(:block_given?)
end
+
+ define_method(:defined_block_inside_block) do
+ yield_self {
+ self.send(:block_given?)
+ }
+ end
end
end
@@ -251,10 +306,28 @@ module KernelSpecs
Kernel.block_given?
end
+ def self.accept_block_inside_block
+ yield_self {
+ Kernel.block_given?
+ }
+ end
+
+ def self.accept_block_as_argument_inside_block(&block)
+ yield_self {
+ Kernel.block_given?
+ }
+ end
+
class << self
define_method(:defined_block) do
Kernel.block_given?
end
+
+ define_method(:defined_block_inside_block) do
+ yield_self {
+ Kernel.block_given?
+ }
+ end
end
end
@@ -281,14 +354,25 @@ module KernelSpecs
@two = two
end
- def initialize_copy(other)
+ def initialize_copy(other, **kw)
ScratchPad.record object_id
end
+
+ # define to support calling #clone with optional :freeze keyword argument
+ def initialize_clone(other, **kw)
+ super(other) # to call #initialize_copy
+ end
end
class Clone
def initialize_clone(other)
- ScratchPad.record other.object_id
+ ScratchPad.record other
+ end
+ end
+
+ class CloneFreeze
+ def initialize_clone(other, **kwargs)
+ ScratchPad.record([other, kwargs])
end
end
@@ -362,18 +446,19 @@ module KernelSpecs
class RespondViaMissing
def respond_to_missing?(method, priv=false)
case method
- when :handled_publicly
- true
- when :handled_privately
- priv
- when :not_handled
- false
- else
- raise "Typo in method name"
+ when :handled_publicly
+ true
+ when :handled_privately
+ priv
+ when :not_handled
+ false
+ else
+ raise "Typo in method name: #{method.inspect}"
end
end
def method_missing(method, *args)
+ raise "the method name should be a Symbol" unless Symbol === method
"Done #{method}(#{args})"
end
end
diff --git a/spec/ruby/core/kernel/fixtures/warn_core_method.rb b/spec/ruby/core/kernel/fixtures/warn_core_method.rb
new file mode 100644
index 0000000000..fd82562404
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/warn_core_method.rb
@@ -0,0 +1,14 @@
+raise 'should be run without RubyGems' if defined?(Gem)
+
+public def deprecated(n=1)
+ # puts nil, caller(0), nil
+ warn "use X instead", uplevel: n
+end
+
+1.times do # to test with a non-empty stack above the reported locations
+ deprecated
+ tap(&:deprecated)
+ tap { deprecated(2) }
+ # eval sources with a <internal: file are also ignored
+ eval "tap(&:deprecated)", nil, "<internal:should-be-skipped-by-warn-uplevel>"
+end
diff --git a/spec/ruby/core/kernel/fork_spec.rb b/spec/ruby/core/kernel/fork_spec.rb
index b37f9980e0..4a2c848202 100644
--- a/spec/ruby/core/kernel/fork_spec.rb
+++ b/spec/ruby/core/kernel/fork_spec.rb
@@ -4,7 +4,7 @@ require_relative '../../shared/process/fork'
describe "Kernel#fork" do
it "is a private method" do
- Kernel.should have_private_instance_method(:fork)
+ Kernel.private_instance_methods(false).should.include?(:fork)
end
it_behaves_like :process_fork, :fork, KernelSpecs::Method.new
diff --git a/spec/ruby/core/kernel/format_spec.rb b/spec/ruby/core/kernel/format_spec.rb
index 72fd40b952..35c752b1ab 100644
--- a/spec/ruby/core/kernel/format_spec.rb
+++ b/spec/ruby/core/kernel/format_spec.rb
@@ -1,9 +1,10 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
+# NOTE: most specs are in sprintf_spec.rb, this is just an alias
describe "Kernel#format" do
it "is a private method" do
- Kernel.should have_private_instance_method(:format)
+ Kernel.private_instance_methods(false).should.include?(:format)
end
end
@@ -11,4 +12,36 @@ describe "Kernel.format" do
it "is accessible as a module function" do
Kernel.format("%s", "hello").should == "hello"
end
+
+ describe "when $VERBOSE is true" do
+ it "warns if too many arguments are passed" do
+ code = <<~RUBY
+ $VERBOSE = true
+ format("test", 1)
+ RUBY
+
+ ruby_exe(code, args: "2>&1").should.include?("warning: too many arguments for format string")
+ end
+
+ it "does not warns if too many keyword arguments are passed" do
+ code = <<~RUBY
+ $VERBOSE = true
+ format("test %{test}", test: 1, unused: 2)
+ RUBY
+
+ ruby_exe(code, args: "2>&1").should_not.include?("warning")
+ end
+
+ ruby_bug "#20593", ""..."3.4" do
+ it "doesn't warns if keyword arguments are passed and none are used" do
+ code = <<~RUBY
+ $VERBOSE = true
+ format("test", test: 1)
+ format("test", {})
+ RUBY
+
+ ruby_exe(code, args: "2>&1").should_not.include?("warning")
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/freeze_spec.rb b/spec/ruby/core/kernel/freeze_spec.rb
index e4a01b5df5..079617dce4 100644
--- a/spec/ruby/core/kernel/freeze_spec.rb
+++ b/spec/ruby/core/kernel/freeze_spec.rb
@@ -4,46 +4,46 @@ require_relative 'fixtures/classes'
describe "Kernel#freeze" do
it "prevents self from being further modified" do
o = mock('o')
- o.frozen?.should be_false
+ o.frozen?.should == false
o.freeze
- o.frozen?.should be_true
+ o.frozen?.should == true
end
it "returns self" do
o = Object.new
- o.freeze.should equal(o)
+ o.freeze.should.equal?(o)
end
describe "on integers" do
it "has no effect since they are already frozen" do
- 1.frozen?.should be_true
+ 1.frozen?.should == true
1.freeze
bignum = bignum_value
- bignum.frozen?.should be_true
+ bignum.frozen?.should == true
bignum.freeze
end
end
describe "on a Float" do
it "has no effect since it is already frozen" do
- 1.2.frozen?.should be_true
+ 1.2.frozen?.should == true
1.2.freeze
end
end
describe "on a Symbol" do
it "has no effect since it is already frozen" do
- :sym.frozen?.should be_true
+ :sym.frozen?.should == true
:sym.freeze
end
end
describe "on true, false and nil" do
it "has no effect since they are already frozen" do
- nil.frozen?.should be_true
- true.frozen?.should be_true
- false.frozen?.should be_true
+ nil.frozen?.should == true
+ true.frozen?.should == true
+ false.frozen?.should == true
nil.freeze
true.freeze
@@ -54,7 +54,7 @@ describe "Kernel#freeze" do
describe "on a Complex" do
it "has no effect since it is already frozen" do
c = Complex(1.3, 3.1)
- c.frozen?.should be_true
+ c.frozen?.should == true
c.freeze
end
end
@@ -62,7 +62,7 @@ describe "Kernel#freeze" do
describe "on a Rational" do
it "has no effect since it is already frozen" do
r = Rational(1, 3)
- r.frozen?.should be_true
+ r.frozen?.should == true
r.freeze
end
end
@@ -72,12 +72,20 @@ describe "Kernel#freeze" do
def mutate; @foo = 1; end
end.new
o.freeze
- -> {o.mutate}.should raise_error(RuntimeError)
+ -> {o.mutate}.should.raise(RuntimeError)
end
it "causes instance_variable_set to raise RuntimeError" do
o = Object.new
o.freeze
- -> {o.instance_variable_set(:@foo, 1)}.should raise_error(RuntimeError)
+ -> {o.instance_variable_set(:@foo, 1)}.should.raise(RuntimeError)
+ end
+
+ it "freezes an object's singleton class" do
+ o = Object.new
+ c = o.singleton_class
+ c.frozen?.should == false
+ o.freeze
+ c.frozen?.should == true
end
end
diff --git a/spec/ruby/core/kernel/frozen_spec.rb b/spec/ruby/core/kernel/frozen_spec.rb
index a4cec4263d..8b8fad3de7 100644
--- a/spec/ruby/core/kernel/frozen_spec.rb
+++ b/spec/ruby/core/kernel/frozen_spec.rb
@@ -12,9 +12,9 @@ describe "Kernel#frozen?" do
describe "on true, false and nil" do
it "returns true" do
- true.frozen?.should be_true
- false.frozen?.should be_true
- nil.frozen?.should be_true
+ true.frozen?.should == true
+ false.frozen?.should == true
+ nil.frozen?.should == true
end
end
@@ -25,8 +25,8 @@ describe "Kernel#frozen?" do
end
it "returns true" do
- @fixnum.frozen?.should be_true
- @bignum.frozen?.should be_true
+ @fixnum.frozen?.should == true
+ @bignum.frozen?.should == true
end
end
@@ -36,7 +36,7 @@ describe "Kernel#frozen?" do
end
it "returns true" do
- @float.frozen?.should be_true
+ @float.frozen?.should == true
end
end
@@ -46,31 +46,31 @@ describe "Kernel#frozen?" do
end
it "returns true" do
- @symbol.frozen?.should be_true
+ @symbol.frozen?.should == true
end
end
describe "on a Complex" do
it "returns true" do
c = Complex(1.3, 3.1)
- c.frozen?.should be_true
+ c.frozen?.should == true
end
it "literal returns true" do
c = eval "1.3i"
- c.frozen?.should be_true
+ c.frozen?.should == true
end
end
describe "on a Rational" do
it "returns true" do
r = Rational(1, 3)
- r.frozen?.should be_true
+ r.frozen?.should == true
end
it "literal returns true" do
r = eval "1/3r"
- r.frozen?.should be_true
+ r.frozen?.should == true
end
end
end
diff --git a/spec/ruby/core/kernel/gets_spec.rb b/spec/ruby/core/kernel/gets_spec.rb
index 104613dbfa..1b2b36fbb9 100644
--- a/spec/ruby/core/kernel/gets_spec.rb
+++ b/spec/ruby/core/kernel/gets_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "Kernel#gets" do
it "is a private method" do
- Kernel.should have_private_instance_method(:gets)
+ Kernel.private_instance_methods(false).should.include?(:gets)
end
it "calls ARGF.gets" do
diff --git a/spec/ruby/core/kernel/global_variables_spec.rb b/spec/ruby/core/kernel/global_variables_spec.rb
index 8bce8e25b7..d41bdff49a 100644
--- a/spec/ruby/core/kernel/global_variables_spec.rb
+++ b/spec/ruby/core/kernel/global_variables_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "Kernel.global_variables" do
it "is a private method" do
- Kernel.should have_private_instance_method(:global_variables)
+ Kernel.private_instance_methods(false).should.include?(:global_variables)
end
before :all do
@@ -11,13 +11,13 @@ describe "Kernel.global_variables" do
end
it "finds subset starting with std" do
- global_variables.grep(/std/).should include(:$stderr, :$stdin, :$stdout)
+ global_variables.grep(/std/).to_set.should >= Set[:$stderr, :$stdin, :$stdout]
a = global_variables.size
gvar_name = "$foolish_global_var#{@i += 1}"
global_variables.include?(gvar_name.to_sym).should == false
eval("#{gvar_name} = 1")
global_variables.size.should == a+1
- global_variables.should include(gvar_name.to_sym)
+ global_variables.should.include?(gvar_name.to_sym)
end
end
diff --git a/spec/ruby/core/kernel/gsub_spec.rb b/spec/ruby/core/kernel/gsub_spec.rb
index a0cb9f2a70..e05349e2b5 100644
--- a/spec/ruby/core/kernel/gsub_spec.rb
+++ b/spec/ruby/core/kernel/gsub_spec.rb
@@ -6,20 +6,20 @@ require_relative 'fixtures/classes'
ruby_version_is ""..."1.9" do
describe "Kernel#gsub" do
it "is a private method" do
- Kernel.should have_private_instance_method(:gsub)
+ Kernel.private_instance_methods(false).should.include?(:gsub)
end
it "raises a TypeError if $_ is not a String" do
-> {
$_ = 123
gsub(/./, "!")
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
it "when matches sets $_ to a new string, leaving the former value unaltered" do
orig_value = $_ = "hello"
gsub("ello", "ola")
- $_.should_not equal(orig_value)
+ $_.should_not.equal?(orig_value)
$_.should == "hola"
orig_value.should == "hello"
end
@@ -86,7 +86,7 @@ ruby_version_is ""..."1.9" do
describe "Kernel#gsub!" do
it "is a private method" do
- Kernel.should have_private_instance_method(:gsub!)
+ Kernel.private_instance_methods(false).should.include?(:gsub!)
end
end
diff --git a/spec/ruby/core/kernel/initialize_clone_spec.rb b/spec/ruby/core/kernel/initialize_clone_spec.rb
new file mode 100644
index 0000000000..0033eadffb
--- /dev/null
+++ b/spec/ruby/core/kernel/initialize_clone_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#initialize_clone" do
+ it "is a private instance method" do
+ Kernel.private_instance_methods(false).should.include?(:initialize_clone)
+ end
+
+ it "returns the receiver" do
+ a = Object.new
+ b = Object.new
+ a.send(:initialize_clone, b).should == a
+ end
+
+ it "calls #initialize_copy" do
+ a = Object.new
+ b = Object.new
+ a.should_receive(:initialize_copy).with(b)
+ a.send(:initialize_clone, b)
+ end
+
+ it "accepts a :freeze keyword argument for obj.clone(freeze: value)" do
+ a = Object.new
+ b = Object.new
+ a.send(:initialize_clone, b, freeze: true).should == a
+ end
+end
diff --git a/spec/ruby/core/kernel/initialize_copy_spec.rb b/spec/ruby/core/kernel/initialize_copy_spec.rb
index fe08d184ad..ebac0c14de 100644
--- a/spec/ruby/core/kernel/initialize_copy_spec.rb
+++ b/spec/ruby/core/kernel/initialize_copy_spec.rb
@@ -1,17 +1,24 @@
require_relative '../../spec_helper'
describe "Kernel#initialize_copy" do
+ it "returns self" do
+ obj = Object.new
+ obj.send(:initialize_copy, obj).should.equal?(obj)
+ end
+
it "does nothing if the argument is the same as the receiver" do
obj = Object.new
obj.send(:initialize_copy, obj).should.equal?(obj)
- obj.freeze
+
+ obj = Object.new.freeze
obj.send(:initialize_copy, obj).should.equal?(obj)
+
1.send(:initialize_copy, 1).should.equal?(1)
end
it "raises FrozenError if the receiver is frozen" do
- -> { Object.new.freeze.send(:initialize_copy, Object.new) }.should raise_error(FrozenError)
- -> { 1.send(:initialize_copy, Object.new) }.should raise_error(FrozenError)
+ -> { Object.new.freeze.send(:initialize_copy, Object.new) }.should.raise(FrozenError)
+ -> { 1.send(:initialize_copy, Object.new) }.should.raise(FrozenError)
end
it "raises TypeError if the objects are of different class" do
@@ -20,10 +27,10 @@ describe "Kernel#initialize_copy" do
a = klass.new
b = sub.new
message = 'initialize_copy should take same class object'
- -> { a.send(:initialize_copy, b) }.should raise_error(TypeError, message)
- -> { b.send(:initialize_copy, a) }.should raise_error(TypeError, message)
+ -> { a.send(:initialize_copy, b) }.should.raise(TypeError, message)
+ -> { b.send(:initialize_copy, a) }.should.raise(TypeError, message)
- -> { a.send(:initialize_copy, 1) }.should raise_error(TypeError, message)
- -> { a.send(:initialize_copy, 1.0) }.should raise_error(TypeError, message)
+ -> { a.send(:initialize_copy, 1) }.should.raise(TypeError, message)
+ -> { a.send(:initialize_copy, 1.0) }.should.raise(TypeError, message)
end
end
diff --git a/spec/ruby/core/kernel/initialize_dup_spec.rb b/spec/ruby/core/kernel/initialize_dup_spec.rb
new file mode 100644
index 0000000000..f144647eb8
--- /dev/null
+++ b/spec/ruby/core/kernel/initialize_dup_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#initialize_dup" do
+ it "is a private instance method" do
+ Kernel.private_instance_methods(false).should.include?(:initialize_dup)
+ end
+
+ it "returns the receiver" do
+ a = Object.new
+ b = Object.new
+ a.send(:initialize_dup, b).should == a
+ end
+
+ it "calls #initialize_copy" do
+ a = Object.new
+ b = Object.new
+ a.should_receive(:initialize_copy).with(b)
+ a.send(:initialize_dup, b)
+ end
+end
diff --git a/spec/ruby/core/kernel/inspect_spec.rb b/spec/ruby/core/kernel/inspect_spec.rb
index a946d032db..8fc3ab51cd 100644
--- a/spec/ruby/core/kernel/inspect_spec.rb
+++ b/spec/ruby/core/kernel/inspect_spec.rb
@@ -3,17 +3,7 @@ require_relative 'fixtures/classes'
describe "Kernel#inspect" do
it "returns a String" do
- Object.new.inspect.should be_an_instance_of(String)
- end
-
- ruby_version_is ''...'2.7' do
- it "returns a tainted string if self is tainted" do
- Object.new.taint.inspect.tainted?.should be_true
- end
-
- it "returns an untrusted string if self is untrusted" do
- Object.new.untrust.inspect.untrusted?.should be_true
- end
+ Object.new.inspect.should.instance_of?(String)
end
it "does not call #to_s if it is defined" do
@@ -30,4 +20,84 @@ describe "Kernel#inspect" do
obj = Object.new
obj.inspect.should =~ /^#<Object:0x[0-9a-f]+>$/
end
+
+ it "returns a String for an object without #class method" do
+ obj = Object.new
+ class << obj
+ undef_method :class
+ end
+ obj.inspect.should.is_a?(String)
+ end
+
+ ruby_version_is "4.0" do
+ it "calls #instance_variables_to_inspect private method to know which variables to display" do
+ obj = Object.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ obj.singleton_class.class_eval do
+ private def instance_variables_to_inspect = %i[@host @user @does_not_exist]
+ end
+
+ inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
+ inspected.should == '#<Object:0x00 @host="localhost", @user="root">'
+
+ obj = Object.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ obj.singleton_class.class_eval do
+ private def instance_variables_to_inspect = []
+ end
+
+ inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
+ inspected.should == "#<Object:0x00>"
+ end
+
+ it "displays all instance variables if #instance_variables_to_inspect returns nil" do
+ obj = Object.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ obj.singleton_class.class_eval do
+ private def instance_variables_to_inspect = nil
+ end
+
+ inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
+ inspected.should == %{#<Object:0x00 @host="localhost", @user="root", @password="hunter2">}
+ end
+
+ it "displays all instance variables if #instance_variables_to_inspect is not defined" do
+ obj = BasicObject.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ method_inspect = Kernel.instance_method(:inspect)
+
+ inspected = method_inspect.bind(obj).call.sub(/^#<BasicObject:0x[0-9a-f]+/, '#<BasicObject:0x00')
+ inspected.should == %{#<BasicObject:0x00 @host="localhost", @user="root", @password="hunter2">}
+ end
+
+ it "raises an error if #instance_variables_to_inspect returns an invalid value" do
+ obj = Object.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ obj.singleton_class.class_eval do
+ private def instance_variables_to_inspect = {}
+ end
+
+ ->{ obj.inspect }.should.raise(TypeError, "Expected #instance_variables_to_inspect to return an Array or nil, but it returned Hash")
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/instance_of_spec.rb b/spec/ruby/core/kernel/instance_of_spec.rb
index d1170d5047..8d19974aa3 100644
--- a/spec/ruby/core/kernel/instance_of_spec.rb
+++ b/spec/ruby/core/kernel/instance_of_spec.rb
@@ -33,8 +33,8 @@ describe "Kernel#instance_of?" do
end
it "raises a TypeError if given an object that is not a Class nor a Module" do
- -> { @o.instance_of?(Object.new) }.should raise_error(TypeError)
- -> { @o.instance_of?('KernelSpecs::InstanceClass') }.should raise_error(TypeError)
- -> { @o.instance_of?(1) }.should raise_error(TypeError)
+ -> { @o.instance_of?(Object.new) }.should.raise(TypeError)
+ -> { @o.instance_of?('KernelSpecs::InstanceClass') }.should.raise(TypeError)
+ -> { @o.instance_of?(1) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/kernel/instance_variable_defined_spec.rb b/spec/ruby/core/kernel/instance_variable_defined_spec.rb
index 2ebb582b43..512f811393 100644
--- a/spec/ruby/core/kernel/instance_variable_defined_spec.rb
+++ b/spec/ruby/core/kernel/instance_variable_defined_spec.rb
@@ -8,21 +8,21 @@ describe "Kernel#instance_variable_defined?" do
describe "when passed a String" do
it "returns false if the instance variable is not defined" do
- @instance.instance_variable_defined?("@goodbye").should be_false
+ @instance.instance_variable_defined?("@goodbye").should == false
end
it "returns true if the instance variable is defined" do
- @instance.instance_variable_defined?("@greeting").should be_true
+ @instance.instance_variable_defined?("@greeting").should == true
end
end
describe "when passed a Symbol" do
it "returns false if the instance variable is not defined" do
- @instance.instance_variable_defined?(:@goodbye).should be_false
+ @instance.instance_variable_defined?(:@goodbye).should == false
end
it "returns true if the instance variable is defined" do
- @instance.instance_variable_defined?(:@greeting).should be_true
+ @instance.instance_variable_defined?(:@greeting).should == true
end
end
@@ -30,12 +30,12 @@ describe "Kernel#instance_variable_defined?" do
-> do
obj = mock("kernel instance_variable_defined?")
@instance.instance_variable_defined? obj
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it "returns false if the instance variable is not defined for different types" do
[nil, false, true, 1, 2.0, :test, "test"].each do |obj|
- obj.instance_variable_defined?("@goodbye").should be_false
+ obj.instance_variable_defined?("@goodbye").should == false
end
end
end
diff --git a/spec/ruby/core/kernel/instance_variable_get_spec.rb b/spec/ruby/core/kernel/instance_variable_get_spec.rb
index 84d3188f07..8404c94192 100644
--- a/spec/ruby/core/kernel/instance_variable_get_spec.rb
+++ b/spec/ruby/core/kernel/instance_variable_get_spec.rb
@@ -20,29 +20,29 @@ describe "Kernel#instance_variable_get" do
end
it "returns nil when the referred instance variable does not exist" do
- @obj.instance_variable_get(:@does_not_exist).should be_nil
+ @obj.instance_variable_get(:@does_not_exist).should == nil
end
it "raises a TypeError when the passed argument does not respond to #to_str" do
- -> { @obj.instance_variable_get(Object.new) }.should raise_error(TypeError)
+ -> { @obj.instance_variable_get(Object.new) }.should.raise(TypeError)
end
it "raises a TypeError when the passed argument can't be converted to a String" do
obj = mock("to_str")
obj.stub!(:to_str).and_return(123)
- -> { @obj.instance_variable_get(obj) }.should raise_error(TypeError)
+ -> { @obj.instance_variable_get(obj) }.should.raise(TypeError)
end
it "raises a NameError when the conversion result does not start with an '@'" do
obj = mock("to_str")
obj.stub!(:to_str).and_return("test")
- -> { @obj.instance_variable_get(obj) }.should raise_error(NameError)
+ -> { @obj.instance_variable_get(obj) }.should.raise(NameError)
end
it "raises a NameError when passed just '@'" do
obj = mock("to_str")
obj.stub!(:to_str).and_return('@')
- -> { @obj.instance_variable_get(obj) }.should raise_error(NameError)
+ -> { @obj.instance_variable_get(obj) }.should.raise(NameError)
end
end
@@ -57,15 +57,21 @@ describe "Kernel#instance_variable_get when passed Symbol" do
end
it "raises a NameError when passed :@ as an instance variable name" do
- -> { @obj.instance_variable_get(:"@") }.should raise_error(NameError)
+ -> { @obj.instance_variable_get(:"@") }.should.raise(NameError)
end
it "raises a NameError when the passed Symbol does not start with an '@'" do
- -> { @obj.instance_variable_get(:test) }.should raise_error(NameError)
+ -> { @obj.instance_variable_get(:test) }.should.raise(NameError)
end
it "raises a NameError when the passed Symbol is an invalid instance variable name" do
- -> { @obj.instance_variable_get(:"@0") }.should raise_error(NameError)
+ -> { @obj.instance_variable_get(:"@0") }.should.raise(NameError)
+ end
+
+ it "returns nil or raises for frozen objects" do
+ nil.instance_variable_get(:@foo).should == nil
+ -> { nil.instance_variable_get(:foo) }.should.raise(NameError)
+ :foo.instance_variable_get(:@foo).should == nil
end
end
@@ -80,26 +86,26 @@ describe "Kernel#instance_variable_get when passed String" do
end
it "raises a NameError when the passed String does not start with an '@'" do
- -> { @obj.instance_variable_get("test") }.should raise_error(NameError)
+ -> { @obj.instance_variable_get("test") }.should.raise(NameError)
end
it "raises a NameError when the passed String is an invalid instance variable name" do
- -> { @obj.instance_variable_get("@0") }.should raise_error(NameError)
+ -> { @obj.instance_variable_get("@0") }.should.raise(NameError)
end
it "raises a NameError when passed '@' as an instance variable name" do
- -> { @obj.instance_variable_get("@") }.should raise_error(NameError)
+ -> { @obj.instance_variable_get("@") }.should.raise(NameError)
end
end
-describe "Kernel#instance_variable_get when passed Fixnum" do
+describe "Kernel#instance_variable_get when passed Integer" do
before :each do
@obj = Object.new
@obj.instance_variable_set("@test", :test)
end
it "raises a TypeError" do
- -> { @obj.instance_variable_get(10) }.should raise_error(TypeError)
- -> { @obj.instance_variable_get(-10) }.should raise_error(TypeError)
+ -> { @obj.instance_variable_get(10) }.should.raise(TypeError)
+ -> { @obj.instance_variable_get(-10) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/kernel/instance_variable_set_spec.rb b/spec/ruby/core/kernel/instance_variable_set_spec.rb
index 12b2185878..bf165a824e 100644
--- a/spec/ruby/core/kernel/instance_variable_set_spec.rb
+++ b/spec/ruby/core/kernel/instance_variable_set_spec.rb
@@ -18,26 +18,26 @@ describe "Kernel#instance_variable_set" do
it "raises a NameError exception if the argument is not of form '@x'" do
no_dog = Class.new
- -> { no_dog.new.instance_variable_set(:c, "cat") }.should raise_error(NameError)
+ -> { no_dog.new.instance_variable_set(:c, "cat") }.should.raise(NameError)
end
it "raises a NameError exception if the argument is an invalid instance variable name" do
digit_dog = Class.new
- -> { digit_dog.new.instance_variable_set(:"@0", "cat") }.should raise_error(NameError)
+ -> { digit_dog.new.instance_variable_set(:"@0", "cat") }.should.raise(NameError)
end
it "raises a NameError when the argument is '@'" do
dog_at = Class.new
- -> { dog_at.new.instance_variable_set(:"@", "cat") }.should raise_error(NameError)
+ -> { dog_at.new.instance_variable_set(:"@", "cat") }.should.raise(NameError)
end
- it "raises a TypeError if the instance variable name is a Fixnum" do
- -> { "".instance_variable_set(1, 2) }.should raise_error(TypeError)
+ it "raises a TypeError if the instance variable name is an Integer" do
+ -> { "".instance_variable_set(1, 2) }.should.raise(TypeError)
end
it "raises a TypeError if the instance variable name is an object that does not respond to to_str" do
class KernelSpecs::A; end
- -> { "".instance_variable_set(KernelSpecs::A.new, 3) }.should raise_error(TypeError)
+ -> { "".instance_variable_set(KernelSpecs::A.new, 3) }.should.raise(TypeError)
end
it "raises a NameError if the passed object, when coerced with to_str, does not start with @" do
@@ -46,11 +46,11 @@ describe "Kernel#instance_variable_set" do
":c"
end
end
- -> { "".instance_variable_set(KernelSpecs::B.new, 4) }.should raise_error(NameError)
+ -> { "".instance_variable_set(KernelSpecs::B.new, 4) }.should.raise(NameError)
end
it "raises a NameError if pass an object that cannot be a symbol" do
- -> { "".instance_variable_set(:c, 1) }.should raise_error(NameError)
+ -> { "".instance_variable_set(:c, 1) }.should.raise(NameError)
end
it "accepts as instance variable name any instance of a class that responds to to_str" do
@@ -78,16 +78,28 @@ describe "Kernel#instance_variable_set" do
end
it "keeps stored object after any exceptions" do
- -> { @frozen.instance_variable_set(:@ivar, :replacement) }.should raise_error(Exception)
- @frozen.ivar.should equal(:origin)
+ -> { @frozen.instance_variable_set(:@ivar, :replacement) }.should.raise(Exception)
+ @frozen.ivar.should.equal?(:origin)
end
it "raises a FrozenError when passed replacement is identical to stored object" do
- -> { @frozen.instance_variable_set(:@ivar, :origin) }.should raise_error(FrozenError)
+ -> { @frozen.instance_variable_set(:@ivar, :origin) }.should.raise(FrozenError)
end
it "raises a FrozenError when passed replacement is different from stored object" do
- -> { @frozen.instance_variable_set(:@ivar, :replacement) }.should raise_error(FrozenError)
+ -> { @frozen.instance_variable_set(:@ivar, :replacement) }.should.raise(FrozenError)
+ end
+
+ it "accepts unicode instance variable names" do
+ o = Object.new
+ o.instance_variable_set(:@💙, 42)
+ o.instance_variable_get(:@💙).should == 42
+ end
+
+ it "raises for frozen objects" do
+ -> { nil.instance_variable_set(:@foo, 42) }.should.raise(FrozenError)
+ -> { nil.instance_variable_set(:foo, 42) }.should.raise(NameError)
+ -> { :foo.instance_variable_set(:@foo, 42) }.should.raise(FrozenError)
end
end
end
diff --git a/spec/ruby/core/kernel/instance_variables_spec.rb b/spec/ruby/core/kernel/instance_variables_spec.rb
index b6d6e27772..75c1d8f752 100644
--- a/spec/ruby/core/kernel/instance_variables_spec.rb
+++ b/spec/ruby/core/kernel/instance_variables_spec.rb
@@ -4,12 +4,14 @@ require_relative 'fixtures/classes'
describe "Kernel#instance_variables" do
describe "immediate values" do
it "returns an empty array if no instance variables are defined" do
- 0.instance_variables.should == []
+ [0, 0.5, true, false, nil].each do |value|
+ value.instance_variables.should == []
+ end
end
it "returns the correct array if an instance variable is added" do
a = 0
- ->{ a.instance_variable_set("@test", 1) }.should raise_error(RuntimeError)
+ ->{ a.instance_variable_set("@test", 1) }.should.raise(RuntimeError)
end
end
@@ -23,5 +25,16 @@ describe "Kernel#instance_variables" do
a.instance_variable_set("@test", 1)
a.instance_variables.should == [:@test]
end
+
+ it "returns the instances variables in the order declared" do
+ c = Class.new do
+ def initialize
+ @c = 1
+ @a = 2
+ @b = 3
+ end
+ end
+ c.new.instance_variables.should == [:@c, :@a, :@b]
+ end
end
end
diff --git a/spec/ruby/core/kernel/is_a_spec.rb b/spec/ruby/core/kernel/is_a_spec.rb
index dc69766f83..bd8c96529a 100644
--- a/spec/ruby/core/kernel/is_a_spec.rb
+++ b/spec/ruby/core/kernel/is_a_spec.rb
@@ -2,5 +2,5 @@ require_relative '../../spec_helper'
require_relative 'shared/kind_of'
describe "Kernel#is_a?" do
- it_behaves_like :kernel_kind_of , :is_a?
+ it_behaves_like :kernel_kind_of, :is_a?
end
diff --git a/spec/ruby/core/kernel/iterator_spec.rb b/spec/ruby/core/kernel/iterator_spec.rb
deleted file mode 100644
index 7fbdade9dc..0000000000
--- a/spec/ruby/core/kernel/iterator_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-
-describe "Kernel#iterator?" do
- it "is a private method" do
- Kernel.should have_private_instance_method(:iterator?)
- end
-end
-
-describe "Kernel.iterator?" do
- it "needs to be reviewed for spec completeness"
-end
diff --git a/spec/ruby/core/kernel/itself_spec.rb b/spec/ruby/core/kernel/itself_spec.rb
index c906d7c3e8..c1f01fdc5e 100644
--- a/spec/ruby/core/kernel/itself_spec.rb
+++ b/spec/ruby/core/kernel/itself_spec.rb
@@ -4,6 +4,6 @@ require_relative 'fixtures/classes'
describe "Kernel#itself" do
it "returns the receiver itself" do
foo = Object.new
- foo.itself.should equal foo
+ foo.itself.should.equal? foo
end
end
diff --git a/spec/ruby/core/kernel/kind_of_spec.rb b/spec/ruby/core/kernel/kind_of_spec.rb
index 734035620c..c988edccb5 100644
--- a/spec/ruby/core/kernel/kind_of_spec.rb
+++ b/spec/ruby/core/kernel/kind_of_spec.rb
@@ -2,5 +2,5 @@ require_relative '../../spec_helper'
require_relative 'shared/kind_of'
describe "Kernel#kind_of?" do
- it_behaves_like :kernel_kind_of , :kind_of?
+ it_behaves_like :kernel_kind_of, :kind_of?
end
diff --git a/spec/ruby/core/kernel/lambda_spec.rb b/spec/ruby/core/kernel/lambda_spec.rb
index dfe36cefaa..6ab89c2bbb 100644
--- a/spec/ruby/core/kernel/lambda_spec.rb
+++ b/spec/ruby/core/kernel/lambda_spec.rb
@@ -8,86 +8,52 @@ describe "Kernel.lambda" do
it_behaves_like :kernel_lambda, :lambda
it "is a private method" do
- Kernel.should have_private_instance_method(:lambda)
+ Kernel.private_instance_methods(false).should.include?(:lambda)
end
it "creates a lambda-style Proc if given a literal block" do
l = lambda { 42 }
- l.lambda?.should be_true
+ l.lambda?.should == true
end
it "creates a lambda-style Proc if given a literal block via #send" do
l = send(:lambda) { 42 }
- l.lambda?.should be_true
+ l.lambda?.should == true
end
it "creates a lambda-style Proc if given a literal block via #__send__" do
l = __send__(:lambda) { 42 }
- l.lambda?.should be_true
- end
-
- it "creates a lambda-style Proc if given a literal block via Kernel.public_send" do
- l = Kernel.public_send(:lambda) { 42 }
- l.lambda?.should be_true
- end
-
- it "returns the passed Proc if given an existing Proc" do
- some_proc = proc {}
- l = suppress_warning {lambda(&some_proc)}
- l.should equal(some_proc)
- l.lambda?.should be_false
- end
-
- it "creates a lambda-style Proc when called with zsuper" do
- l = KernelSpecs::LambdaSpecs::ForwardBlockWithZSuper.new.lambda { 42 }
- l.lambda?.should be_true
- l.call.should == 42
-
- lambda { l.call(:extra) }.should raise_error(ArgumentError)
- end
-
- it "returns the passed Proc if given an existing Proc through super" do
- some_proc = proc { }
- l = KernelSpecs::LambdaSpecs::SuperAmpersand.new.lambda(&some_proc)
- l.should equal(some_proc)
- l.lambda?.should be_false
- end
-
- it "does not create lambda-style Procs when captured with #method" do
- kernel_lambda = method(:lambda)
- l = suppress_warning {kernel_lambda.call { 42 }}
- l.lambda?.should be_false
- l.call(:extra).should == 42
+ l.lambda?.should == true
end
it "checks the arity of the call when no args are specified" do
l = lambda { :called }
l.call.should == :called
- lambda { l.call(1) }.should raise_error(ArgumentError)
- lambda { l.call(1, 2) }.should raise_error(ArgumentError)
+ lambda { l.call(1) }.should.raise(ArgumentError)
+ lambda { l.call(1, 2) }.should.raise(ArgumentError)
end
it "checks the arity when 1 arg is specified" do
l = lambda { |a| :called }
l.call(1).should == :called
- lambda { l.call }.should raise_error(ArgumentError)
- lambda { l.call(1, 2) }.should raise_error(ArgumentError)
+ lambda { l.call }.should.raise(ArgumentError)
+ lambda { l.call(1, 2) }.should.raise(ArgumentError)
end
it "does not check the arity when passing a Proc with &" do
l = lambda { || :called }
p = proc { || :called }
- lambda { l.call(1) }.should raise_error(ArgumentError)
+ lambda { l.call(1) }.should.raise(ArgumentError)
p.call(1).should == :called
end
it "accepts 0 arguments when used with ||" do
lambda {
lambda { || }.call(1)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "strictly checks the arity when 0 or 2..inf args are specified" do
@@ -95,15 +61,15 @@ describe "Kernel.lambda" do
lambda {
l.call
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
lambda {
l.call(1)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
lambda {
l.call(1,2)
- }.should_not raise_error(ArgumentError)
+ }.should_not.raise(ArgumentError)
end
it "returns from the lambda itself, not the creation site of the lambda" do
@@ -113,10 +79,32 @@ describe "Kernel.lambda" do
@reached_end_of_method = true
end
test
- @reached_end_of_method.should be_true
+ @reached_end_of_method.should == true
end
it "allows long returns to flow through it" do
KernelSpecs::Lambda.new.outer.should == :good
end
+
+ it "treats the block as a Proc when lambda is re-defined" do
+ klass = Class.new do
+ def lambda (&block); block; end
+ def ret
+ lambda { return 1 }.call
+ 2
+ end
+ end
+ klass.new.lambda { 42 }.should.instance_of? Proc
+ klass.new.ret.should == 1
+ end
+
+ context "when called without a literal block" do
+ it "raises when proc isn't a lambda" do
+ -> { lambda(&proc{}) }.should.raise(ArgumentError, /the lambda method requires a literal block/)
+ end
+
+ it "doesn't warn when proc is lambda" do
+ -> { lambda(&lambda{}) }.should_not complain(verbose: true)
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/load_spec.rb b/spec/ruby/core/kernel/load_spec.rb
index a165cc4acd..890aab8c27 100644
--- a/spec/ruby/core/kernel/load_spec.rb
+++ b/spec/ruby/core/kernel/load_spec.rb
@@ -13,7 +13,7 @@ describe "Kernel#load" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:load)
+ Kernel.private_instance_methods(false).should.include?(:load)
end
it_behaves_like :kernel_require_basic, :load, CodeLoadingSpecs::Method.new
diff --git a/spec/ruby/core/kernel/local_variables_spec.rb b/spec/ruby/core/kernel/local_variables_spec.rb
index f6f1e15f52..40c343f7e4 100644
--- a/spec/ruby/core/kernel/local_variables_spec.rb
+++ b/spec/ruby/core/kernel/local_variables_spec.rb
@@ -7,14 +7,13 @@ describe "Kernel#local_variables" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:local_variables)
+ Kernel.private_instance_methods(false).should.include?(:local_variables)
end
it "contains locals as they are added" do
a = 1
b = 2
- local_variables.should include(:a, :b)
- local_variables.length.should == 2
+ local_variables.sort.should == [:a, :b]
end
it "is accessible from bindings" do
@@ -25,14 +24,12 @@ describe "Kernel#local_variables" do
end
foo_binding = local_var_foo()
res = eval("local_variables",foo_binding)
- res.should include(:a, :b)
- res.length.should == 2
+ res.sort.should == [:a, :b]
end
it "is accessible in eval" do
eval "a=1; b=2; ScratchPad.record local_variables"
- ScratchPad.recorded.should include(:a, :b)
- ScratchPad.recorded.length.should == 2
+ ScratchPad.recorded.sort.should == [:a, :b]
end
it "includes only unique variable names" do
diff --git a/spec/ruby/core/kernel/loop_spec.rb b/spec/ruby/core/kernel/loop_spec.rb
index 7c76c7d28e..c2976e5cc5 100644
--- a/spec/ruby/core/kernel/loop_spec.rb
+++ b/spec/ruby/core/kernel/loop_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "Kernel.loop" do
it "is a private method" do
- Kernel.should have_private_instance_method(:loop)
+ Kernel.private_instance_methods(false).should.include?(:loop)
end
it "calls block until it is terminated by a break" do
@@ -30,7 +30,7 @@ describe "Kernel.loop" do
it "returns an enumerator if no block given" do
enum = loop
- enum.instance_of?(Enumerator).should be_true
+ enum.instance_of?(Enumerator).should == true
cnt = 0
enum.each do |*args|
raise "Args should be empty #{args.inspect}" unless args.empty?
@@ -55,7 +55,7 @@ describe "Kernel.loop" do
end
it "does not rescue other errors" do
- ->{ loop do raise StandardError end }.should raise_error( StandardError )
+ ->{ loop do raise StandardError end }.should.raise( StandardError )
end
it "returns StopIteration#result, the result value of a finished iterator" do
diff --git a/spec/ruby/core/kernel/match_spec.rb b/spec/ruby/core/kernel/match_spec.rb
index 6dc1eb7de8..cd6330fe91 100644
--- a/spec/ruby/core/kernel/match_spec.rb
+++ b/spec/ruby/core/kernel/match_spec.rb
@@ -1,24 +1,7 @@
require_relative '../../spec_helper'
describe "Kernel#=~" do
- it "returns nil matching any object" do
- o = Object.new
-
- suppress_warning do
- (o =~ /Object/).should be_nil
- (o =~ 'Object').should be_nil
- (o =~ Object).should be_nil
- (o =~ Object.new).should be_nil
- (o =~ nil).should be_nil
- (o =~ true).should be_nil
- end
- end
-
- ruby_version_is "2.6" do
- it "is deprecated" do
- -> do
- Object.new =~ /regexp/
- end.should complain(/deprecated Object#=~ is called on Object/, verbose: true)
- end
+ it "is no longer defined" do
+ Object.new.should_not.respond_to?(:=~)
end
end
diff --git a/spec/ruby/core/kernel/method_spec.rb b/spec/ruby/core/kernel/method_spec.rb
index 25c6691e10..9187b8c7e7 100644
--- a/spec/ruby/core/kernel/method_spec.rb
+++ b/spec/ruby/core/kernel/method_spec.rb
@@ -11,12 +11,12 @@ describe "Kernel#method" do
it "can be called on a private method" do
@obj.send(:private_method).should == :private_method
- @obj.method(:private_method).should be_an_instance_of(Method)
+ @obj.method(:private_method).should.instance_of?(Method)
end
it "can be called on a protected method" do
@obj.send(:protected_method).should == :protected_method
- @obj.method(:protected_method).should be_an_instance_of(Method)
+ @obj.method(:protected_method).should.instance_of?(Method)
end
it "will see an alias of the original method as == when in a derived class" do
@@ -29,9 +29,60 @@ describe "Kernel#method" do
m.call.should == :defined
end
- it "can be called even if we only repond_to_missing? method, true" do
+ it "can be called even if we only respond_to_missing? method, true" do
m = KernelSpecs::RespondViaMissing.new.method(:handled_privately)
- m.should be_an_instance_of(Method)
+ m.should.instance_of?(Method)
m.call(1, 2, 3).should == "Done handled_privately([1, 2, 3])"
end
+
+ it "can call a #method_missing accepting zero or one arguments" do
+ cls = Class.new do
+ def respond_to_missing?(name, *)
+ name == :foo or super
+ end
+ def method_missing
+ :no_args
+ end
+ end
+ m = cls.new.method(:foo)
+ -> { m.call }.should.raise(ArgumentError)
+
+ cls = Class.new do
+ def respond_to_missing?(name, *)
+ name == :bar or super
+ end
+ def method_missing(m)
+ m
+ end
+ end
+ m = cls.new.method(:bar)
+ m.call.should == :bar
+ end
+
+ describe "converts the given name to a String using #to_str" do
+ it "calls #to_str to convert the given name to a String" do
+ name = mock("method-name")
+ name.should_receive(:to_str).and_return("hash")
+ Object.method(name).should == Object.method(:hash)
+ end
+
+ it "raises a TypeError if the given name can't be converted to a String" do
+ -> { Object.method(nil) }.should.raise(TypeError)
+ -> { Object.method([]) }.should.raise(TypeError)
+ end
+
+ it "raises a NoMethodError if the given argument raises a NoMethodError during type coercion to a String" do
+ name = mock("method-name")
+ name.should_receive(:to_str).and_raise(NoMethodError)
+ -> { Object.method(name) }.should.raise(NoMethodError)
+
+ name = mock("method-name")
+ name.should_receive(:to_str).and_raise(NoMethodError)
+ begin
+ raise RuntimeError.new
+ rescue => cause
+ -> { Object.method(name) }.should.raise(NoMethodError, cause:)
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/methods_spec.rb b/spec/ruby/core/kernel/methods_spec.rb
index fb7a7e8be9..cfa22aa05a 100644
--- a/spec/ruby/core/kernel/methods_spec.rb
+++ b/spec/ruby/core/kernel/methods_spec.rb
@@ -5,67 +5,67 @@ require_relative '../../fixtures/reflection'
# TODO: rewrite
describe "Kernel#methods" do
it "returns singleton methods defined by obj.meth" do
- KernelSpecs::Methods.methods(false).should include(:ichi)
+ KernelSpecs::Methods.methods(false).should.include?(:ichi)
end
it "returns singleton methods defined in 'class << self'" do
- KernelSpecs::Methods.methods(false).should include(:san)
+ KernelSpecs::Methods.methods(false).should.include?(:san)
end
it "returns private singleton methods defined by obj.meth" do
- KernelSpecs::Methods.methods(false).should include(:shi)
+ KernelSpecs::Methods.methods(false).should.include?(:shi)
end
it "returns singleton methods defined in 'class << self' when it follows 'private'" do
- KernelSpecs::Methods.methods(false).should include(:roku)
+ KernelSpecs::Methods.methods(false).should.include?(:roku)
end
it "does not return private singleton methods defined in 'class << self'" do
- KernelSpecs::Methods.methods(false).should_not include(:shichi)
+ KernelSpecs::Methods.methods(false).should_not.include?(:shichi)
end
it "returns the publicly accessible methods of the object" do
meths = KernelSpecs::Methods.methods(false)
- meths.should include(:hachi, :ichi, :juu, :juu_ichi,
- :juu_ni, :roku, :san, :shi)
+ meths.to_set.should >= Set[:hachi, :ichi, :juu, :juu_ichi,
+ :juu_ni, :roku, :san, :shi]
KernelSpecs::Methods.new.methods(false).should == []
end
it "returns the publicly accessible methods in the object, its ancestors and mixed-in modules" do
meths = KernelSpecs::Methods.methods(false) & KernelSpecs::Methods.methods
- meths.should include(:hachi, :ichi, :juu, :juu_ichi,
- :juu_ni, :roku, :san, :shi)
+ meths.to_set.should >= Set[:hachi, :ichi, :juu, :juu_ichi,
+ :juu_ni, :roku, :san, :shi]
- KernelSpecs::Methods.new.methods.should include(:ku, :ni, :juu_san)
+ KernelSpecs::Methods.new.methods.to_set.should >= Set[:ku, :ni, :juu_san]
end
it "returns methods added to the metaclass through extend" do
meth = KernelSpecs::Methods.new
- meth.methods.should_not include(:peekaboo)
+ meth.methods.should_not.include?(:peekaboo)
meth.extend(KernelSpecs::Methods::MetaclassMethods)
- meth.methods.should include(:peekaboo)
+ meth.methods.should.include?(:peekaboo)
end
it "does not return undefined singleton methods defined by obj.meth" do
o = KernelSpecs::Child.new
def o.single; end
- o.methods.should include(:single)
+ o.methods.should.include?(:single)
class << o; self; end.send :undef_method, :single
- o.methods.should_not include(:single)
+ o.methods.should_not.include?(:single)
end
it "does not return superclass methods undefined in the object's class" do
- KernelSpecs::Child.new.methods.should_not include(:parent_method)
+ KernelSpecs::Child.new.methods.should_not.include?(:parent_method)
end
it "does not return superclass methods undefined in a superclass" do
- KernelSpecs::Grandchild.new.methods.should_not include(:parent_method)
+ KernelSpecs::Grandchild.new.methods.should_not.include?(:parent_method)
end
it "does not return included module methods undefined in the object's class" do
- KernelSpecs::Grandchild.new.methods.should_not include(:parent_mixin_method)
+ KernelSpecs::Grandchild.new.methods.should_not.include?(:parent_mixin_method)
end
end
diff --git a/spec/ruby/core/kernel/nil_spec.rb b/spec/ruby/core/kernel/nil_spec.rb
index b63705f7bc..7418245f26 100644
--- a/spec/ruby/core/kernel/nil_spec.rb
+++ b/spec/ruby/core/kernel/nil_spec.rb
@@ -1,6 +1,12 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-describe "Kernel#nil?" do
- it "needs to be reviewed for spec completeness"
+describe 'Kernel#nil?' do
+ it 'returns false' do
+ Object.should_not.nil?
+ Object.new.should_not.nil?
+ ''.should_not.nil?
+ [].should_not.nil?
+ {}.should_not.nil?
+ end
end
diff --git a/spec/ruby/core/kernel/not_match_spec.rb b/spec/ruby/core/kernel/not_match_spec.rb
index 906f18df2c..4ed7ea7b42 100644
--- a/spec/ruby/core/kernel/not_match_spec.rb
+++ b/spec/ruby/core/kernel/not_match_spec.rb
@@ -14,6 +14,10 @@ describe "Kernel#!~" do
(obj !~ :foo).should == false
end
+ it "raises NoMethodError if self does not respond to #=~" do
+ -> { Object.new !~ :foo }.should.raise(NoMethodError)
+ end
+
it 'can be overridden in subclasses' do
obj = KernelSpecs::NotMatch.new
(obj !~ :bar).should == :foo
diff --git a/spec/ruby/core/kernel/open_spec.rb b/spec/ruby/core/kernel/open_spec.rb
index 981b5291b3..14a3c43cad 100644
--- a/spec/ruby/core/kernel/open_spec.rb
+++ b/spec/ruby/core/kernel/open_spec.rb
@@ -2,7 +2,6 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Kernel#open" do
-
before :each do
@name = tmp("kernel_open.txt")
@content = "This is a test"
@@ -16,61 +15,90 @@ describe "Kernel#open" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:open)
+ Kernel.private_instance_methods(false).should.include?(:open)
end
it "opens a file when given a valid filename" do
@file = open(@name)
- @file.should be_kind_of(File)
+ @file.should.is_a?(File)
end
it "opens a file when called with a block" do
open(@name, "r") { |f| f.gets }.should == @content
end
- platform_is_not :windows do
- it "opens an io when path starts with a pipe" do
- @io = open("|date")
- begin
- @io.should be_kind_of(IO)
- @io.read
- ensure
- @io.close
+ ruby_version_is ""..."4.0" do
+ platform_is_not :windows, :wasi do
+ it "opens an io when path starts with a pipe" do
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @io = open("|date")
+ end
+ begin
+ @io.should.is_a?(IO)
+ @io.read
+ ensure
+ @io.close
+ end
end
- end
- it "opens an io when called with a block" do
- @output = open("|date") { |f| f.read }
- @output.should_not == ''
- end
+ it "opens an io when called with a block" do
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @output = open("|date") { |f| f.read }
+ end
+ @output.should_not == ''
+ end
- it "opens an io for writing" do
- -> do
- bytes = open("|cat", "w") { |io| io.write(".") }
- bytes.should == 1
- end.should output_to_fd(".")
+ it "opens an io for writing" do
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ -> {
+ bytes = open("|cat", "w") { |io| io.write(".") }
+ bytes.should == 1
+ }.should output_to_fd(".")
+ end
+ end
end
- end
- platform_is :windows do
- it "opens an io when path starts with a pipe" do
- @io = open("|date /t")
- begin
- @io.should be_kind_of(IO)
- @io.read
- ensure
- @io.close
+ platform_is :windows do
+ it "opens an io when path starts with a pipe" do
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @io = open("|date /t")
+ end
+ begin
+ @io.should.is_a?(IO)
+ @io.read
+ ensure
+ @io.close
+ end
+ end
+
+ it "opens an io when called with a block" do
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @output = open("|date /t") { |f| f.read }
+ end
+ @output.should_not == ''
end
end
- it "opens an io when called with a block" do
- @output = open("|date /t") { |f| f.read }
- @output.should_not == ''
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ cmd = "|echo ok"
+ -> {
+ open(cmd) { |f| f.read }
+ }.should complain(/Kernel#open with a leading '\|'/)
end
end
it "raises an ArgumentError if not passed one argument" do
- -> { open }.should raise_error(ArgumentError)
+ -> { open }.should.raise(ArgumentError)
+ end
+
+ it "accepts options as keyword arguments" do
+ @file = open(@name, "r", 0666, flags: File::CREAT)
+ @file.should.is_a?(File)
+
+ -> {
+ open(@name, "r", 0666, {flags: File::CREAT})
+ }.should.raise(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
describe "when given an object that responds to to_open" do
@@ -78,7 +106,7 @@ describe "Kernel#open" do
ScratchPad.clear
end
- it "calls #to_path to covert the argument to a String before calling #to_str" do
+ it "calls #to_path to convert the argument to a String before calling #to_str" do
obj = mock("open to_path")
obj.should_receive(:to_path).at_least(1).times.and_return(@name)
obj.should_not_receive(:to_str)
@@ -98,7 +126,7 @@ describe "Kernel#open" do
@file = File.open(@name)
obj.should_receive(:to_open).and_return(@file)
@file = open(obj)
- @file.should be_kind_of(File)
+ @file.should.is_a?(File)
end
it "returns the value from #to_open" do
@@ -110,11 +138,21 @@ describe "Kernel#open" do
it "passes its arguments onto #to_open" do
obj = mock('to_open')
- obj.should_receive(:to_open).with(1,2,3)
-
+ obj.should_receive(:to_open).with(1, 2, 3)
open(obj, 1, 2, 3)
end
+ it "passes keyword arguments onto #to_open as keyword arguments if to_open accepts them" do
+ obj = Object.new
+ def obj.to_open(*args, **kw)
+ ScratchPad << {args: args, kw: kw}
+ end
+
+ ScratchPad.record []
+ open(obj, 1, 2, 3, a: "b")
+ ScratchPad.recorded.should == [args: [1, 2, 3], kw: {a: "b"}]
+ end
+
it "passes the return value from #to_open to a block" do
obj = mock('to_open')
obj.should_receive(:to_open).and_return(:value)
@@ -129,14 +167,24 @@ describe "Kernel#open" do
it "raises a TypeError if passed a non-String that does not respond to #to_open" do
obj = mock('non-fileish')
- -> { open(obj) }.should raise_error(TypeError)
- -> { open(nil) }.should raise_error(TypeError)
- -> { open(7) }.should raise_error(TypeError)
+ -> { open(obj) }.should.raise(TypeError)
+ -> { open(nil) }.should.raise(TypeError)
+ -> { open(7) }.should.raise(TypeError)
end
it "accepts nil for mode and permission" do
open(@name, nil, nil) { |f| f.gets }.should == @content
end
+
+ it "is not redefined by open-uri" do
+ code = <<~RUBY
+ before = Kernel.instance_method(:open)
+ require 'open-uri'
+ after = Kernel.instance_method(:open)
+ p before == after
+ RUBY
+ ruby_exe(code, args: "2>&1").should == "true\n"
+ end
end
describe "Kernel.open" do
diff --git a/spec/ruby/core/kernel/p_spec.rb b/spec/ruby/core/kernel/p_spec.rb
index 1bdd1740ca..f89716899a 100644
--- a/spec/ruby/core/kernel/p_spec.rb
+++ b/spec/ruby/core/kernel/p_spec.rb
@@ -13,7 +13,7 @@ describe "Kernel#p" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:p)
+ Kernel.private_instance_methods(false).should.include?(:p)
end
# TODO: fix
@@ -76,10 +76,8 @@ describe "Kernel#p" do
-> { p(*[]) }.should output("")
end
-=begin Not sure how to spec this, but wanted to note the behavior here
- it "does not flush if receiver is not a TTY or a File" do
- end
-=end
+ # Not sure how to spec this, but wanted to note the behavior here
+ it "does not flush if receiver is not a TTY or a File"
end
describe "Kernel.p" do
diff --git a/spec/ruby/core/kernel/print_spec.rb b/spec/ruby/core/kernel/print_spec.rb
index c8c4453d1e..5473d22f71 100644
--- a/spec/ruby/core/kernel/print_spec.rb
+++ b/spec/ruby/core/kernel/print_spec.rb
@@ -3,7 +3,19 @@ require_relative 'fixtures/classes'
describe "Kernel#print" do
it "is a private method" do
- Kernel.should have_private_instance_method(:print)
+ Kernel.private_instance_methods(false).should.include?(:print)
+ end
+
+ it "delegates to $stdout" do
+ -> { print :arg }.should output("arg")
+ end
+
+ it "prints $_ when no arguments are given" do
+ orig_value = $_
+ $_ = 'foo'
+ -> { print }.should output("foo")
+ ensure
+ $_ = orig_value
end
end
diff --git a/spec/ruby/core/kernel/printf_spec.rb b/spec/ruby/core/kernel/printf_spec.rb
index d8f93ce429..50939b3794 100644
--- a/spec/ruby/core/kernel/printf_spec.rb
+++ b/spec/ruby/core/kernel/printf_spec.rb
@@ -4,7 +4,7 @@ require_relative 'shared/sprintf'
describe "Kernel#printf" do
it "is a private method" do
- Kernel.should have_private_instance_method(:printf)
+ Kernel.private_instance_methods(false).should.include?(:printf)
end
end
@@ -31,6 +31,13 @@ describe "Kernel.printf" do
object.should_receive(:write).with("string")
Kernel.printf(object, "%s", "string")
end
+
+ it "calls #to_str to convert the format object to a String" do
+ object = mock('format string')
+ object.should_receive(:to_str).and_return("to_str: %i")
+ $stdout.should_receive(:write).with("to_str: 42")
+ Kernel.printf($stdout, object, 42)
+ end
end
describe "Kernel.printf" do
diff --git a/spec/ruby/core/kernel/private_methods_spec.rb b/spec/ruby/core/kernel/private_methods_spec.rb
index 041634d1e5..b0e6d042a9 100644
--- a/spec/ruby/core/kernel/private_methods_spec.rb
+++ b/spec/ruby/core/kernel/private_methods_spec.rb
@@ -6,23 +6,23 @@ require_relative '../../fixtures/reflection'
describe "Kernel#private_methods" do
it "returns a list of the names of privately accessible methods in the object" do
m = KernelSpecs::Methods.private_methods(false)
- m.should include(:shichi)
+ m.should.include?(:shichi)
m = KernelSpecs::Methods.new.private_methods(false)
- m.should include(:juu_shi)
+ m.should.include?(:juu_shi)
end
it "returns a list of the names of privately accessible methods in the object and its ancestors and mixed-in modules" do
m = (KernelSpecs::Methods.private_methods(false) & KernelSpecs::Methods.private_methods)
- m.should include(:shichi)
+ m.should.include?(:shichi)
m = KernelSpecs::Methods.new.private_methods
- m.should include(:juu_shi)
+ m.should.include?(:juu_shi)
end
it "returns private methods mixed in to the metaclass" do
m = KernelSpecs::Methods.new
m.extend(KernelSpecs::Methods::MetaclassMethods)
- m.private_methods.should include(:shoo)
+ m.private_methods.should.include?(:shoo)
end
end
diff --git a/spec/ruby/core/kernel/proc_spec.rb b/spec/ruby/core/kernel/proc_spec.rb
index 7b4493dcc4..1ba662177b 100644
--- a/spec/ruby/core/kernel/proc_spec.rb
+++ b/spec/ruby/core/kernel/proc_spec.rb
@@ -6,20 +6,20 @@ require_relative 'shared/lambda'
describe "Kernel.proc" do
it "is a private method" do
- Kernel.should have_private_instance_method(:proc)
+ Kernel.private_instance_methods(false).should.include?(:proc)
end
it "creates a proc-style Proc if given a literal block" do
l = proc { 42 }
- l.lambda?.should be_false
+ l.lambda?.should == false
end
it "returned the passed Proc if given an existing Proc" do
some_lambda = -> {}
- some_lambda.lambda?.should be_true
+ some_lambda.lambda?.should == true
l = proc(&some_lambda)
- l.should equal(some_lambda)
- l.lambda?.should be_true
+ l.should.equal?(some_lambda)
+ l.lambda?.should == true
end
it_behaves_like :kernel_lambda, :proc
@@ -31,7 +31,7 @@ describe "Kernel.proc" do
@reached_end_of_method = true
end
test
- @reached_end_of_method.should be_nil
+ @reached_end_of_method.should == nil
end
end
@@ -40,27 +40,9 @@ describe "Kernel#proc" do
proc
end
- ruby_version_is ""..."2.7" do
- it "uses the implicit block from an enclosing method" do
- prc = some_method { "hello" }
-
- prc.call.should == "hello"
- end
- end
-
- ruby_version_is "2.7"..."2.8" do
- it "can be created when called with no block" do
- -> {
- some_method { "hello" }
- }.should complain(/Capturing the given block using Kernel#proc is deprecated/)
- end
- end
-
- ruby_version_is "2.8" do
- it "raises an ArgumentError when passed no block" do
- -> {
- some_method { "hello" }
- }.should raise_error(ArgumentError, 'tried to create Proc object without a block')
- end
+ it "raises an ArgumentError when passed no block" do
+ -> {
+ some_method { "hello" }
+ }.should.raise(ArgumentError, 'tried to create Proc object without a block')
end
end
diff --git a/spec/ruby/core/kernel/protected_methods_spec.rb b/spec/ruby/core/kernel/protected_methods_spec.rb
index d3334e886b..aecb9f5ffb 100644
--- a/spec/ruby/core/kernel/protected_methods_spec.rb
+++ b/spec/ruby/core/kernel/protected_methods_spec.rb
@@ -8,21 +8,21 @@ require_relative '../../fixtures/reflection'
# You should use have_protected_method() with the exception of this spec.
describe "Kernel#protected_methods" do
it "returns a list of the names of protected methods accessible in the object" do
- KernelSpecs::Methods.protected_methods(false).sort.should include(:juu_ichi)
- KernelSpecs::Methods.new.protected_methods(false).should include(:ku)
+ KernelSpecs::Methods.protected_methods(false).sort.should.include?(:juu_ichi)
+ KernelSpecs::Methods.new.protected_methods(false).should.include?(:ku)
end
it "returns a list of the names of protected methods accessible in the object and from its ancestors and mixed-in modules" do
l1 = KernelSpecs::Methods.protected_methods(false)
l2 = KernelSpecs::Methods.protected_methods
- (l1 & l2).should include(:juu_ichi)
- KernelSpecs::Methods.new.protected_methods.should include(:ku)
+ (l1 & l2).should.include?(:juu_ichi)
+ KernelSpecs::Methods.new.protected_methods.should.include?(:ku)
end
it "returns methods mixed in to the metaclass" do
m = KernelSpecs::Methods.new
m.extend(KernelSpecs::Methods::MetaclassMethods)
- m.protected_methods.should include(:nopeeking)
+ m.protected_methods.should.include?(:nopeeking)
end
end
diff --git a/spec/ruby/core/kernel/public_method_spec.rb b/spec/ruby/core/kernel/public_method_spec.rb
index c5d54c777e..42b8f797d3 100644
--- a/spec/ruby/core/kernel/public_method_spec.rb
+++ b/spec/ruby/core/kernel/public_method_spec.rb
@@ -13,20 +13,20 @@ describe "Kernel#public_method" do
@obj.send(:private_method).should == :private_method
-> do
@obj.public_method(:private_method)
- end.should raise_error(NameError)
+ end.should.raise(NameError)
end
it "raises a NameError when called on a protected method" do
@obj.send(:protected_method).should == :protected_method
-> {
@obj.public_method(:protected_method)
- }.should raise_error(NameError)
+ }.should.raise(NameError)
end
it "raises a NameError if we only repond_to_missing? method, true" do
obj = KernelSpecs::RespondViaMissing.new
-> do
obj.public_method(:handled_privately)
- end.should raise_error(NameError)
+ end.should.raise(NameError)
end
end
diff --git a/spec/ruby/core/kernel/public_methods_spec.rb b/spec/ruby/core/kernel/public_methods_spec.rb
index a5512784fb..e334ac9a55 100644
--- a/spec/ruby/core/kernel/public_methods_spec.rb
+++ b/spec/ruby/core/kernel/public_methods_spec.rb
@@ -5,31 +5,30 @@ require_relative '../../fixtures/reflection'
# TODO: rewrite
describe "Kernel#public_methods" do
it "returns a list of the names of publicly accessible methods in the object" do
- KernelSpecs::Methods.public_methods(false).sort.should include(:hachi,
- :ichi, :juu, :juu_ni, :roku, :san, :shi)
- KernelSpecs::Methods.new.public_methods(false).sort.should include(:juu_san, :ni)
+ KernelSpecs::Methods.public_methods(false).to_set.should >= Set[:hachi, :ichi, :juu, :juu_ni, :roku, :san, :shi]
+ KernelSpecs::Methods.new.public_methods(false).to_set.should >= Set[:juu_san, :ni]
end
it "returns a list of names without protected accessible methods in the object" do
- KernelSpecs::Methods.public_methods(false).sort.should_not include(:juu_ichi)
- KernelSpecs::Methods.new.public_methods(false).sort.should_not include(:ku)
+ KernelSpecs::Methods.public_methods(false).sort.should_not.include?(:juu_ichi)
+ KernelSpecs::Methods.new.public_methods(false).sort.should_not.include?(:ku)
end
it "returns a list of the names of publicly accessible methods in the object and its ancestors and mixed-in modules" do
- (KernelSpecs::Methods.public_methods(false) & KernelSpecs::Methods.public_methods).sort.should include(
- :hachi, :ichi, :juu, :juu_ni, :roku, :san, :shi)
+ (KernelSpecs::Methods.public_methods(false) & KernelSpecs::Methods.public_methods).to_set.should >= Set[
+ :hachi, :ichi, :juu, :juu_ni, :roku, :san, :shi]
m = KernelSpecs::Methods.new.public_methods
- m.should include(:ni, :juu_san)
+ m.to_set.should >= Set[:ni, :juu_san]
end
it "returns methods mixed in to the metaclass" do
m = KernelSpecs::Methods.new
m.extend(KernelSpecs::Methods::MetaclassMethods)
- m.public_methods.should include(:peekaboo)
+ m.public_methods.should.include?(:peekaboo)
end
it "returns public methods for immediates" do
- 10.public_methods.should include(:divmod)
+ 10.public_methods.should.include?(:divmod)
end
end
diff --git a/spec/ruby/core/kernel/public_send_spec.rb b/spec/ruby/core/kernel/public_send_spec.rb
index 6b942a2e4b..6a4a969c77 100644
--- a/spec/ruby/core/kernel/public_send_spec.rb
+++ b/spec/ruby/core/kernel/public_send_spec.rb
@@ -29,7 +29,7 @@ describe "Kernel#public_send" do
'done'
end
end
- -> { KernelSpecs::Foo.new.public_send(:bar)}.should raise_error(NoMethodError)
+ -> { KernelSpecs::Foo.new.public_send(:bar)}.should.raise(NoMethodError)
end
it "raises a NoMethodError if the named method is private" do
@@ -41,7 +41,7 @@ describe "Kernel#public_send" do
end
-> {
KernelSpecs::Foo.new.public_send(:bar)
- }.should raise_error(NoMethodError)
+ }.should.raise(NoMethodError)
end
context 'called from own public method' do
@@ -70,11 +70,11 @@ describe "Kernel#public_send" do
end
it "raises a NoMethodError if the method is protected" do
- -> { @receiver.call_protected_method }.should raise_error(NoMethodError)
+ -> { @receiver.call_protected_method }.should.raise(NoMethodError)
end
it "raises a NoMethodError if the method is private" do
- -> { @receiver.call_private_method }.should raise_error(NoMethodError)
+ -> { @receiver.call_private_method }.should.raise(NoMethodError)
end
end
@@ -88,7 +88,7 @@ describe "Kernel#public_send" do
end
-> {
KernelSpecs::Foo.new.public_send(:aka)
- }.should raise_error(NoMethodError)
+ }.should.raise(NoMethodError)
end
it "raises a NoMethodError if the named method is an alias of a protected method" do
@@ -101,7 +101,15 @@ describe "Kernel#public_send" do
end
-> {
KernelSpecs::Foo.new.public_send(:aka)
- }.should raise_error(NoMethodError)
+ }.should.raise(NoMethodError)
+ end
+
+ it "includes `public_send` in the backtrace when passed not enough arguments" do
+ -> { public_send() }.should.raise(ArgumentError) { |e| e.backtrace[0].should =~ /[`'](?:Kernel#)?public_send'/ }
+ end
+
+ it "includes `public_send` in the backtrace when passed a single incorrect argument" do
+ -> { public_send(Object.new) }.should.raise(TypeError) { |e| e.backtrace[0].should =~ /[`'](?:Kernel#)?public_send'/ }
end
it_behaves_like :basicobject_send, :public_send
diff --git a/spec/ruby/core/kernel/putc_spec.rb b/spec/ruby/core/kernel/putc_spec.rb
index 74bd3765db..e6a20a9af1 100644
--- a/spec/ruby/core/kernel/putc_spec.rb
+++ b/spec/ruby/core/kernel/putc_spec.rb
@@ -4,7 +4,7 @@ require_relative '../../shared/io/putc'
describe "Kernel#putc" do
it "is a private instance method" do
- Kernel.should have_private_instance_method(:putc)
+ Kernel.private_instance_methods(false).should.include?(:putc)
end
end
diff --git a/spec/ruby/core/kernel/puts_spec.rb b/spec/ruby/core/kernel/puts_spec.rb
index 6eb38e8fcf..eed18fcd50 100644
--- a/spec/ruby/core/kernel/puts_spec.rb
+++ b/spec/ruby/core/kernel/puts_spec.rb
@@ -15,7 +15,7 @@ describe "Kernel#puts" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:puts)
+ Kernel.private_instance_methods(false).should.include?(:puts)
end
it "delegates to $stdout.puts" do
diff --git a/spec/ruby/core/kernel/raise_spec.rb b/spec/ruby/core/kernel/raise_spec.rb
index 591daa03cf..6162677e17 100644
--- a/spec/ruby/core/kernel/raise_spec.rb
+++ b/spec/ruby/core/kernel/raise_spec.rb
@@ -4,9 +4,19 @@ require_relative '../../shared/kernel/raise'
describe "Kernel#raise" do
it "is a private method" do
- Kernel.should have_private_instance_method(:raise)
+ Kernel.private_instance_methods.should.include?(:raise)
end
+ # Shared specs expect a public #raise method.
+ public_raiser = Object.new
+ class << public_raiser
+ public :raise
+ end
+ it_behaves_like :kernel_raise, :raise, public_raiser
+ it_behaves_like :kernel_raise_with_cause, :raise, public_raiser
+end
+
+describe "Kernel#raise with previously rescued exception" do
it "re-raises the previously rescued exception if no exception is specified" do
ScratchPad.record nil
@@ -23,16 +33,190 @@ describe "Kernel#raise" do
raise
ScratchPad.record :no_reraise
end
- end.should raise_error(Exception, "outer")
+ end.should.raise(Exception, "outer")
- ScratchPad.recorded.should be_nil
+ ScratchPad.recorded.should == nil
end
-end
-describe "Kernel#raise" do
- it_behaves_like :kernel_raise, :raise, Kernel
+ it "re-raises a previously rescued exception without overwriting the cause" do
+ begin
+ begin
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ raise "Error 2"
+ end
+ rescue => e2
+ raise "Error 3"
+ end
+ rescue
+ e2.cause.should == e1
+ raise e2
+ end
+ rescue => e
+ e.cause.should == e1
+ end
+ end
+
+ it "re-raises a previously rescued exception with overwriting the cause when it's explicitly specified with :cause option" do
+ e4 = RuntimeError.new("Error 4")
+
+ begin
+ begin
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ raise "Error 2"
+ end
+ rescue => e2
+ raise "Error 3"
+ end
+ rescue
+ e2.cause.should == e1
+ raise e2, cause: e4
+ end
+ rescue => e
+ e.cause.should == e4
+ end
+ end
+
+ it "re-raises a previously rescued exception without overwriting the cause when it's explicitly specified with :cause option and has nil value" do
+ begin
+ begin
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ raise "Error 2"
+ end
+ rescue => e2
+ raise "Error 3"
+ end
+ rescue
+ e2.cause.should == e1
+ raise e2, cause: nil
+ end
+ rescue => e
+ e.cause.should == e1
+ end
+ end
+
+ it "re-raises a previously rescued exception without setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ raise
+ end
+ rescue => e
+ e.should == e1
+ e.cause.should == nil
+ end
+ end
+
+ it "re-raises a previously rescued exception that has a cause without setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ raise "Error 2"
+ rescue => e2
+ raise
+ end
+ end
+ rescue => e
+ e.should == e2
+ e.cause.should == e1
+ end
+ end
+
+ it "re-raises a previously rescued exception that doesn't have a cause and isn't a cause of any other exception with setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ raise "Error 2"
+ rescue => e2
+ raise "Error 3"
+ end
+ end
+ rescue => e
+ e.message.should == "Error 3"
+ e.cause.should == e2
+ end
+ end
+
+ it "re-raises a previously rescued exception that doesn't have a cause and is a cause of other exception without setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ raise "Error 2"
+ rescue => e2
+ e1.cause.should == nil
+ e2.cause.should == e1
+ raise e1
+ end
+ end
+ rescue => e
+ e.should == e1
+ e.cause.should == nil
+ end
+ end
+
+ it "re-raises a previously rescued exception that doesn't have a cause and is a cause of other exception (that wasn't raised explicitly) without setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ foo # raises NameError
+ rescue => e2
+ e1.cause.should == nil
+ e2.cause.should == e1
+ raise e1
+ end
+ end
+ rescue => e
+ e.should == e1
+ e.cause.should == nil
+ end
+ end
+
+ it "re-raises a previously rescued exception that has a cause but isn't a cause of any other exception without setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ raise "Error 2"
+ rescue => e2
+ begin
+ raise "Error 3", cause: RuntimeError.new("Error 4")
+ rescue => e3
+ e2.cause.should == e1
+ e3.cause.should_not == e2
+ raise e2
+ end
+ end
+ end
+ rescue => e
+ e.should == e2
+ e.cause.should == e1
+ end
+ end
end
describe "Kernel.raise" do
- it "needs to be reviewed for spec completeness"
+ it "is a public method" do
+ Kernel.singleton_class.should.public_method_defined?(:raise)
+ end
+
+ it_behaves_like :kernel_raise, :raise, Kernel
+ it_behaves_like :kernel_raise_with_cause, :raise, Kernel
end
diff --git a/spec/ruby/core/kernel/rand_spec.rb b/spec/ruby/core/kernel/rand_spec.rb
index a82b4fba74..1bf5e225d9 100644
--- a/spec/ruby/core/kernel/rand_spec.rb
+++ b/spec/ruby/core/kernel/rand_spec.rb
@@ -1,44 +1,44 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-describe "Kernel.rand" do
+describe "Kernel#rand" do
it "is a private method" do
- Kernel.should have_private_instance_method(:rand)
+ Kernel.private_instance_methods(false).should.include?(:rand)
end
it "returns a float if no argument is passed" do
- rand.should be_kind_of(Float)
+ rand.should.is_a?(Float)
end
it "returns an integer for an integer argument" do
- rand(77).should be_kind_of(Integer)
+ rand(77).should.is_a?(Integer)
end
it "returns an integer for a float argument greater than 1" do
- rand(1.3).should be_kind_of(Integer)
+ rand(1.3).should.is_a?(Integer)
end
it "returns a float for an argument between -1 and 1" do
- rand(-0.999).should be_kind_of(Float)
- rand(-0.01).should be_kind_of(Float)
- rand(0).should be_kind_of(Float)
- rand(0.01).should be_kind_of(Float)
- rand(0.999).should be_kind_of(Float)
+ rand(-0.999).should.is_a?(Float)
+ rand(-0.01).should.is_a?(Float)
+ rand(0).should.is_a?(Float)
+ rand(0.01).should.is_a?(Float)
+ rand(0.999).should.is_a?(Float)
end
it "ignores the sign of the argument" do
- [0, 1, 2, 3].should include(rand(-4))
+ [0, 1, 2, 3].should.include?(rand(-4))
end
it "never returns a value greater or equal to 1.0 with no arguments" do
1000.times do
- (0...1.0).should include(rand)
+ (0...1.0).should.include?(rand)
end
end
it "never returns a value greater or equal to any passed in max argument" do
1000.times do
- (0...100).to_a.should include(rand(100))
+ (0...100).to_a.should.include?(rand(100))
end
end
@@ -53,32 +53,32 @@ describe "Kernel.rand" do
it "returns an Integer between the two Integers" do
1000.times do
x = rand(4...6)
- x.should be_kind_of(Integer)
- (4...6).should include(x)
+ x.should.is_a?(Integer)
+ (4...6).should.include?(x)
end
end
it "returns a Float between the given Integer and Float" do
1000.times do
x = rand(4...6.5)
- x.should be_kind_of(Float)
- (4...6.5).should include(x)
+ x.should.is_a?(Float)
+ (4...6.5).should.include?(x)
end
end
it "returns a Float between the given Float and Integer" do
1000.times do
x = rand(3.5...6)
- x.should be_kind_of(Float)
- (3.5...6).should include(x)
+ x.should.is_a?(Float)
+ (3.5...6).should.include?(x)
end
end
it "returns a Float between the two given Floats" do
1000.times do
x = rand(3.5...6.5)
- x.should be_kind_of(Float)
- (3.5...6.5).should include(x)
+ x.should.is_a?(Float)
+ (3.5...6.5).should.include?(x)
end
end
end
@@ -87,56 +87,98 @@ describe "Kernel.rand" do
it "returns an Integer between the two Integers" do
1000.times do
x = rand(4..6)
- x.should be_kind_of(Integer)
- (4..6).should include(x)
+ x.should.is_a?(Integer)
+ (4..6).should.include?(x)
end
end
it "returns a Float between the given Integer and Float" do
1000.times do
x = rand(4..6.5)
- x.should be_kind_of(Float)
- (4..6.5).should include(x)
+ x.should.is_a?(Float)
+ (4..6.5).should.include?(x)
end
end
it "returns a Float between the given Float and Integer" do
1000.times do
x = rand(3.5..6)
- x.should be_kind_of(Float)
- (3.5..6).should include(x)
+ x.should.is_a?(Float)
+ (3.5..6).should.include?(x)
end
end
it "returns a Float between the two given Floats" do
1000.times do
x = rand(3.5..6.5)
- x.should be_kind_of(Float)
- (3.5..6.5).should include(x)
+ x.should.is_a?(Float)
+ (3.5..6.5).should.include?(x)
end
end
end
+ context "given an inclusive range between 0 and 1" do
+ it "returns an Integer between the two Integers" do
+ x = rand(0..1)
+ x.should.is_a?(Integer)
+ (0..1).should.include?(x)
+ end
+
+ it "returns a Float if at least one side is Float" do
+ seed = 42
+ x1 = Random.new(seed).rand(0..1.0)
+ x2 = Random.new(seed).rand(0.0..1.0)
+ x3 = Random.new(seed).rand(0.0..1)
+
+ x3.should.is_a?(Float)
+ x1.should.eql?(x3)
+ x2.should.eql?(x3)
+
+ (0.0..1.0).should.include?(x3)
+ end
+ end
+
+ context "given an exclusive range between 0 and 1" do
+ it "returns zero as an Integer" do
+ x = rand(0...1)
+ x.should.is_a?(Integer)
+ x.should.eql?(0)
+ end
+
+ it "returns a Float if at least one side is Float" do
+ seed = 42
+ x1 = Random.new(seed).rand(0...1.0)
+ x2 = Random.new(seed).rand(0.0...1.0)
+ x3 = Random.new(seed).rand(0.0...1)
+
+ x3.should.is_a?(Float)
+ x1.should.eql?(x3)
+ x2.should.eql?(x3)
+
+ (0.0...1.0).should.include?(x3)
+ end
+ end
+
it "returns a numeric for an range argument where max is < 1" do
- rand(0.25..0.75).should be_kind_of(Numeric)
+ rand(0.25..0.75).should.is_a?(Numeric)
end
it "returns nil when range is backwards" do
- rand(1..0).should be_nil
+ rand(1..0).should == nil
end
it "returns the range start/end when Float range is 0" do
- rand(1.0..1.0).should eql(1.0)
+ rand(1.0..1.0).should.eql?(1.0)
end
it "returns the range start/end when Integer range is 0" do
- rand(42..42).should eql(42)
+ rand(42..42).should.eql?(42)
end
it "supports custom object types" do
- rand(KernelSpecs::CustomRangeInteger.new(1)..KernelSpecs::CustomRangeInteger.new(42)).should be_an_instance_of(KernelSpecs::CustomRangeInteger)
- rand(KernelSpecs::CustomRangeFloat.new(1.0)..KernelSpecs::CustomRangeFloat.new(42.0)).should be_an_instance_of(KernelSpecs::CustomRangeFloat)
- rand(Time.now..Time.now).should be_an_instance_of(Time)
+ rand(KernelSpecs::CustomRangeInteger.new(1)..KernelSpecs::CustomRangeInteger.new(42)).should.instance_of?(KernelSpecs::CustomRangeInteger)
+ rand(KernelSpecs::CustomRangeFloat.new(1.0)..KernelSpecs::CustomRangeFloat.new(42.0)).should.instance_of?(KernelSpecs::CustomRangeFloat)
+ rand(Time.now..Time.now).should.instance_of?(Time)
end
it "is random on boot" do
@@ -150,6 +192,6 @@ describe "Kernel.rand" do
end
end
-describe "Kernel#rand" do
+describe "Kernel.rand" do
it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/core/kernel/readline_spec.rb b/spec/ruby/core/kernel/readline_spec.rb
index dce7b03dc8..86899d78d2 100644
--- a/spec/ruby/core/kernel/readline_spec.rb
+++ b/spec/ruby/core/kernel/readline_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "Kernel#readline" do
it "is a private method" do
- Kernel.should have_private_instance_method(:readline)
+ Kernel.private_instance_methods(false).should.include?(:readline)
end
end
diff --git a/spec/ruby/core/kernel/readlines_spec.rb b/spec/ruby/core/kernel/readlines_spec.rb
index 2b6d65fff2..c758014aab 100644
--- a/spec/ruby/core/kernel/readlines_spec.rb
+++ b/spec/ruby/core/kernel/readlines_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "Kernel#readlines" do
it "is a private method" do
- Kernel.should have_private_instance_method(:readlines)
+ Kernel.private_instance_methods(false).should.include?(:readlines)
end
end
diff --git a/spec/ruby/core/kernel/remove_instance_variable_spec.rb b/spec/ruby/core/kernel/remove_instance_variable_spec.rb
index e90efc8aed..114536064f 100644
--- a/spec/ruby/core/kernel/remove_instance_variable_spec.rb
+++ b/spec/ruby/core/kernel/remove_instance_variable_spec.rb
@@ -9,7 +9,7 @@ describe :kernel_remove_instance_variable, shared: true do
it "removes the instance variable" do
@instance.send :remove_instance_variable, @object
- @instance.instance_variable_defined?(@object).should be_false
+ @instance.instance_variable_defined?(@object).should == false
end
end
@@ -19,26 +19,39 @@ describe "Kernel#remove_instance_variable" do
end
it "is a public method" do
- Kernel.should have_public_instance_method(:remove_instance_variable, false)
+ Kernel.public_instance_methods(false).should.include?(:remove_instance_variable)
end
it "raises a NameError if the instance variable is not defined" do
-> do
@instance.send :remove_instance_variable, :@unknown
- end.should raise_error(NameError)
+ end.should.raise(NameError)
end
it "raises a NameError if the argument is not a valid instance variable name" do
-> do
@instance.send :remove_instance_variable, :"@0"
- end.should raise_error(NameError)
+ end.should.raise(NameError)
end
it "raises a TypeError if passed an Object not defining #to_str" do
-> do
obj = mock("kernel remove_instance_variable")
@instance.send :remove_instance_variable, obj
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
+ end
+
+ it "raises a FrozenError if self is frozen" do
+ o = Object.new
+ o.freeze
+ -> { o.remove_instance_variable(:@foo) }.should.raise(FrozenError)
+ -> { o.remove_instance_variable(:foo) }.should.raise(NameError)
+ end
+
+ it "raises for frozen objects" do
+ -> { nil.remove_instance_variable(:@foo) }.should.raise(FrozenError)
+ -> { nil.remove_instance_variable(:foo) }.should.raise(NameError)
+ -> { :foo.remove_instance_variable(:@foo) }.should.raise(FrozenError)
end
describe "when passed a String" do
diff --git a/spec/ruby/core/kernel/require_relative_spec.rb b/spec/ruby/core/kernel/require_relative_spec.rb
index d4146eb3c8..332045b200 100644
--- a/spec/ruby/core/kernel/require_relative_spec.rb
+++ b/spec/ruby/core/kernel/require_relative_spec.rb
@@ -5,9 +5,9 @@ describe "Kernel#require_relative with a relative path" do
before :each do
CodeLoadingSpecs.spec_setup
@dir = "../../fixtures/code"
- @abs_dir = File.realpath(@dir, File.dirname(__FILE__))
+ @abs_dir = File.realpath(@dir, __dir__)
@path = "#{@dir}/load_fixture.rb"
- @abs_path = File.realpath(@path, File.dirname(__FILE__))
+ @abs_path = File.realpath(@path, __dir__)
end
after :each do
@@ -27,14 +27,14 @@ describe "Kernel#require_relative with a relative path" do
end
it "loads a path relative to current file" do
- require_relative(@link).should be_true
+ require_relative(@link).should == true
ScratchPad.recorded.should == [:loaded]
end
end
end
it "loads a path relative to the current file" do
- require_relative(@path).should be_true
+ require_relative(@path).should == true
ScratchPad.recorded.should == [:loaded]
end
@@ -42,14 +42,14 @@ describe "Kernel#require_relative with a relative path" do
it "synthetic file base name loads a file base name relative to the working directory" do
Dir.chdir @abs_dir do
- Object.new.instance_eval("require_relative(#{File.basename(@path).inspect})", "foo.rb").should be_true
+ Object.new.instance_eval("require_relative(#{File.basename(@path).inspect})", "foo.rb").should == true
end
ScratchPad.recorded.should == [:loaded]
end
it "synthetic file path loads a relative path relative to the working directory plus the directory of the synthetic path" do
Dir.chdir @abs_dir do
- Object.new.instance_eval("require_relative(File.join('..', #{File.basename(@path).inspect}))", "bar/foo.rb").should be_true
+ Object.new.instance_eval("require_relative(File.join('..', #{File.basename(@path).inspect}))", "bar/foo.rb").should == true
end
ScratchPad.recorded.should == [:loaded]
end
@@ -57,14 +57,14 @@ describe "Kernel#require_relative with a relative path" do
platform_is_not :windows do
it "synthetic relative file path with a Windows path separator specified loads a relative path relative to the working directory" do
Dir.chdir @abs_dir do
- Object.new.instance_eval("require_relative(#{File.basename(@path).inspect})", "bar\\foo.rb").should be_true
+ Object.new.instance_eval("require_relative(#{File.basename(@path).inspect})", "bar\\foo.rb").should == true
end
ScratchPad.recorded.should == [:loaded]
end
end
it "absolute file path loads a path relative to the absolute path" do
- Object.new.instance_eval("require_relative(#{@path.inspect})", __FILE__).should be_true
+ Object.new.instance_eval("require_relative(#{@path.inspect})", __FILE__).should == true
ScratchPad.recorded.should == [:loaded]
end
@@ -74,34 +74,34 @@ describe "Kernel#require_relative with a relative path" do
root = File.dirname(root)
end
root_relative = @abs_path[root.size..-1]
- Object.new.instance_eval("require_relative(#{root_relative.inspect})", "/").should be_true
+ Object.new.instance_eval("require_relative(#{root_relative.inspect})", "/").should == true
ScratchPad.recorded.should == [:loaded]
end
end
it "loads a file defining many methods" do
- require_relative("#{@dir}/methods_fixture.rb").should be_true
+ require_relative("#{@dir}/methods_fixture.rb").should == true
ScratchPad.recorded.should == [:loaded]
end
it "raises a LoadError if the file does not exist" do
- -> { require_relative("#{@dir}/nonexistent.rb") }.should raise_error(LoadError)
+ -> { require_relative("#{@dir}/nonexistent.rb") }.should.raise(LoadError)
ScratchPad.recorded.should == []
end
it "raises a LoadError that includes the missing path" do
missing_path = "#{@dir}/nonexistent.rb"
- expanded_missing_path = File.expand_path(missing_path, File.dirname(__FILE__))
- -> { require_relative(missing_path) }.should raise_error(LoadError) { |e|
- e.message.should include(expanded_missing_path)
+ expanded_missing_path = File.expand_path(missing_path, __dir__)
+ -> { require_relative(missing_path) }.should.raise(LoadError) { |e|
+ e.message.should.include?(expanded_missing_path)
e.path.should == expanded_missing_path
}
ScratchPad.recorded.should == []
end
it "raises a LoadError if basepath does not exist" do
- -> { eval("require_relative('#{@dir}/nonexistent.rb')") }.should raise_error(LoadError)
+ -> { eval("require_relative('#{@dir}/nonexistent.rb')") }.should.raise(LoadError)
end
it "stores the missing path in a LoadError object" do
@@ -117,34 +117,34 @@ describe "Kernel#require_relative with a relative path" do
it "calls #to_str on non-String objects" do
name = mock("load_fixture.rb mock")
name.should_receive(:to_str).and_return(@path)
- require_relative(name).should be_true
+ require_relative(name).should == true
ScratchPad.recorded.should == [:loaded]
end
it "raises a TypeError if argument does not respond to #to_str" do
- -> { require_relative(nil) }.should raise_error(TypeError)
- -> { require_relative(42) }.should raise_error(TypeError)
+ -> { require_relative(nil) }.should.raise(TypeError)
+ -> { require_relative(42) }.should.raise(TypeError)
-> {
require_relative([@path,@path])
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
it "raises a TypeError if passed an object that has #to_s but not #to_str" do
name = mock("load_fixture.rb mock")
name.stub!(:to_s).and_return(@path)
- -> { require_relative(name) }.should raise_error(TypeError)
+ -> { require_relative(name) }.should.raise(TypeError)
end
it "raises a TypeError if #to_str does not return a String" do
name = mock("#to_str returns nil")
name.should_receive(:to_str).at_least(1).times.and_return(nil)
- -> { require_relative(name) }.should raise_error(TypeError)
+ -> { require_relative(name) }.should.raise(TypeError)
end
it "calls #to_path on non-String objects" do
name = mock("load_fixture.rb mock")
name.should_receive(:to_path).and_return(@path)
- require_relative(name).should be_true
+ require_relative(name).should == true
ScratchPad.recorded.should == [:loaded]
end
@@ -153,13 +153,13 @@ describe "Kernel#require_relative with a relative path" do
to_path = mock("load_fixture_rb #to_path mock")
name.should_receive(:to_path).and_return(to_path)
to_path.should_receive(:to_str).and_return(@path)
- require_relative(name).should be_true
+ require_relative(name).should == true
ScratchPad.recorded.should == [:loaded]
end
describe "(file extensions)" do
it "loads a .rb extensioned file when passed a non-extensioned path" do
- require_relative("#{@dir}/load_fixture").should be_true
+ require_relative("#{@dir}/load_fixture").should == true
ScratchPad.recorded.should == [:loaded]
end
@@ -168,20 +168,20 @@ describe "Kernel#require_relative with a relative path" do
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.dylib"
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.so"
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.dll"
- require_relative(@path).should be_true
+ require_relative(@path).should == true
ScratchPad.recorded.should == [:loaded]
end
it "does not load a C-extension file if a .rb extensioned file is already loaded" do
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.rb"
- require_relative("#{@dir}/load_fixture").should be_false
+ require_relative("#{@dir}/load_fixture").should == false
ScratchPad.recorded.should == []
end
it "loads a .rb extensioned file when passed a non-.rb extensioned path" do
- require_relative("#{@dir}/load_fixture.ext").should be_true
+ require_relative("#{@dir}/load_fixture.ext").should == true
ScratchPad.recorded.should == [:loaded]
- $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb"
+ $LOADED_FEATURES.should.include? "#{@abs_dir}/load_fixture.ext.rb"
end
it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do
@@ -189,25 +189,25 @@ describe "Kernel#require_relative with a relative path" do
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dylib"
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.so"
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dll"
- require_relative("#{@dir}/load_fixture.ext").should be_true
+ require_relative("#{@dir}/load_fixture.ext").should == true
ScratchPad.recorded.should == [:loaded]
- $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb"
+ $LOADED_FEATURES.should.include? "#{@abs_dir}/load_fixture.ext.rb"
end
it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.rb"
- require_relative("#{@dir}/load_fixture.ext").should be_false
+ require_relative("#{@dir}/load_fixture.ext").should == false
ScratchPad.recorded.should == []
end
end
describe "($LOADED_FEATURES)" do
it "stores an absolute path" do
- require_relative(@path).should be_true
- $LOADED_FEATURES.should include(@abs_path)
+ require_relative(@path).should == true
+ $LOADED_FEATURES.should.include?(@abs_path)
end
- platform_is_not :windows do
+ platform_is_not :windows, :wasi do
describe "with symlinks" do
before :each do
@symlink_to_code_dir = tmp("codesymlink")
@@ -231,8 +231,8 @@ describe "Kernel#require_relative with a relative path" do
ScratchPad.recorded.should == [:loaded]
features = $LOADED_FEATURES.select { |path| path.end_with?('load_fixture.rb') }
- features.should include(absolute_path)
- features.should_not include(canonical_path)
+ features.should.include?(absolute_path)
+ features.should_not.include?(canonical_path)
end
it "stores the same path that __FILE__ returns in the required file" do
@@ -249,26 +249,26 @@ describe "Kernel#require_relative with a relative path" do
it "does not store the path if the load fails" do
saved_loaded_features = $LOADED_FEATURES.dup
- -> { require_relative("#{@dir}/raise_fixture.rb") }.should raise_error(RuntimeError)
+ -> { require_relative("#{@dir}/raise_fixture.rb") }.should.raise(RuntimeError)
$LOADED_FEATURES.should == saved_loaded_features
end
it "does not load an absolute path that is already stored" do
$LOADED_FEATURES << @abs_path
- require_relative(@path).should be_false
+ require_relative(@path).should == false
ScratchPad.recorded.should == []
end
it "adds the suffix of the resolved filename" do
- require_relative("#{@dir}/load_fixture").should be_true
- $LOADED_FEATURES.should include("#{@abs_dir}/load_fixture.rb")
+ require_relative("#{@dir}/load_fixture").should == true
+ $LOADED_FEATURES.should.include?("#{@abs_dir}/load_fixture.rb")
end
it "loads a path for a file already loaded with a relative path" do
$LOAD_PATH << File.expand_path(@dir)
$LOADED_FEATURES << "load_fixture.rb" << "load_fixture"
- require_relative(@path).should be_true
- $LOADED_FEATURES.should include(@abs_path)
+ require_relative(@path).should == true
+ $LOADED_FEATURES.should.include?(@abs_path)
ScratchPad.recorded.should == [:loaded]
end
end
@@ -277,7 +277,7 @@ end
describe "Kernel#require_relative with an absolute path" do
before :each do
CodeLoadingSpecs.spec_setup
- @dir = File.expand_path "../../fixtures/code", File.dirname(__FILE__)
+ @dir = File.expand_path "../../fixtures/code", __dir__
@abs_dir = @dir
@path = File.join @dir, "load_fixture.rb"
@abs_path = @path
@@ -288,22 +288,22 @@ describe "Kernel#require_relative with an absolute path" do
end
it "loads a path relative to the current file" do
- require_relative(@path).should be_true
+ require_relative(@path).should == true
ScratchPad.recorded.should == [:loaded]
end
it "loads a file defining many methods" do
- require_relative("#{@dir}/methods_fixture.rb").should be_true
+ require_relative("#{@dir}/methods_fixture.rb").should == true
ScratchPad.recorded.should == [:loaded]
end
it "raises a LoadError if the file does not exist" do
- -> { require_relative("#{@dir}/nonexistent.rb") }.should raise_error(LoadError)
+ -> { require_relative("#{@dir}/nonexistent.rb") }.should.raise(LoadError)
ScratchPad.recorded.should == []
end
it "raises a LoadError if basepath does not exist" do
- -> { eval("require_relative('#{@dir}/nonexistent.rb')") }.should raise_error(LoadError)
+ -> { eval("require_relative('#{@dir}/nonexistent.rb')") }.should.raise(LoadError)
end
it "stores the missing path in a LoadError object" do
@@ -319,34 +319,34 @@ describe "Kernel#require_relative with an absolute path" do
it "calls #to_str on non-String objects" do
name = mock("load_fixture.rb mock")
name.should_receive(:to_str).and_return(@path)
- require_relative(name).should be_true
+ require_relative(name).should == true
ScratchPad.recorded.should == [:loaded]
end
it "raises a TypeError if argument does not respond to #to_str" do
- -> { require_relative(nil) }.should raise_error(TypeError)
- -> { require_relative(42) }.should raise_error(TypeError)
+ -> { require_relative(nil) }.should.raise(TypeError)
+ -> { require_relative(42) }.should.raise(TypeError)
-> {
require_relative([@path,@path])
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
it "raises a TypeError if passed an object that has #to_s but not #to_str" do
name = mock("load_fixture.rb mock")
name.stub!(:to_s).and_return(@path)
- -> { require_relative(name) }.should raise_error(TypeError)
+ -> { require_relative(name) }.should.raise(TypeError)
end
it "raises a TypeError if #to_str does not return a String" do
name = mock("#to_str returns nil")
name.should_receive(:to_str).at_least(1).times.and_return(nil)
- -> { require_relative(name) }.should raise_error(TypeError)
+ -> { require_relative(name) }.should.raise(TypeError)
end
it "calls #to_path on non-String objects" do
name = mock("load_fixture.rb mock")
name.should_receive(:to_path).and_return(@path)
- require_relative(name).should be_true
+ require_relative(name).should == true
ScratchPad.recorded.should == [:loaded]
end
@@ -355,13 +355,13 @@ describe "Kernel#require_relative with an absolute path" do
to_path = mock("load_fixture_rb #to_path mock")
name.should_receive(:to_path).and_return(to_path)
to_path.should_receive(:to_str).and_return(@path)
- require_relative(name).should be_true
+ require_relative(name).should == true
ScratchPad.recorded.should == [:loaded]
end
describe "(file extensions)" do
it "loads a .rb extensioned file when passed a non-extensioned path" do
- require_relative("#{@dir}/load_fixture").should be_true
+ require_relative("#{@dir}/load_fixture").should == true
ScratchPad.recorded.should == [:loaded]
end
@@ -370,20 +370,20 @@ describe "Kernel#require_relative with an absolute path" do
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.dylib"
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.so"
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.dll"
- require_relative(@path).should be_true
+ require_relative(@path).should == true
ScratchPad.recorded.should == [:loaded]
end
it "does not load a C-extension file if a .rb extensioned file is already loaded" do
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.rb"
- require_relative("#{@dir}/load_fixture").should be_false
+ require_relative("#{@dir}/load_fixture").should == false
ScratchPad.recorded.should == []
end
it "loads a .rb extensioned file when passed a non-.rb extensioned path" do
- require_relative("#{@dir}/load_fixture.ext").should be_true
+ require_relative("#{@dir}/load_fixture.ext").should == true
ScratchPad.recorded.should == [:loaded]
- $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb"
+ $LOADED_FEATURES.should.include? "#{@abs_dir}/load_fixture.ext.rb"
end
it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do
@@ -391,46 +391,46 @@ describe "Kernel#require_relative with an absolute path" do
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dylib"
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.so"
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dll"
- require_relative("#{@dir}/load_fixture.ext").should be_true
+ require_relative("#{@dir}/load_fixture.ext").should == true
ScratchPad.recorded.should == [:loaded]
- $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb"
+ $LOADED_FEATURES.should.include? "#{@abs_dir}/load_fixture.ext.rb"
end
it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do
$LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.rb"
- require_relative("#{@dir}/load_fixture.ext").should be_false
+ require_relative("#{@dir}/load_fixture.ext").should == false
ScratchPad.recorded.should == []
end
end
describe "($LOAD_FEATURES)" do
it "stores an absolute path" do
- require_relative(@path).should be_true
- $LOADED_FEATURES.should include(@abs_path)
+ require_relative(@path).should == true
+ $LOADED_FEATURES.should.include?(@abs_path)
end
it "does not store the path if the load fails" do
saved_loaded_features = $LOADED_FEATURES.dup
- -> { require_relative("#{@dir}/raise_fixture.rb") }.should raise_error(RuntimeError)
+ -> { require_relative("#{@dir}/raise_fixture.rb") }.should.raise(RuntimeError)
$LOADED_FEATURES.should == saved_loaded_features
end
it "does not load an absolute path that is already stored" do
$LOADED_FEATURES << @abs_path
- require_relative(@path).should be_false
+ require_relative(@path).should == false
ScratchPad.recorded.should == []
end
it "adds the suffix of the resolved filename" do
- require_relative("#{@dir}/load_fixture").should be_true
- $LOADED_FEATURES.should include("#{@abs_dir}/load_fixture.rb")
+ require_relative("#{@dir}/load_fixture").should == true
+ $LOADED_FEATURES.should.include?("#{@abs_dir}/load_fixture.rb")
end
it "loads a path for a file already loaded with a relative path" do
$LOAD_PATH << File.expand_path(@dir)
$LOADED_FEATURES << "load_fixture.rb" << "load_fixture"
- require_relative(@path).should be_true
- $LOADED_FEATURES.should include(@abs_path)
+ require_relative(@path).should == true
+ $LOADED_FEATURES.should.include?(@abs_path)
ScratchPad.recorded.should == [:loaded]
end
end
diff --git a/spec/ruby/core/kernel/require_spec.rb b/spec/ruby/core/kernel/require_spec.rb
index dc3da4b7e6..62d954c2bf 100644
--- a/spec/ruby/core/kernel/require_spec.rb
+++ b/spec/ruby/core/kernel/require_spec.rb
@@ -13,7 +13,40 @@ describe "Kernel#require" do
# if this fails, update your rubygems
it "is a private method" do
- Kernel.should have_private_instance_method(:require)
+ Kernel.private_instance_methods(false).should.include?(:require)
+ end
+
+ it "provided features are already required" do
+ provided = %w[complex enumerator fiber rational thread ruby2_keywords]
+ ruby_version_is "4.0" do
+ provided += %w[set pathname]
+ end
+ ruby_version_is "4.1" do
+ provided += %w[monitor]
+ end
+
+ out = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems --disable-did-you-mean')
+ features = out.lines.map(&:chomp)
+
+ # Ignore engine-specific internals
+ case RUBY_ENGINE
+ when "jruby"
+ features -= %w[java.rb jruby/util.rb]
+ when "ruby"
+ so = RbConfig::CONFIG['DLEXT']
+ features -= ["windows_1252.#{so}", "windows_31.#{so}"]
+ features.reject! { |feature| feature.end_with? "encdb.#{so}" }
+ features.reject! { |feature| feature.end_with? "transdb.#{so}" }
+ features.reject! { |feature| feature.include?('-fake') }
+ end
+
+ features_no_ext = features.map { |path| File.basename(path, '.*') }
+ features_no_ext.sort.should == provided.sort
+
+ requires = features
+ code = requires.map { |f| "puts require #{f.inspect}\n" }.join
+ required = ruby_exe(code, options: '--disable-gems')
+ required.should == "false\n" * requires.size
end
it_behaves_like :kernel_require_basic, :require, CodeLoadingSpecs::Method.new
diff --git a/spec/ruby/core/kernel/respond_to_missing_spec.rb b/spec/ruby/core/kernel/respond_to_missing_spec.rb
index cc82031e26..08c9184fb4 100644
--- a/spec/ruby/core/kernel/respond_to_missing_spec.rb
+++ b/spec/ruby/core/kernel/respond_to_missing_spec.rb
@@ -7,7 +7,7 @@ describe "Kernel#respond_to_missing?" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:respond_to_missing?, false)
+ Kernel.private_instance_methods(false).should.include?(:respond_to_missing?)
end
it "is only an instance method" do
@@ -18,7 +18,7 @@ describe "Kernel#respond_to_missing?" do
obj = mock('object')
obj.stub!(:glark)
obj.should_not_receive(:respond_to_missing?)
- obj.respond_to?(:glark).should be_true
+ obj.respond_to?(:glark).should == true
end
it "is called with a 2nd argument of false when #respond_to? is" do
@@ -48,39 +48,39 @@ describe "Kernel#respond_to_missing?" do
it "causes #respond_to? to return true if called and not returning false" do
obj = mock('object')
obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(:glark)
- obj.respond_to?(:undefined_method).should be_true
+ obj.respond_to?(:undefined_method).should == true
end
it "causes #respond_to? to return false if called and returning false" do
obj = mock('object')
obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(false)
- obj.respond_to?(:undefined_method).should be_false
+ obj.respond_to?(:undefined_method).should == false
end
it "causes #respond_to? to return false if called and returning nil" do
obj = mock('object')
obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(nil)
- obj.respond_to?(:undefined_method).should be_false
+ obj.respond_to?(:undefined_method).should == false
end
it "isn't called when obj responds to the given public method" do
@a.should_not_receive(:respond_to_missing?)
- @a.respond_to?(:pub_method).should be_true
+ @a.respond_to?(:pub_method).should == true
end
it "isn't called when obj responds to the given public method, include_private = true" do
@a.should_not_receive(:respond_to_missing?)
- @a.respond_to?(:pub_method, true).should be_true
+ @a.respond_to?(:pub_method, true).should == true
end
it "is called when obj responds to the given protected method, include_private = false" do
@a.should_receive(:respond_to_missing?)
- @a.respond_to?(:protected_method, false).should be_false
+ @a.respond_to?(:protected_method, false).should == false
end
it "isn't called when obj responds to the given protected method, include_private = true" do
@a.should_not_receive(:respond_to_missing?)
- @a.respond_to?(:protected_method, true).should be_true
+ @a.respond_to?(:protected_method, true).should == true
end
it "is called when obj responds to the given private method, include_private = false" do
@@ -90,7 +90,7 @@ describe "Kernel#respond_to_missing?" do
it "isn't called when obj responds to the given private method, include_private = true" do
@a.should_not_receive(:respond_to_missing?)
- @a.respond_to?(:private_method, true).should be_true
+ @a.respond_to?(:private_method, true).should == true
end
it "is called for missing class methods" do
diff --git a/spec/ruby/core/kernel/respond_to_spec.rb b/spec/ruby/core/kernel/respond_to_spec.rb
index e7efc9f275..72ab4eebe1 100644
--- a/spec/ruby/core/kernel/respond_to_spec.rb
+++ b/spec/ruby/core/kernel/respond_to_spec.rb
@@ -7,7 +7,7 @@ describe "Kernel#respond_to?" do
end
it "is a public method" do
- Kernel.should have_public_instance_method(:respond_to?, false)
+ Kernel.public_instance_methods(false).should.include?(:respond_to?)
end
it "is only an instance method" do
@@ -25,7 +25,7 @@ describe "Kernel#respond_to?" do
end
it "throws a type error if argument can't be coerced into a Symbol" do
- -> { @a.respond_to?(Object.new) }.should raise_error(TypeError)
+ -> { @a.respond_to?(Object.new) }.should.raise(TypeError, /is not a symbol nor a string/)
end
it "returns false if obj responds to the given protected method" do
@@ -61,7 +61,7 @@ describe "Kernel#respond_to?" do
it "does not change method visibility when finding private method" do
KernelSpecs::VisibilityChange.respond_to?(:new, false).should == false
KernelSpecs::VisibilityChange.respond_to?(:new, true).should == true
- -> { KernelSpecs::VisibilityChange.new }.should raise_error(NoMethodError)
+ -> { KernelSpecs::VisibilityChange.new }.should.raise(NoMethodError)
end
it "indicates if an object responds to a particular message" do
@@ -70,4 +70,30 @@ describe "Kernel#respond_to?" do
KernelSpecs::Foo.new.respond_to?(:invalid_and_silly_method_name).should == false
end
+ context "if object does not have #respond_to_missing?" do
+ it "returns true if object responds to the given public method" do
+ KernelSpecs::BasicA.new.respond_to?(:pub_method).should == true
+ KernelSpecs::MissingA.new.respond_to?(:pub_method).should == true
+ end
+
+ it "returns false if object responds to the given protected method" do
+ KernelSpecs::BasicA.new.respond_to?(:protected_method).should == false
+ KernelSpecs::MissingA.new.respond_to?(:protected_method).should == false
+ end
+
+ it "returns false if object responds to the given private method" do
+ KernelSpecs::BasicA.new.respond_to?(:private_method).should == false
+ KernelSpecs::MissingA.new.respond_to?(:private_method).should == false
+ end
+
+ it "returns false if the given method was undefined" do
+ KernelSpecs::BasicA.new.respond_to?(:undefed_method).should == false
+ KernelSpecs::MissingA.new.respond_to?(:undefed_method).should == false
+ end
+
+ it "returns false if the given method never existed" do
+ KernelSpecs::BasicA.new.respond_to?(:invalid_and_silly_method_name).should == false
+ KernelSpecs::MissingA.new.respond_to?(:invalid_and_silly_method_name).should == false
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/select_spec.rb b/spec/ruby/core/kernel/select_spec.rb
index e0d82f3079..58c963c1a4 100644
--- a/spec/ruby/core/kernel/select_spec.rb
+++ b/spec/ruby/core/kernel/select_spec.rb
@@ -3,16 +3,16 @@ require_relative 'fixtures/classes'
describe "Kernel#select" do
it "is a private method" do
- Kernel.should have_private_instance_method(:select)
+ Kernel.private_instance_methods(false).should.include?(:select)
end
end
describe "Kernel.select" do
it 'does not block when timeout is 0' do
IO.pipe do |read, write|
- IO.select([read], [], [], 0).should == nil
+ select([read], [], [], 0).should == nil
write.write 'data'
- IO.select([read], [], [], 0).should == [[read], [], []]
+ select([read], [], [], 0).should == [[read], [], []]
end
end
end
diff --git a/spec/ruby/core/kernel/set_trace_func_spec.rb b/spec/ruby/core/kernel/set_trace_func_spec.rb
index 1f43e7a009..5201812f2f 100644
--- a/spec/ruby/core/kernel/set_trace_func_spec.rb
+++ b/spec/ruby/core/kernel/set_trace_func_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "Kernel#set_trace_func" do
it "is a private method" do
- Kernel.should have_private_instance_method(:set_trace_func)
+ Kernel.private_instance_methods(false).should.include?(:set_trace_func)
end
end
diff --git a/spec/ruby/core/kernel/shared/dup_clone.rb b/spec/ruby/core/kernel/shared/dup_clone.rb
index 84ad49cbde..3fbf918283 100644
--- a/spec/ruby/core/kernel/shared/dup_clone.rb
+++ b/spec/ruby/core/kernel/shared/dup_clone.rb
@@ -40,7 +40,7 @@ describe :kernel_dup_clone, shared: true do
o.obj = array
o2 = o.send(@method)
- o2.obj.should equal(o.obj)
+ o2.obj.should.equal?(o.obj)
end
it "calls #initialize_copy on the NEW object if available, passing in original object" do
@@ -49,19 +49,7 @@ describe :kernel_dup_clone, shared: true do
o.obj.should == :original
o2.obj.should == :init_copy
- o2.original.should equal(o)
- end
-
- ruby_version_is ''...'2.7' do
- it "preserves tainted state from the original" do
- o = ObjectSpecDupInitCopy.new
- o2 = o.send(@method)
- o.taint
- o3 = o.send(@method)
-
- o2.should_not.tainted?
- o3.should.tainted?
- end
+ o2.original.should.equal?(o)
end
it "does not preserve the object_id" do
@@ -71,18 +59,6 @@ describe :kernel_dup_clone, shared: true do
o2.object_id.should_not == old_object_id
end
- ruby_version_is ''...'2.7' do
- it "preserves untrusted state from the original" do
- o = ObjectSpecDupInitCopy.new
- o2 = o.send(@method)
- o.untrust
- o3 = o.send(@method)
-
- o2.should_not.untrusted?
- o3.should.untrusted?
- end
- end
-
it "returns nil for NilClass" do
nil.send(@method).should == nil
end
@@ -105,11 +81,11 @@ describe :kernel_dup_clone, shared: true do
it "returns self for Complex" do
c = Complex(1.3, 3.1)
- c.send(@method).should equal c
+ c.send(@method).should.equal? c
end
it "returns self for Rational" do
r = Rational(1, 3)
- r.send(@method).should equal r
+ r.send(@method).should.equal? r
end
end
diff --git a/spec/ruby/core/kernel/shared/kind_of.rb b/spec/ruby/core/kernel/shared/kind_of.rb
index aef6f1c1d8..a5e0eab08f 100644
--- a/spec/ruby/core/kernel/shared/kind_of.rb
+++ b/spec/ruby/core/kernel/shared/kind_of.rb
@@ -40,10 +40,10 @@ describe :kernel_kind_of, shared: true do
end
it "raises a TypeError if given an object that is not a Class nor a Module" do
- -> { @o.send(@method, 1) }.should raise_error(TypeError)
- -> { @o.send(@method, 'KindaClass') }.should raise_error(TypeError)
- -> { @o.send(@method, :KindaClass) }.should raise_error(TypeError)
- -> { @o.send(@method, Object.new) }.should raise_error(TypeError)
+ -> { @o.send(@method, 1) }.should.raise(TypeError)
+ -> { @o.send(@method, 'KindaClass') }.should.raise(TypeError)
+ -> { @o.send(@method, :KindaClass) }.should.raise(TypeError)
+ -> { @o.send(@method, Object.new) }.should.raise(TypeError)
end
it "does not take into account `class` method overriding" do
diff --git a/spec/ruby/core/kernel/shared/lambda.rb b/spec/ruby/core/kernel/shared/lambda.rb
index c7180e1442..83de06bb41 100644
--- a/spec/ruby/core/kernel/shared/lambda.rb
+++ b/spec/ruby/core/kernel/shared/lambda.rb
@@ -4,6 +4,8 @@ describe :kernel_lambda, shared: true do
end
it "raises an ArgumentError when no block is given" do
- -> { send(@method) }.should raise_error(ArgumentError)
+ suppress_warning do
+ -> { send(@method) }.should.raise(ArgumentError)
+ end
end
end
diff --git a/spec/ruby/core/kernel/shared/load.rb b/spec/ruby/core/kernel/shared/load.rb
index 120619abef..20d23a623e 100644
--- a/spec/ruby/core/kernel/shared/load.rb
+++ b/spec/ruby/core/kernel/shared/load.rb
@@ -1,5 +1,6 @@
main = self
+# The big difference is Kernel#load does not attempt to add an extension to the passed path, unlike Kernel#require
describe :kernel_load, shared: true do
before :each do
CodeLoadingSpecs.spec_setup
@@ -10,114 +11,123 @@ describe :kernel_load, shared: true do
CodeLoadingSpecs.spec_cleanup
end
- it "loads a non-extensioned file as a Ruby source file" do
- path = File.expand_path "load_fixture", CODE_LOADING_DIR
- @object.load(path).should be_true
- ScratchPad.recorded.should == [:no_ext]
- end
+ describe "(path resolution)" do
+ # This behavior is specific to Kernel#load, it differs for Kernel#require
+ it "loads a non-extensioned file as a Ruby source file" do
+ path = File.expand_path "load_fixture", CODE_LOADING_DIR
+ @object.load(path).should == true
+ ScratchPad.recorded.should == [:no_ext]
+ end
- it "loads a non .rb extensioned file as a Ruby source file" do
- path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
- @object.load(path).should be_true
- ScratchPad.recorded.should == [:no_rb_ext]
- end
+ it "loads a non .rb extensioned file as a Ruby source file" do
+ path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
+ @object.load(path).should == true
+ ScratchPad.recorded.should == [:no_rb_ext]
+ end
- it "loads from the current working directory" do
- Dir.chdir CODE_LOADING_DIR do
- @object.load("load_fixture.rb").should be_true
- ScratchPad.recorded.should == [:loaded]
+ it "loads from the current working directory" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.load("load_fixture.rb").should == true
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+
+ # This behavior is specific to Kernel#load, it differs for Kernel#require
+ it "does not look for a c-extension file when passed a path without extension (when no .rb is present)" do
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture"
+ -> { @object.send(@method, path) }.should.raise(LoadError)
end
end
it "loads a file that recursively requires itself" do
path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR
-> {
- @object.load(path).should be_true
+ @object.load(path).should == true
}.should complain(/circular require considered harmful/, verbose: true)
ScratchPad.recorded.should == [:loaded, :loaded]
end
it "loads a file that recursively loads itself" do
path = File.expand_path "recursive_load_fixture.rb", CODE_LOADING_DIR
- @object.load(path).should be_true
+ @object.load(path).should == true
ScratchPad.recorded.should == [:loaded, :loaded]
end
it "loads a file each time the method is called" do
- @object.load(@path).should be_true
- @object.load(@path).should be_true
+ @object.load(@path).should == true
+ @object.load(@path).should == true
ScratchPad.recorded.should == [:loaded, :loaded]
end
it "loads a file even when the name appears in $LOADED_FEATURES" do
$LOADED_FEATURES << @path
- @object.load(@path).should be_true
+ @object.load(@path).should == true
ScratchPad.recorded.should == [:loaded]
end
it "loads a file that has been loaded by #require" do
- @object.require(@path).should be_true
- @object.load(@path).should be_true
+ @object.require(@path).should == true
+ @object.load(@path).should == true
ScratchPad.recorded.should == [:loaded, :loaded]
end
it "loads file even after $LOAD_PATH change" do
$LOAD_PATH << CODE_LOADING_DIR
- @object.load("load_fixture.rb").should be_true
+ @object.load("load_fixture.rb").should == true
$LOAD_PATH.unshift CODE_LOADING_DIR + "/gem"
- @object.load("load_fixture.rb").should be_true
+ @object.load("load_fixture.rb").should == true
ScratchPad.recorded.should == [:loaded, :loaded_gem]
end
it "does not cause #require with the same path to fail" do
- @object.load(@path).should be_true
- @object.require(@path).should be_true
+ @object.load(@path).should == true
+ @object.require(@path).should == true
ScratchPad.recorded.should == [:loaded, :loaded]
end
it "does not add the loaded path to $LOADED_FEATURES" do
saved_loaded_features = $LOADED_FEATURES.dup
- @object.load(@path).should be_true
+ @object.load(@path).should == true
$LOADED_FEATURES.should == saved_loaded_features
end
it "raises a LoadError if passed a non-extensioned path that does not exist but a .rb extensioned path does exist" do
path = File.expand_path "load_ext_fixture", CODE_LOADING_DIR
- -> { @object.load(path) }.should raise_error(LoadError)
+ -> { @object.load(path) }.should.raise(LoadError)
end
describe "when passed true for 'wrap'" do
it "loads from an existing path" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
- @object.load(path, true).should be_true
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ @object.load(path, true).should == true
end
it "sets the enclosing scope to an anonymous module" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
- Object.const_defined?(:LoadSpecWrap).should be_false
+ Object.const_defined?(:LoadSpecWrap).should == false
wrap_module = ScratchPad.recorded[1]
- wrap_module.should be_an_instance_of(Module)
+ wrap_module.should.instance_of?(Module)
end
it "allows referencing outside namespaces" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
- ScratchPad.recorded[0].should equal(String)
+ ScratchPad.recorded[0].should.equal?(String)
end
it "sets self as a copy of the top-level main" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
top_level = ScratchPad.recorded[2]
top_level.to_s.should == "main"
top_level.method(:to_s).owner.should == top_level.singleton_class
- top_level.should_not equal(main)
- top_level.should be_an_instance_of(Object)
+ top_level.should_not.equal?(main)
+ top_level.should.instance_of?(Object)
end
it "includes modules included in main's singleton class in self's class" do
@@ -127,7 +137,7 @@ describe :kernel_load, shared: true do
main_ancestors = main.singleton_class.ancestors[1..-1]
main_ancestors.first.should == mod
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
top_level = ScratchPad.recorded[2]
@@ -149,11 +159,44 @@ describe :kernel_load, shared: true do
end
it "does not pollute the receiver" do
- -> { @object.send(:top_level_method) }.should raise_error(NameError)
+ -> { @object.send(:top_level_method) }.should.raise(NameError)
end
end
end
+ describe "when passed a module for 'wrap'" do
+ it "sets the enclosing scope to the supplied module" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ Object.const_defined?(:LoadSpecWrap).should == false
+ mod.const_defined?(:LoadSpecWrap).should == true
+
+ wrap_module = ScratchPad.recorded[1]
+ wrap_module.should == mod
+ end
+
+ it "makes constants and instance methods in the source file reachable with the supplied module" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ mod::LOAD_WRAP_SPECS_TOP_LEVEL_CONSTANT.should == 1
+ obj = Object.new
+ obj.extend(mod)
+ obj.send(:load_wrap_specs_top_level_method).should == :load_wrap_specs_top_level_method
+ end
+
+ it "makes instance methods in the source file private" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ mod.private_instance_methods.include?(:load_wrap_specs_top_level_method).should == true
+ end
+ end
+
describe "(shell expansion)" do
before :each do
@env_home = ENV["HOME"]
@@ -165,7 +208,7 @@ describe :kernel_load, shared: true do
end
it "expands a tilde to the HOME environment variable as the path to load" do
- @object.require("~/load_fixture.rb").should be_true
+ @object.require("~/load_fixture.rb").should == true
ScratchPad.recorded.should == [:loaded]
end
end
diff --git a/spec/ruby/core/kernel/shared/method.rb b/spec/ruby/core/kernel/shared/method.rb
index 3418966b1b..82abc287d1 100644
--- a/spec/ruby/core/kernel/shared/method.rb
+++ b/spec/ruby/core/kernel/shared/method.rb
@@ -4,20 +4,26 @@ describe :kernel_method, shared: true do
it "returns a method object for a valid method" do
class KernelSpecs::Foo; def bar; 'done'; end; end
m = KernelSpecs::Foo.new.send(@method, :bar)
- m.should be_an_instance_of Method
+ m.should.instance_of? Method
m.call.should == 'done'
end
it "returns a method object for a valid singleton method" do
class KernelSpecs::Foo; def self.bar; 'class done'; end; end
m = KernelSpecs::Foo.send(@method, :bar)
- m.should be_an_instance_of Method
+ m.should.instance_of? Method
m.call.should == 'class done'
end
- it "returns a method object if we repond_to_missing? method" do
+ it "returns a method object if respond_to_missing?(method) is true" do
m = KernelSpecs::RespondViaMissing.new.send(@method, :handled_publicly)
- m.should be_an_instance_of Method
+ m.should.instance_of? Method
+ m.call(42).should == "Done handled_publicly([42])"
+ end
+
+ it "the returned method object if respond_to_missing?(method) calls #method_missing with a Symbol name" do
+ m = KernelSpecs::RespondViaMissing.new.send(@method, "handled_publicly")
+ m.should.instance_of? Method
m.call(42).should == "Done handled_publicly([42])"
end
@@ -25,12 +31,12 @@ describe :kernel_method, shared: true do
class KernelSpecs::Foo; def bar; 'done'; end; end
-> {
KernelSpecs::Foo.new.send(@method, :invalid_and_silly_method_name)
- }.should raise_error(NameError)
+ }.should.raise(NameError)
end
it "raises a NameError for an invalid singleton method name" do
class KernelSpecs::Foo; def self.bar; 'done'; end; end
- -> { KernelSpecs::Foo.send(@method, :baz) }.should raise_error(NameError)
+ -> { KernelSpecs::Foo.send(@method, :baz) }.should.raise(NameError)
end
it "changes the method called for super on a target aliased method" do
diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb
index 28fdb5e451..6272b00665 100644
--- a/spec/ruby/core/kernel/shared/require.rb
+++ b/spec/ruby/core/kernel/shared/require.rb
@@ -2,26 +2,26 @@ describe :kernel_require_basic, shared: true do
describe "(path resolution)" do
it "loads an absolute path" do
path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR
- @object.send(@method, path).should be_true
+ @object.send(@method, path).should == true
ScratchPad.recorded.should == [:loaded]
end
it "loads a non-canonical absolute path" do
path = File.join CODE_LOADING_DIR, "..", "code", "load_fixture.rb"
- @object.send(@method, path).should be_true
+ @object.send(@method, path).should == true
ScratchPad.recorded.should == [:loaded]
end
it "loads a file defining many methods" do
path = File.expand_path "methods_fixture.rb", CODE_LOADING_DIR
- @object.send(@method, path).should be_true
+ @object.send(@method, path).should == true
ScratchPad.recorded.should == [:loaded]
end
it "raises a LoadError if the file does not exist" do
path = File.expand_path "nonexistent.rb", CODE_LOADING_DIR
File.should_not.exist?(path)
- -> { @object.send(@method, path) }.should raise_error(LoadError)
+ -> { @object.send(@method, path) }.should.raise(LoadError)
ScratchPad.recorded.should == []
end
@@ -42,7 +42,7 @@ describe :kernel_require_basic, shared: true do
it "raises a LoadError" do
File.should.exist?(@path)
- -> { @object.send(@method, @path) }.should raise_error(LoadError)
+ -> { @object.send(@method, @path) }.should.raise(LoadError)
end
end
end
@@ -52,24 +52,24 @@ describe :kernel_require_basic, shared: true do
path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR
name = mock("load_fixture.rb mock")
name.should_receive(:to_str).and_return(path)
- @object.send(@method, name).should be_true
+ @object.send(@method, name).should == true
ScratchPad.recorded.should == [:loaded]
end
it "raises a TypeError if passed nil" do
- -> { @object.send(@method, nil) }.should raise_error(TypeError)
+ -> { @object.send(@method, nil) }.should.raise(TypeError)
end
- it "raises a TypeError if passed a Fixnum" do
- -> { @object.send(@method, 42) }.should raise_error(TypeError)
+ it "raises a TypeError if passed an Integer" do
+ -> { @object.send(@method, 42) }.should.raise(TypeError)
end
it "raises a TypeError if passed an Array" do
- -> { @object.send(@method, []) }.should raise_error(TypeError)
+ -> { @object.send(@method, []) }.should.raise(TypeError)
end
it "raises a TypeError if passed an object that does not provide #to_str" do
- -> { @object.send(@method, mock("not a filename")) }.should raise_error(TypeError)
+ -> { @object.send(@method, mock("not a filename")) }.should.raise(TypeError)
end
it "raises a TypeError if passed an object that has #to_s but not #to_str" do
@@ -77,14 +77,14 @@ describe :kernel_require_basic, shared: true do
name.stub!(:to_s).and_return("load_fixture.rb")
$LOAD_PATH << "."
Dir.chdir CODE_LOADING_DIR do
- -> { @object.send(@method, name) }.should raise_error(TypeError)
+ -> { @object.send(@method, name) }.should.raise(TypeError)
end
end
it "raises a TypeError if #to_str does not return a String" do
name = mock("#to_str returns nil")
name.should_receive(:to_str).at_least(1).times.and_return(nil)
- -> { @object.send(@method, name) }.should raise_error(TypeError)
+ -> { @object.send(@method, name) }.should.raise(TypeError)
end
it "calls #to_path on non-String objects" do
@@ -92,7 +92,7 @@ describe :kernel_require_basic, shared: true do
name.stub!(:to_path).and_return("load_fixture.rb")
$LOAD_PATH << "."
Dir.chdir CODE_LOADING_DIR do
- @object.send(@method, name).should be_true
+ @object.send(@method, name).should == true
end
ScratchPad.recorded.should == [:loaded]
end
@@ -101,7 +101,7 @@ describe :kernel_require_basic, shared: true do
path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR
str = mock("load_fixture.rb mock")
str.should_receive(:to_path).and_return(path)
- @object.send(@method, str).should be_true
+ @object.send(@method, str).should == true
ScratchPad.recorded.should == [:loaded]
end
@@ -111,21 +111,21 @@ describe :kernel_require_basic, shared: true do
to_path = mock("load_fixture_rb #to_path mock")
name.should_receive(:to_path).and_return(to_path)
to_path.should_receive(:to_str).and_return(path)
- @object.send(@method, name).should be_true
+ @object.send(@method, name).should == true
ScratchPad.recorded.should == [:loaded]
end
# "http://redmine.ruby-lang.org/issues/show/2578"
it "loads a ./ relative path from the current working directory with empty $LOAD_PATH" do
Dir.chdir CODE_LOADING_DIR do
- @object.send(@method, "./load_fixture.rb").should be_true
+ @object.send(@method, "./load_fixture.rb").should == true
end
ScratchPad.recorded.should == [:loaded]
end
it "loads a ../ relative path from the current working directory with empty $LOAD_PATH" do
Dir.chdir CODE_LOADING_DIR do
- @object.send(@method, "../code/load_fixture.rb").should be_true
+ @object.send(@method, "../code/load_fixture.rb").should == true
end
ScratchPad.recorded.should == [:loaded]
end
@@ -133,7 +133,7 @@ describe :kernel_require_basic, shared: true do
it "loads a ./ relative path from the current working directory with non-empty $LOAD_PATH" do
$LOAD_PATH << "an_irrelevant_dir"
Dir.chdir CODE_LOADING_DIR do
- @object.send(@method, "./load_fixture.rb").should be_true
+ @object.send(@method, "./load_fixture.rb").should == true
end
ScratchPad.recorded.should == [:loaded]
end
@@ -141,7 +141,7 @@ describe :kernel_require_basic, shared: true do
it "loads a ../ relative path from the current working directory with non-empty $LOAD_PATH" do
$LOAD_PATH << "an_irrelevant_dir"
Dir.chdir CODE_LOADING_DIR do
- @object.send(@method, "../code/load_fixture.rb").should be_true
+ @object.send(@method, "../code/load_fixture.rb").should == true
end
ScratchPad.recorded.should == [:loaded]
end
@@ -149,22 +149,30 @@ describe :kernel_require_basic, shared: true do
it "loads a non-canonical path from the current working directory with non-empty $LOAD_PATH" do
$LOAD_PATH << "an_irrelevant_dir"
Dir.chdir CODE_LOADING_DIR do
- @object.send(@method, "../code/../code/load_fixture.rb").should be_true
+ @object.send(@method, "../code/../code/load_fixture.rb").should == true
end
ScratchPad.recorded.should == [:loaded]
end
it "resolves a filename against $LOAD_PATH entries" do
$LOAD_PATH << CODE_LOADING_DIR
- @object.send(@method, "load_fixture.rb").should be_true
+ @object.send(@method, "load_fixture.rb").should == true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "accepts an Object with #to_path in $LOAD_PATH" do
+ obj = mock("to_path")
+ obj.should_receive(:to_path).at_least(:once).and_return(CODE_LOADING_DIR)
+ $LOAD_PATH << obj
+ @object.send(@method, "load_fixture.rb").should == true
ScratchPad.recorded.should == [:loaded]
end
it "does not require file twice after $LOAD_PATH change" do
$LOAD_PATH << CODE_LOADING_DIR
- @object.require("load_fixture.rb").should be_true
+ @object.require("load_fixture.rb").should == true
$LOAD_PATH.push CODE_LOADING_DIR + "/gem"
- @object.require("load_fixture.rb").should be_false
+ @object.require("load_fixture.rb").should == false
ScratchPad.recorded.should == [:loaded]
end
@@ -172,7 +180,7 @@ describe :kernel_require_basic, shared: true do
$LOAD_PATH << CODE_LOADING_DIR
-> do
@object.send(@method, "./load_fixture.rb")
- end.should raise_error(LoadError)
+ end.should.raise(LoadError)
ScratchPad.recorded.should == []
end
@@ -180,13 +188,13 @@ describe :kernel_require_basic, shared: true do
$LOAD_PATH << CODE_LOADING_DIR
-> do
@object.send(@method, "../code/load_fixture.rb")
- end.should raise_error(LoadError)
+ end.should.raise(LoadError)
ScratchPad.recorded.should == []
end
it "resolves a non-canonical path against $LOAD_PATH entries" do
$LOAD_PATH << File.dirname(CODE_LOADING_DIR)
- @object.send(@method, "code/../code/load_fixture.rb").should be_true
+ @object.send(@method, "code/../code/load_fixture.rb").should == true
ScratchPad.recorded.should == [:loaded]
end
@@ -195,7 +203,7 @@ describe :kernel_require_basic, shared: true do
sep = File::Separator + File::Separator
path = ["..", "code", "load_fixture.rb"].join(sep)
Dir.chdir CODE_LOADING_DIR do
- @object.send(@method, path).should be_true
+ @object.send(@method, path).should == true
end
ScratchPad.recorded.should == [:loaded]
end
@@ -204,11 +212,39 @@ end
describe :kernel_require, shared: true do
describe "(path resolution)" do
+ it "loads .rb file when passed absolute path without extension" do
+ path = File.expand_path "load_fixture", CODE_LOADING_DIR
+ @object.send(@method, path).should == true
+ # This should _not_ be [:no_ext]
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ platform_is :linux, :darwin do
+ it "loads c-extension file when passed absolute path without extension when no .rb is present" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture"
+ -> { @object.send(@method, path) }.should.raise(LoadError)
+ end
+ end
+
+ platform_is :darwin do
+ it "loads .bundle file when passed absolute path with .so" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture.so"
+ -> { @object.send(@method, path) }.should.raise(LoadError)
+ end
+ end
+
+ it "does not try an extra .rb if the path already ends in .rb" do
+ path = File.join CODE_LOADING_DIR, "d", "load_fixture.rb"
+ -> { @object.send(@method, path) }.should.raise(LoadError)
+ end
+
# For reference see [ruby-core:24155] in which matz confirms this feature is
# intentional for security reasons.
it "does not load a bare filename unless the current working directory is in $LOAD_PATH" do
Dir.chdir CODE_LOADING_DIR do
- -> { @object.require("load_fixture.rb") }.should raise_error(LoadError)
+ -> { @object.require("load_fixture.rb") }.should.raise(LoadError)
ScratchPad.recorded.should == []
end
end
@@ -217,7 +253,7 @@ describe :kernel_require, shared: true do
Dir.chdir File.dirname(CODE_LOADING_DIR) do
-> do
@object.require("code/load_fixture.rb")
- end.should raise_error(LoadError)
+ end.should.raise(LoadError)
ScratchPad.recorded.should == []
end
end
@@ -225,10 +261,19 @@ describe :kernel_require, shared: true do
it "loads a file that recursively requires itself" do
path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR
-> {
- @object.require(path).should be_true
+ @object.require(path).should == true
}.should complain(/circular require considered harmful/, verbose: true)
ScratchPad.recorded.should == [:loaded]
end
+
+ it "loads a file concurrently" do
+ path = File.expand_path "concurrent_require_fixture.rb", CODE_LOADING_DIR
+ ScratchPad.record(@object)
+ -> {
+ @object.require(path)
+ }.should_not complain(/circular require considered harmful/, verbose: true)
+ ScratchPad.recorded.join
+ end
end
describe "(non-extensioned path)" do
@@ -239,17 +284,25 @@ describe :kernel_require, shared: true do
end
it "loads a .rb extensioned file when a C-extension file exists on an earlier load path" do
- @object.require("load_fixture").should be_true
+ @object.require("load_fixture").should == true
ScratchPad.recorded.should == [:loaded]
end
- ruby_bug "#16926", "2.7"..."2.8" do
- it "does not load a feature twice when $LOAD_PATH has been modified" do
- $LOAD_PATH.replace [CODE_LOADING_DIR]
- @object.require("load_fixture").should be_true
- $LOAD_PATH.replace [File.expand_path("b", CODE_LOADING_DIR), CODE_LOADING_DIR]
- @object.require("load_fixture").should be_false
- end
+ it "does not load a feature twice when $LOAD_PATH has been modified" do
+ $LOAD_PATH.replace [CODE_LOADING_DIR]
+ @object.require("load_fixture").should == true
+ $LOAD_PATH.replace [File.expand_path("b", CODE_LOADING_DIR), CODE_LOADING_DIR]
+ @object.require("load_fixture").should == false
+ end
+
+ it "stores the missing path in a LoadError object" do
+ path = "abcd1234"
+
+ -> {
+ @object.send(@method, path)
+ }.should.raise(LoadError) { |e|
+ e.path.should == path
+ }
end
end
@@ -257,7 +310,7 @@ describe :kernel_require, shared: true do
it "loads a .rb extensioned file when passed a non-extensioned path" do
path = File.expand_path "load_fixture", CODE_LOADING_DIR
File.should.exist?(path)
- @object.require(path).should be_true
+ @object.require(path).should == true
ScratchPad.recorded.should == [:loaded]
end
@@ -267,21 +320,21 @@ describe :kernel_require, shared: true do
$LOADED_FEATURES << File.expand_path("load_fixture.so", CODE_LOADING_DIR)
$LOADED_FEATURES << File.expand_path("load_fixture.dll", CODE_LOADING_DIR)
path = File.expand_path "load_fixture", CODE_LOADING_DIR
- @object.require(path).should be_true
+ @object.require(path).should == true
ScratchPad.recorded.should == [:loaded]
end
it "does not load a C-extension file if a .rb extensioned file is already loaded" do
$LOADED_FEATURES << File.expand_path("load_fixture.rb", CODE_LOADING_DIR)
path = File.expand_path "load_fixture", CODE_LOADING_DIR
- @object.require(path).should be_false
+ @object.require(path).should == false
ScratchPad.recorded.should == []
end
it "loads a .rb extensioned file when passed a non-.rb extensioned path" do
path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
File.should.exist?(path)
- @object.require(path).should be_true
+ @object.require(path).should == true
ScratchPad.recorded.should == [:loaded]
end
@@ -291,14 +344,14 @@ describe :kernel_require, shared: true do
$LOADED_FEATURES << File.expand_path("load_fixture.ext.so", CODE_LOADING_DIR)
$LOADED_FEATURES << File.expand_path("load_fixture.ext.dll", CODE_LOADING_DIR)
path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
- @object.require(path).should be_true
+ @object.require(path).should == true
ScratchPad.recorded.should == [:loaded]
end
it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do
$LOADED_FEATURES << File.expand_path("load_fixture.ext.rb", CODE_LOADING_DIR)
path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
- @object.require(path).should be_false
+ @object.require(path).should == false
ScratchPad.recorded.should == []
end
end
@@ -309,8 +362,8 @@ describe :kernel_require, shared: true do
end
it "stores an absolute path" do
- @object.require(@path).should be_true
- $LOADED_FEATURES.should include(@path)
+ @object.require(@path).should == true
+ $LOADED_FEATURES.should.include?(@path)
end
platform_is_not :windows do
@@ -330,20 +383,35 @@ describe :kernel_require, shared: true do
it "does not canonicalize the path and stores a path with symlinks" do
symlink_path = "#{@symlink_to_code_dir}/load_fixture.rb"
canonical_path = "#{CODE_LOADING_DIR}/load_fixture.rb"
- @object.require(symlink_path).should be_true
+ @object.require(symlink_path).should == true
ScratchPad.recorded.should == [:loaded]
features = $LOADED_FEATURES.select { |path| path.end_with?('load_fixture.rb') }
- features.should include(symlink_path)
- features.should_not include(canonical_path)
+ features.should.include?(symlink_path)
+ features.should_not.include?(canonical_path)
end
it "stores the same path that __FILE__ returns in the required file" do
symlink_path = "#{@symlink_to_code_dir}/load_fixture_and__FILE__.rb"
- @object.require(symlink_path).should be_true
+ @object.require(symlink_path).should == true
loaded_feature = $LOADED_FEATURES.last
ScratchPad.recorded.should == [loaded_feature]
end
+
+ it "requires only once when a new matching file added to path" do
+ @object.require('load_fixture').should == true
+ ScratchPad.recorded.should == [:loaded]
+
+ symlink_to_code_dir_two = tmp("codesymlinktwo")
+ File.symlink("#{CODE_LOADING_DIR}/b", symlink_to_code_dir_two)
+ begin
+ $LOAD_PATH.unshift(symlink_to_code_dir_two)
+
+ @object.require('load_fixture').should == false
+ ensure
+ rm_r symlink_to_code_dir_two
+ end
+ end
end
describe "with symlinks in the required feature and $LOAD_PATH" do
@@ -365,7 +433,7 @@ describe :kernel_require, shared: true do
it "canonicalizes the entry in $LOAD_PATH but not the filename passed to #require" do
$LOAD_PATH.unshift(@symlink_to_dir)
- @object.require("symfile").should be_true
+ @object.require("symfile").should == true
loaded_feature = "#{@dir}/symfile.rb"
ScratchPad.recorded.should == [loaded_feature]
$".last.should == loaded_feature
@@ -377,20 +445,20 @@ describe :kernel_require, shared: true do
it "does not store the path if the load fails" do
$LOAD_PATH << CODE_LOADING_DIR
saved_loaded_features = $LOADED_FEATURES.dup
- -> { @object.require("raise_fixture.rb") }.should raise_error(RuntimeError)
+ -> { @object.require("raise_fixture.rb") }.should.raise(RuntimeError)
$LOADED_FEATURES.should == saved_loaded_features
end
it "does not load an absolute path that is already stored" do
$LOADED_FEATURES << @path
- @object.require(@path).should be_false
+ @object.require(@path).should == false
ScratchPad.recorded.should == []
end
it "does not load a ./ relative path that is already stored" do
$LOADED_FEATURES << "./load_fixture.rb"
Dir.chdir CODE_LOADING_DIR do
- @object.require("./load_fixture.rb").should be_false
+ @object.require("./load_fixture.rb").should == false
end
ScratchPad.recorded.should == []
end
@@ -398,7 +466,7 @@ describe :kernel_require, shared: true do
it "does not load a ../ relative path that is already stored" do
$LOADED_FEATURES << "../load_fixture.rb"
Dir.chdir CODE_LOADING_DIR do
- @object.require("../load_fixture.rb").should be_false
+ @object.require("../load_fixture.rb").should == false
end
ScratchPad.recorded.should == []
end
@@ -406,27 +474,27 @@ describe :kernel_require, shared: true do
it "does not load a non-canonical path that is already stored" do
$LOADED_FEATURES << "code/../code/load_fixture.rb"
$LOAD_PATH << File.dirname(CODE_LOADING_DIR)
- @object.require("code/../code/load_fixture.rb").should be_false
+ @object.require("code/../code/load_fixture.rb").should == false
ScratchPad.recorded.should == []
end
it "respects being replaced with a new array" do
prev = $LOADED_FEATURES.dup
- @object.require(@path).should be_true
- $LOADED_FEATURES.should include(@path)
+ @object.require(@path).should == true
+ $LOADED_FEATURES.should.include?(@path)
$LOADED_FEATURES.replace(prev)
- $LOADED_FEATURES.should_not include(@path)
- @object.require(@path).should be_true
- $LOADED_FEATURES.should include(@path)
+ $LOADED_FEATURES.should_not.include?(@path)
+ @object.require(@path).should == true
+ $LOADED_FEATURES.should.include?(@path)
end
it "does not load twice the same file with and without extension" do
$LOAD_PATH << CODE_LOADING_DIR
- @object.require("load_fixture.rb").should be_true
- @object.require("load_fixture").should be_false
+ @object.require("load_fixture.rb").should == true
+ @object.require("load_fixture").should == false
end
describe "when a non-extensioned file is in $LOADED_FEATURES" do
@@ -436,19 +504,19 @@ describe :kernel_require, shared: true do
it "loads a .rb extensioned file when a non extensioned file is in $LOADED_FEATURES" do
$LOAD_PATH << CODE_LOADING_DIR
- @object.require("load_fixture").should be_true
+ @object.require("load_fixture").should == true
ScratchPad.recorded.should == [:loaded]
end
it "loads a .rb extensioned file from a subdirectory" do
$LOAD_PATH << File.dirname(CODE_LOADING_DIR)
- @object.require("code/load_fixture").should be_true
+ @object.require("code/load_fixture").should == true
ScratchPad.recorded.should == [:loaded]
end
it "returns false if the file is not found" do
Dir.chdir File.dirname(CODE_LOADING_DIR) do
- @object.require("load_fixture").should be_false
+ @object.require("load_fixture").should == false
ScratchPad.recorded.should == []
end
end
@@ -456,7 +524,7 @@ describe :kernel_require, shared: true do
it "returns false when passed a path and the file is not found" do
$LOADED_FEATURES << "code/load_fixture"
Dir.chdir CODE_LOADING_DIR do
- @object.require("code/load_fixture").should be_false
+ @object.require("code/load_fixture").should == false
ScratchPad.recorded.should == []
end
end
@@ -464,16 +532,16 @@ describe :kernel_require, shared: true do
it "stores ../ relative paths as absolute paths" do
Dir.chdir CODE_LOADING_DIR do
- @object.require("../code/load_fixture.rb").should be_true
+ @object.require("../code/load_fixture.rb").should == true
end
- $LOADED_FEATURES.should include(@path)
+ $LOADED_FEATURES.should.include?(@path)
end
it "stores ./ relative paths as absolute paths" do
Dir.chdir CODE_LOADING_DIR do
- @object.require("./load_fixture.rb").should be_true
+ @object.require("./load_fixture.rb").should == true
end
- $LOADED_FEATURES.should include(@path)
+ $LOADED_FEATURES.should.include?(@path)
end
it "collapses duplicate path separators" do
@@ -481,27 +549,27 @@ describe :kernel_require, shared: true do
sep = File::Separator + File::Separator
path = ["..", "code", "load_fixture.rb"].join(sep)
Dir.chdir CODE_LOADING_DIR do
- @object.require(path).should be_true
+ @object.require(path).should == true
end
- $LOADED_FEATURES.should include(@path)
+ $LOADED_FEATURES.should.include?(@path)
end
it "expands absolute paths containing .." do
path = File.join CODE_LOADING_DIR, "..", "code", "load_fixture.rb"
- @object.require(path).should be_true
- $LOADED_FEATURES.should include(@path)
+ @object.require(path).should == true
+ $LOADED_FEATURES.should.include?(@path)
end
it "adds the suffix of the resolved filename" do
$LOAD_PATH << CODE_LOADING_DIR
- @object.require("load_fixture").should be_true
- $LOADED_FEATURES.should include(@path)
+ @object.require("load_fixture").should == true
+ $LOADED_FEATURES.should.include?(@path)
end
it "does not load a non-canonical path for a file already loaded" do
$LOADED_FEATURES << @path
$LOAD_PATH << File.dirname(CODE_LOADING_DIR)
- @object.require("code/../code/load_fixture.rb").should be_false
+ @object.require("code/../code/load_fixture.rb").should == false
ScratchPad.recorded.should == []
end
@@ -509,7 +577,7 @@ describe :kernel_require, shared: true do
$LOADED_FEATURES << @path
$LOAD_PATH << "an_irrelevant_dir"
Dir.chdir CODE_LOADING_DIR do
- @object.require("./load_fixture.rb").should be_false
+ @object.require("./load_fixture.rb").should == false
end
ScratchPad.recorded.should == []
end
@@ -518,30 +586,35 @@ describe :kernel_require, shared: true do
$LOADED_FEATURES << @path
$LOAD_PATH << "an_irrelevant_dir"
Dir.chdir CODE_LOADING_DIR do
- @object.require("../code/load_fixture.rb").should be_false
+ @object.require("../code/load_fixture.rb").should == false
end
ScratchPad.recorded.should == []
end
- it "complex, enumerator, rational and thread are already required" do
- provided = %w[complex enumerator rational thread]
+ it "unicode_normalize is part of core and not $LOADED_FEATURES" do
features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
- provided.each { |feature|
- features.should =~ /\b#{feature}\.(rb|so|jar)$/
+ features.lines.each { |feature|
+ feature.should_not.include?("unicode_normalize")
}
- code = provided.map { |f| "puts require #{f.inspect}\n" }.join
- required = ruby_exe(code, options: '--disable-gems')
- required.should == "false\n" * provided.size
+ -> { @object.require("unicode_normalize") }.should.raise(LoadError)
end
- it "unicode_normalize is part of core and not $LOADED_FEATURES" do
- features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
- features.lines.each { |feature|
- feature.should_not include("unicode_normalize")
- }
+ it "does not load a file earlier on the $LOAD_PATH when other similar features were already loaded" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, "../code/load_fixture").should == true
+ end
+ ScratchPad.recorded.should == [:loaded]
- -> { @object.require("unicode_normalize") }.should raise_error(LoadError)
+ $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/b"
+ # This loads because the above load was not on the $LOAD_PATH
+ @object.send(@method, "load_fixture").should == true
+ ScratchPad.recorded.should == [:loaded, :loaded]
+
+ $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/c"
+ # This does not load because the above load was on the $LOAD_PATH
+ @object.send(@method, "load_fixture").should == false
+ ScratchPad.recorded.should == [:loaded, :loaded]
end
end
@@ -558,13 +631,13 @@ describe :kernel_require, shared: true do
# "#3171"
it "performs tilde expansion on a .rb file before storing paths in $LOADED_FEATURES" do
- @object.require("~/load_fixture.rb").should be_true
- $LOADED_FEATURES.should include(@path)
+ @object.require("~/load_fixture.rb").should == true
+ $LOADED_FEATURES.should.include?(@path)
end
it "performs tilde expansion on a non-extensioned file before storing paths in $LOADED_FEATURES" do
- @object.require("~/load_fixture").should be_true
- $LOADED_FEATURES.should include(@path)
+ @object.require("~/load_fixture").should == true
+ $LOADED_FEATURES.should.include?(@path)
end
end
@@ -621,8 +694,8 @@ describe :kernel_require, shared: true do
t1.join
t2.join
- t1_res.should be_true
- t2_res.should be_false
+ t1_res.should == true
+ t2_res.should == false
ScratchPad.recorded.should == [:con_pre, :con_post, :t2_post, :t1_post]
end
@@ -646,8 +719,8 @@ describe :kernel_require, shared: true do
t1.join
t2.join
- t1_res.should be_true
- t2_res.should be_true
+ t1_res.should == true
+ t2_res.should == true
ScratchPad.recorded.should == [:con2_pre, :con3, :con2_post]
end
@@ -665,7 +738,7 @@ describe :kernel_require, shared: true do
-> {
@object.require(@path)
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
Thread.pass until fin
ScratchPad.recorded << :t1_post
@@ -686,7 +759,7 @@ describe :kernel_require, shared: true do
t1.join
t2.join
- t2_res.should be_true
+ t2_res.should == true
ScratchPad.recorded.should == [:con_pre, :con_pre, :con_post, :t2_post, :t1_post]
end
@@ -706,7 +779,7 @@ describe :kernel_require, shared: true do
-> {
@object.require(@path)
- }.should raise_error(RuntimeError)
+ }.should.raise(RuntimeError)
raised = true
@@ -734,8 +807,8 @@ describe :kernel_require, shared: true do
t1.join
t2.join
- t1_res.should be_false
- t2_res.should be_true
+ t1_res.should == false
+ t2_res.should == true
ScratchPad.recorded.should == [:con_pre, :con_pre, :con_post, :t2_post, :t1_post]
end
@@ -746,8 +819,28 @@ describe :kernel_require, shared: true do
-> {
@object.send(@method, path)
- }.should raise_error(LoadError) { |e|
+ }.should.raise(LoadError) { |e|
e.path.should == path
}
end
+
+ platform_is :linux, :darwin do
+ it "does not store the missing path in a LoadError object when c-extension file exists but loading fails and passed absolute path without extension" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture"
+ -> { @object.send(@method, path) }.should.raise(LoadError) { |e|
+ e.path.should == nil
+ }
+ end
+ end
+
+ platform_is :darwin do
+ it "does not store the missing path in a LoadError object when c-extension file exists but loading fails and passed absolute path with extension" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture.bundle"
+ -> { @object.send(@method, path) }.should.raise(LoadError) { |e|
+ e.path.should == nil
+ }
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb
index ca1e6bb2ed..b40bd95f59 100644
--- a/spec/ruby/core/kernel/shared/sprintf.rb
+++ b/spec/ruby/core/kernel/shared/sprintf.rb
@@ -22,12 +22,13 @@ describe :kernel_sprintf, shared: true do
@method.call("%d", "112").should == "112"
@method.call("%d", "0127").should == "87"
@method.call("%d", "0xc4").should == "196"
+ @method.call("%d", "0").should == "0"
end
it "raises TypeError exception if cannot convert to Integer" do
-> {
@method.call("%b", Object.new)
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
["b", "B"].each do |f|
@@ -104,6 +105,20 @@ describe :kernel_sprintf, shared: true do
@method.call("%X", -1).should == "..F"
end
end
+
+ %w[b B d i u o x X].each do |f|
+ describe f do
+ it "converts to the empty string if precision is 0 and value is 0" do
+ @method.call("%.#{f}", 0).should == ""
+ @method.call("%.0#{f}", 0).should == ""
+ end
+
+ it "pads the empty string if precision is 0 and value is 0" do
+ @method.call("%2.#{f}", 0).should == " "
+ @method.call("%2.0#{f}", 0).should == " "
+ end
+ end
+ end
end
describe "float formats" do
@@ -116,7 +131,7 @@ describe :kernel_sprintf, shared: true do
it "raises TypeError exception if cannot convert to Float" do
-> {
@method.call("%f", Object.new)
- }.should raise_error(TypeError)
+ }.should.raise(TypeError)
end
{"e" => "e", "E" => "E"}.each_pair do |f, exp|
@@ -289,21 +304,68 @@ describe :kernel_sprintf, shared: true do
@method.call("%c", "a").should == "a"
end
- it "raises ArgumentError if argument is a string of several characters" do
+ it "displays only the first character if argument is a string of several characters" do
+ @method.call("%c", "abc").should == "a"
+ end
+
+ it "displays only the first character if argument is a string of several multibyte characters" do
+ @method.call("%c", "あいうえお").should == "あ"
+ end
+
+ it "displays no characters if argument is an empty string" do
+ @method.call("%c", "").should == ""
+ end
+
+ it "raises TypeError if argument is not String or Integer and cannot be converted to them" do
+ -> {
+ @method.call("%c", [])
+ }.should raise_consistent_error(TypeError, /no implicit conversion of Array into Integer/)
+ end
+
+ it "raises TypeError if argument is nil" do
-> {
- @method.call("%c", "abc")
- }.should raise_error(ArgumentError)
+ @method.call("%c", nil)
+ }.should raise_consistent_error(TypeError, /no implicit conversion of nil into Integer/)
+ end
+
+ it "tries to convert argument to String with to_str" do
+ obj = BasicObject.new
+ def obj.to_str
+ "a"
+ end
+
+ @method.call("%c", obj).should == "a"
+ end
+
+ it "tries to convert argument to Integer with to_int" do
+ obj = BasicObject.new
+ def obj.to_int
+ 90
+ end
+
+ @method.call("%c", obj).should == "Z"
end
- it "raises ArgumentError if argument is an empty string" do
+ it "raises TypeError if converting to String with to_str returns non-String" do
+ obj = BasicObject.new
+ def obj.to_str
+ :foo
+ end
+
-> {
- @method.call("%c", "")
- }.should raise_error(ArgumentError)
+ @method.call("%c", obj)
+ }.should raise_consistent_error(TypeError, /can't convert BasicObject into String/)
end
- it "supports Unicode characters" do
- @method.call("%c", 1286).should == "Ԇ"
- @method.call("%c", "ش").should == "ش"
+ it "raises TypeError if converting to Integer with to_int returns non-Integer" do
+ obj = BasicObject.new
+ def obj.to_int
+ :foo
+ end
+
+ -> {
+ @method.call("%c", obj)
+ }.should raise_consistent_error(TypeError, /can't convert BasicObject into Integer/)
end
end
@@ -313,6 +375,10 @@ describe :kernel_sprintf, shared: true do
obj.should_receive(:inspect).and_return("<inspect-result>")
@method.call("%p", obj).should == "<inspect-result>"
end
+
+ it "substitutes 'nil' for nil" do
+ @method.call("%p", nil).should == "nil"
+ end
end
describe "s" do
@@ -320,6 +386,10 @@ describe :kernel_sprintf, shared: true do
@method.call("%s", "abc").should == "abc"
end
+ it "substitutes '' for nil" do
+ @method.call("%s", nil).should == ""
+ end
+
it "converts argument to string with to_s" do
obj = mock("string")
obj.should_receive(:to_s).and_return("abc")
@@ -334,7 +404,7 @@ describe :kernel_sprintf, shared: true do
-> {
@method.call("%s", obj)
- }.should raise_error(NoMethodError)
+ }.should.raise(NoMethodError)
end
it "formats a partial substring without including omitted characters" do
@@ -342,18 +412,57 @@ describe :kernel_sprintf, shared: true do
sub_string = long_string[8, 5]
sprintf("%.#{1 * 3}s", sub_string).should == "hel"
end
+
+ it "formats string with precision" do
+ Kernel.format("%.3s", "hello").should == "hel"
+ Kernel.format("%-3.3s", "hello").should == "hel"
+ end
+
+ it "formats string with width" do
+ @method.call("%6s", "abc").should == " abc"
+ @method.call("%6s", "abcdefg").should == "abcdefg"
+ end
+
+ it "formats string with width and precision" do
+ @method.call("%4.6s", "abc").should == " abc"
+ @method.call("%4.6s", "abcdefg").should == "abcdef"
+ end
+
+ it "formats nil with width" do
+ @method.call("%6s", nil).should == " "
+ end
+
+ it "formats nil with precision" do
+ @method.call("%.6s", nil).should == ""
+ end
+
+ it "formats nil with width and precision" do
+ @method.call("%4.6s", nil).should == " "
+ end
+
+ it "formats multibyte string with precision" do
+ Kernel.format("%.2s", "été").should == "ét"
+ end
+
+ it "preserves encoding of the format string" do
+ str = format('%s'.encode(Encoding::UTF_8), 'foobar')
+ str.encoding.should == Encoding::UTF_8
+
+ str = format('%s'.encode(Encoding::US_ASCII), 'foobar')
+ str.encoding.should == Encoding::US_ASCII
+ end
end
describe "%" do
it "alone raises an ArgumentError" do
-> {
@method.call("%")
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "is escaped by %" do
@method.call("%%").should == "%"
- @method.call("%%d", 10).should == "%d"
+ @method.call("%%d").should == "%d"
end
end
end
@@ -455,7 +564,7 @@ describe :kernel_sprintf, shared: true do
it "raises exception if argument number is bigger than actual arguments list" do
-> {
@method.call("%4$d", 1, 2, 3)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "ignores '-' sign" do
@@ -466,7 +575,7 @@ describe :kernel_sprintf, shared: true do
it "raises ArgumentError exception when absolute and relative argument numbers are mixed" do
-> {
@method.call("%1$d %d", 1, 2)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
end
@@ -498,11 +607,28 @@ describe :kernel_sprintf, shared: true do
@method.call("%#b", 0).should == "0"
@method.call("%#B", 0).should == "0"
- @method.call("%#o", 0).should == "0"
-
@method.call("%#x", 0).should == "0"
@method.call("%#X", 0).should == "0"
end
+
+ it "does nothing for zero argument when combined with zero precision" do
+ @method.call("%#.0b", 0).should == ""
+ @method.call("%#.0B", 0).should == ""
+
+ @method.call("%#.0x", 0).should == ""
+ @method.call("%#.0X", 0).should == ""
+ end
+ end
+
+ context "applies to format o" do
+ it "does nothing for zero argument" do
+ @method.call("%#o", 0).should == "0"
+ @method.call("%#.1o", 0).should == "0"
+ end
+
+ it "increases the precision if precision zero is requested with zero argument" do
+ @method.call("%#.0o", 0).should == "0"
+ end
end
context "applies to formats aAeEfgG" do
@@ -716,7 +842,7 @@ describe :kernel_sprintf, shared: true do
it "raises ArgumentError when is mixed with width" do
-> {
@method.call("%*10d", 10, 112)
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
end
end
@@ -815,7 +941,7 @@ describe :kernel_sprintf, shared: true do
it "cannot be mixed with unnamed style" do
-> {
@method.call("%d %<foo>d", 1, foo: "123")
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
end
@@ -835,13 +961,30 @@ describe :kernel_sprintf, shared: true do
it "cannot be mixed with unnamed style" do
-> {
@method.call("%d %{foo}", 1, foo: "123")
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
- it "raises KeyError when there is no matching key" do
+ it "respects Hash#default when there is no set key" do
+ @method.call("%{foo}", Hash.new(123)).should == "123"
+ @method.call("%{foo}", Hash.new { 123 }).should == "123"
+ end
+
+ it "raises KeyError when Hash#default returns nil" do
-> {
@method.call("%{foo}", {})
- }.should raise_error(KeyError)
+ }.should.raise(KeyError, 'key{foo} not found')
+
+ -> {
+ @method.call("%{foo}", Hash.new(nil))
+ }.should.raise(KeyError, 'key{foo} not found')
+
+ -> {
+ @method.call("%{foo}", Hash.new { nil })
+ }.should.raise(KeyError, 'key{foo} not found')
+ end
+
+ it "accepts a nil value for an existing key" do
+ @method.call("%{foo}", { foo: nil }).should == ""
end
it "converts value to String with to_s" do
@@ -865,23 +1008,27 @@ describe :kernel_sprintf, shared: true do
it "raises a KeyError" do
-> {
@method.call("%<foo>s", @object)
- }.should raise_error(KeyError)
+ }.should.raise(KeyError)
end
it "sets the Hash as the receiver of KeyError" do
-> {
@method.call("%<foo>s", @object)
- }.should raise_error(KeyError) { |err|
- err.receiver.should equal(@object)
+ }.should.raise(KeyError) { |err|
+ err.receiver.should.equal?(@object)
}
end
it "sets the unmatched key as the key of KeyError" do
-> {
@method.call("%<foo>s", @object)
- }.should raise_error(KeyError) { |err|
+ }.should.raise(KeyError) { |err|
err.key.to_s.should == 'foo'
}
end
end
+
+ it "does not raise error when passed more arguments than needed" do
+ sprintf("%s %d %c", "string", 2, "c", []).should == "string 2 c"
+ end
end
diff --git a/spec/ruby/core/kernel/shared/sprintf_encoding.rb b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
index 5ca66b9083..849c95cbb7 100644
--- a/spec/ruby/core/kernel/shared/sprintf_encoding.rb
+++ b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
@@ -1,36 +1,67 @@
+# Keep encoding-related specs in a separate shared example to be able to skip them in IO/File/StringIO specs.
+# It's difficult to check result's encoding in the test after writing to a file/io buffer.
describe :kernel_sprintf_encoding, shared: true do
it "can produce a string with valid encoding" do
string = @method.call("good day %{valid}", valid: "e")
string.encoding.should == Encoding::UTF_8
- string.valid_encoding?.should be_true
+ string.valid_encoding?.should == true
end
it "can produce a string with invalid encoding" do
string = @method.call("good day %{invalid}", invalid: "\x80")
string.encoding.should == Encoding::UTF_8
- string.valid_encoding?.should be_false
+ string.valid_encoding?.should == false
end
it "returns a String in the same encoding as the format String if compatible" do
- string = "%s".force_encoding(Encoding::KOI8_U)
+ string = "%s".dup.force_encoding(Encoding::KOI8_U)
result = @method.call(string, "dogs")
- result.encoding.should equal(Encoding::KOI8_U)
+ result.encoding.should.equal?(Encoding::KOI8_U)
end
it "returns a String in the argument's encoding if format encoding is more restrictive" do
- string = "foo %s".force_encoding(Encoding::US_ASCII)
- argument = "b\303\274r".force_encoding(Encoding::UTF_8)
+ string = "foo %s".dup.force_encoding(Encoding::US_ASCII)
+ argument = "b\303\274r".dup.force_encoding(Encoding::UTF_8)
result = @method.call(string, argument)
- result.encoding.should equal(Encoding::UTF_8)
+ result.encoding.should.equal?(Encoding::UTF_8)
end
- it "raises Encoding::CompatibilityError if both encodings are ASCII compatible and there ano not ASCII characters" do
+ it "raises Encoding::CompatibilityError if both encodings are ASCII compatible and there are not ASCII characters" do
string = "Ä %s".encode('windows-1252')
argument = "Ђ".encode('windows-1251')
-> {
@method.call(string, argument)
- }.should raise_error(Encoding::CompatibilityError)
+ }.should.raise(Encoding::CompatibilityError)
+ end
+
+ describe "%c" do
+ it "supports Unicode characters" do
+ result = @method.call("%c", 1286)
+ result.should == "Ԇ"
+ result.bytes.should == [212, 134]
+
+ result = @method.call("%c", "ش")
+ result.should == "ش"
+ result.bytes.should == [216, 180]
+ end
+
+ it "raises error when a codepoint isn't representable in an encoding of a format string" do
+ format = "%c".encode("ASCII")
+
+ -> {
+ @method.call(format, 1286)
+ }.should.raise(RangeError, /out of char range/)
+ end
+
+ it "uses the encoding of the format string to interpret codepoints" do
+ format = "%c".dup.force_encoding("euc-jp")
+ result = @method.call(format, 9415601)
+
+ result.encoding.should == Encoding::EUC_JP
+ result.should == "é".encode(Encoding::EUC_JP)
+ result.bytes.should == [143, 171, 177]
+ end
end
end
diff --git a/spec/ruby/core/kernel/shared/then.rb b/spec/ruby/core/kernel/shared/then.rb
index b52075371f..c71393bf50 100644
--- a/spec/ruby/core/kernel/shared/then.rb
+++ b/spec/ruby/core/kernel/shared/then.rb
@@ -1,20 +1,20 @@
describe :kernel_then, shared: true do
it "yields self" do
object = Object.new
- object.send(@method) { |o| o.should equal object }
+ object.send(@method) { |o| o.should.equal? object }
end
it "returns the block return value" do
object = Object.new
- object.send(@method) { 42 }.should equal 42
+ object.send(@method) { 42 }.should.equal? 42
end
it "returns a sized Enumerator when no block given" do
object = Object.new
enum = object.send(@method)
- enum.should be_an_instance_of Enumerator
- enum.size.should equal 1
- enum.peek.should equal object
- enum.first.should equal object
+ enum.should.instance_of? Enumerator
+ enum.size.should.equal? 1
+ enum.peek.should.equal? object
+ enum.first.should.equal? object
end
end
diff --git a/spec/ruby/core/kernel/singleton_class_spec.rb b/spec/ruby/core/kernel/singleton_class_spec.rb
index 5dbb4f8c05..7915272937 100644
--- a/spec/ruby/core/kernel/singleton_class_spec.rb
+++ b/spec/ruby/core/kernel/singleton_class_spec.rb
@@ -1,3 +1,6 @@
+# truffleruby_primitives: true
+require_relative '../../spec_helper'
+
describe "Kernel#singleton_class" do
it "returns class extended from an object" do
x = Object.new
@@ -17,11 +20,55 @@ describe "Kernel#singleton_class" do
false.singleton_class.should == FalseClass
end
- it "raises TypeError for Fixnum" do
- -> { 123.singleton_class }.should raise_error(TypeError)
+ it "raises TypeError for Integer" do
+ -> { 123.singleton_class }.should.raise(TypeError, "can't define singleton")
+ end
+
+ it "raises TypeError for Float" do
+ -> { 3.14.singleton_class }.should.raise(TypeError, "can't define singleton")
end
it "raises TypeError for Symbol" do
- -> { :foo.singleton_class }.should raise_error(TypeError)
+ -> { :foo.singleton_class }.should.raise(TypeError, "can't define singleton")
+ end
+
+ it "raises TypeError for a frozen deduplicated String" do
+ -> { (-"string").singleton_class }.should.raise(TypeError, "can't define singleton")
+ -> { a = -"string"; a.singleton_class }.should.raise(TypeError, "can't define singleton")
+ -> { a = "string"; (-a).singleton_class }.should.raise(TypeError, "can't define singleton")
+ end
+
+ it "returns a frozen singleton class if object is frozen" do
+ obj = Object.new
+ obj.freeze
+ obj.singleton_class.frozen?.should == true
+ end
+
+ context "for an IO object with a replaced singleton class" do
+ it "looks up singleton methods from the fresh singleton class after an object instance got a new one" do
+ proxy = -> io { io.foo }
+ if RUBY_ENGINE == 'truffleruby'
+ # We need an inline cache with only this object seen, the best way to do that is to use a Primitive
+ sclass = -> io { Primitive.singleton_class(io) }
+ else
+ sclass = -> io { io.singleton_class }
+ end
+
+ io = File.new(__FILE__)
+ io.define_singleton_method(:foo) { "old" }
+ sclass1 = sclass.call(io)
+ proxy.call(io).should == "old"
+
+ # IO#reopen is the only method which can replace an object's singleton class
+ io2 = File.new(__FILE__)
+ io.reopen(io2)
+ io.define_singleton_method(:foo) { "new" }
+ sclass2 = sclass.call(io)
+ sclass2.should_not.equal?(sclass1)
+ proxy.call(io).should == "new"
+ ensure
+ io2.close
+ io.close
+ end
end
end
diff --git a/spec/ruby/core/kernel/singleton_method_spec.rb b/spec/ruby/core/kernel/singleton_method_spec.rb
index 0bdf125ad8..fe8e23eb02 100644
--- a/spec/ruby/core/kernel/singleton_method_spec.rb
+++ b/spec/ruby/core/kernel/singleton_method_spec.rb
@@ -1,10 +1,10 @@
require_relative '../../spec_helper'
describe "Kernel#singleton_method" do
- it "find a method defined on the singleton class" do
+ it "finds a method defined on the singleton class" do
obj = Object.new
def obj.foo; end
- obj.singleton_method(:foo).should be_an_instance_of(Method)
+ obj.singleton_method(:foo).should.instance_of?(Method)
end
it "returns a Method which can be called" do
@@ -23,7 +23,7 @@ describe "Kernel#singleton_method" do
obj.foo.should == 42
-> {
obj.singleton_method(:foo)
- }.should raise_error(NameError) { |e|
+ }.should.raise(NameError) { |e|
# a NameError and not a NoMethodError
e.class.should == NameError
}
@@ -33,9 +33,53 @@ describe "Kernel#singleton_method" do
obj = Object.new
-> {
obj.singleton_method(:not_existing)
- }.should raise_error(NameError) { |e|
+ }.should.raise(NameError) { |e|
# a NameError and not a NoMethodError
e.class.should == NameError
}
end
+
+ ruby_bug "#20620", ""..."3.4" do
+ it "finds a method defined in a module included in the singleton class" do
+ m = Module.new do
+ def foo
+ :foo
+ end
+ end
+
+ obj = Object.new
+ obj.singleton_class.include(m)
+
+ obj.singleton_method(:foo).should.instance_of?(Method)
+ obj.singleton_method(:foo).call.should == :foo
+ end
+
+ it "finds a method defined in a module prepended in the singleton class" do
+ m = Module.new do
+ def foo
+ :foo
+ end
+ end
+
+ obj = Object.new
+ obj.singleton_class.prepend(m)
+
+ obj.singleton_method(:foo).should.instance_of?(Method)
+ obj.singleton_method(:foo).call.should == :foo
+ end
+
+ it "finds a method defined in a module that an object is extended with" do
+ m = Module.new do
+ def foo
+ :foo
+ end
+ end
+
+ obj = Object.new
+ obj.extend(m)
+
+ obj.singleton_method(:foo).should.instance_of?(Method)
+ obj.singleton_method(:foo).call.should == :foo
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/singleton_methods_spec.rb b/spec/ruby/core/kernel/singleton_methods_spec.rb
index a127a439de..a7f6969519 100644
--- a/spec/ruby/core/kernel/singleton_methods_spec.rb
+++ b/spec/ruby/core/kernel/singleton_methods_spec.rb
@@ -8,33 +8,33 @@ describe :kernel_singleton_methods, shared: true do
end
it "returns the names of module methods for a module" do
- ReflectSpecs::M.singleton_methods(*@object).should include(:ms_pro, :ms_pub)
+ ReflectSpecs::M.singleton_methods(*@object).to_set.should >= Set[:ms_pro, :ms_pub]
end
it "does not return private module methods for a module" do
- ReflectSpecs::M.singleton_methods(*@object).should_not include(:ms_pri)
+ ReflectSpecs::M.singleton_methods(*@object).should_not.include?(:ms_pri)
end
it "returns the names of class methods for a class" do
- ReflectSpecs::A.singleton_methods(*@object).should include(:as_pro, :as_pub)
+ ReflectSpecs::A.singleton_methods(*@object).to_set.should >= Set[:as_pro, :as_pub]
end
it "does not return private class methods for a class" do
- ReflectSpecs::A.singleton_methods(*@object).should_not include(:as_pri)
+ ReflectSpecs::A.singleton_methods(*@object).should_not.include?(:as_pri)
end
it "returns the names of singleton methods for an object" do
- ReflectSpecs.os.singleton_methods(*@object).should include(:os_pro, :os_pub)
+ ReflectSpecs.os.singleton_methods(*@object).to_set.should >= Set[:os_pro, :os_pub]
end
end
describe :kernel_singleton_methods_modules, shared: true do
it "does not return any included methods for a module including a module" do
- ReflectSpecs::N.singleton_methods(*@object).should include(:ns_pro, :ns_pub)
+ ReflectSpecs::N.singleton_methods(*@object).to_set.should >= Set[:ns_pro, :ns_pub]
end
it "does not return any included methods for a class including a module" do
- ReflectSpecs::D.singleton_methods(*@object).should include(:ds_pro, :ds_pub)
+ ReflectSpecs::D.singleton_methods(*@object).to_set.should >= Set[:ds_pro, :ds_pub]
end
it "for a module does not return methods in a module prepended to Module itself" do
@@ -44,7 +44,7 @@ describe :kernel_singleton_methods_modules, shared: true do
ancestors = mod.singleton_class.ancestors
ancestors[0...2].should == [ mod.singleton_class, mod ]
- ancestors.should include(SingletonMethodsSpecs::Prepended)
+ ancestors.should.include?(SingletonMethodsSpecs::Prepended)
# Do not search prepended modules of `Module`, as that's a non-singleton class
mod.singleton_methods.should == []
@@ -53,7 +53,7 @@ end
describe :kernel_singleton_methods_supers, shared: true do
it "returns the names of singleton methods for an object extended with a module" do
- ReflectSpecs.oe.singleton_methods(*@object).should include(:m_pro, :m_pub)
+ ReflectSpecs.oe.singleton_methods(*@object).to_set.should >= Set[:m_pro, :m_pub]
end
it "returns a unique list for an object extended with a module" do
@@ -63,15 +63,15 @@ describe :kernel_singleton_methods_supers, shared: true do
end
it "returns the names of singleton methods for an object extended with two modules" do
- ReflectSpecs.oee.singleton_methods(*@object).should include(:m_pro, :m_pub, :n_pro, :n_pub)
+ ReflectSpecs.oee.singleton_methods(*@object).to_set.should >= Set[:m_pro, :m_pub, :n_pro, :n_pub]
end
it "returns the names of singleton methods for an object extended with a module including a module" do
- ReflectSpecs.oei.singleton_methods(*@object).should include(:n_pro, :n_pub, :m_pro, :m_pub)
+ ReflectSpecs.oei.singleton_methods(*@object).to_set.should >= Set[:n_pro, :n_pub, :m_pro, :m_pub]
end
it "returns the names of inherited singleton methods for a subclass" do
- ReflectSpecs::B.singleton_methods(*@object).should include(:as_pro, :as_pub, :bs_pro, :bs_pub)
+ ReflectSpecs::B.singleton_methods(*@object).to_set.should >= Set[:as_pro, :as_pub, :bs_pro, :bs_pub]
end
it "returns a unique list for a subclass" do
@@ -81,7 +81,7 @@ describe :kernel_singleton_methods_supers, shared: true do
end
it "returns the names of inherited singleton methods for a subclass including a module" do
- ReflectSpecs::C.singleton_methods(*@object).should include(:as_pro, :as_pub, :cs_pro, :cs_pub)
+ ReflectSpecs::C.singleton_methods(*@object).to_set.should >= Set[:as_pro, :as_pub, :cs_pro, :cs_pub]
end
it "returns a unique list for a subclass including a module" do
@@ -91,57 +91,62 @@ describe :kernel_singleton_methods_supers, shared: true do
end
it "returns the names of inherited singleton methods for a subclass of a class including a module" do
- ReflectSpecs::E.singleton_methods(*@object).should include(:ds_pro, :ds_pub, :es_pro, :es_pub)
+ ReflectSpecs::E.singleton_methods(*@object).to_set.should >= Set[:ds_pro, :ds_pub, :es_pro, :es_pub]
end
it "returns the names of inherited singleton methods for a subclass of a class that includes a module, where the subclass also includes a module" do
- ReflectSpecs::F.singleton_methods(*@object).should include(:ds_pro, :ds_pub, :fs_pro, :fs_pub)
+ ReflectSpecs::F.singleton_methods(*@object).to_set.should >= Set[:ds_pro, :ds_pub, :fs_pro, :fs_pub]
end
it "returns the names of inherited singleton methods for a class extended with a module" do
- ReflectSpecs::P.singleton_methods(*@object).should include(:m_pro, :m_pub)
+ ReflectSpecs::P.singleton_methods(*@object).to_set.should >= Set[:m_pro, :m_pub]
end
end
describe :kernel_singleton_methods_private_supers, shared: true do
it "does not return private singleton methods for an object extended with a module" do
- ReflectSpecs.oe.singleton_methods(*@object).should_not include(:m_pri)
+ ReflectSpecs.oe.singleton_methods(*@object).should_not.include?(:m_pri)
end
it "does not return private singleton methods for an object extended with two modules" do
- ReflectSpecs.oee.singleton_methods(*@object).should_not include(:m_pri)
+ ReflectSpecs.oee.singleton_methods(*@object).should_not.include?(:m_pri)
end
it "does not return private singleton methods for an object extended with a module including a module" do
- ReflectSpecs.oei.singleton_methods(*@object).should_not include(:n_pri, :m_pri)
+ ReflectSpecs.oei.singleton_methods(*@object).should_not.include?(:n_pri)
+ ReflectSpecs.oei.singleton_methods(*@object).should_not.include?(:m_pri)
end
it "does not return private singleton methods for a class extended with a module" do
- ReflectSpecs::P.singleton_methods(*@object).should_not include(:m_pri)
+ ReflectSpecs::P.singleton_methods(*@object).should_not.include?(:m_pri)
end
it "does not return private inherited singleton methods for a module including a module" do
- ReflectSpecs::N.singleton_methods(*@object).should_not include(:ns_pri)
+ ReflectSpecs::N.singleton_methods(*@object).should_not.include?(:ns_pri)
end
it "does not return private inherited singleton methods for a class including a module" do
- ReflectSpecs::D.singleton_methods(*@object).should_not include(:ds_pri)
+ ReflectSpecs::D.singleton_methods(*@object).should_not.include?(:ds_pri)
end
it "does not return private inherited singleton methods for a subclass" do
- ReflectSpecs::B.singleton_methods(*@object).should_not include(:as_pri, :bs_pri)
+ ReflectSpecs::B.singleton_methods(*@object).should_not.include?(:as_pri)
+ ReflectSpecs::B.singleton_methods(*@object).should_not.include?(:bs_pri)
end
it "does not return private inherited singleton methods for a subclass including a module" do
- ReflectSpecs::C.singleton_methods(*@object).should_not include(:as_pri, :cs_pri)
+ ReflectSpecs::C.singleton_methods(*@object).should_not.include?(:as_pri)
+ ReflectSpecs::C.singleton_methods(*@object).should_not.include?(:cs_pri)
end
it "does not return private inherited singleton methods for a subclass of a class including a module" do
- ReflectSpecs::E.singleton_methods(*@object).should_not include(:ds_pri, :es_pri)
+ ReflectSpecs::E.singleton_methods(*@object).should_not.include?(:ds_pri)
+ ReflectSpecs::E.singleton_methods(*@object).should_not.include?(:es_pri)
end
it "does not return private inherited singleton methods for a subclass of a class that includes a module, where the subclass also includes a module" do
- ReflectSpecs::F.singleton_methods(*@object).should_not include(:ds_pri, :fs_pri)
+ ReflectSpecs::F.singleton_methods(*@object).should_not.include?(:ds_pri)
+ ReflectSpecs::F.singleton_methods(*@object).should_not.include?(:fs_pri)
end
end
@@ -178,15 +183,17 @@ describe "Kernel#singleton_methods" do
end
it "returns the names of singleton methods of the subclass" do
- ReflectSpecs::B.singleton_methods(false).should include(:bs_pro, :bs_pub)
+ ReflectSpecs::B.singleton_methods(false).to_set.should >= Set[:bs_pro, :bs_pub]
end
it "does not return names of inherited singleton methods for a subclass" do
- ReflectSpecs::B.singleton_methods(false).should_not include(:as_pro, :as_pub)
+ ReflectSpecs::B.singleton_methods(false).should_not.include?(:as_pro)
+ ReflectSpecs::B.singleton_methods(false).should_not.include?(:as_pub)
end
it "does not return the names of inherited singleton methods for a class extended with a module" do
- ReflectSpecs::P.singleton_methods(false).should_not include(:m_pro, :m_pub)
+ ReflectSpecs::P.singleton_methods(false).should_not.include?(:m_pro)
+ ReflectSpecs::P.singleton_methods(false).should_not.include?(:m_pub)
end
end
end
diff --git a/spec/ruby/core/kernel/sleep_spec.rb b/spec/ruby/core/kernel/sleep_spec.rb
index 387dc4787b..61d8cc2380 100644
--- a/spec/ruby/core/kernel/sleep_spec.rb
+++ b/spec/ruby/core/kernel/sleep_spec.rb
@@ -1,20 +1,20 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
+require_relative '../fiber/fixtures/scheduler'
describe "Kernel#sleep" do
it "is a private method" do
- Kernel.should have_private_instance_method(:sleep)
+ Kernel.private_instance_methods(false).should.include?(:sleep)
end
it "returns an Integer" do
- sleep(0.001).should be_kind_of(Integer)
+ sleep(0.001).should.is_a?(Integer)
end
it "accepts a Float" do
sleep(0.001).should >= 0
end
- it "accepts a Fixnum" do
+ it "accepts an Integer" do
sleep(0).should >= 0
end
@@ -22,17 +22,19 @@ describe "Kernel#sleep" do
sleep(Rational(1, 999)).should >= 0
end
- it "raises an ArgumentError when passed a negative duration" do
- -> { sleep(-0.1) }.should raise_error(ArgumentError)
- -> { sleep(-1) }.should raise_error(ArgumentError)
+ it "accepts any Object that responds to divmod" do
+ o = Object.new
+ def o.divmod(*); [0, 0.001]; end
+ sleep(o).should >= 0
end
- it "raises a TypeError when passed nil" do
- -> { sleep(nil) }.should raise_error(TypeError)
+ it "raises an ArgumentError when passed a negative duration" do
+ -> { sleep(-0.1) }.should.raise(ArgumentError)
+ -> { sleep(-1) }.should.raise(ArgumentError)
end
it "raises a TypeError when passed a String" do
- -> { sleep('2') }.should raise_error(TypeError)
+ -> { sleep('2') }.should.raise(TypeError)
end
it "pauses execution indefinitely if not given a duration" do
@@ -49,6 +51,66 @@ describe "Kernel#sleep" do
t.wakeup
t.value.should == 5
end
+
+ it "sleeps with nanosecond precision" do
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ 100.times do
+ sleep(0.0001)
+ end
+ end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+
+ actual_duration = end_time - start_time
+ actual_duration.should > 0.01 # 100 * 0.0001 => 0.01
+ end
+
+ it "accepts a nil duration" do
+ running = false
+ t = Thread.new do
+ running = true
+ sleep(nil)
+ 5
+ end
+
+ Thread.pass until running
+ Thread.pass while t.status and t.status != "sleep"
+
+ t.wakeup
+ t.value.should == 5
+ end
+
+ context "Kernel.sleep with Fiber scheduler" do
+ before :each do
+ Fiber.set_scheduler(FiberSpecs::LoggingScheduler.new)
+ end
+
+ after :each do
+ Fiber.set_scheduler(nil)
+ end
+
+ it "calls the scheduler without arguments when no duration is given" do
+ sleeper = Fiber.new(blocking: false) do
+ sleep
+ end
+ sleeper.resume
+ Fiber.scheduler.events.should == [{ event: :kernel_sleep, fiber: sleeper, args: [] }]
+ end
+
+ it "calls the scheduler with the given duration" do
+ sleeper = Fiber.new(blocking: false) do
+ sleep(0.01)
+ end
+ sleeper.resume
+ Fiber.scheduler.events.should == [{ event: :kernel_sleep, fiber: sleeper, args: [0.01] }]
+ end
+
+ it "does not call the scheduler if the fiber is blocking" do
+ sleeper = Fiber.new(blocking: true) do
+ sleep(0.01)
+ end
+ sleeper.resume
+ Fiber.scheduler.events.should == []
+ end
+ end
end
describe "Kernel.sleep" do
diff --git a/spec/ruby/core/kernel/spawn_spec.rb b/spec/ruby/core/kernel/spawn_spec.rb
index ba05b629d5..3432fc31da 100644
--- a/spec/ruby/core/kernel/spawn_spec.rb
+++ b/spec/ruby/core/kernel/spawn_spec.rb
@@ -6,7 +6,7 @@ require_relative 'fixtures/classes'
# run here as it is redundant and takes too long for little gain.
describe "Kernel#spawn" do
it "is a private method" do
- Kernel.should have_private_instance_method(:spawn)
+ Kernel.private_instance_methods(false).should.include?(:spawn)
end
it "executes the given command" do
diff --git a/spec/ruby/core/kernel/sprintf_spec.rb b/spec/ruby/core/kernel/sprintf_spec.rb
index 7adf71be76..5a4a90ff7a 100644
--- a/spec/ruby/core/kernel/sprintf_spec.rb
+++ b/spec/ruby/core/kernel/sprintf_spec.rb
@@ -3,22 +3,62 @@ require_relative 'fixtures/classes'
require_relative 'shared/sprintf'
require_relative 'shared/sprintf_encoding'
+describe :kernel_sprintf_to_str, shared: true do
+ it "calls #to_str to convert the format object to a String" do
+ obj = mock('format string')
+ obj.should_receive(:to_str).and_return("to_str: %i")
+ @method.call(obj, 42).should == "to_str: 42"
+ end
+end
+
describe "Kernel#sprintf" do
it_behaves_like :kernel_sprintf, -> format, *args {
- sprintf(format, *args)
+ r = nil
+ -> {
+ r = sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
}
it_behaves_like :kernel_sprintf_encoding, -> format, *args {
- sprintf(format, *args)
+ r = nil
+ -> {
+ r = sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
+ }
+
+ it_behaves_like :kernel_sprintf_to_str, -> format, *args {
+ r = nil
+ -> {
+ r = sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
}
end
describe "Kernel.sprintf" do
it_behaves_like :kernel_sprintf, -> format, *args {
- Kernel.sprintf(format, *args)
+ r = nil
+ -> {
+ r = Kernel.sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
}
it_behaves_like :kernel_sprintf_encoding, -> format, *args {
- Kernel.sprintf(format, *args)
+ r = nil
+ -> {
+ r = Kernel.sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
+ }
+
+ it_behaves_like :kernel_sprintf_to_str, -> format, *args {
+ r = nil
+ -> {
+ r = Kernel.sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
}
end
diff --git a/spec/ruby/core/kernel/srand_spec.rb b/spec/ruby/core/kernel/srand_spec.rb
index 0985c76cb0..cbc3a7f4b8 100644
--- a/spec/ruby/core/kernel/srand_spec.rb
+++ b/spec/ruby/core/kernel/srand_spec.rb
@@ -1,9 +1,17 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
-describe "Kernel.srand" do
+describe "Kernel#srand" do
+ before :each do
+ @seed = srand
+ end
+
+ after :each do
+ srand(@seed)
+ end
+
it "is a private method" do
- Kernel.should have_private_instance_method(:srand)
+ Kernel.private_instance_methods(false).should.include?(:srand)
end
it "returns the previous seed value" do
@@ -11,8 +19,8 @@ describe "Kernel.srand" do
srand(20).should == 10
end
- it "returns the previous seed value on the first call" do
- ruby_exe('p srand(10)', options: '--disable-gems').chomp.should =~ /\A\d+\z/
+ it "returns the system-initialized seed value on the first call" do
+ ruby_exe('print srand(10)', options: '--disable-gems').should =~ /\A\d+\z/
end
it "seeds the RNG correctly and repeatably" do
@@ -23,7 +31,7 @@ describe "Kernel.srand" do
end
it "defaults number to a random value" do
- -> { srand }.should_not raise_error
+ -> { srand }.should_not.raise
srand.should_not == 0
end
@@ -37,7 +45,7 @@ describe "Kernel.srand" do
srand.should == -17
end
- it "accepts a Bignum as a seed" do
+ it "accepts an Integer as a seed" do
srand(0x12345678901234567890)
srand.should == 0x12345678901234567890
end
@@ -52,14 +60,14 @@ describe "Kernel.srand" do
end
it "raises a TypeError when passed nil" do
- -> { srand(nil) }.should raise_error(TypeError)
+ -> { srand(nil) }.should.raise(TypeError)
end
it "raises a TypeError when passed a String" do
- -> { srand("7") }.should raise_error(TypeError)
+ -> { srand("7") }.should.raise(TypeError)
end
end
-describe "Kernel#srand" do
+describe "Kernel.srand" do
it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/core/kernel/sub_spec.rb b/spec/ruby/core/kernel/sub_spec.rb
index 9130bd159c..b175e371dc 100644
--- a/spec/ruby/core/kernel/sub_spec.rb
+++ b/spec/ruby/core/kernel/sub_spec.rb
@@ -6,13 +6,13 @@ require_relative 'fixtures/classes'
ruby_version_is ""..."1.9" do
describe "Kernel#sub" do
it "is a private method" do
- Kernel.should have_private_instance_method(:sub)
+ Kernel.private_instance_methods(false).should.include?(:sub)
end
end
describe "Kernel#sub!" do
it "is a private method" do
- Kernel.should have_private_instance_method(:sub!)
+ Kernel.private_instance_methods(false).should.include?(:sub!)
end
end
diff --git a/spec/ruby/core/kernel/syscall_spec.rb b/spec/ruby/core/kernel/syscall_spec.rb
index 32d07b3ae2..63943cdad5 100644
--- a/spec/ruby/core/kernel/syscall_spec.rb
+++ b/spec/ruby/core/kernel/syscall_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "Kernel#syscall" do
it "is a private method" do
- Kernel.should have_private_instance_method(:syscall)
+ Kernel.private_instance_methods(false).should.include?(:syscall)
end
end
diff --git a/spec/ruby/core/kernel/system_spec.rb b/spec/ruby/core/kernel/system_spec.rb
index 696e6ae3d7..b24956104a 100644
--- a/spec/ruby/core/kernel/system_spec.rb
+++ b/spec/ruby/core/kernel/system_spec.rb
@@ -5,14 +5,14 @@ describe :kernel_system, shared: true do
it "executes the specified command in a subprocess" do
-> { @object.system("echo a") }.should output_to_fd("a\n")
- $?.should be_an_instance_of Process::Status
+ $?.should.instance_of? Process::Status
$?.should.success?
end
it "returns true when the command exits with a zero exit status" do
@object.system(ruby_cmd('exit 0')).should == true
- $?.should be_an_instance_of Process::Status
+ $?.should.instance_of? Process::Status
$?.should.success?
$?.exitstatus.should == 0
end
@@ -20,26 +20,24 @@ describe :kernel_system, shared: true do
it "returns false when the command exits with a non-zero exit status" do
@object.system(ruby_cmd('exit 1')).should == false
- $?.should be_an_instance_of Process::Status
+ $?.should.instance_of? Process::Status
$?.should_not.success?
$?.exitstatus.should == 1
end
- ruby_version_is "2.6" do
- it "raises RuntimeError when `exception: true` is given and the command exits with a non-zero exit status" do
- -> { @object.system(ruby_cmd('exit 1'), exception: true) }.should raise_error(RuntimeError)
- end
+ it "raises RuntimeError when `exception: true` is given and the command exits with a non-zero exit status" do
+ -> { @object.system(ruby_cmd('exit 1'), exception: true) }.should.raise(RuntimeError)
+ end
- it "raises Errno::ENOENT when `exception: true` is given and the specified command does not exist" do
- -> { @object.system('feature_14386', exception: true) }.should raise_error(Errno::ENOENT)
- end
+ it "raises Errno::ENOENT when `exception: true` is given and the specified command does not exist" do
+ -> { @object.system('feature_14386', exception: true) }.should.raise(Errno::ENOENT)
end
it "returns nil when command execution fails" do
- @object.system("sad").should be_nil
+ @object.system("sad").should == nil
- $?.should be_an_instance_of Process::Status
- $?.pid.should be_kind_of(Integer)
+ $?.should.instance_of? Process::Status
+ $?.pid.should.is_a?(Integer)
$?.should_not.success?
end
@@ -66,6 +64,23 @@ describe :kernel_system, shared: true do
end
end
+ platform_is_not :windows do
+ before :each do
+ require 'tmpdir'
+ @shell_command = File.join(Dir.mktmpdir, "noshebang.cmd")
+ File.write(@shell_command, %[echo "$PATH"\n], perm: 0o700)
+ end
+
+ after :each do
+ File.unlink(@shell_command)
+ Dir.rmdir(File.dirname(@shell_command))
+ end
+
+ it "executes with `sh` if the command is executable but not binary and there is no shebang" do
+ -> { @object.system(@shell_command) }.should output_to_fd(ENV['PATH'] + "\n")
+ end
+ end
+
before :each do
ENV['TEST_SH_EXPANSION'] = 'foo'
@shell_var = '$TEST_SH_EXPANSION'
@@ -106,7 +121,7 @@ end
describe "Kernel#system" do
it "is a private method" do
- Kernel.should have_private_instance_method(:system)
+ Kernel.private_instance_methods(false).should.include?(:system)
end
it_behaves_like :kernel_system, :system, KernelSpecs::Method.new
diff --git a/spec/ruby/core/kernel/taint_spec.rb b/spec/ruby/core/kernel/taint_spec.rb
index 060e73963e..9a2efbaea0 100644
--- a/spec/ruby/core/kernel/taint_spec.rb
+++ b/spec/ruby/core/kernel/taint_spec.rb
@@ -2,46 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Kernel#taint" do
- ruby_version_is ''...'2.7' do
- it "returns self" do
- o = Object.new
- o.taint.should equal(o)
- end
-
- it "sets the tainted bit" do
- o = Object.new
- o.taint
- o.should.tainted?
- end
-
- it "raises FrozenError on an untainted, frozen object" do
- o = Object.new.freeze
- -> { o.taint }.should raise_error(FrozenError)
- end
-
- it "does not raise an error on a tainted, frozen object" do
- o = Object.new.taint.freeze
- o.taint.should equal(o)
- end
-
- it "has no effect on immediate values" do
- [nil, true, false].each do |v|
- v.taint
- v.should_not.tainted?
- end
- end
-
- it "no raises a RuntimeError on symbols" do
- v = :sym
- -> { v.taint }.should_not raise_error(RuntimeError)
- v.should_not.tainted?
- end
-
- it "no raises error on fixnum values" do
- [1].each do |v|
- -> { v.taint }.should_not raise_error(RuntimeError)
- v.should_not.tainted?
- end
- end
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:taint)
end
end
diff --git a/spec/ruby/core/kernel/tainted_spec.rb b/spec/ruby/core/kernel/tainted_spec.rb
index dbd6bc939a..837eb1dafb 100644
--- a/spec/ruby/core/kernel/tainted_spec.rb
+++ b/spec/ruby/core/kernel/tainted_spec.rb
@@ -2,13 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Kernel#tainted?" do
- ruby_version_is ''...'2.7' do
- it "returns true if Object is tainted" do
- o = mock('o')
- p = mock('p')
- p.taint
- o.should_not.tainted?
- p.should.tainted?
- end
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:tainted?)
end
end
diff --git a/spec/ruby/core/kernel/tap_spec.rb b/spec/ruby/core/kernel/tap_spec.rb
index f7720a6dc7..453b9b84d5 100644
--- a/spec/ruby/core/kernel/tap_spec.rb
+++ b/spec/ruby/core/kernel/tap_spec.rb
@@ -4,10 +4,10 @@ require_relative 'fixtures/classes'
describe "Kernel#tap" do
it "always yields self and returns self" do
a = KernelSpecs::A.new
- a.tap{|o| o.should equal(a); 42}.should equal(a)
+ a.tap{|o| o.should.equal?(a); 42}.should.equal?(a)
end
it "raises a LocalJumpError when no block given" do
- -> { 3.tap }.should raise_error(LocalJumpError)
+ -> { 3.tap }.should.raise(LocalJumpError)
end
end
diff --git a/spec/ruby/core/kernel/test_spec.rb b/spec/ruby/core/kernel/test_spec.rb
index abb365aed2..30717dfd98 100644
--- a/spec/ruby/core/kernel/test_spec.rb
+++ b/spec/ruby/core/kernel/test_spec.rb
@@ -3,12 +3,12 @@ require_relative 'fixtures/classes'
describe "Kernel#test" do
before :all do
- @file = File.dirname(__FILE__) + '/fixtures/classes.rb'
- @dir = File.dirname(__FILE__) + '/fixtures'
+ @file = __dir__ + '/fixtures/classes.rb'
+ @dir = __dir__ + '/fixtures'
end
it "is a private method" do
- Kernel.should have_private_instance_method(:test)
+ Kernel.private_instance_methods(false).should.include?(:test)
end
it "returns true when passed ?f if the argument is a regular file" do
@@ -28,7 +28,7 @@ describe "Kernel#test" do
link = tmp("file_symlink.lnk")
File.symlink(@file, link)
begin
- Kernel.test(?l, link).should be_true
+ Kernel.test(?l, link).should == true
ensure
rm_r link
end
@@ -36,11 +36,11 @@ describe "Kernel#test" do
end
it "returns true when passed ?r if the argument is readable by the effective uid" do
- Kernel.test(?r, @file).should be_true
+ Kernel.test(?r, @file).should == true
end
it "returns true when passed ?R if the argument is readable by the real uid" do
- Kernel.test(?R, @file).should be_true
+ Kernel.test(?R, @file).should == true
end
context "writable test" do
@@ -54,11 +54,11 @@ describe "Kernel#test" do
end
it "returns true when passed ?w if the argument is readable by the effective uid" do
- Kernel.test(?w, @tmp_file).should be_true
+ Kernel.test(?w, @tmp_file).should == true
end
it "returns true when passed ?W if the argument is readable by the real uid" do
- Kernel.test(?W, @tmp_file).should be_true
+ Kernel.test(?W, @tmp_file).should == true
end
end
diff --git a/spec/ruby/core/kernel/then_spec.rb b/spec/ruby/core/kernel/then_spec.rb
index fa896b52b1..8109a2960a 100644
--- a/spec/ruby/core/kernel/then_spec.rb
+++ b/spec/ruby/core/kernel/then_spec.rb
@@ -1,8 +1,6 @@
require_relative '../../spec_helper'
require_relative 'shared/then'
-ruby_version_is "2.6" do
- describe "Kernel#then" do
- it_behaves_like :kernel_then, :then
- end
+describe "Kernel#then" do
+ it_behaves_like :kernel_then, :then
end
diff --git a/spec/ruby/core/kernel/throw_spec.rb b/spec/ruby/core/kernel/throw_spec.rb
index 64bfccb413..1086126176 100644
--- a/spec/ruby/core/kernel/throw_spec.rb
+++ b/spec/ruby/core/kernel/throw_spec.rb
@@ -7,7 +7,7 @@ describe "Kernel.throw" do
:value
throw :blah
fail("throw didn't transfer the control")
- end.should be_nil
+ end.should == nil
end
it "transfers control to the innermost catch block waiting for the same symbol" do
@@ -42,11 +42,11 @@ describe "Kernel.throw" do
end
it "raises an ArgumentError if there is no catch block for the symbol" do
- -> { throw :blah }.should raise_error(ArgumentError)
+ -> { throw :blah }.should.raise(ArgumentError)
end
it "raises an UncaughtThrowError if there is no catch block for the symbol" do
- -> { throw :blah }.should raise_error(UncaughtThrowError)
+ -> { throw :blah }.should.raise(UncaughtThrowError)
end
it "raises ArgumentError if 3 or more arguments provided" do
@@ -54,13 +54,13 @@ describe "Kernel.throw" do
catch :blah do
throw :blah, :return_value, 2
end
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
-> {
catch :blah do
throw :blah, :return_value, 2, 3, 4, 5
end
- }.should raise_error(ArgumentError)
+ }.should.raise(ArgumentError)
end
it "can throw an object" do
@@ -69,12 +69,12 @@ describe "Kernel.throw" do
catch obj do
throw obj
end
- }.should_not raise_error(NameError)
+ }.should_not.raise(NameError)
end
end
describe "Kernel#throw" do
it "is a private method" do
- Kernel.should have_private_instance_method(:throw)
+ Kernel.private_instance_methods(false).should.include?(:throw)
end
end
diff --git a/spec/ruby/core/kernel/to_s_spec.rb b/spec/ruby/core/kernel/to_s_spec.rb
index 64b40f46e5..ea4b00151e 100644
--- a/spec/ruby/core/kernel/to_s_spec.rb
+++ b/spec/ruby/core/kernel/to_s_spec.rb
@@ -5,14 +5,4 @@ describe "Kernel#to_s" do
it "returns a String containing the name of self's class" do
Object.new.to_s.should =~ /Object/
end
-
- ruby_version_is ''...'2.7' do
- it "returns a tainted result if self is tainted" do
- Object.new.taint.to_s.tainted?.should be_true
- end
-
- it "returns an untrusted result if self is untrusted" do
- Object.new.untrust.to_s.untrusted?.should be_true
- end
- end
end
diff --git a/spec/ruby/core/kernel/trace_var_spec.rb b/spec/ruby/core/kernel/trace_var_spec.rb
index 3c84aa5e60..150c3da266 100644
--- a/spec/ruby/core/kernel/trace_var_spec.rb
+++ b/spec/ruby/core/kernel/trace_var_spec.rb
@@ -14,7 +14,7 @@ describe "Kernel#trace_var" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:trace_var)
+ Kernel.private_instance_methods(false).should.include?(:trace_var)
end
it "hooks assignments to a global variable" do
@@ -49,6 +49,6 @@ describe "Kernel#trace_var" do
it "raises ArgumentError if no block or proc is provided" do
-> do
trace_var :$Kernel_trace_var_global
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
end
end
diff --git a/spec/ruby/core/kernel/trap_spec.rb b/spec/ruby/core/kernel/trap_spec.rb
index 465aacb0fb..8f44f3af4b 100644
--- a/spec/ruby/core/kernel/trap_spec.rb
+++ b/spec/ruby/core/kernel/trap_spec.rb
@@ -1,12 +1,9 @@
require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
describe "Kernel#trap" do
it "is a private method" do
- Kernel.should have_private_instance_method(:trap)
+ Kernel.private_instance_methods(false).should.include?(:trap)
end
-end
-describe "Kernel.trap" do
- it "needs to be reviewed for spec completeness"
+ # Behaviour is specified for Signal.trap
end
diff --git a/spec/ruby/core/kernel/trust_spec.rb b/spec/ruby/core/kernel/trust_spec.rb
index 1d209ea1dc..ef3fa9a3e1 100644
--- a/spec/ruby/core/kernel/trust_spec.rb
+++ b/spec/ruby/core/kernel/trust_spec.rb
@@ -2,26 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Kernel#trust" do
- ruby_version_is ''...'2.7' do
- it "returns self" do
- o = Object.new
- o.trust.should equal(o)
- end
-
- it "clears the untrusted bit" do
- o = Object.new.untrust
- o.trust
- o.should_not.untrusted?
- end
-
- it "raises FrozenError on an untrusted, frozen object" do
- o = Object.new.untrust.freeze
- -> { o.trust }.should raise_error(FrozenError)
- end
-
- it "does not raise an error on a trusted, frozen object" do
- o = Object.new.freeze
- o.trust.should equal(o)
- end
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:trust)
end
end
diff --git a/spec/ruby/core/kernel/untaint_spec.rb b/spec/ruby/core/kernel/untaint_spec.rb
index 171d32b356..47e8544bd4 100644
--- a/spec/ruby/core/kernel/untaint_spec.rb
+++ b/spec/ruby/core/kernel/untaint_spec.rb
@@ -2,26 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Kernel#untaint" do
- ruby_version_is ''...'2.7' do
- it "returns self" do
- o = Object.new
- o.untaint.should equal(o)
- end
-
- it "clears the tainted bit" do
- o = Object.new.taint
- o.untaint
- o.should_not.tainted?
- end
-
- it "raises FrozenError on a tainted, frozen object" do
- o = Object.new.taint.freeze
- -> { o.untaint }.should raise_error(FrozenError)
- end
-
- it "does not raise an error on an untainted, frozen object" do
- o = Object.new.freeze
- o.untaint.should equal(o)
- end
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:untaint)
end
end
diff --git a/spec/ruby/core/kernel/untrace_var_spec.rb b/spec/ruby/core/kernel/untrace_var_spec.rb
index 1925a3a836..8b219801c8 100644
--- a/spec/ruby/core/kernel/untrace_var_spec.rb
+++ b/spec/ruby/core/kernel/untrace_var_spec.rb
@@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "Kernel#untrace_var" do
it "is a private method" do
- Kernel.should have_private_instance_method(:untrace_var)
+ Kernel.private_instance_methods(false).should.include?(:untrace_var)
end
end
diff --git a/spec/ruby/core/kernel/untrust_spec.rb b/spec/ruby/core/kernel/untrust_spec.rb
index fca7c9ea47..8787ab3fc9 100644
--- a/spec/ruby/core/kernel/untrust_spec.rb
+++ b/spec/ruby/core/kernel/untrust_spec.rb
@@ -2,26 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Kernel#untrust" do
- ruby_version_is ''...'2.7' do
- it "returns self" do
- o = Object.new
- o.untrust.should equal(o)
- end
-
- it "sets the untrusted bit" do
- o = Object.new
- o.untrust
- o.should.untrusted?
- end
-
- it "raises FrozenError on a trusted, frozen object" do
- o = Object.new.freeze
- -> { o.untrust }.should raise_error(FrozenError)
- end
-
- it "does not raise an error on an untrusted, frozen object" do
- o = Object.new.untrust.freeze
- o.untrust.should equal(o)
- end
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:untrust)
end
end
diff --git a/spec/ruby/core/kernel/untrusted_spec.rb b/spec/ruby/core/kernel/untrusted_spec.rb
index 65cbffa3ad..29261be9c4 100644
--- a/spec/ruby/core/kernel/untrusted_spec.rb
+++ b/spec/ruby/core/kernel/untrusted_spec.rb
@@ -2,29 +2,7 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Kernel#untrusted?" do
- ruby_version_is ''...'2.7' do
- it "returns the untrusted status of an object" do
- o = mock('o')
- o.should_not.untrusted?
- o.untrust
- o.should.untrusted?
- end
-
- it "has no effect on immediate values" do
- a = nil
- b = true
- c = false
- a.untrust
- b.untrust
- c.untrust
- a.should_not.untrusted?
- b.should_not.untrusted?
- c.should_not.untrusted?
- end
-
- it "has effect on immediate values" do
- d = 1
- -> { d.untrust }.should_not raise_error(RuntimeError)
- end
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:untrusted?)
end
end
diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb
index 79e78dd4a3..189129dd31 100644
--- a/spec/ruby/core/kernel/warn_spec.rb
+++ b/spec/ruby/core/kernel/warn_spec.rb
@@ -14,10 +14,10 @@ describe "Kernel#warn" do
end
it "is a private method" do
- Kernel.should have_private_instance_method(:warn)
+ Kernel.private_instance_methods(false).should.include?(:warn)
end
- it "requires multiple arguments" do
+ it "accepts multiple arguments" do
Kernel.method(:warn).arity.should < 0
end
@@ -107,13 +107,63 @@ describe "Kernel#warn" do
ruby_exe(file, options: "--disable-gems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n"
end
- ruby_version_is "2.6" do
- it "shows the caller of #require and not #require itself with RubyGems loaded" do
- file = fixture(__FILE__ , "warn_require_caller.rb")
- ruby_exe(file, options: "-rrubygems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n"
+ it "shows the caller of #require and not #require itself with RubyGems loaded" do
+ file = fixture(__FILE__ , "warn_require_caller.rb")
+ ruby_exe(file, options: "-rrubygems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n"
+ end
+
+ it "doesn't show the caller when the uplevel is `nil`" do
+ w = KernelSpecs::WarnInNestedCall.new
+
+ -> { w.f4("foo", nil) }.should output(nil, "foo\n")
+ end
+
+ guard -> { Kernel.instance_method(:tap).source_location } do
+ it "skips <internal: core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ file = fixture(__FILE__ , "warn_core_method.rb")
+ n = 9
+ ruby_exe(file, options: "--disable-gems", args: "2>&1").lines.should == [
+ "#{file}:#{n+0}: warning: use X instead\n",
+ "#{file}:#{n+1}: warning: use X instead\n",
+ "#{file}:#{n+2}: warning: use X instead\n",
+ "#{file}:#{n+4}: warning: use X instead\n",
+ ]
end
end
+ it "accepts :category keyword with a symbol" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: :deprecated)
+ }.should output(nil, "message\n")
+ end
+
+ it "accepts :category keyword with nil" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: nil)
+ }.should output(nil, "message\n")
+ end
+
+ it "accepts :category keyword with object convertible to symbol" do
+ o = Object.new
+ def o.to_sym; :deprecated; end
+ -> {
+ $VERBOSE = true
+ warn("message", category: o)
+ }.should output(nil, "message\n")
+ end
+
+ it "raises if :category keyword is not nil and not convertible to symbol" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: Object.new)
+ }.should.raise(TypeError)
+ end
+
it "converts first arg using to_s" do
w = KernelSpecs::WarnInNestedCall.new
@@ -144,19 +194,19 @@ describe "Kernel#warn" do
end
it "raises ArgumentError if passed negative value" do
- -> { warn "", uplevel: -2 }.should raise_error(ArgumentError)
- -> { warn "", uplevel: -100 }.should raise_error(ArgumentError)
+ -> { warn "", uplevel: -2 }.should.raise(ArgumentError)
+ -> { warn "", uplevel: -100 }.should.raise(ArgumentError)
end
it "raises ArgumentError if passed -1" do
- -> { warn "", uplevel: -1 }.should raise_error(ArgumentError)
+ -> { warn "", uplevel: -1 }.should.raise(ArgumentError)
end
it "raises TypeError if passed not Integer" do
- -> { warn "", uplevel: "" }.should raise_error(TypeError)
- -> { warn "", uplevel: [] }.should raise_error(TypeError)
- -> { warn "", uplevel: {} }.should raise_error(TypeError)
- -> { warn "", uplevel: Object.new }.should raise_error(TypeError)
+ -> { warn "", uplevel: "" }.should.raise(TypeError)
+ -> { warn "", uplevel: [] }.should.raise(TypeError)
+ -> { warn "", uplevel: {} }.should.raise(TypeError)
+ -> { warn "", uplevel: Object.new }.should.raise(TypeError)
end
end
@@ -165,4 +215,84 @@ describe "Kernel#warn" do
-> { warn(**h) }.should_not complain(verbose: true)
-> { warn('foo', **h) }.should complain("foo\n")
end
+
+ it "calls Warning.warn without keyword arguments if Warning.warn does not accept keyword arguments" do
+ verbose = $VERBOSE
+ $VERBOSE = false
+ class << Warning
+ alias_method :_warn, :warn
+ def warn(message)
+ ScratchPad.record(message)
+ end
+ end
+
+ begin
+ ScratchPad.clear
+ Kernel.warn("Chunky bacon!")
+ ScratchPad.recorded.should == "Chunky bacon!\n"
+
+ Kernel.warn("Deprecated bacon!", category: :deprecated)
+ ScratchPad.recorded.should == "Deprecated bacon!\n"
+ ensure
+ class << Warning
+ remove_method :warn
+ alias_method :warn, :_warn
+ remove_method :_warn
+ end
+ $VERBOSE = verbose
+ end
+ end
+
+ it "calls Warning.warn with category: nil if Warning.warn accepts keyword arguments" do
+ Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil)
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ Kernel.warn("Chunky bacon!")
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+
+ it "calls Warning.warn with given category keyword converted to a symbol" do
+ Warning.should_receive(:warn).with("Chunky bacon!\n", category: :deprecated)
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ Kernel.warn("Chunky bacon!", category: 'deprecated')
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+
+ it "does not call Warning.warn if self is the Warning module" do
+ # RubyGems redefines Kernel#warn so we need to use a subprocess and disable RubyGems here
+ code = <<-RUBY
+ def Warning.warn(*args, **kwargs)
+ raise 'should not be called'
+ end
+ Kernel.instance_method(:warn).bind(Warning).call('Kernel#warn spec edge case')
+ RUBY
+ out = ruby_exe(code, args: "2>&1", options: "--disable-gems")
+ out.should == "Kernel#warn spec edge case\n"
+ $?.should.success?
+ end
+
+ it "avoids recursion if Warning#warn is redefined and calls super" do
+ # This works because of the spec above, which is the workaround for it.
+ # Note that redefining Warning#warn is a mistake which would naturally end in infinite recursion,
+ # Warning.extend Module.new { def warn } should be used instead.
+ # RubyGems redefines Kernel#warn so we need to use a subprocess and disable RubyGems here
+ code = <<-RUBY
+ module Warning
+ def warn(*args, **kwargs)
+ super
+ end
+ end
+ warn "avoid infinite recursion"
+ RUBY
+ out = ruby_exe(code, args: "2>&1", options: "--disable-gems")
+ out.should == "avoid infinite recursion\n"
+ $?.should.success?
+ end
end
diff --git a/spec/ruby/core/main/define_method_spec.rb b/spec/ruby/core/main/define_method_spec.rb
index d85c5e8119..5279d078c3 100644
--- a/spec/ruby/core/main/define_method_spec.rb
+++ b/spec/ruby/core/main/define_method_spec.rb
@@ -14,15 +14,15 @@ describe "main#define_method" do
it 'creates a public method in TOPLEVEL_BINDING' do
eval @code, TOPLEVEL_BINDING
- Object.should have_method :boom
+ Object.should.respond_to? :boom
end
it 'creates a public method in script binding' do
eval @code, script_binding
- Object.should have_method :boom
+ Object.should.respond_to? :boom
end
it 'returns the method name as symbol' do
- eval(@code, TOPLEVEL_BINDING).should equal :boom
+ eval(@code, TOPLEVEL_BINDING).should.equal? :boom
end
end
diff --git a/spec/ruby/core/main/fixtures/classes.rb b/spec/ruby/core/main/fixtures/classes.rb
index 6aba948ce0..757cee4e4a 100644
--- a/spec/ruby/core/main/fixtures/classes.rb
+++ b/spec/ruby/core/main/fixtures/classes.rb
@@ -13,6 +13,14 @@ def main_public_method
end
public :main_public_method
+def main_public_method2
+end
+public :main_public_method2
+
def main_private_method
end
private :main_private_method
+
+def main_private_method2
+end
+private :main_private_method2
diff --git a/spec/ruby/core/main/fixtures/using.rb b/spec/ruby/core/main/fixtures/using.rb
new file mode 100644
index 0000000000..30713ef309
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/using.rb
@@ -0,0 +1 @@
+using Module.new
diff --git a/spec/ruby/core/main/fixtures/using_in_main.rb b/spec/ruby/core/main/fixtures/using_in_main.rb
new file mode 100644
index 0000000000..a4a71c89cc
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/using_in_main.rb
@@ -0,0 +1,5 @@
+MAIN = self
+
+module X
+ MAIN.send(:using, Module.new)
+end
diff --git a/spec/ruby/core/main/fixtures/using_in_method.rb b/spec/ruby/core/main/fixtures/using_in_method.rb
new file mode 100644
index 0000000000..d9ea2e9ef0
--- /dev/null
+++ b/spec/ruby/core/main/fixtures/using_in_method.rb
@@ -0,0 +1,5 @@
+def foo
+ using Module.new
+end
+
+foo
diff --git a/spec/ruby/core/main/include_spec.rb b/spec/ruby/core/main/include_spec.rb
index 9f5a5f54ea..09f8d4d3b5 100644
--- a/spec/ruby/core/main/include_spec.rb
+++ b/spec/ruby/core/main/include_spec.rb
@@ -4,13 +4,13 @@ require_relative 'fixtures/classes'
describe "main#include" do
it "includes the given Module in Object" do
eval "include MainSpecs::Module", TOPLEVEL_BINDING
- Object.ancestors.should include(MainSpecs::Module)
+ Object.ancestors.should.include?(MainSpecs::Module)
end
context "in a file loaded with wrapping" do
it "includes the given Module in the load wrapper" do
load(File.expand_path("../fixtures/wrapped_include.rb", __FILE__), true)
- Object.ancestors.should_not include(MainSpecs::WrapIncludeModule)
+ Object.ancestors.should_not.include?(MainSpecs::WrapIncludeModule)
end
end
end
diff --git a/spec/ruby/core/main/private_spec.rb b/spec/ruby/core/main/private_spec.rb
index e34e0c7b7b..56a39ae3c1 100644
--- a/spec/ruby/core/main/private_spec.rb
+++ b/spec/ruby/core/main/private_spec.rb
@@ -4,20 +4,39 @@ require_relative 'fixtures/classes'
describe "main#private" do
after :each do
Object.send(:public, :main_public_method)
+ Object.send(:public, :main_public_method2)
end
- it "sets the visibility of the given method to private" do
- eval "private :main_public_method", TOPLEVEL_BINDING
- Object.should have_private_method(:main_public_method)
+ context "when single argument is passed and it is not an array" do
+ it "sets the visibility of the given methods to private" do
+ eval "private :main_public_method", TOPLEVEL_BINDING
+ Object.private_methods(true).should.include?(:main_public_method)
+ end
end
- it "returns Object" do
- eval("private :main_public_method", TOPLEVEL_BINDING).should equal(Object)
+ context "when multiple arguments are passed" do
+ it "sets the visibility of the given methods to private" do
+ eval "private :main_public_method, :main_public_method2", TOPLEVEL_BINDING
+ Object.private_methods(true).should.include?(:main_public_method)
+ Object.private_methods(true).should.include?(:main_public_method2)
+ end
end
- it "raises a NameError when given an undefined name" do
+ context "when single argument is passed and is an array" do
+ it "sets the visibility of the given methods to private" do
+ eval "private [:main_public_method, :main_public_method2]", TOPLEVEL_BINDING
+ Object.private_methods(true).should.include?(:main_public_method)
+ Object.private_methods(true).should.include?(:main_public_method2)
+ end
+ end
+
+ it "returns argument" do
+ eval("private :main_public_method", TOPLEVEL_BINDING).should.equal?(:main_public_method)
+ end
+
+ it "raises a NameError when at least one of given method names is undefined" do
-> do
- eval "private :main_undefined_method", TOPLEVEL_BINDING
- end.should raise_error(NameError)
+ eval "private :main_public_method, :main_undefined_method", TOPLEVEL_BINDING
+ end.should.raise(NameError)
end
end
diff --git a/spec/ruby/core/main/public_spec.rb b/spec/ruby/core/main/public_spec.rb
index afe25c705a..89368ebb0d 100644
--- a/spec/ruby/core/main/public_spec.rb
+++ b/spec/ruby/core/main/public_spec.rb
@@ -4,20 +4,40 @@ require_relative 'fixtures/classes'
describe "main#public" do
after :each do
Object.send(:private, :main_private_method)
+ Object.send(:private, :main_private_method2)
end
- it "sets the visibility of the given method to public" do
- eval "public :main_private_method", TOPLEVEL_BINDING
- Object.should_not have_private_method(:main_private_method)
+ context "when single argument is passed and it is not an array" do
+ it "sets the visibility of the given methods to public" do
+ eval "public :main_private_method", TOPLEVEL_BINDING
+ Object.private_methods(true).should_not.include?(:main_private_method)
+ end
end
- it "returns Object" do
- eval("public :main_private_method", TOPLEVEL_BINDING).should equal(Object)
+ context "when multiple arguments are passed" do
+ it "sets the visibility of the given methods to public" do
+ eval "public :main_private_method, :main_private_method2", TOPLEVEL_BINDING
+ Object.private_methods(true).should_not.include?(:main_private_method)
+ Object.private_methods(true).should_not.include?(:main_private_method2)
+ end
end
+ context "when single argument is passed and is an array" do
+ it "sets the visibility of the given methods to public" do
+ eval "public [:main_private_method, :main_private_method2]", TOPLEVEL_BINDING
+ Object.private_methods(true).should_not.include?(:main_private_method)
+ Object.private_methods(true).should_not.include?(:main_private_method2)
+ end
+ end
+
+ it "returns argument" do
+ eval("public :main_private_method", TOPLEVEL_BINDING).should.equal?(:main_private_method)
+ end
+
+
it "raises a NameError when given an undefined name" do
-> do
eval "public :main_undefined_method", TOPLEVEL_BINDING
- end.should raise_error(NameError)
+ end.should.raise(NameError)
end
end
diff --git a/spec/ruby/core/main/ruby2_keywords_spec.rb b/spec/ruby/core/main/ruby2_keywords_spec.rb
new file mode 100644
index 0000000000..d12c0ed4e4
--- /dev/null
+++ b/spec/ruby/core/main/ruby2_keywords_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "main.ruby2_keywords" do
+ it "is the same as Object.ruby2_keywords" do
+ main = TOPLEVEL_BINDING.receiver
+ main.private_methods(false).should.include?(:ruby2_keywords)
+ end
+end
diff --git a/spec/ruby/core/main/using_spec.rb b/spec/ruby/core/main/using_spec.rb
index f9f709f7dc..314a6be416 100644
--- a/spec/ruby/core/main/using_spec.rb
+++ b/spec/ruby/core/main/using_spec.rb
@@ -5,11 +5,11 @@ describe "main.using" do
it "requires one Module argument" do
-> do
eval('using', TOPLEVEL_BINDING)
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
-> do
eval('using "foo"', TOPLEVEL_BINDING)
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it "uses refinements from the given module only in the target file" do
@@ -19,7 +19,7 @@ describe "main.using" do
MainSpecs::DATA[:toplevel].should == 'foo'
-> do
'hello'.foo
- end.should raise_error(NoMethodError)
+ end.should.raise(NoMethodError)
end
it "uses refinements from the given module for method calls in the target file" do
@@ -27,7 +27,7 @@ describe "main.using" do
load File.expand_path('../fixtures/string_refinement_user.rb', __FILE__)
-> do
'hello'.foo
- end.should raise_error(NoMethodError)
+ end.should.raise(NoMethodError)
MainSpecs.call_foo('hello').should == 'foo'
end
@@ -129,4 +129,22 @@ describe "main.using" do
x.call_bar(cls2.new).should == 'bar'
end
+
+ it "raises error when called from method in wrapped script" do
+ -> do
+ load File.expand_path('../fixtures/using_in_method.rb', __FILE__), true
+ end.should.raise(RuntimeError)
+ end
+
+ it "raises error when called on toplevel from module" do
+ -> do
+ load File.expand_path('../fixtures/using_in_main.rb', __FILE__), true
+ end.should.raise(RuntimeError)
+ end
+
+ it "does not raise error when wrapped with module" do
+ -> do
+ load File.expand_path('../fixtures/using.rb', __FILE__), true
+ end.should_not.raise
+ end
end
diff --git a/spec/ruby/core/marshal/dump_spec.rb b/spec/ruby/core/marshal/dump_spec.rb
index 4ffc586364..9bbb7809af 100644
--- a/spec/ruby/core/marshal/dump_spec.rb
+++ b/spec/ruby/core/marshal/dump_spec.rb
@@ -1,5 +1,6 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
require_relative 'fixtures/marshal_data'
describe "Marshal.dump" do
@@ -37,7 +38,7 @@ describe "Marshal.dump" do
].should be_computed_by(:dump)
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
it "dumps a positive Fixnum > 31 bits as a Bignum" do
Marshal.dump(2**31 + 1).should == "\x04\bl+\a\x01\x00\x00\x80"
end
@@ -46,6 +47,11 @@ describe "Marshal.dump" do
Marshal.dump(-2**31 - 1).should == "\x04\bl-\a\x01\x00\x00\x80"
end
end
+
+ it "does not use object links for objects repeatedly dumped" do
+ Marshal.dump([0, 0]).should == "\x04\b[\ai\x00i\x00"
+ Marshal.dump([2**16, 2**16]).should == "\x04\b[\ai\x03\x00\x00\x01i\x03\x00\x00\x01"
+ end
end
describe "with a Symbol" do
@@ -75,9 +81,28 @@ describe "Marshal.dump" do
end
it "dumps a binary encoded Symbol" do
- s = "\u2192".force_encoding("binary").to_sym
+ s = "\u2192".dup.force_encoding("binary").to_sym
Marshal.dump(s).should == "\x04\b:\b\xE2\x86\x92"
end
+
+ it "dumps multiple Symbols sharing the same encoding" do
+ # Note that the encoding is a link for the second Symbol
+ symbol1 = "I:\t\xE2\x82\xACa\x06:\x06ET"
+ symbol2 = "I:\t\xE2\x82\xACb\x06;\x06T"
+ value = [
+ "€a".dup.force_encoding(Encoding::UTF_8).to_sym,
+ "€b".dup.force_encoding(Encoding::UTF_8).to_sym
+ ]
+ Marshal.dump(value).should == "\x04\b[\a#{symbol1}#{symbol2}"
+
+ value = [*value, value[0]]
+ Marshal.dump(value).should == "\x04\b[\b#{symbol1}#{symbol2};\x00"
+ end
+
+ it "uses symbol links for objects repeatedly dumped" do
+ symbol = :foo
+ Marshal.dump([symbol, symbol]).should == "\x04\b[\a:\bfoo;\x00" # ;\x00 is a link to the symbol object
+ end
end
describe "with an object responding to #marshal_dump" do
@@ -89,17 +114,54 @@ describe "Marshal.dump" do
UserMarshal.should_not_receive(:name)
Marshal.dump(UserMarshal.new)
end
+
+ it "raises TypeError if an Object is an instance of an anonymous class" do
+ -> { Marshal.dump(Class.new(UserMarshal).new) }.should.raise(TypeError, /can't dump anonymous class/)
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ obj = UserMarshal.new
+ Marshal.dump([obj, obj]).should == "\x04\b[\aU:\x10UserMarshal:\tdata@\x06" # @\x06 is a link to the object
+ end
+
+ it "adds instance variables of a dumped object after the object itself into the objects table" do
+ value = "<foo>"
+ obj = MarshalSpec::UserMarshalDumpWithIvar.new("string", value)
+
+ # expect a link to the object (@\x06, that means Integer 1) is smaller than a link
+ # to the instance variable value (@\t, that means Integer 4)
+ Marshal.dump([obj, obj, value]).should == "\x04\b[\bU:)MarshalSpec::UserMarshalDumpWithIvarI[\x06\"\vstring\x06:\t@foo\"\n<foo>@\x06@\t"
+ end
end
describe "with an object responding to #_dump" do
- it "dumps the object returned by #marshal_dump" do
+ it "dumps the String returned by #_dump" do
Marshal.dump(UserDefined.new).should == "\004\bu:\020UserDefined\022\004\b[\a:\nstuff;\000"
end
+ it "dumps the String in non US-ASCII and non UTF-8 encoding" do
+ object = UserDefinedString.new("a".encode("windows-1251"))
+ Marshal.dump(object).should == "\x04\bIu:\x16UserDefinedString\x06a\x06:\rencoding\"\x11Windows-1251"
+ end
+
+ it "dumps the String in multibyte encoding" do
+ object = UserDefinedString.new("a".encode("utf-32le"))
+ Marshal.dump(object).should == "\x04\bIu:\x16UserDefinedString\ta\x00\x00\x00\x06:\rencoding\"\rUTF-32LE"
+ end
+
+ it "ignores overridden name method" do
+ obj = MarshalSpec::UserDefinedWithOverriddenName.new
+ Marshal.dump(obj).should == "\x04\bu:/MarshalSpec::UserDefinedWithOverriddenName\x12\x04\b[\a:\nstuff;\x00"
+ end
+
it "raises a TypeError if _dump returns a non-string" do
m = mock("marshaled")
m.should_receive(:_dump).and_return(0)
- -> { Marshal.dump(m) }.should raise_error(TypeError)
+ -> { Marshal.dump(m) }.should.raise(TypeError)
+ end
+
+ it "raises TypeError if an Object is an instance of an anonymous class" do
+ -> { Marshal.dump(Class.new(UserDefined).new) }.should.raise(TypeError, /can't dump anonymous class/)
end
it "favors marshal_dump over _dump" do
@@ -108,6 +170,48 @@ describe "Marshal.dump" do
m.should_not_receive(:_dump)
Marshal.dump(m)
end
+
+ it "indexes instance variables of a String returned by #_dump at first and then indexes the object itself" do
+ class MarshalSpec::M1::A
+ def _dump(level)
+ s = +"<dump>"
+ s.instance_variable_set(:@foo, "bar")
+ s
+ end
+ end
+
+ a = MarshalSpec::M1::A.new
+
+ # 0-based index of the object a = 2, that is encoded as \x07 and printed as "\a" character.
+ # Objects are serialized in the following order: Array, a, "bar".
+ # But they are indexed in different order: Array (index=0), "bar" (index=1), a (index=2)
+ # So the second occurenc of the object a is encoded as an index 2.
+ reference = "@\a"
+ Marshal.dump([a, a]).should == "\x04\b[\aIu:\x17MarshalSpec::M1::A\v<dump>\x06:\t@foo\"\bbar#{reference}"
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ obj = UserDefined.new
+ Marshal.dump([obj, obj]).should == "\x04\b[\au:\x10UserDefined\x12\x04\b[\a:\nstuff;\x00@\x06" # @\x06 is a link to the object
+ end
+
+ it "adds instance variables of a dumped String before the object itself into the objects table" do
+ value = "<foo>"
+ obj = MarshalSpec::UserDefinedDumpWithIVars.new(+"string", value)
+
+ # expect a link to the object (@\a, that means Integer 2) is greater than a link
+ # to the instance variable value (@\x06, that means Integer 1)
+ Marshal.dump([obj, obj, value]).should == "\x04\b[\bIu:*MarshalSpec::UserDefinedDumpWithIVars\vstring\x06:\t@foo\"\n<foo>@\a@\x06"
+ end
+
+ describe "Core library classes with #_dump returning a String with instance variables" do
+ it "indexes instance variables and then a Time object itself" do
+ t = Time.utc(2022)
+ reference = "@\a"
+
+ Marshal.dump([t, t]).should == "\x04\b[\aIu:\tTime\r \x80\x1E\xC0\x00\x00\x00\x00\x06:\tzoneI\"\bUTC\x06:\x06EF#{reference}"
+ end
+ end
end
describe "with a Class" do
@@ -123,12 +227,28 @@ describe "Marshal.dump" do
Marshal.dump(UserDefined::Nested).should == "\004\bc\030UserDefined::Nested"
end
+ it "ignores overridden name method" do
+ Marshal.dump(MarshalSpec::ClassWithOverriddenName).should == "\x04\bc)MarshalSpec::ClassWithOverriddenName"
+ end
+
+ ruby_version_is "4.0" do
+ it "dumps a class with multibyte characters in name" do
+ source_object = eval("MarshalSpec::MultibyteぁあぃいClass".dup.force_encoding(Encoding::UTF_8))
+ Marshal.dump(source_object).should == "\x04\bIc,MarshalSpec::Multibyte\xE3\x81\x81\xE3\x81\x82\xE3\x81\x83\xE3\x81\x84Class\x06:\x06ET"
+ Marshal.load(Marshal.dump(source_object)) == source_object
+ end
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ Marshal.dump([String, String]).should == "\x04\b[\ac\vString@\x06" # @\x06 is a link to the object
+ end
+
it "raises TypeError with an anonymous Class" do
- -> { Marshal.dump(Class.new) }.should raise_error(TypeError)
+ -> { Marshal.dump(Class.new) }.should.raise(TypeError, /can't dump anonymous class/)
end
it "raises TypeError with a singleton Class" do
- -> { Marshal.dump(class << self; self end) }.should raise_error(TypeError)
+ -> { Marshal.dump(class << self; self end) }.should.raise(TypeError)
end
end
@@ -137,8 +257,24 @@ describe "Marshal.dump" do
Marshal.dump(Marshal).should == "\004\bm\fMarshal"
end
+ it "ignores overridden name method" do
+ Marshal.dump(MarshalSpec::ModuleWithOverriddenName).should == "\x04\bc*MarshalSpec::ModuleWithOverriddenName"
+ end
+
+ ruby_version_is "4.0" do
+ it "dumps a module with multibyte characters in name" do
+ source_object = eval("MarshalSpec::MultibyteけげこごModule".dup.force_encoding(Encoding::UTF_8))
+ Marshal.dump(source_object).should == "\x04\bIm-MarshalSpec::Multibyte\xE3\x81\x91\xE3\x81\x92\xE3\x81\x93\xE3\x81\x94Module\x06:\x06ET"
+ Marshal.load(Marshal.dump(source_object)) == source_object
+ end
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ Marshal.dump([Marshal, Marshal]).should == "\x04\b[\am\fMarshal@\x06" # @\x06 is a link to the object
+ end
+
it "raises TypeError with an anonymous Module" do
- -> { Marshal.dump(Module.new) }.should raise_error(TypeError)
+ -> { Marshal.dump(Module.new) }.should.raise(TypeError, /can't dump anonymous module/)
end
end
@@ -155,6 +291,23 @@ describe "Marshal.dump" do
[Marshal, nan_value, "\004\bf\bnan"],
].should be_computed_by(:dump)
end
+
+ it "may or may not use object links for objects repeatedly dumped" do
+ # it's an MRI implementation detail - on x86 architecture object links
+ # aren't used for Float values but on amd64 - object links are used
+
+ dump = Marshal.dump([0.0, 0.0])
+ ["\x04\b[\af\x060@\x06", "\x04\b[\af\x060f\x060"].should.include?(dump)
+
+ # if object links aren't used - entries in the objects table are still
+ # occupied by Float values
+ if dump == "\x04\b[\af\x060f\x060"
+ s = "string"
+ # an index of "string" ("@\b") in the object table equals 3 (`"\b".ord - 5`),
+ # so `0.0, 0,0` elements occupied indices 1 and 2
+ Marshal.dump([0.0, 0.0, s, s]).should == "\x04\b[\tf\x060f\x060\"\vstring@\b"
+ end
+ end
end
describe "with a Bignum" do
@@ -171,15 +324,81 @@ describe "Marshal.dump" do
[Marshal, -2**64, "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
].should be_computed_by(:dump)
end
+
+ it "uses object links for objects repeatedly dumped" do
+ n = 2**64
+ Marshal.dump([n, n]).should == "\x04\b[\al+\n\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00@\x06" # @\x06 is a link to the object
+ end
+
+ it "increases the object links counter" do
+ obj = Object.new
+ object_1_link = "\x06" # representing of (0-based) index=1 (by adding 5 for small Integers)
+ object_2_link = "\x07" # representing of index=2
+
+ # objects: Array, Object, Object
+ Marshal.dump([obj, obj]).should == "\x04\b[\ao:\vObject\x00@#{object_1_link}"
+
+ # objects: Array, Bignum, Object, Object
+ Marshal.dump([2**64, obj, obj]).should == "\x04\b[\bl+\n\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}"
+ Marshal.dump([2**48, obj, obj]).should == "\x04\b[\bl+\t\x00\x00\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}"
+ Marshal.dump([2**32, obj, obj]).should == "\x04\b[\bl+\b\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}"
+ end
+ end
+
+ describe "with a Rational" do
+ it "dumps a Rational" do
+ Marshal.dump(Rational(2, 3)).should == "\x04\bU:\rRational[\ai\ai\b"
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ r = Rational(2, 3)
+ Marshal.dump([r, r]).should == "\x04\b[\aU:\rRational[\ai\ai\b@\x06" # @\x06 is a link to the object
+ end
+ end
+
+ describe "with a Complex" do
+ it "dumps a Complex" do
+ Marshal.dump(Complex(2, 3)).should == "\x04\bU:\fComplex[\ai\ai\b"
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ c = Complex(2, 3)
+ Marshal.dump([c, c]).should == "\x04\b[\aU:\fComplex[\ai\ai\b@\x06" # @\x06 is a link to the object
+ end
+ end
+
+ describe "with a Data" do
+ it "dumps a Data" do
+ Marshal.dump(MarshalSpec::DataSpec::Measure.new(100, 'km')).should == "\x04\bS:#MarshalSpec::DataSpec::Measure\a:\vamountii:\tunit\"\akm"
+ end
+
+ it "dumps an extended Data" do
+ obj = MarshalSpec::DataSpec::MeasureExtended.new(100, "km")
+ Marshal.dump(obj).should == "\x04\bS:+MarshalSpec::DataSpec::MeasureExtended\a:\vamountii:\tunit\"\akm"
+ end
+
+ it "ignores overridden name method" do
+ obj = MarshalSpec::DataSpec::MeasureWithOverriddenName.new(100, "km")
+ Marshal.dump(obj).should == "\x04\bS:5MarshalSpec::DataSpec::MeasureWithOverriddenName\a:\vamountii:\tunit\"\akm"
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ d = MarshalSpec::DataSpec::Measure.new(100, 'km')
+ Marshal.dump([d, d]).should == "\x04\b[\aS:#MarshalSpec::DataSpec::Measure\a:\vamountii:\tunit\"\akm@\x06" # @\x06 is a link to the object
+ end
+
+ it "raises TypeError with an anonymous Struct" do
+ -> { Marshal.dump(Data.define(:a).new(1)) }.should.raise(TypeError, /can't dump anonymous class/)
+ end
end
describe "with a String" do
it "dumps a blank String" do
- Marshal.dump("".force_encoding("binary")).should == "\004\b\"\000"
+ Marshal.dump("".dup.force_encoding("binary")).should == "\004\b\"\000"
end
it "dumps a short String" do
- Marshal.dump("short".force_encoding("binary")).should == "\004\b\"\012short"
+ Marshal.dump("short".dup.force_encoding("binary")).should == "\004\b\"\012short"
end
it "dumps a long String" do
@@ -187,7 +406,7 @@ describe "Marshal.dump" do
end
it "dumps a String extended with a Module" do
- Marshal.dump("".extend(Meths).force_encoding("binary")).should == "\004\be:\nMeths\"\000"
+ Marshal.dump("".dup.extend(Meths).force_encoding("binary")).should == "\004\be:\nMeths\"\000"
end
it "dumps a String subclass" do
@@ -198,24 +417,29 @@ describe "Marshal.dump" do
Marshal.dump(UserString.new.extend(Meths).force_encoding("binary")).should == "\004\be:\nMethsC:\017UserString\"\000"
end
+ it "ignores overridden name method when dumps a String subclass" do
+ obj = MarshalSpec::StringWithOverriddenName.new
+ Marshal.dump(obj).should == "\x04\bC:*MarshalSpec::StringWithOverriddenName\"\x00"
+ end
+
it "dumps a String with instance variables" do
- str = ""
+ str = +""
str.instance_variable_set("@foo", "bar")
Marshal.dump(str.force_encoding("binary")).should == "\x04\bI\"\x00\x06:\t@foo\"\bbar"
end
it "dumps a US-ASCII String" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
Marshal.dump(str).should == "\x04\bI\"\babc\x06:\x06EF"
end
it "dumps a UTF-8 String" do
- str = "\x6d\xc3\xb6\x68\x72\x65".force_encoding("utf-8")
+ str = "\x6d\xc3\xb6\x68\x72\x65".dup.force_encoding("utf-8")
Marshal.dump(str).should == "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET"
end
it "dumps a String in another encoding" do
- str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".force_encoding("utf-16le")
+ str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".dup.force_encoding("utf-16le")
result = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE"
Marshal.dump(str).should == result
end
@@ -223,6 +447,21 @@ describe "Marshal.dump" do
it "dumps multiple strings using symlinks for the :E (encoding) symbol" do
Marshal.dump(["".encode("us-ascii"), "".encode("utf-8")]).should == "\x04\b[\aI\"\x00\x06:\x06EFI\"\x00\x06;\x00T"
end
+
+ it "uses object links for objects repeatedly dumped" do
+ s = "string"
+ Marshal.dump([s, s]).should == "\x04\b[\a\"\vstring@\x06" # @\x06 is a link to the object
+ end
+
+ it "adds instance variables after the object itself into the objects table" do
+ obj = +"string"
+ value = "<foo>"
+ obj.instance_variable_set :@foo, value
+
+ # expect a link to the object (@\x06, that means Integer 1) is smaller than a link
+ # to the instance variable value (@\a, that means Integer 2)
+ Marshal.dump([obj, obj, value]).should == "\x04\b[\bI\"\vstring\x06:\t@foo\"\n<foo>@\x06@\a"
+ end
end
describe "with a Regexp" do
@@ -234,14 +473,26 @@ describe "Marshal.dump" do
Marshal.dump(//im).should == "\x04\bI/\x00\x05\x06:\x06EF"
end
- it "dumps a Regexp with instance variables" do
- o = Regexp.new("")
+ it "dumps a Regexp subclass with instance variables" do
+ o = UserRegexp.new("")
o.instance_variable_set(:@ivar, :ivar)
- Marshal.dump(o).should == "\x04\bI/\x00\x00\a:\x06EF:\n@ivar:\tivar"
+ Marshal.dump(o).should == "\x04\bIC:\x0FUserRegexp/\x00\x00\a:\x06EF:\n@ivar:\tivar"
+ end
+
+ it "dumps an extended Regexp subclass" do
+ Marshal.dump(UserRegexp.new("").extend(Meths)).should == "\x04\bIe:\nMethsC:\x0FUserRegexp/\x00\x00\x06:\x06EF"
end
- it "dumps an extended Regexp" do
- Marshal.dump(Regexp.new("").extend(Meths)).should == "\x04\bIe:\nMeths/\x00\x00\x06:\x06EF"
+ ruby_version_is ""..."4.1" do
+ it "dumps a Regexp with instance variables" do
+ o = Regexp.new("")
+ o.instance_variable_set(:@ivar, :ivar)
+ Marshal.dump(o).should == "\x04\bI/\x00\x00\a:\x06EF:\n@ivar:\tivar"
+ end
+
+ it "dumps an extended Regexp" do
+ Marshal.dump(Regexp.new("").extend(Meths)).should == "\x04\bIe:\nMeths/\x00\x00\x06:\x06EF"
+ end
end
it "dumps a Regexp subclass" do
@@ -249,18 +500,51 @@ describe "Marshal.dump" do
end
it "dumps a binary Regexp" do
- o = Regexp.new("".force_encoding("binary"), Regexp::FIXEDENCODING)
+ o = Regexp.new("".dup.force_encoding("binary"), Regexp::FIXEDENCODING)
Marshal.dump(o).should == "\x04\b/\x00\x10"
end
+ it "dumps an ascii-compatible Regexp" do
+ o = Regexp.new("a".encode("us-ascii"), Regexp::FIXEDENCODING)
+ Marshal.dump(o).should == "\x04\bI/\x06a\x10\x06:\x06EF"
+
+ o = Regexp.new("a".encode("us-ascii"))
+ Marshal.dump(o).should == "\x04\bI/\x06a\x00\x06:\x06EF"
+
+ o = Regexp.new("a".encode("windows-1251"), Regexp::FIXEDENCODING)
+ Marshal.dump(o).should == "\x04\bI/\x06a\x10\x06:\rencoding\"\x11Windows-1251"
+
+ o = Regexp.new("a".encode("windows-1251"))
+ Marshal.dump(o).should == "\x04\bI/\x06a\x00\x06:\x06EF"
+ end
+
it "dumps a UTF-8 Regexp" do
- o = Regexp.new("".force_encoding("utf-8"), Regexp::FIXEDENCODING)
+ o = Regexp.new("".dup.force_encoding("utf-8"), Regexp::FIXEDENCODING)
Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\x06ET"
+
+ o = Regexp.new("a".dup.force_encoding("utf-8"), Regexp::FIXEDENCODING)
+ Marshal.dump(o).should == "\x04\bI/\x06a\x10\x06:\x06ET"
+
+ o = Regexp.new("\u3042".dup.force_encoding("utf-8"), Regexp::FIXEDENCODING)
+ Marshal.dump(o).should == "\x04\bI/\b\xE3\x81\x82\x10\x06:\x06ET"
end
it "dumps a Regexp in another encoding" do
- o = Regexp.new("".force_encoding("utf-16le"), Regexp::FIXEDENCODING)
+ o = Regexp.new("".dup.force_encoding("utf-16le"), Regexp::FIXEDENCODING)
Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\rencoding\"\rUTF-16LE"
+
+ o = Regexp.new("a".encode("utf-16le"), Regexp::FIXEDENCODING)
+ Marshal.dump(o).should == "\x04\bI/\aa\x00\x10\x06:\rencoding\"\rUTF-16LE"
+ end
+
+ it "ignores overridden name method when dumps a Regexp subclass" do
+ obj = MarshalSpec::RegexpWithOverriddenName.new("")
+ Marshal.dump(obj).should == "\x04\bIC:*MarshalSpec::RegexpWithOverriddenName/\x00\x00\x06:\x06EF"
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ r = /\A.\Z/
+ Marshal.dump([r, r]).should == "\x04\b[\aI/\n\\A.\\Z\x00\x06:\x06EF@\x06" # @\x06 is a link to the object
end
end
@@ -292,6 +576,26 @@ describe "Marshal.dump" do
it "dumps an extended Array" do
Marshal.dump([].extend(Meths)).should == "\004\be:\nMeths[\000"
end
+
+ it "ignores overridden name method when dumps an Array subclass" do
+ obj = MarshalSpec::ArrayWithOverriddenName.new
+ Marshal.dump(obj).should == "\x04\bC:)MarshalSpec::ArrayWithOverriddenName[\x00"
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ a = [1]
+ Marshal.dump([a, a]).should == "\x04\b[\a[\x06i\x06@\x06" # @\x06 is a link to the object
+ end
+
+ it "adds instance variables after the object itself into the objects table" do
+ obj = []
+ value = "<foo>"
+ obj.instance_variable_set :@foo, value
+
+ # expect a link to the object (@\x06, that means Integer 1) is smaller than a link
+ # to the instance variable value (@\a, that means Integer 2)
+ Marshal.dump([obj, obj, value]).should == "\x04\b[\bI[\x00\x06:\t@foo\"\n<foo>@\x06@\a"
+ end
end
describe "with a Hash" do
@@ -299,6 +603,10 @@ describe "Marshal.dump" do
Marshal.dump({}).should == "\004\b{\000"
end
+ it "dumps a non-empty Hash" do
+ Marshal.dump({a: 1}).should == "\x04\b{\x06:\x06ai\x06"
+ end
+
it "dumps a Hash subclass" do
Marshal.dump(UserHash.new).should == "\004\bC:\rUserHash{\000"
end
@@ -307,8 +615,22 @@ describe "Marshal.dump" do
Marshal.dump(Hash.new(1)).should == "\004\b}\000i\006"
end
+ it "dumps a Hash with compare_by_identity" do
+ h = {}
+ h.compare_by_identity
+
+ Marshal.dump(h).should == "\004\bC:\tHash{\x00"
+ end
+
+ it "dumps a Hash subclass with compare_by_identity" do
+ h = UserHash.new
+ h.compare_by_identity
+
+ Marshal.dump(h).should == "\x04\bC:\rUserHashC:\tHash{\x00"
+ end
+
it "raises a TypeError with hash having default proc" do
- -> { Marshal.dump(Hash.new {}) }.should raise_error(TypeError)
+ -> { Marshal.dump(Hash.new {}) }.should.raise(TypeError, "can't dump hash with default proc")
end
it "dumps a Hash with instance variables" do
@@ -324,6 +646,26 @@ describe "Marshal.dump" do
it "dumps an Hash subclass with a parameter to initialize" do
Marshal.dump(UserHashInitParams.new(1)).should == "\004\bIC:\027UserHashInitParams{\000\006:\a@ai\006"
end
+
+ it "ignores overridden name method when dumps a Hash subclass" do
+ obj = MarshalSpec::HashWithOverriddenName.new
+ Marshal.dump(obj).should == "\x04\bC:(MarshalSpec::HashWithOverriddenName{\x00"
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ h = {a: 1}
+ Marshal.dump([h, h]).should == "\x04\b[\a{\x06:\x06ai\x06@\x06" # @\x06 is a link to the object
+ end
+
+ it "adds instance variables after the object itself into the objects table" do
+ obj = {}
+ value = "<foo>"
+ obj.instance_variable_set :@foo, value
+
+ # expect a link to the object (@\x06, that means Integer 1) is smaller than a link
+ # to the instance variable value (@\a, that means Integer 2)
+ Marshal.dump([obj, obj, value]).should == "\x04\b[\bI{\x00\x06:\t@foo\"\n<foo>@\x06@\a"
+ end
end
describe "with a Struct" do
@@ -343,10 +685,39 @@ describe "Marshal.dump" do
end
it "dumps an extended Struct" do
- st = Struct.new("Extended", :a, :b).new
- Marshal.dump(st.extend(Meths)).should == "\004\be:\nMethsS:\025Struct::Extended\a:\006a0:\006b0"
+ obj = Struct.new("Extended", :a, :b).new.extend(Meths)
+ Marshal.dump(obj).should == "\004\be:\nMethsS:\025Struct::Extended\a:\006a0:\006b0"
+
+ s = 'hi'
+ obj.a = [:a, s]
+ obj.b = [:Meths, s]
+ Marshal.dump(obj).should == "\004\be:\nMethsS:\025Struct::Extended\a:\006a[\a;\a\"\ahi:\006b[\a;\000@\a"
Struct.send(:remove_const, :Extended)
end
+
+ it "ignores overridden name method" do
+ obj = MarshalSpec::StructWithOverriddenName.new("member")
+ Marshal.dump(obj).should == "\x04\bS:*MarshalSpec::StructWithOverriddenName\x06:\x06a\"\vmember"
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ s = Struct::Pyramid.new
+ Marshal.dump([s, s]).should == "\x04\b[\aS:\x14Struct::Pyramid\x00@\x06" # @\x06 is a link to the object
+ end
+
+ it "raises TypeError with an anonymous Struct" do
+ -> { Marshal.dump(Struct.new(:a).new(1)) }.should.raise(TypeError, /can't dump anonymous class/)
+ end
+
+ it "adds instance variables after the object itself into the objects table" do
+ obj = Struct::Pyramid.new
+ value = "<foo>"
+ obj.instance_variable_set :@foo, value
+
+ # expect a link to the object (@\x06, that means Integer 1) is smaller than a link
+ # to the instance variable value (@\a, that means Integer 2)
+ Marshal.dump([obj, obj, value]).should == "\x04\b[\bIS:\x14Struct::Pyramid\x00\x06:\t@foo\"\n<foo>@\x06@\a"
+ end
end
describe "with an Object" do
@@ -366,7 +737,7 @@ describe "Marshal.dump" do
it "dumps an Object with a non-US-ASCII instance variable" do
obj = Object.new
- ivar = "@é".force_encoding(Encoding::UTF_8).to_sym
+ ivar = "@é".dup.force_encoding(Encoding::UTF_8).to_sym
obj.instance_variable_set(ivar, 1)
Marshal.dump(obj).should == "\x04\bo:\vObject\x06I:\b@\xC3\xA9\x06:\x06ETi\x06"
end
@@ -378,46 +749,105 @@ describe "Marshal.dump" do
Marshal.dump(obj).should == "\004\bo:\x0BObject\x00"
end
- it "dumps an Object if it has a singleton class but no singleton methods" do
+ it "dumps an Object if it has a singleton class but no singleton methods and no singleton instance variables" do
obj = Object.new
obj.singleton_class
Marshal.dump(obj).should == "\004\bo:\x0BObject\x00"
end
- it "raises if an Object has a singleton class and singleton methods" do
+ it "ignores overridden name method" do
+ obj = MarshalSpec::ClassWithOverriddenName.new
+ Marshal.dump(obj).should == "\x04\bo:)MarshalSpec::ClassWithOverriddenName\x00"
+ end
+
+ it "raises TypeError if an Object has a singleton class and singleton methods" do
obj = Object.new
def obj.foo; end
-> {
Marshal.dump(obj)
- }.should raise_error(TypeError, "singleton can't be dumped")
+ }.should.raise(TypeError, "singleton can't be dumped")
+ end
+
+ it "raises TypeError if an Object has a singleton class and singleton instance variables" do
+ obj = Object.new
+ class << obj
+ @v = 1
+ end
+
+ -> {
+ Marshal.dump(obj)
+ }.should.raise(TypeError, "singleton can't be dumped")
+ end
+
+ it "raises TypeError if an Object is an instance of an anonymous class" do
+ anonymous_class = Class.new
+ obj = anonymous_class.new
+
+ -> { Marshal.dump(obj) }.should.raise(TypeError, /can't dump anonymous class/)
+ end
+
+ it "raises TypeError if an Object extends an anonymous module" do
+ anonymous_module = Module.new
+ obj = Object.new
+ obj.extend(anonymous_module)
+
+ -> { Marshal.dump(obj) }.should.raise(TypeError, /can't dump anonymous class/)
end
it "dumps a BasicObject subclass if it defines respond_to?" do
obj = MarshalSpec::BasicObjectSubWithRespondToFalse.new
Marshal.dump(obj).should == "\x04\bo:2MarshalSpec::BasicObjectSubWithRespondToFalse\x00"
end
+
+ it "dumps without marshaling any attached finalizer" do
+ obj = Object.new
+ finalizer = Object.new
+ def finalizer.noop(_)
+ end
+ ObjectSpace.define_finalizer(obj, finalizer.method(:noop))
+ Marshal.load(Marshal.dump(obj)).class.should == Object
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ obj = Object.new
+ Marshal.dump([obj, obj]).should == "\x04\b[\ao:\vObject\x00@\x06" # @\x06 is a link to the object
+ end
+
+ it "adds instance variables after the object itself into the objects table" do
+ obj = Object.new
+ value = "<foo>"
+ obj.instance_variable_set :@foo, value
+
+ # expect a link to the object (@\x06, that means Integer 1) is smaller than a link
+ # to the instance variable value (@\a, that means Integer 2)
+ Marshal.dump([obj, obj, value]).should == "\x04\b[\bo:\vObject\x06:\t@foo\"\n<foo>@\x06@\a"
+ end
end
describe "with a Range" do
- it "dumps a Range inclusive of end (with indeterminant order)" do
+ it "dumps a Range inclusive of end" do
dump = Marshal.dump(1..2)
+ dump.should == "\x04\bo:\nRange\b:\texclF:\nbegini\x06:\bendi\a"
+
load = Marshal.load(dump)
load.should == (1..2)
end
- it "dumps a Range exclusive of end (with indeterminant order)" do
+ it "dumps a Range exclusive of end" do
dump = Marshal.dump(1...2)
+ dump.should == "\x04\bo:\nRange\b:\texclT:\nbegini\x06:\bendi\a"
+
load = Marshal.load(dump)
load.should == (1...2)
end
- it "dumps a Range with extra instance variables" do
- range = (1...3)
- range.instance_variable_set :@foo, 42
- dump = Marshal.dump(range)
- load = Marshal.load(dump)
- load.should == range
- load.instance_variable_get(:@foo).should == 42
+ it "uses object links for objects repeatedly dumped" do
+ r = 1..2
+ Marshal.dump([r, r]).should == "\x04\b[\ao:\nRange\b:\texclF:\nbegini\x06:\bendi\a@\x06" # @\x06 is a link to the object
+ end
+
+ it "raises TypeError with an anonymous Range subclass" do
+ -> { Marshal.dump(Class.new(Range).new(1, 2)) }.should.raise(TypeError, /can't dump anonymous class/)
end
end
@@ -447,7 +877,7 @@ describe "Marshal.dump" do
base = "\x04\bIu:\tTime\r#{@t_dump}\a"
offset = ":\voffseti\x020*"
zone = ":\tzoneI\"\bAST\x06:\x06EF" # Last is 'F' (US-ASCII)
- [ "#{base}#{offset}#{zone}", "#{base}#{zone}#{offset}" ].should include(dump)
+ [ "#{base}#{offset}#{zone}", "#{base}#{zone}#{offset}" ].should.include?(dump)
end
end
@@ -456,6 +886,43 @@ describe "Marshal.dump" do
zone = ":\tzoneI\"\bUTC\x06:\x06EF" # Last is 'F' (US-ASCII)
dump.should == "\x04\bIu:\tTime\r#{@utc_dump}\x06#{zone}"
end
+
+ it "ignores overridden name method" do
+ obj = MarshalSpec::TimeWithOverriddenName.new
+ Marshal.dump(obj).should.include?("MarshalSpec::TimeWithOverriddenName")
+ end
+
+ ruby_version_is "4.0" do
+ it "dumps a Time subclass with multibyte characters in name" do
+ source_object = eval("MarshalSpec::MultibyteぁあぃいTime".dup.force_encoding(Encoding::UTF_8))
+ Marshal.dump(source_object).should == "\x04\bIc+MarshalSpec::Multibyte\xE3\x81\x81\xE3\x81\x82\xE3\x81\x83\xE3\x81\x84Time\x06:\x06ET"
+ Marshal.load(Marshal.dump(source_object)) == source_object
+ end
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ # order of the offset and zone instance variables is a subject to change
+ # and may be different on different CRuby versions
+ base = Regexp.quote("\x04\b[\aIu:\tTime\r\xF5\xEF\e\x80\x00\x00\x00\x00\a")
+ offset = Regexp.quote(":\voffseti\x020*:\tzoneI\"\bAST\x06:\x06EF")
+ zone = Regexp.quote(":\tzoneI\"\bAST\x06:\x06EF:\voffseti\x020*")
+ instance_variables = /#{offset}|#{zone}/
+ Marshal.dump([@t, @t]).should =~ /\A#{base}#{instance_variables}@\a\Z/ # @\a is a link to the object
+ end
+
+ it "adds instance variables before the object itself into the objects table" do
+ obj = @utc
+ value = "<foo>"
+ obj.instance_variable_set :@foo, value
+
+ # expect a link to the object (@\b, that means Integer 3) is greater than a link
+ # to the instance variable value (@\x06, that means Integer 1)
+ Marshal.dump([obj, obj, value]).should == "\x04\b[\bIu:\tTime\r \x00\x1C\xC0\x00\x00\x00\x00\a:\t@foo\"\n<foo>:\tzoneI\"\bUTC\x06:\x06EF@\b@\x06"
+ end
+
+ it "raises TypeError with an anonymous Time subclass" do
+ -> { Marshal.dump(Class.new(Time).now) }.should.raise(TypeError)
+ end
end
describe "with an Exception" do
@@ -487,15 +954,47 @@ describe "Marshal.dump" do
begin
raise RuntimeError, "the consequence"
rescue RuntimeError => e
- e.cause.should equal(cause)
+ e.cause.should.equal?(cause)
exc = e
end
end
reloaded = Marshal.load(Marshal.dump(exc))
- reloaded.cause.should be_an_instance_of(StandardError)
+ reloaded.cause.should.instance_of?(StandardError)
reloaded.cause.message.should == "the cause"
end
+
+ # NoMethodError uses an exception formatter on TruffleRuby and computes a message lazily
+ it "dumps the message for the raised NoMethodError exception" do
+ begin
+ "".foo
+ rescue => e
+ end
+
+ Marshal.dump(e).should =~ /undefined method [`']foo' for ("":String|an instance of String)/
+ end
+
+ it "uses object links for objects repeatedly dumped" do
+ e = Exception.new
+ Marshal.dump([e, e]).should == "\x04\b[\ao:\x0EException\a:\tmesg0:\abt0@\x06" # @\x\a is a link to the object
+ end
+
+ it "adds instance variables after the object itself into the objects table" do
+ obj = Exception.new
+ value = "<foo>"
+ obj.instance_variable_set :@foo, value
+
+ # expect a link to the object (@\x06, that means Integer 1) is smaller than a link
+ # to the instance variable value (@\a, that means Integer 2)
+ Marshal.dump([obj, obj, value]).should == "\x04\b[\bo:\x0EException\b:\tmesg0:\abt0:\t@foo\"\n<foo>@\x06@\a"
+ end
+
+ it "raises TypeError if an Object is an instance of an anonymous class" do
+ anonymous_class = Class.new(Exception)
+ obj = anonymous_class.new
+
+ -> { Marshal.dump(obj) }.should.raise(TypeError, /can't dump anonymous class/)
+ end
end
it "dumps subsequent appearances of a symbol as a link" do
@@ -515,10 +1014,10 @@ describe "Marshal.dump" do
it "raises an ArgumentError when the recursion limit is exceeded" do
h = {'one' => {'two' => {'three' => 0}}}
- -> { Marshal.dump(h, 3) }.should raise_error(ArgumentError)
- -> { Marshal.dump([h], 4) }.should raise_error(ArgumentError)
- -> { Marshal.dump([], 0) }.should raise_error(ArgumentError)
- -> { Marshal.dump([[[]]], 1) }.should raise_error(ArgumentError)
+ -> { Marshal.dump(h, 3) }.should.raise(ArgumentError)
+ -> { Marshal.dump([h], 4) }.should.raise(ArgumentError)
+ -> { Marshal.dump([], 0) }.should.raise(ArgumentError)
+ -> { Marshal.dump([[[]]], 1) }.should.raise(ArgumentError)
end
it "ignores the recursion limit if the limit is negative" do
@@ -528,7 +1027,6 @@ describe "Marshal.dump" do
end
describe "when passed an IO" do
-
it "writes the serialized data to the IO-Object" do
(obj = mock('test')).should_receive(:write).at_least(1)
Marshal.dump("test", obj)
@@ -541,7 +1039,7 @@ describe "Marshal.dump" do
it "raises an Error when the IO-Object does not respond to #write" do
obj = mock('test')
- -> { Marshal.dump("test", obj) }.should raise_error(TypeError)
+ -> { Marshal.dump("test", obj) }.should.raise(TypeError)
end
@@ -551,62 +1049,34 @@ describe "Marshal.dump" do
obj.should_receive(:binmode).at_least(1)
Marshal.dump("test", obj)
end
-
-
end
describe "when passed a StringIO" do
it "should raise an error" do
require "stringio"
- -> { Marshal.dump(StringIO.new) }.should raise_error(TypeError)
+ -> { Marshal.dump(StringIO.new) }.should.raise(TypeError)
end
end
it "raises a TypeError if marshalling a Method instance" do
- -> { Marshal.dump(Marshal.method(:dump)) }.should raise_error(TypeError)
+ -> { Marshal.dump(Marshal.method(:dump)) }.should.raise(TypeError)
end
it "raises a TypeError if marshalling a Proc" do
- -> { Marshal.dump(proc {}) }.should raise_error(TypeError)
+ -> { Marshal.dump(proc {}) }.should.raise(TypeError)
end
it "raises a TypeError if dumping a IO/File instance" do
- -> { Marshal.dump(STDIN) }.should raise_error(TypeError)
- -> { File.open(__FILE__) { |f| Marshal.dump(f) } }.should raise_error(TypeError)
+ -> { Marshal.dump(STDIN) }.should.raise(TypeError)
+ -> { File.open(__FILE__) { |f| Marshal.dump(f) } }.should.raise(TypeError)
end
it "raises a TypeError if dumping a MatchData instance" do
- -> { Marshal.dump(/(.)/.match("foo")) }.should raise_error(TypeError)
+ -> { Marshal.dump(/(.)/.match("foo")) }.should.raise(TypeError)
end
it "raises a TypeError if dumping a Mutex instance" do
m = Mutex.new
- -> { Marshal.dump(m) }.should raise_error(TypeError)
- end
-
- ruby_version_is ''...'2.7' do
- it "returns an untainted string if object is untainted" do
- Marshal.dump(Object.new).tainted?.should be_false
- end
-
- it "returns a tainted string if object is tainted" do
- Marshal.dump(Object.new.taint).tainted?.should be_true
- end
-
- it "returns a tainted string if nested object is tainted" do
- Marshal.dump([[Object.new.taint]]).tainted?.should be_true
- end
-
- it "returns a trusted string if object is trusted" do
- Marshal.dump(Object.new).untrusted?.should be_false
- end
-
- it "returns an untrusted string if object is untrusted" do
- Marshal.dump(Object.new.untrust).untrusted?.should be_true
- end
-
- it "returns an untrusted string if nested object is untrusted" do
- Marshal.dump([[Object.new.untrust]]).untrusted?.should be_true
- end
+ -> { Marshal.dump(m) }.should.raise(TypeError)
end
end
diff --git a/spec/ruby/core/marshal/fixtures/classes.rb b/spec/ruby/core/marshal/fixtures/classes.rb
new file mode 100644
index 0000000000..7c81c64927
--- /dev/null
+++ b/spec/ruby/core/marshal/fixtures/classes.rb
@@ -0,0 +1,4 @@
+module MarshalSpec
+ # empty modules
+ module M1 end
+end
diff --git a/spec/ruby/core/marshal/fixtures/marshal_data.rb b/spec/ruby/core/marshal/fixtures/marshal_data.rb
index 2931278290..c16d9e4bb6 100644
--- a/spec/ruby/core/marshal/fixtures/marshal_data.rb
+++ b/spec/ruby/core/marshal/fixtures/marshal_data.rb
@@ -1,4 +1,7 @@
-# -*- encoding: binary -*-
+# encoding: binary
+
+require_relative 'marshal_multibyte_data'
+
class UserDefined
class Nested
def ==(other)
@@ -38,7 +41,7 @@ class UserDefinedWithIvar
attr_reader :a, :b, :c
def initialize
- @a = 'stuff'
+ @a = +'stuff'
@a.instance_variable_set :@foo, :UserDefinedWithIvar
@b = 'more'
@c = @b
@@ -78,12 +81,47 @@ class UserDefinedImmediate
end
end
+class UserDefinedString
+ attr_reader :string
+
+ def initialize(string)
+ @string = string
+ end
+
+ def _dump(depth)
+ @string
+ end
+
+ def self._load(data)
+ new(data)
+ end
+end
+
+module MarshalSpec
+ class UserDefinedDumpWithIVars
+ attr_reader :string
+
+ def initialize(string, ivar_value)
+ @string = string
+ @string.instance_variable_set(:@foo, ivar_value)
+ end
+
+ def _dump(depth)
+ @string
+ end
+
+ def self._load(data)
+ new(data)
+ end
+ end
+end
+
class UserPreviouslyDefinedWithInitializedIvar
attr_accessor :field1, :field2
end
class UserMarshal
- attr_reader :data
+ attr_accessor :data
def initialize
@data = 'stuff'
@@ -120,6 +158,32 @@ class UserMarshalWithIvar
end
end
+module MarshalSpec
+ class UserMarshalDumpWithIvar
+ attr_reader :data
+
+ def initialize(data, ivar_value)
+ @data = data
+ @ivar_value = ivar_value
+ end
+
+ def marshal_dump
+ obj = [data]
+ obj.instance_variable_set(:@foo, @ivar_value)
+ obj
+ end
+
+ def marshal_load(o)
+ @data = o[0]
+ end
+
+ def ==(other)
+ self.class === other and
+ @data = other.data
+ end
+ end
+end
+
class UserArray < Array
end
@@ -167,12 +231,17 @@ module MarshalSpec
end
end
+ StructToDump = Struct.new(:a, :b)
+
class BasicObjectSubWithRespondToFalse < BasicObject
def respond_to?(method_name, include_all=false)
false
end
end
+ module ModuleToExtendBy
+ end
+
def self.random_data
randomizer = Random.new(42)
1000.times{randomizer.rand} # Make sure we exhaust his first state of 624 random words
@@ -192,6 +261,70 @@ module MarshalSpec
set_swapped_class(nil)
end
+ class ClassWithOverriddenName
+ def self.name
+ "Foo"
+ end
+ end
+
+ class ModuleWithOverriddenName
+ def self.name
+ "Foo"
+ end
+ end
+
+ class TimeWithOverriddenName < Time
+ def self.name
+ "Foo"
+ end
+ end
+
+ class StructWithOverriddenName < Struct.new(:a)
+ def self.name
+ "Foo"
+ end
+ end
+
+ class UserDefinedWithOverriddenName < UserDefined
+ def self.name
+ "Foo"
+ end
+ end
+
+ class StringWithOverriddenName < String
+ def self.name
+ "Foo"
+ end
+ end
+
+ class ArrayWithOverriddenName < Array
+ def self.name
+ "Foo"
+ end
+ end
+
+ class HashWithOverriddenName < Hash
+ def self.name
+ "Foo"
+ end
+ end
+
+ class RegexpWithOverriddenName < Regexp
+ def self.name
+ "Foo"
+ end
+ end
+
+ class ObjectWithFreezeRaisingException < Object
+ def freeze
+ raise
+ end
+ end
+
+ class ObjectWithoutFreeze < Object
+ undef freeze
+ end
+
DATA = {
"nil" => [nil, "\004\b0"],
"1..2" => [(1..2),
@@ -217,7 +350,7 @@ module MarshalSpec
"\004\b\"\012small"],
"String big" => ['big' * 100,
"\004\b\"\002,\001#{'big' * 100}"],
- "String extended" => [''.extend(Meths), # TODO: check for module on load
+ "String extended" => [''.dup.extend(Meths), # TODO: check for module on load
"\004\be:\nMeths\"\000"],
"String subclass" => [UserString.new,
"\004\bC:\017UserString\"\000"],
@@ -227,36 +360,36 @@ module MarshalSpec
"\004\b:\010big"],
"Symbol big" => [('big' * 100).to_sym,
"\004\b:\002,\001#{'big' * 100}"],
- "Bignum -2**64" => [-2**64,
+ "Integer -2**64" => [-2**64,
"\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
- "Bignum -2**63" => [-2**63,
+ "Integer -2**63" => [-2**63,
"\004\bl-\t\000\000\000\000\000\000\000\200"],
- "Fixnum -2**24" => [-2**24,
+ "Integer -2**24" => [-2**24,
"\004\bi\375\000\000\000"],
- "Fixnum -4516727" => [-4516727,
+ "Integer -4516727" => [-4516727,
"\004\bi\375\211\024\273"],
- "Fixnum -2**16" => [-2**16,
+ "Integer -2**16" => [-2**16,
"\004\bi\376\000\000"],
- "Fixnum -2**8" => [-2**8,
+ "Integer -2**8" => [-2**8,
"\004\bi\377\000"],
- "Fixnum -123" => [-123,
+ "Integer -123" => [-123,
"\004\bi\200"],
- "Fixnum -124" => [-124, "\004\bi\377\204"],
- "Fixnum 0" => [0,
+ "Integer -124" => [-124, "\004\bi\377\204"],
+ "Integer 0" => [0,
"\004\bi\000"],
- "Fixnum 5" => [5,
+ "Integer 5" => [5,
"\004\bi\n"],
- "Fixnum 122" => [122, "\004\bi\177"],
- "Fixnum 123" => [123, "\004\bi\001{"],
- "Fixnum 2**8" => [2**8,
+ "Integer 122" => [122, "\004\bi\177"],
+ "Integer 123" => [123, "\004\bi\001{"],
+ "Integer 2**8" => [2**8,
"\004\bi\002\000\001"],
- "Fixnum 2**16" => [2**16,
+ "Integer 2**16" => [2**16,
"\004\bi\003\000\000\001"],
- "Fixnum 2**24" => [2**24,
+ "Integer 2**24" => [2**24,
"\004\bi\004\000\000\000\001"],
- "Bignum 2**64" => [2**64,
+ "Integer 2**64" => [2**64,
"\004\bl+\n\000\000\000\000\000\000\000\000\001\000"],
- "Bignum 2**90" => [2**90,
+ "Integer 2**90" => [2**90,
"\004\bl+\v#{"\000" * 11}\004"],
"Class String" => [String,
"\004\bc\vString"],
@@ -324,7 +457,7 @@ module MarshalSpec
"\x04\bI\"\nsmall\x06:\x06EF"],
"String big" => ['big' * 100,
"\x04\bI\"\x02,\x01bigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbig\x06:\x06EF"],
- "String extended" => [''.extend(Meths), # TODO: check for module on load
+ "String extended" => [''.dup.extend(Meths), # TODO: check for module on load
"\x04\bIe:\nMeths\"\x00\x06:\x06EF"],
"String subclass" => [UserString.new,
"\004\bC:\017UserString\"\000"],
@@ -334,31 +467,31 @@ module MarshalSpec
"\004\b:\010big"],
"Symbol big" => [('big' * 100).to_sym,
"\004\b:\002,\001#{'big' * 100}"],
- "Bignum -2**64" => [-2**64,
+ "Integer -2**64" => [-2**64,
"\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
- "Bignum -2**63" => [-2**63,
+ "Integer -2**63" => [-2**63,
"\004\bl-\t\000\000\000\000\000\000\000\200"],
- "Fixnum -2**24" => [-2**24,
+ "Integer -2**24" => [-2**24,
"\004\bi\375\000\000\000"],
- "Fixnum -2**16" => [-2**16,
+ "Integer -2**16" => [-2**16,
"\004\bi\376\000\000"],
- "Fixnum -2**8" => [-2**8,
+ "Integer -2**8" => [-2**8,
"\004\bi\377\000"],
- "Fixnum -123" => [-123,
+ "Integer -123" => [-123,
"\004\bi\200"],
- "Fixnum 0" => [0,
+ "Integer 0" => [0,
"\004\bi\000"],
- "Fixnum 5" => [5,
+ "Integer 5" => [5,
"\004\bi\n"],
- "Fixnum 2**8" => [2**8,
+ "Integer 2**8" => [2**8,
"\004\bi\002\000\001"],
- "Fixnum 2**16" => [2**16,
+ "Integer 2**16" => [2**16,
"\004\bi\003\000\000\001"],
- "Fixnum 2**24" => [2**24,
+ "Integer 2**24" => [2**24,
"\004\bi\004\000\000\000\001"],
- "Bignum 2**64" => [2**64,
+ "Integer 2**64" => [2**64,
"\004\bl+\n\000\000\000\000\000\000\000\000\001\000"],
- "Bignum 2**90" => [2**90,
+ "Integer 2**90" => [2**90,
"\004\bl+\v#{"\000" * 11}\004"],
"Class String" => [String,
"\004\bc\vString"],
@@ -398,6 +531,20 @@ module MarshalSpec
"\004\bS:\024Struct::Pyramid\000"],
"Random" => random_data,
}
+
+ module DataSpec
+ Measure = Data.define(:amount, :unit)
+ Empty = Data.define
+
+ MeasureExtended = Class.new(Measure)
+ MeasureExtended.extend(Enumerable)
+
+ class MeasureWithOverriddenName < Measure
+ def self.name
+ "Foo"
+ end
+ end
+ end
end
class ArraySub < Array
diff --git a/spec/ruby/core/marshal/fixtures/marshal_multibyte_data.rb b/spec/ruby/core/marshal/fixtures/marshal_multibyte_data.rb
new file mode 100644
index 0000000000..98a0d43392
--- /dev/null
+++ b/spec/ruby/core/marshal/fixtures/marshal_multibyte_data.rb
@@ -0,0 +1,12 @@
+# -*- encoding: utf-8 -*-
+
+module MarshalSpec
+ class MultibyteぁあぃいClass
+ end
+
+ module MultibyteけげこごModule
+ end
+
+ class MultibyteぁあぃいTime < Time
+ end
+end
diff --git a/spec/ruby/core/marshal/float_spec.rb b/spec/ruby/core/marshal/float_spec.rb
index 5793bbd564..5b2d68d6e1 100644
--- a/spec/ruby/core/marshal/float_spec.rb
+++ b/spec/ruby/core/marshal/float_spec.rb
@@ -40,7 +40,7 @@ end
describe "Marshal.load with Float" do
it "loads NaN" do
- Marshal.load("\004\bf\bnan").should be_nan
+ Marshal.load("\004\bf\bnan").should.nan?
end
it "loads +Infinity" do
diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb
index 85e645507f..02c8e7f0b1 100644
--- a/spec/ruby/core/marshal/shared/load.rb
+++ b/spec/ruby/core/marshal/shared/load.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../fixtures/marshal_data'
describe :marshal_load, shared: true do
@@ -8,7 +8,11 @@ describe :marshal_load, shared: true do
it "raises an ArgumentError when the dumped data is truncated" do
obj = {first: 1, second: 2, third: 3}
- -> { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should.raise(ArgumentError, "marshal data too short")
+ end
+
+ it "raises an ArgumentError when the argument is empty String" do
+ -> { Marshal.send(@method, "") }.should.raise(ArgumentError, "marshal data too short")
end
it "raises an ArgumentError when the dumped class is missing" do
@@ -16,10 +20,227 @@ describe :marshal_load, shared: true do
kaboom = Marshal.dump(KaBoom.new)
Object.send(:remove_const, :KaBoom)
- -> { Marshal.send(@method, kaboom) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, kaboom) }.should.raise(ArgumentError)
+ end
+
+ describe "when called with freeze: true" do
+ it "returns frozen strings" do
+ string = Marshal.send(@method, Marshal.dump("foo"), freeze: true)
+ string.should == "foo"
+ string.should.frozen?
+
+ utf8_string = "foo".encode(Encoding::UTF_8)
+ string = Marshal.send(@method, Marshal.dump(utf8_string), freeze: true)
+ string.should == utf8_string
+ string.should.frozen?
+ end
+
+ it "returns frozen arrays" do
+ array = Marshal.send(@method, Marshal.dump([1, 2, 3]), freeze: true)
+ array.should == [1, 2, 3]
+ array.should.frozen?
+ end
+
+ it "returns frozen hashes" do
+ hash = Marshal.send(@method, Marshal.dump({foo: 42}), freeze: true)
+ hash.should == {foo: 42}
+ hash.should.frozen?
+ end
+
+ it "returns frozen regexps" do
+ regexp = Marshal.send(@method, Marshal.dump(/foo/), freeze: true)
+ regexp.should == /foo/
+ regexp.should.frozen?
+ end
+
+ it "returns frozen structs" do
+ struct = Marshal.send(@method, Marshal.dump(MarshalSpec::StructToDump.new(1, 2)), freeze: true)
+ struct.should == MarshalSpec::StructToDump.new(1, 2)
+ struct.should.frozen?
+ end
+
+ it "returns frozen objects" do
+ source_object = Object.new
+
+ object = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ object.should.frozen?
+ end
+
+ describe "deep freezing" do
+ it "returns hashes with frozen keys and values" do
+ key = Object.new
+ value = Object.new
+ source_object = {key => value}
+
+ hash = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ hash.size.should == 1
+ hash.keys[0].should.frozen?
+ hash.values[0].should.frozen?
+ end
+
+ it "returns arrays with frozen elements" do
+ object = Object.new
+ source_object = [object]
+
+ array = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ array.size.should == 1
+ array[0].should.frozen?
+ end
+
+ it "returns structs with frozen members" do
+ object1 = Object.new
+ object2 = Object.new
+ source_object = MarshalSpec::StructToDump.new(object1, object2)
+
+ struct = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ struct.a.should.frozen?
+ struct.b.should.frozen?
+ end
+
+ it "returns objects with frozen instance variables" do
+ source_object = Object.new
+ instance_variable = Object.new
+ source_object.instance_variable_set(:@a, instance_variable)
+
+ object = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ object.instance_variable_get(:@a).should != nil
+ object.instance_variable_get(:@a).should.frozen?
+ end
+
+ it "deduplicates frozen strings" do
+ source_object = ["foo" + "bar", "foobar"]
+ object = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+
+ object[0].should.equal?(object[1])
+ end
+ end
+
+ it "does not freeze modules" do
+ object = Marshal.send(@method, Marshal.dump(Kernel), freeze: true)
+ object.should_not.frozen?
+ Kernel.should_not.frozen?
+ end
+
+ it "does not freeze classes" do
+ object = Marshal.send(@method, Marshal.dump(Object), freeze: true)
+ object.should_not.frozen?
+ Object.should_not.frozen?
+ end
+
+ it "does freeze extended objects" do
+ object = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x00", freeze: true)
+ object.should.frozen?
+ end
+
+ it "does freeze extended objects with instance variables" do
+ object = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x06:\n@ivarT", freeze: true)
+ object.should.frozen?
+ end
+
+ it "returns frozen object having #_dump method" do
+ object = Marshal.send(@method, Marshal.dump(UserDefined.new), freeze: true)
+ object.should.frozen?
+ end
+
+ it "returns frozen object responding to #marshal_dump and #marshal_load" do
+ object = Marshal.send(@method, Marshal.dump(UserMarshal.new), freeze: true)
+ object.should.frozen?
+ end
+
+ it "returns frozen object extended by a module" do
+ object = Object.new
+ object.extend(MarshalSpec::ModuleToExtendBy)
+
+ object = Marshal.send(@method, Marshal.dump(object), freeze: true)
+ object.should.frozen?
+ end
+
+ it "does not call freeze method" do
+ object = MarshalSpec::ObjectWithFreezeRaisingException.new
+ object = Marshal.send(@method, Marshal.dump(object), freeze: true)
+ object.should.frozen?
+ end
+
+ it "returns frozen object even if object does not respond to freeze method" do
+ object = MarshalSpec::ObjectWithoutFreeze.new
+ object = Marshal.send(@method, Marshal.dump(object), freeze: true)
+ object.should.frozen?
+ end
+
+ it "returns a frozen object when is an instance of String/Array/Regexp/Hash subclass and has instance variables" do
+ source_object = UserString.new
+ source_object.instance_variable_set(:@foo, "bar")
+
+ object = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ object.should.frozen?
+ end
+
+ describe "when called with a proc" do
+ it "call the proc with frozen objects" 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,
+ freeze: true,
+ )
+
+ 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]],
+ ]
+
+ arr.each do |v|
+ v.should.frozen?
+ end
+
+ Struct.send(:remove_const, :Brittle)
+ end
+
+ it "does not freeze the object returned by the proc" do
+ string = Marshal.send(@method, Marshal.dump("foo"), proc { |o| o.upcase }, freeze: true)
+ string.should == "FOO"
+ string.should_not.frozen?
+ end
+ end
end
describe "when called with a proc" 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 "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
+
+ it "call the proc with extended objects" do
+ objs = []
+ obj = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x00", Proc.new { |o| objs << o; o })
+ objs.should == [obj]
+ end
+
it "returns the value of the proc" do
Marshal.send(@method, Marshal.dump([1,2]), proc { [3,4] }).should == [3,4]
end
@@ -28,16 +249,15 @@ describe :marshal_load, shared: true 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
+ 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
it "loads an Array with proc" do
arr = []
- s = 'hi'
+ s = +'hi'
s.instance_variable_set(:@foo, 5)
st = Struct.new("Brittle", :a).new
st.instance_variable_set(:@clue, 'none')
@@ -48,13 +268,14 @@ describe :marshal_load, shared: true do
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}
+ 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 == ["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]
-
+ 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
@@ -76,12 +297,24 @@ describe :marshal_load, shared: true do
UserPreviouslyDefinedWithInitializedIvar.should_receive(:_load).and_return(UserPreviouslyDefinedWithInitializedIvar.new)
marshaled_obj = Marshal.send(@method, dump_str)
- marshaled_obj.should be_an_instance_of(UserPreviouslyDefinedWithInitializedIvar)
- marshaled_obj.field1.should be_nil
- marshaled_obj.field2.should be_nil
+ marshaled_obj.should.instance_of?(UserPreviouslyDefinedWithInitializedIvar)
+ marshaled_obj.field1.should == nil
+ marshaled_obj.field2.should == nil
+ end
+
+ it "loads the String in non US-ASCII and non UTF-8 encoding" do
+ source_object = UserDefinedString.new("a".encode("windows-1251"))
+ object = Marshal.send(@method, Marshal.dump(source_object))
+ object.string.should == "a".encode("windows-1251")
+ end
+
+ it "loads the String in multibyte encoding" do
+ source_object = UserDefinedString.new("a".encode("utf-32le"))
+ object = Marshal.send(@method, Marshal.dump(source_object))
+ object.string.should == "a".encode("utf-32le")
end
- describe "that return an immediate value" do
+ describe "that returns an immediate value" do
it "loads an array containing an instance of the object, followed by multiple instances of another object" do
str = "string"
@@ -121,26 +354,35 @@ describe :marshal_load, shared: true do
it "loads an array containing objects having _dump method, and with proc" do
arr = []
- myproc = Proc.new { |o| arr << o; o }
+ 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)
- arr.should == [o1, o2, o1, o2, obj]
+ arr[0].should == o1
+ arr[1].should == o2
+ arr[2].should == obj
+ arr.size.should == 3
end
it "loads an array containing objects having marshal_dump method, and with proc" do
arr = []
- proc = Proc.new { |o| arr << o; o }
+ proc = Proc.new { |o| arr << o.dup; o }
o1 = UserMarshal.new
o2 = UserMarshalWithIvar.new
- obj = [o1, o2, o1, o2]
Marshal.send(@method, "\004\b[\tU:\020UserMarshal\"\nstuffU:\030UserMarshalWithIvar[\006\"\fmy data@\006@\b", proc)
- arr.should == ['stuff', o1, 'my data', ['my data'], o2, o1, o2, obj]
+ 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.size.should == 6
end
it "assigns classes to nested subclasses of Array correctly" do
@@ -157,113 +399,30 @@ describe :marshal_load, shared: true do
end
it "raises a TypeError with bad Marshal version" do
- marshal_data = '\xff\xff'
+ marshal_data = +'\xff\xff'
marshal_data[0] = (Marshal::MAJOR_VERSION).chr
marshal_data[1] = (Marshal::MINOR_VERSION + 1).chr
- -> { Marshal.send(@method, marshal_data) }.should raise_error(TypeError)
+ -> { Marshal.send(@method, marshal_data) }.should.raise(TypeError)
- marshal_data = '\xff\xff'
+ marshal_data = +'\xff\xff'
marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
marshal_data[1] = (Marshal::MINOR_VERSION).chr
- -> { Marshal.send(@method, marshal_data) }.should raise_error(TypeError)
+ -> { Marshal.send(@method, marshal_data) }.should.raise(TypeError)
end
it "raises EOFError on loading an empty file" do
temp_file = tmp("marshal.rubyspec.tmp.#{Process.pid}")
file = File.new(temp_file, "w+")
begin
- -> { Marshal.send(@method, file) }.should raise_error(EOFError)
+ -> { Marshal.send(@method, file) }.should.raise(EOFError)
ensure
file.close
rm_r temp_file
end
end
- ruby_version_is ''...'2.7' do
- it "returns an untainted object if source is untainted" do
- x = Object.new
- y = Marshal.send(@method, Marshal.dump(x))
- y.tainted?.should be_false
- end
-
- describe "when source is tainted" do
- it "returns a tainted object" do
- x = Object.new
- x.taint
- s = Marshal.dump(x)
- y = Marshal.send(@method, s)
- y.tainted?.should be_true
-
- # note that round-trip via Marshal does not preserve
- # the taintedness at each level of the nested structure
- y = Marshal.send(@method, Marshal.dump([[x]]))
- y.tainted?.should be_true
- y.first.tainted?.should be_true
- y.first.first.tainted?.should be_true
- end
-
- it "does not taint Symbols" do
- x = [:x]
- y = Marshal.send(@method, Marshal.dump(x).taint)
- y.tainted?.should be_true
- y.first.tainted?.should be_false
- end
-
- it "does not taint Fixnums" do
- x = [1]
- y = Marshal.send(@method, Marshal.dump(x).taint)
- y.tainted?.should be_true
- y.first.tainted?.should be_false
- end
-
- it "does not taint Bignums" do
- x = [bignum_value]
- y = Marshal.send(@method, Marshal.dump(x).taint)
- y.tainted?.should be_true
- y.first.tainted?.should be_false
- end
-
- it "does not taint Floats" do
- x = [1.2]
- y = Marshal.send(@method, Marshal.dump(x).taint)
- y.tainted?.should be_true
- y.first.tainted?.should be_false
- end
- end
-
- it "preserves taintedness of nested structure" do
- x = Object.new
- a = [[x]]
- x.taint
- y = Marshal.send(@method, Marshal.dump(a))
- y.tainted?.should be_true
- y.first.tainted?.should be_true
- y.first.first.tainted?.should be_true
- end
-
- it "returns a trusted object if source is trusted" do
- x = Object.new
- y = Marshal.send(@method, Marshal.dump(x))
- y.untrusted?.should be_false
- end
-
- it "returns an untrusted object if source is untrusted" do
- x = Object.new
- x.untrust
- y = Marshal.send(@method, Marshal.dump(x))
- y.untrusted?.should be_true
-
- # note that round-trip via Marshal does not preserve
- # the untrustedness at each level of the nested structure
- y = Marshal.send(@method, Marshal.dump([[x]]))
- y.untrusted?.should be_true
- y.first.untrusted?.should be_true
- y.first.first.untrusted?.should be_true
- end
- end
-
# Note: Ruby 1.9 should be compatible with older marshal format
MarshalSpec::DATA.each do |description, (object, marshal, attributes)|
it "loads a #{description}" do
@@ -297,19 +456,20 @@ describe :marshal_load, shared: true do
end
it "loads an array having ivar" do
- s = 'well'
+ s = +'well'
s.instance_variable_set(:@foo, 10)
obj = ['5', s, 'hi'].extend(Meths, MethsMore)
obj.instance_variable_set(:@mix, s)
new_obj = Marshal.send(@method, "\004\bI[\b\"\0065I\"\twell\006:\t@fooi\017\"\ahi\006:\t@mix@\a")
new_obj.should == obj
- new_obj.instance_variable_get(:@mix).should equal new_obj[1]
+ new_obj.instance_variable_get(:@mix).should.equal? new_obj[1]
new_obj[1].instance_variable_get(:@foo).should == 10
end
it "loads an extended Array object containing a user-marshaled object" do
obj = [UserMarshal.new, UserMarshal.new].extend(Meths)
- new_obj = Marshal.send(@method, "\x04\be:\nMeths[\ao:\x10UserMarshal\x06:\n@dataI\"\nstuff\x06:\x06ETo;\x06\x06;\aI\"\nstuff\x06;\bT")
+ dump = "\x04\be:\nMeths[\ao:\x10UserMarshal\x06:\n@dataI\"\nstuff\x06:\x06ETo;\x06\x06;\aI\"\nstuff\x06;\bT"
+ new_obj = Marshal.send(@method, dump)
new_obj.should == obj
obj_ancestors = class << obj; ancestors[1..-1]; end
@@ -342,7 +502,7 @@ describe :marshal_load, shared: true do
end
it "preserves hash ivars when hash contains a string having ivar" do
- s = 'string'
+ s = +'string'
s.instance_variable_set :@string_ivar, 'string ivar'
h = { key: s }
h.instance_variable_set :@hash_ivar, 'hash ivar'
@@ -351,6 +511,36 @@ describe :marshal_load, shared: true do
unmarshalled.instance_variable_get(:@hash_ivar).should == 'hash ivar'
unmarshalled[:key].instance_variable_get(:@string_ivar).should == 'string ivar'
end
+
+ it "preserves compare_by_identity behaviour" do
+ h = { a: 1 }
+ h.compare_by_identity
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.should.compare_by_identity?
+
+ h = { a: 1 }
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.should_not.compare_by_identity?
+ end
+
+ it "preserves compare_by_identity behaviour for a Hash subclass" do
+ h = UserHash.new({ a: 1 })
+ h.compare_by_identity
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.should.compare_by_identity?
+
+ h = UserHash.new({ a: 1 })
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.should_not.compare_by_identity?
+ end
+
+ it "allocates an instance of the proper class when Hash subclass with compare_by_identity behaviour" do
+ h = UserHash.new({ a: 1 })
+ h.compare_by_identity
+
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.should.kind_of?(UserHash)
+ end
end
describe "for a Symbol" do
@@ -394,16 +584,42 @@ describe :marshal_load, shared: true do
end
it "loads a binary encoded Symbol" do
- s = "\u2192".force_encoding("binary").to_sym
+ s = "\u2192".dup.force_encoding("binary").to_sym
sym = Marshal.send(@method, "\x04\b:\b\xE2\x86\x92")
sym.should == s
sym.encoding.should == Encoding::BINARY
end
+
+ it "loads multiple Symbols sharing the same encoding" do
+ # Note that the encoding is a link for the second Symbol
+ symbol1 = "I:\t\xE2\x82\xACa\x06:\x06ET"
+ symbol2 = "I:\t\xE2\x82\xACb\x06;\x06T"
+ dump = "\x04\b[\a#{symbol1}#{symbol2}"
+ value = Marshal.send(@method, dump)
+ value.map(&:encoding).should == [Encoding::UTF_8, Encoding::UTF_8]
+ expected = [
+ "€a".dup.force_encoding(Encoding::UTF_8).to_sym,
+ "€b".dup.force_encoding(Encoding::UTF_8).to_sym
+ ]
+ value.should == expected
+
+ value = Marshal.send(@method, "\x04\b[\b#{symbol1}#{symbol2};\x00")
+ value.map(&:encoding).should == [Encoding::UTF_8, Encoding::UTF_8, Encoding::UTF_8]
+ value.should == [*expected, expected[0]]
+ end
+
+ it "raises ArgumentError when end of byte sequence reached before symbol characters end" do
+ Marshal.dump(:hello).should == "\x04\b:\nhello"
+
+ -> {
+ Marshal.send(@method, "\x04\b:\nhel")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
end
describe "for a String" do
it "loads a string having ivar with ref to self" do
- obj = 'hi'
+ obj = +'hi'
obj.instance_variable_set(:@self, obj)
Marshal.send(@method, "\004\bI\"\ahi\006:\n@self@\000").should == obj
end
@@ -414,6 +630,12 @@ describe :marshal_load, shared: true do
Marshal.send(@method, StringIO.new(Marshal.dump(obj))).should == obj
end
+ it "sets binmode if it is loading through StringIO stream" do
+ io = StringIO.new("\004\b:\vsymbol")
+ def io.binmode; raise "binmode"; end
+ -> { Marshal.load(io) }.should.raise(RuntimeError, "binmode")
+ end
+
it "loads a string with an ivar" do
str = Marshal.send(@method, "\x04\bI\"\x00\x06:\t@fooI\"\bbar\x06:\x06EF")
str.instance_variable_get("@foo").should == "bar"
@@ -421,59 +643,70 @@ describe :marshal_load, shared: true do
it "loads a String subclass with custom constructor" do
str = Marshal.send(@method, "\x04\bC: UserCustomConstructorString\"\x00")
- str.should be_an_instance_of(UserCustomConstructorString)
+ str.should.instance_of?(UserCustomConstructorString)
end
it "loads a US-ASCII String" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
data = "\x04\bI\"\babc\x06:\x06EF"
result = Marshal.send(@method, data)
result.should == str
- result.encoding.should equal(Encoding::US_ASCII)
+ result.encoding.should.equal?(Encoding::US_ASCII)
end
it "loads a UTF-8 String" do
- str = "\x6d\xc3\xb6\x68\x72\x65".force_encoding("utf-8")
+ str = "\x6d\xc3\xb6\x68\x72\x65".dup.force_encoding("utf-8")
data = "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET"
result = Marshal.send(@method, data)
result.should == str
- result.encoding.should equal(Encoding::UTF_8)
+ result.encoding.should.equal?(Encoding::UTF_8)
end
it "loads a String in another encoding" do
- str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".force_encoding("utf-16le")
+ str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".dup.force_encoding("utf-16le")
data = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE"
result = Marshal.send(@method, data)
result.should == str
- result.encoding.should equal(Encoding::UTF_16LE)
+ result.encoding.should.equal?(Encoding::UTF_16LE)
end
it "loads a String as BINARY if no encoding is specified at the end" do
- str = "\xC3\xB8".force_encoding("BINARY")
- data = "\x04\b\"\a\xC3\xB8".force_encoding("UTF-8")
+ str = "\xC3\xB8".dup.force_encoding("BINARY")
+ data = "\x04\b\"\a\xC3\xB8".dup.force_encoding("UTF-8")
result = Marshal.send(@method, data)
result.encoding.should == Encoding::BINARY
result.should == str
end
+
+ it "raises ArgumentError when end of byte sequence reached before string characters end" do
+ Marshal.dump("hello").should == "\x04\b\"\nhello"
+
+ -> {
+ Marshal.send(@method, "\x04\b\"\nhel")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
end
describe "for a Struct" do
it "loads a extended_struct having fields with same objects" do
s = 'hi'
- obj = Struct.new("Ure2", :a, :b).new.extend(Meths)
+ obj = Struct.new("Extended", :a, :b).new.extend(Meths)
+ dump = "\004\be:\nMethsS:\025Struct::Extended\a:\006a0:\006b0"
+ Marshal.send(@method, dump).should == obj
+
obj.a = [:a, s]
obj.b = [:Meths, s]
-
- Marshal.send(@method,
- "\004\be:\nMethsS:\021Struct::Ure2\a:\006a[\a;\a\"\ahi:\006b[\a;\000@\a"
- ).should == obj
- Struct.send(:remove_const, :Ure2)
+ dump = "\004\be:\nMethsS:\025Struct::Extended\a:\006a[\a;\a\"\ahi:\006b[\a;\000@\a"
+ Marshal.send(@method, dump).should == obj
+ Struct.send(:remove_const, :Extended)
end
it "loads a struct having ivar" do
obj = Struct.new("Thick").new
obj.instance_variable_set(:@foo, 5)
- Marshal.send(@method, "\004\bIS:\022Struct::Thick\000\006:\t@fooi\n").should == obj
+ reloaded = Marshal.send(@method, "\004\bIS:\022Struct::Thick\000\006:\t@fooi\n")
+ reloaded.should == obj
+ reloaded.instance_variable_get(:@foo).should == 5
Struct.send(:remove_const, :Thick)
end
@@ -500,6 +733,32 @@ describe :marshal_load, shared: true do
end
end
+ describe "for a Data" do
+ it "loads a Data" do
+ obj = MarshalSpec::DataSpec::Measure.new(100, 'km')
+ dumped = "\x04\bS:#MarshalSpec::DataSpec::Measure\a:\vamountii:\tunit\"\akm"
+ Marshal.dump(obj).should == dumped
+
+ Marshal.send(@method, dumped).should == obj
+ end
+
+ it "loads an extended Data" do
+ obj = MarshalSpec::DataSpec::MeasureExtended.new(100, "km")
+ dumped = "\x04\bS:+MarshalSpec::DataSpec::MeasureExtended\a:\vamountii:\tunit\"\akm"
+ Marshal.dump(obj).should == dumped
+
+ Marshal.send(@method, dumped).should == obj
+ end
+
+ it "returns a frozen object" do
+ obj = MarshalSpec::DataSpec::Measure.new(100, 'km')
+ dumped = "\x04\bS:#MarshalSpec::DataSpec::Measure\a:\vamountii:\tunit\"\akm"
+ Marshal.dump(obj).should == dumped
+
+ Marshal.send(@method, dumped).should.frozen?
+ end
+ end
+
describe "for an Exception" do
it "loads a marshalled exception with no message" do
obj = Exception.new
@@ -548,7 +807,7 @@ describe :marshal_load, shared: true do
describe "for an Object" do
it "loads an object" do
- Marshal.send(@method, "\004\bo:\vObject\000").should be_kind_of(Object)
+ Marshal.send(@method, "\004\bo:\vObject\000").should.is_a?(Object)
end
it "loads an extended Object" do
@@ -574,7 +833,7 @@ describe :marshal_load, shared: true do
end
it "loads an Object with a non-US-ASCII instance variable" do
- ivar = "@é".force_encoding(Encoding::UTF_8).to_sym
+ ivar = "@é".dup.force_encoding(Encoding::UTF_8).to_sym
obj = Marshal.send(@method, "\x04\bo:\vObject\x06I:\b@\xC3\xA9\x06:\x06ETi\x06")
obj.instance_variables.should == [ivar]
obj.instance_variables[0].encoding.should == Encoding::UTF_8
@@ -584,7 +843,27 @@ describe :marshal_load, shared: true do
it "raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class" do
-> do
Marshal.send(@method, "\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd")
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
+ end
+
+ it "raises ArgumentError when end of byte sequence reached before class name end" do
+ Marshal.dump(Object.new).should == "\x04\bo:\vObject\x00"
+
+ -> {
+ Marshal.send(@method, "\x04\bo:\vObj")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
+ end
+
+ describe "for an object responding to #marshal_dump and #marshal_load" do
+ it "loads a user-marshaled object" do
+ obj = UserMarshal.new
+ obj.data = :data
+ value = [obj, :data]
+ dump = Marshal.dump(value)
+ dump.should == "\x04\b[\aU:\x10UserMarshal:\tdata;\x06"
+ reloaded = Marshal.load(dump)
+ reloaded.should == value
end
end
@@ -600,7 +879,7 @@ describe :marshal_load, shared: true do
end
it "loads a UserObject" do
- Marshal.send(@method, "\004\bo:\017UserObject\000").should be_kind_of(UserObject)
+ Marshal.send(@method, "\004\bo:\017UserObject\000").should.is_a?(UserObject)
end
describe "that extends a core type other than Object or BasicObject" do
@@ -613,26 +892,64 @@ describe :marshal_load, shared: true do
data = Marshal.dump(MarshalSpec::SwappedClass.new)
MarshalSpec.set_swapped_class(Class.new(Array))
- -> { Marshal.send(@method, data) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, data) }.should.raise(ArgumentError)
MarshalSpec.set_swapped_class(Class.new)
- -> { Marshal.send(@method, data) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, data) }.should.raise(ArgumentError)
end
end
end
describe "for a Regexp" do
- it "loads an extended Regexp" do
- obj = /[a-z]/.dup.extend(Meths, MethsMore)
- new_obj = Marshal.send(@method, "\004\be:\nMethse:\016MethsMore/\n[a-z]\000")
+ ruby_version_is "4.1" do
+ it "raises FrozenError for an extended Regexp" do
+ -> {
+ Marshal.send(@method, "\004\be:\nMethse:\016MethsMore/\n[a-z]\000")
+ }.should.raise(FrozenError)
+ end
+
+ it "raises FrozenError when regexp has instance variables" do
+ -> {
+ Marshal.send(@method, "\x04\bI/\nhello\x00\a:\x06EF:\x11@regexp_ivar[\x06i/")
+ }.should.raise(FrozenError)
+ end
+ end
+
+ ruby_version_is ""..."4.1" do
+ it "loads an extended Regexp" do
+ obj = /[a-z]/.dup.extend(Meths, MethsMore)
+ new_obj = Marshal.send(@method, "\004\be:\nMethse:\016MethsMore/\n[a-z]\000")
+
+ new_obj.should == obj
+ new_obj_metaclass_ancestors = class << new_obj; ancestors; end
+ new_obj_metaclass_ancestors[@num_self_class, 3].should ==
+ [Meths, MethsMore, Regexp]
+ end
+
+ it "restore the regexp instance variables" do
+ obj = Regexp.new("hello")
+ obj.instance_variable_set(:@regexp_ivar, [42])
+
+ new_obj = Marshal.send(@method, "\x04\bI/\nhello\x00\a:\x06EF:\x11@regexp_ivar[\x06i/")
+ new_obj.instance_variables.should == [:@regexp_ivar]
+ new_obj.instance_variable_get(:@regexp_ivar).should == [42]
+ end
+ end
+
+ it "loads a Regexp subclass instance variables" do
+ obj = UserRegexp.new('abc')
+ obj.instance_variable_set(:@noise, 'much')
+
+ new_obj = Marshal.send(@method, Marshal.dump(obj))
new_obj.should == obj
+ new_obj.instance_variable_get(:@noise).should == 'much'
new_obj_metaclass_ancestors = class << new_obj; ancestors; end
- new_obj_metaclass_ancestors[@num_self_class, 3].should ==
- [Meths, MethsMore, Regexp]
+ new_obj_metaclass_ancestors[@num_self_class, 2].should ==
+ [UserRegexp, Regexp]
end
- it "loads a extended_user_regexp having ivar" do
+ it "loads a Regexp subclass instance variables when it is extended with a module" do
obj = UserRegexp.new('').extend(Meths)
obj.instance_variable_set(:@noise, 'much')
@@ -644,6 +961,22 @@ describe :marshal_load, shared: true do
new_obj_metaclass_ancestors[@num_self_class, 3].should ==
[Meths, UserRegexp, Regexp]
end
+
+ it "preserves Regexp encoding" do
+ source_object = Regexp.new("a".encode("utf-32le"))
+ regexp = Marshal.send(@method, Marshal.dump(source_object))
+
+ regexp.encoding.should == Encoding::UTF_32LE
+ regexp.source.should == "a".encode("utf-32le")
+ end
+
+ it "raises ArgumentError when end of byte sequence reached before source string end" do
+ Marshal.dump(/hello world/).should == "\x04\bI/\x10hello world\x00\x06:\x06EF"
+
+ -> {
+ Marshal.send(@method, "\x04\bI/\x10hel")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
end
describe "for a Float" do
@@ -665,9 +998,17 @@ describe :marshal_load, shared: true do
obj = 1.1867345e+22
Marshal.send(@method, "\004\bf\0361.1867344999999999e+22\000\344@").should == obj
end
+
+ it "raises ArgumentError when end of byte sequence reached before float string representation end" do
+ Marshal.dump(1.3).should == "\x04\bf\b1.3"
+
+ -> {
+ Marshal.send(@method, "\004\bf\v1")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
end
- describe "for a Integer" do
+ describe "for an Integer" do
it "loads 0" do
Marshal.send(@method, "\004\bi\000").should == 0
Marshal.send(@method, "\004\bi\005").should == 0
@@ -717,7 +1058,7 @@ describe :marshal_load, shared: true do
"\004\bi\004\0",
"\004\bi\004\0\0",
"\004\bi\004\0\0\0"].each do |invalid|
- -> { Marshal.send(@method, invalid) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, invalid) }.should.raise(ArgumentError)
end
end
@@ -730,29 +1071,33 @@ describe :marshal_load, shared: true do
describe "for a Rational" do
it "loads" do
- Marshal.send(@method, Marshal.dump(Rational(1, 3))).should == Rational(1, 3)
+ r = Marshal.send(@method, Marshal.dump(Rational(1, 3)))
+ r.should == Rational(1, 3)
+ r.should.frozen?
end
end
describe "for a Complex" do
it "loads" do
- Marshal.send(@method, Marshal.dump(Complex(4, 3))).should == Complex(4, 3)
+ c = Marshal.send(@method, Marshal.dump(Complex(4, 3)))
+ c.should == Complex(4, 3)
+ c.should.frozen?
end
end
describe "for a Bignum" do
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
context "that is Bignum on 32-bit platforms but Fixnum on 64-bit" do
it "dumps a Fixnum" do
val = Marshal.send(@method, "\004\bl+\ab:wU")
val.should == 1433877090
- val.class.should == Fixnum
+ val.class.should == Integer
end
it "dumps an array containing multiple references to the Bignum as an array of Fixnum" do
arr = Marshal.send(@method, "\004\b[\al+\a\223BwU@\006")
arr.should == [1433879187, 1433879187]
- arr.each { |v| v.class.should == Fixnum }
+ arr.each { |v| v.class.should == Integer }
end
end
end
@@ -774,37 +1119,67 @@ describe :marshal_load, shared: true do
t = Time.new
t1, t2 = Marshal.send(@method, Marshal.dump([t, t]))
- t1.should equal t2
+ t1.should.equal? t2
end
- it "loads the zone" do
+ it "keeps the local zone" do
with_timezone 'AST', 3 do
t = Time.local(2012, 1, 1)
Marshal.send(@method, Marshal.dump(t)).zone.should == t.zone
end
end
- it "loads nanoseconds" do
+ it "keeps UTC zone" do
+ t = Time.now.utc
+ t2 = Marshal.send(@method, Marshal.dump(t))
+ t2.should.utc?
+ end
+
+ it "keeps the zone" do
+ t = nil
+
+ with_timezone 'AST', 4 do
+ t = Time.local(2012, 1, 1)
+ end
+
+ with_timezone 'EET', -2 do
+ Marshal.send(@method, Marshal.dump(t)).zone.should == 'AST'
+ end
+ end
+
+ it "keeps utc offset" do
+ t = Time.new(2007,11,1,15,25,0, "+09:00")
+ t2 = Marshal.send(@method, Marshal.dump(t))
+ t2.utc_offset.should == 32400
+ end
+
+ it "keeps nanoseconds" do
t = Time.now
Marshal.send(@method, Marshal.dump(t)).nsec.should == t.nsec
end
+
+ it "does not add any additional instance variable" do
+ t = Time.now
+ t2 = Marshal.send(@method, Marshal.dump(t))
+ t2.instance_variables.should.empty?
+ end
end
describe "for nil" do
it "loads" do
- Marshal.send(@method, "\x04\b0").should be_nil
+ Marshal.send(@method, "\x04\b0").should == nil
end
end
describe "for true" do
it "loads" do
- Marshal.send(@method, "\x04\bT").should be_true
+ Marshal.send(@method, "\x04\bT").should == true
end
end
describe "for false" do
it "loads" do
- Marshal.send(@method, "\x04\bF").should be_false
+ Marshal.send(@method, "\x04\bF").should == false
end
end
@@ -814,11 +1189,19 @@ describe :marshal_load, shared: true do
end
it "raises ArgumentError if given the name of a non-Module" do
- -> { Marshal.send(@method, "\x04\bc\vKernel") }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, "\x04\bc\vKernel") }.should.raise(ArgumentError)
end
it "raises ArgumentError if given a nonexistent class" do
- -> { Marshal.send(@method, "\x04\bc\vStrung") }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, "\x04\bc\vStrung") }.should.raise(ArgumentError)
+ end
+
+ it "raises ArgumentError when end of byte sequence reached before class name end" do
+ Marshal.dump(String).should == "\x04\bc\vString"
+
+ -> {
+ Marshal.send(@method, "\x04\bc\vStr")
+ }.should.raise(ArgumentError, "marshal data too short")
end
end
@@ -828,12 +1211,20 @@ describe :marshal_load, shared: true do
end
it "raises ArgumentError if given the name of a non-Class" do
- -> { Marshal.send(@method, "\x04\bm\vString") }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, "\x04\bm\vString") }.should.raise(ArgumentError)
end
it "loads an old module" do
Marshal.send(@method, "\x04\bM\vKernel").should == Kernel
end
+
+ it "raises ArgumentError when end of byte sequence reached before module name end" do
+ Marshal.dump(Kernel).should == "\x04\bm\vKernel"
+
+ -> {
+ Marshal.send(@method, "\x04\bm\vKer")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
end
describe "for a wrapped C pointer" do
@@ -867,13 +1258,13 @@ describe :marshal_load, shared: true do
data = "\x04\bd:\x1AUnloadableDumpableDirI\"\x06.\x06:\x06ET"
- -> { Marshal.send(@method, data) }.should raise_error(TypeError)
+ -> { Marshal.send(@method, data) }.should.raise(TypeError)
end
it "raises ArgumentError when the local class is a regular object" do
data = "\004\bd:\020UserDefined\0"
- -> { Marshal.send(@method, data) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, data) }.should.raise(ArgumentError)
end
end
@@ -886,7 +1277,7 @@ describe :marshal_load, shared: true do
it "raises an ArgumentError" do
message = "undefined class/module NamespaceTest::SameName"
- -> { Marshal.send(@method, @data) }.should raise_error(ArgumentError, message)
+ -> { Marshal.send(@method, @data) }.should.raise(ArgumentError, message)
end
end
@@ -895,6 +1286,6 @@ describe :marshal_load, shared: true do
@data = Marshal.dump(NamespaceTest::KaBoom.new)
NamespaceTest.send(:remove_const, :KaBoom)
- -> { Marshal.send(@method, @data) }.should raise_error(ArgumentError, /NamespaceTest::KaBoom/)
+ -> { Marshal.send(@method, @data) }.should.raise(ArgumentError, /NamespaceTest::KaBoom/)
end
end
diff --git a/spec/ruby/core/matchdata/allocate_spec.rb b/spec/ruby/core/matchdata/allocate_spec.rb
index 9f3ada4018..f41e2d5481 100644
--- a/spec/ruby/core/matchdata/allocate_spec.rb
+++ b/spec/ruby/core/matchdata/allocate_spec.rb
@@ -1,10 +1,8 @@
require_relative '../../spec_helper'
describe "MatchData.allocate" do
- ruby_version_is "2.7" do
- it "is undefined" do
- # https://bugs.ruby-lang.org/issues/16294
- -> { MatchData.allocate }.should raise_error(NoMethodError)
- end
+ it "is undefined" do
+ # https://bugs.ruby-lang.org/issues/16294
+ -> { MatchData.allocate }.should.raise(NoMethodError)
end
end
diff --git a/spec/ruby/core/matchdata/begin_spec.rb b/spec/ruby/core/matchdata/begin_spec.rb
index 85c454da56..b4be077ae4 100644
--- a/spec/ruby/core/matchdata/begin_spec.rb
+++ b/spec/ruby/core/matchdata/begin_spec.rb
@@ -12,7 +12,7 @@ describe "MatchData#begin" do
it "returns nil when the nth match isn't found" do
match_data = /something is( not)? (right)/.match("something is right")
- match_data.begin(1).should be_nil
+ match_data.begin(1).should == nil
end
it "returns the character offset for multi-byte strings" do
@@ -36,6 +36,18 @@ describe "MatchData#begin" do
match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
match_data.begin(obj).should == 2
end
+
+ it "raises IndexError if index is out of bounds" do
+ match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ match_data.begin(-1)
+ }.should.raise(IndexError, "index -1 out of matches")
+
+ -> {
+ match_data.begin(3)
+ }.should.raise(IndexError, "index 3 out of matches")
+ end
end
context "when passed a String argument" do
@@ -68,6 +80,14 @@ describe "MatchData#begin" do
match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
match_data.begin("æ").should == 1
end
+
+ it "raises IndexError if there is no group with the provided name" do
+ match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ match_data.begin("y")
+ }.should.raise(IndexError, "undefined group name reference: y")
+ end
end
context "when passed a Symbol argument" do
@@ -100,5 +120,13 @@ describe "MatchData#begin" do
match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
match_data.begin(:æ).should == 1
end
+
+ it "raises IndexError if there is no group with the provided name" do
+ match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ match_data.begin(:y)
+ }.should.raise(IndexError, "undefined group name reference: y")
+ end
end
end
diff --git a/spec/ruby/core/matchdata/bytebegin_spec.rb b/spec/ruby/core/matchdata/bytebegin_spec.rb
new file mode 100644
index 0000000000..fa44ec3b41
--- /dev/null
+++ b/spec/ruby/core/matchdata/bytebegin_spec.rb
@@ -0,0 +1,132 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.4" do
+ describe "MatchData#bytebegin" do
+ context "when passed an integer argument" do
+ it "returns the byte-based offset of the start of the nth element" do
+ match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ match_data.bytebegin(0).should == 1
+ match_data.bytebegin(2).should == 2
+ end
+
+ it "returns nil when the nth match isn't found" do
+ match_data = /something is( not)? (right)/.match("something is right")
+ match_data.bytebegin(1).should == nil
+ end
+
+ it "returns the byte-based offset for multi-byte strings" do
+ match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.")
+ match_data.bytebegin(0).should == 1
+ match_data.bytebegin(2).should == 3
+ end
+
+ not_supported_on :opal do
+ it "returns the byte-based offset for multi-byte strings with unicode regexp" do
+ match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.")
+ match_data.bytebegin(0).should == 1
+ match_data.bytebegin(2).should == 3
+ end
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+
+ match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ match_data.bytebegin(obj).should == 2
+ end
+
+ it "raises IndexError if index is out of bounds" do
+ match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ match_data.bytebegin(-1)
+ }.should.raise(IndexError, "index -1 out of matches")
+
+ -> {
+ match_data.bytebegin(3)
+ }.should.raise(IndexError, "index 3 out of matches")
+ end
+ end
+
+ context "when passed a String argument" do
+ it "return the byte-based offset of the start of the named capture" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
+ match_data.bytebegin("a").should == 1
+ match_data.bytebegin("b").should == 3
+ end
+
+ it "returns the byte-based offset for multi byte strings" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.")
+ match_data.bytebegin("a").should == 1
+ match_data.bytebegin("b").should == 4
+ end
+
+ not_supported_on :opal do
+ it "returns the byte-based offset for multi byte strings with unicode regexp" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.")
+ match_data.bytebegin("a").should == 1
+ match_data.bytebegin("b").should == 4
+ end
+ end
+
+ it "returns the byte-based offset for the farthest match when multiple named captures use the same name" do
+ match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.")
+ match_data.bytebegin("a").should == 3
+ end
+
+ it "returns the byte-based offset for multi-byte names" do
+ match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
+ match_data.bytebegin("æ").should == 1
+ end
+
+ it "raises IndexError if there is no group with the provided name" do
+ match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ match_data.bytebegin("y")
+ }.should.raise(IndexError, "undefined group name reference: y")
+ end
+ end
+
+ context "when passed a Symbol argument" do
+ it "return the byte-based offset of the start of the named capture" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
+ match_data.bytebegin(:a).should == 1
+ match_data.bytebegin(:b).should == 3
+ end
+
+ it "returns the byte-based offset for multi byte strings" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.")
+ match_data.bytebegin(:a).should == 1
+ match_data.bytebegin(:b).should == 4
+ end
+
+ not_supported_on :opal do
+ it "returns the byte-based offset for multi byte strings with unicode regexp" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.")
+ match_data.bytebegin(:a).should == 1
+ match_data.bytebegin(:b).should == 4
+ end
+ end
+
+ it "returns the byte-based offset for the farthest match when multiple named captures use the same name" do
+ match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.")
+ match_data.bytebegin(:a).should == 3
+ end
+
+ it "returns the byte-based offset for multi-byte names" do
+ match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
+ match_data.bytebegin(:æ).should == 1
+ end
+
+ it "raises IndexError if there is no group with the provided name" do
+ match_data = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ match_data.bytebegin(:y)
+ }.should.raise(IndexError, "undefined group name reference: y")
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/matchdata/byteend_spec.rb b/spec/ruby/core/matchdata/byteend_spec.rb
new file mode 100644
index 0000000000..e77cbf8b0d
--- /dev/null
+++ b/spec/ruby/core/matchdata/byteend_spec.rb
@@ -0,0 +1,104 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.4" do
+ describe "MatchData#byteend" do
+ context "when passed an integer argument" do
+ it "returns the byte-based offset of the end of the nth element" do
+ match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ match_data.byteend(0).should == 7
+ match_data.byteend(2).should == 3
+ end
+
+ it "returns nil when the nth match isn't found" do
+ match_data = /something is( not)? (right)/.match("something is right")
+ match_data.byteend(1).should == nil
+ end
+
+ it "returns the byte-based offset for multi-byte strings" do
+ match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.")
+ match_data.byteend(0).should == 8
+ match_data.byteend(2).should == 4
+ end
+
+ not_supported_on :opal do
+ it "returns the byte-based offset for multi-byte strings with unicode regexp" do
+ match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.")
+ match_data.byteend(0).should == 8
+ match_data.byteend(2).should == 4
+ end
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('to_int')
+ obj.should_receive(:to_int).and_return(2)
+
+ match_data = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ match_data.byteend(obj).should == 3
+ end
+ end
+
+ context "when passed a String argument" do
+ it "return the byte-based offset of the start of the named capture" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
+ match_data.byteend("a").should == 2
+ match_data.byteend("b").should == 6
+ end
+
+ it "returns the byte-based offset for multi byte strings" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.")
+ match_data.byteend("a").should == 3
+ match_data.byteend("b").should == 7
+ end
+
+ not_supported_on :opal do
+ it "returns the byte-based offset for multi byte strings with unicode regexp" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.")
+ match_data.byteend("a").should == 3
+ match_data.byteend("b").should == 7
+ end
+ end
+
+ it "returns the byte-based offset for the farthest match when multiple named captures use the same name" do
+ match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.")
+ match_data.byteend("a").should == 6
+ end
+
+ it "returns the byte-based offset for multi-byte names" do
+ match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
+ match_data.byteend("æ").should == 2
+ end
+ end
+
+ context "when passed a Symbol argument" do
+ it "return the byte-based offset of the start of the named capture" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
+ match_data.byteend(:a).should == 2
+ match_data.byteend(:b).should == 6
+ end
+
+ it "returns the byte-based offset for multi byte strings" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.")
+ match_data.byteend(:a).should == 3
+ match_data.byteend(:b).should == 7
+ end
+
+ not_supported_on :opal do
+ it "returns the byte-based offset for multi byte strings with unicode regexp" do
+ match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.")
+ match_data.byteend(:a).should == 3
+ match_data.byteend(:b).should == 7
+ end
+ end
+
+ it "returns the byte-based offset for the farthest match when multiple named captures use the same name" do
+ match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.")
+ match_data.byteend(:a).should == 6
+ end
+
+ it "returns the byte-based offset for multi-byte names" do
+ match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.")
+ match_data.byteend(:æ).should == 2
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/matchdata/byteoffset_spec.rb b/spec/ruby/core/matchdata/byteoffset_spec.rb
new file mode 100644
index 0000000000..062e84027c
--- /dev/null
+++ b/spec/ruby/core/matchdata/byteoffset_spec.rb
@@ -0,0 +1,93 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#byteoffset" do
+ it "returns beginning and ending byte-based offset of whole matched substring for 0 element" do
+ m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+ m.byteoffset(0).should == [1, 7]
+ end
+
+ it "returns beginning and ending byte-based offset of n-th match, all the subsequent elements are capturing groups" do
+ m = /(.)(.)(\d+)(\d)/.match("THX1138.")
+
+ m.byteoffset(2).should == [2, 3]
+ m.byteoffset(3).should == [3, 6]
+ m.byteoffset(4).should == [6, 7]
+ end
+
+ it "accepts String as a reference to a named capture" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ m.byteoffset("f").should == [0, 3]
+ m.byteoffset("b").should == [3, 6]
+ end
+
+ it "accepts Symbol as a reference to a named capture" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ m.byteoffset(:f).should == [0, 3]
+ m.byteoffset(:b).should == [3, 6]
+ end
+
+ it "returns [nil, nil] if a capturing group is optional and doesn't match" do
+ m = /(?<x>q..)?/.match("foobarbaz")
+
+ m.byteoffset("x").should == [nil, nil]
+ m.byteoffset(1).should == [nil, nil]
+ end
+
+ it "returns correct beginning and ending byte-based offset for multi-byte strings" do
+ m = /\A\u3042(.)(.)?(.)\z/.match("\u3042\u3043\u3044")
+
+ m.byteoffset(1).should == [3, 6]
+ m.byteoffset(3).should == [6, 9]
+ end
+
+ it "returns [nil, nil] if a capturing group is optional and doesn't match for multi-byte string" do
+ m = /\A\u3042(.)(.)?(.)\z/.match("\u3042\u3043\u3044")
+
+ m.byteoffset(2).should == [nil, nil]
+ end
+
+ it "converts argument into integer if is not String nor Symbol" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ obj = Object.new
+ def obj.to_int; 2; end
+
+ m.byteoffset(1r).should == [0, 3]
+ m.byteoffset(1.1).should == [0, 3]
+ m.byteoffset(obj).should == [3, 6]
+ end
+
+ it "raises IndexError if there is no group with the provided name" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ m.byteoffset("y")
+ }.should.raise(IndexError, "undefined group name reference: y")
+
+ -> {
+ m.byteoffset(:y)
+ }.should.raise(IndexError, "undefined group name reference: y")
+ end
+
+ it "raises IndexError if index is out of bounds" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ m.byteoffset(-1)
+ }.should.raise(IndexError, "index -1 out of matches")
+
+ -> {
+ m.byteoffset(3)
+ }.should.raise(IndexError, "index 3 out of matches")
+ end
+
+ it "raises TypeError if can't convert argument into Integer" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ m.byteoffset([])
+ }.should.raise(TypeError, "no implicit conversion of Array into Integer")
+ end
+end
diff --git a/spec/ruby/core/matchdata/captures_spec.rb b/spec/ruby/core/matchdata/captures_spec.rb
index 8c0d2978b7..f829a25481 100644
--- a/spec/ruby/core/matchdata/captures_spec.rb
+++ b/spec/ruby/core/matchdata/captures_spec.rb
@@ -1,7 +1,6 @@
require_relative '../../spec_helper'
+require_relative 'shared/captures'
describe "MatchData#captures" do
- it "returns an array of the match captures" do
- /(.)(.)(\d+)(\d)/.match("THX1138.").captures.should == ["H","X","113","8"]
- end
+ it_behaves_like :matchdata_captures, :captures
end
diff --git a/spec/ruby/core/matchdata/deconstruct_keys_spec.rb b/spec/ruby/core/matchdata/deconstruct_keys_spec.rb
new file mode 100644
index 0000000000..9126d20394
--- /dev/null
+++ b/spec/ruby/core/matchdata/deconstruct_keys_spec.rb
@@ -0,0 +1,78 @@
+require_relative '../../spec_helper'
+
+describe "MatchData#deconstruct_keys" do
+ it "returns whole hash for nil as an argument" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ m.deconstruct_keys(nil).should == { f: "foo", b: "bar" }
+ end
+
+ it "returns only specified keys" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ m.deconstruct_keys([:f]).should == { f: "foo" }
+ end
+
+ it "requires one argument" do
+ m = /l/.match("l")
+
+ -> {
+ m.deconstruct_keys
+ }.should.raise(ArgumentError, "wrong number of arguments (given 0, expected 1)")
+ end
+
+ it "it raises error when argument is neither nil nor array" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> { m.deconstruct_keys(1) }.should.raise(TypeError, "wrong argument type Integer (expected Array)")
+ -> { m.deconstruct_keys("asd") }.should.raise(TypeError, "wrong argument type String (expected Array)")
+ -> { m.deconstruct_keys(:x) }.should.raise(TypeError, "wrong argument type Symbol (expected Array)")
+ -> { m.deconstruct_keys({}) }.should.raise(TypeError, "wrong argument type Hash (expected Array)")
+ end
+
+ it "returns {} when passed []" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ m.deconstruct_keys([]).should == {}
+ end
+
+ it "does not accept non-Symbol keys" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+
+ -> {
+ m.deconstruct_keys(['year', :foo])
+ }.should.raise(TypeError, "wrong argument type String (expected Symbol)")
+ end
+
+ it "process keys till the first non-existing one" do
+ m = /(?<f>foo)(?<b>bar)(?<c>baz)/.match("foobarbaz")
+
+ m.deconstruct_keys([:f, :a, :b]).should == { f: "foo" }
+ end
+
+ it "returns {} when there are no named captured groups at all" do
+ m = /foo.+/.match("foobar")
+
+ m.deconstruct_keys(nil).should == {}
+ end
+
+ it "returns {} when passed more keys than named captured groups" do
+ m = /(?<f>foo)(?<b>bar)/.match("foobar")
+ m.deconstruct_keys([:f, :b, :c]).should == {}
+ end
+
+ it "includes non-participating captures as nil for nil argument" do
+ m = "hello".match(/(?<a>hello)(?<b>world)?/)
+ m.deconstruct_keys(nil).should == { a: "hello", b: nil }
+ end
+
+ it "includes non-participating captures as nil for explicit keys" do
+ m = "hello".match(/(?<a>hello)(?<b>world)?/)
+ m.deconstruct_keys([:a, :b]).should == { a: "hello", b: nil }
+ end
+
+ it "does not stop iterating at a non-participating capture" do
+ m = "hello!".match(/(?<a>hello)(?<b>world)?(?<c>!)/)
+ m.deconstruct_keys([:b, :c, :a]).should == { b: nil, c: "!", a: "hello" }
+ end
+end
diff --git a/spec/ruby/core/matchdata/deconstruct_spec.rb b/spec/ruby/core/matchdata/deconstruct_spec.rb
new file mode 100644
index 0000000000..c55095665d
--- /dev/null
+++ b/spec/ruby/core/matchdata/deconstruct_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/captures'
+
+describe "MatchData#deconstruct" do
+ it_behaves_like :matchdata_captures, :deconstruct
+end
diff --git a/spec/ruby/core/matchdata/element_reference_spec.rb b/spec/ruby/core/matchdata/element_reference_spec.rb
index 26550ac94d..5509371cd2 100644
--- a/spec/ruby/core/matchdata/element_reference_spec.rb
+++ b/spec/ruby/core/matchdata/element_reference_spec.rb
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "MatchData#[]" do
it "acts as normal array indexing [index]" do
@@ -15,10 +16,46 @@ describe "MatchData#[]" do
it "supports accessors [start, length]" do
/(.)(.)(\d+)(\d)/.match("THX1138.")[1, 2].should == %w|H X|
/(.)(.)(\d+)(\d)/.match("THX1138.")[-3, 2].should == %w|X 113|
+
+ # negative index is larger than the number of match values
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[-30, 2].should == nil
+
+ # positive index larger than number of match values
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[5, 2].should == []
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[6, 2].should == nil
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[30, 2].should == nil
+
+ # length argument larger than number of match values is capped to match value length
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3, 10].should == %w|113 8|
+
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3, 0].should == []
+
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3, -1].should == nil
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3, -30].should == nil
end
it "supports ranges [start..end]" do
/(.)(.)(\d+)(\d)/.match("THX1138.")[1..3].should == %w|H X 113|
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3..10].should == %w|113 8|
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[-30..2].should == nil
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3..1].should == []
+ end
+
+ it "supports endless ranges [start..]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[3..].should == %w|113 8|
+ end
+
+ it "supports beginningless ranges [..end]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[..1].should == %w|HX1138 H|
+ end
+
+ it "supports beginningless endless ranges [nil..nil]" do
+ /(.)(.)(\d+)(\d)/.match("THX1138.")[nil..nil].should == %w|HX1138 H X 113 8|
+ end
+
+ it "returns instances of String when given a String subclass" do
+ str = MatchDataSpecs::MyString.new("THX1138.")
+ /(.)(.)(\d+)(\d)/.match(str)[0..-1].each { |m| m.should.instance_of?(String) }
end
end
@@ -71,17 +108,17 @@ describe "MatchData#[Symbol]" do
it "raises an IndexError if there is no named match corresponding to the Symbol" do
md = 'haystack'.match(/(?<t>t(?<a>ack))/)
- -> { md[:baz] }.should raise_error(IndexError, /baz/)
+ -> { md[:baz] }.should.raise(IndexError, /baz/)
end
it "raises an IndexError if there is no named match corresponding to the String" do
md = 'haystack'.match(/(?<t>t(?<a>ack))/)
- -> { md['baz'] }.should raise_error(IndexError, /baz/)
+ -> { md['baz'] }.should.raise(IndexError, /baz/)
end
it "returns matches in the String's encoding" do
rex = /(?<t>t(?<a>ack))/u
- md = 'haystack'.force_encoding('euc-jp').match(rex)
+ md = 'haystack'.dup.force_encoding('euc-jp').match(rex)
md[:t].encoding.should == Encoding::EUC_JP
end
end
diff --git a/spec/ruby/core/matchdata/end_spec.rb b/spec/ruby/core/matchdata/end_spec.rb