From 9a145f5708a587268ac3cfc5baf8447e257aed3c Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Mon, 24 Jun 2024 23:23:50 -0500 Subject: [PATCH 01/18] Use sonatype secrets for maven config --- .github/workflows/snapshot-publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/snapshot-publish.yml b/.github/workflows/snapshot-publish.yml index 92786871a6a..b785c7b9711 100644 --- a/.github/workflows/snapshot-publish.yml +++ b/.github/workflows/snapshot-publish.yml @@ -24,8 +24,8 @@ jobs: distribution: 'zulu' java-version: '8' server-id: sonatype-nexus-snapshots - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD + server-username: ${{ secrets.SONATYPE_USERNAME }} + server-password: ${{ secrets.SONATYPE_PASSWORD }} - name: Publish package run: ./mvnw -B clean deploy -Prelease env: From ec0d704099319fe188181a1771aefab5d7d4ae0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Duarte?= Date: Tue, 25 Jun 2024 09:53:03 +0100 Subject: [PATCH 02/18] update jruby-openssl to 0.15.0 --- lib/pom.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pom.rb b/lib/pom.rb index d5b4dc5a7d9..a0077c02025 100644 --- a/lib/pom.rb +++ b/lib/pom.rb @@ -58,7 +58,7 @@ def log(message=nil) ['irb', '1.4.2'], ['jar-dependencies', '0.4.1'], ['jruby-readline', '1.3.7'], - ['jruby-openssl', '0.14.6'], + ['jruby-openssl', '0.15.0'], ['json', '2.7.1'], ['logger', '1.5.1'], ['mutex_m', '0.1.1'], From 902e03c1335e8ca9694779ee23a2960881517de4 Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Wed, 26 Jun 2024 12:23:31 -0400 Subject: [PATCH 03/18] Add release script to 9.3 branch --- tool/release.sh | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100755 tool/release.sh diff --git a/tool/release.sh b/tool/release.sh new file mode 100755 index 00000000000..4757da2f467 --- /dev/null +++ b/tool/release.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +# Notes on this script: +# 1. on failed run you must undo the version commit and probably should drop in nexus staging repo +# 2. Assumes jruby you are using in your PATH works + +export REPO=${REPO:=jruby} + +java_version=$(java -version 2>&1 | head -1 | awk -F\" -e '{ print $2 }' | awk -F. '{print $1 "." $2 }') + +if [ "$java_version" != "1.8" ]; then + echo "You must use Java 1.8 to release JRuby" + exit 1 +fi + +[[ -z "$JRUBY_VERSION" ]] && { echo "set JRUBY_VERSION to something" ; exit 1; } + +export WINDOWS_VERSION=`echo $JRUBY_VERSION|sed -e 's/\./_/g'` +echo $JRUBY_VERSION > VERSION + +set -x + +mvn +git add VERSION core/pom.xml lib/pom.xml pom.xml shaded/pom.xml +git commit -m "Version $JRUBY_VERSION updated for release" +cd .. +rm -rf release +git clone $REPO release +cd release +pwd +mvn clean deploy -Psonatype-oss-release -Prelease +jruby -S rake post_process_artifacts + +cd release + +# Install4j has changed how it writes this out so fix it in post +mv jruby_windows-x32_${WINDOWS_VERSION}.exe jruby_windows_${WINDOWS_VERSION}.exe +mv jruby_windows-x32_${WINDOWS_VERSION}.exe.md5 jruby_windows_${WINDOWS_VERSION}.exe.md5 +mv jruby_windows-x32_${WINDOWS_VERSION}.exe.sha1 jruby_windows_${WINDOWS_VERSION}.exe.sha1 +mv jruby_windows-x32_${WINDOWS_VERSION}.exe.sha256 jruby_windows_${WINDOWS_VERSION}.exe.sha256 +mv jruby_windows-x32_${WINDOWS_VERSION}.exe.sha512 jruby_windows_${WINDOWS_VERSION}.exe.sha512 + +pwd +DOWNLOADS=s3://jruby.org/downloads +VERSION=$JRUBY_VERSION + +for FILE in jruby-bin-${VERSION}.tar.gz jruby-bin-${VERSION}.zip jruby-complete-${VERSION}.jar jruby-jars-${VERSION}.gem jruby-src-${VERSION}.tar.gz jruby-src-${VERSION}.zip jruby_windows_${WINDOWS_VERSION}.exe jruby_windows_x64_${WINDOWS_VERSION}.exe +do + + echo s3cmd sync --verbose ${FILE} ${DOWNLOADS}/${VERSION}/${FILE} + s3cmd sync --verbose ${FILE} ${DOWNLOADS}/${VERSION}/${FILE} + + for FINGERPRINT in md5 sha1 sha256 sha512 + do + + s3cmd put --verbose ${FILE}.${FINGERPRINT} ${DOWNLOADS}/${VERSION}/${FILE}.${FINGERPRINT} + + done + +done + + +s3cmd setacl ${DOWNLOADS}/${VERSION} --acl-public --recursive + +# At this point you should run scripts on platforms to make sure things look ok. +# FIXME: we should consider running a collection of scripts we can automate here and add to CI. + From 8162f6050dec59eb251513d9c40e31e3938f993f Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 26 Jun 2024 12:44:32 -0500 Subject: [PATCH 04/18] Revert "Use sonatype secrets for maven config" This reverts commit 9a145f5708a587268ac3cfc5baf8447e257aed3c. These are supposed to be the names of env vars where the username and password are stored, and were not the cause of #8302. --- .github/workflows/snapshot-publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/snapshot-publish.yml b/.github/workflows/snapshot-publish.yml index b785c7b9711..92786871a6a 100644 --- a/.github/workflows/snapshot-publish.yml +++ b/.github/workflows/snapshot-publish.yml @@ -24,8 +24,8 @@ jobs: distribution: 'zulu' java-version: '8' server-id: sonatype-nexus-snapshots - server-username: ${{ secrets.SONATYPE_USERNAME }} - server-password: ${{ secrets.SONATYPE_PASSWORD }} + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD - name: Publish package run: ./mvnw -B clean deploy -Prelease env: From f09acf589a5e33112fa103e9aa6c36d6f3aec725 Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Wed, 26 Jun 2024 12:27:07 -0400 Subject: [PATCH 05/18] Version 9.3.15.0 updated for release --- VERSION | 2 +- core/pom.xml | 2 +- lib/pom.xml | 4 ++-- pom.xml | 5 ++++- shaded/pom.xml | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/VERSION b/VERSION index 44f728f7e70..896292a7f38 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.3.15.0-SNAPSHOT +9.3.15.0 diff --git a/core/pom.xml b/core/pom.xml index 4c157863c29..5a8b14d2bc2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.3.15.0-SNAPSHOT + 9.3.15.0 jruby-base JRuby Base diff --git a/lib/pom.xml b/lib/pom.xml index 147515211cd..eaea280aa8b 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.3.15.0-SNAPSHOT + 9.3.15.0 jruby-stdlib JRuby Lib Setup @@ -28,7 +28,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-core - 9.3.15.0-SNAPSHOT + 9.3.15.0 test diff --git a/pom.xml b/pom.xml index 64ee82e54f6..ac917263dac 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.3.15.0-SNAPSHOT + 9.3.15.0 pom JRuby JRuby is the effort to recreate the Ruby (https://www.ruby-lang.org) interpreter in Java. @@ -274,6 +274,9 @@ DO NOT MODIFY - GENERATED CODE [3.3.0,) + + No Snapshots Allowed! + diff --git a/shaded/pom.xml b/shaded/pom.xml index 113492885f0..77769b5339c 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.3.15.0-SNAPSHOT + 9.3.15.0 jruby-core JRuby Core From e035e160d661153853f118a1ce22d5aa42926bc0 Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Wed, 26 Jun 2024 16:02:54 -0400 Subject: [PATCH 06/18] Update for next development cycle --- VERSION | 2 +- core/pom.xml | 2 +- lib/pom.xml | 4 ++-- pom.xml | 5 +---- shaded/pom.xml | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/VERSION b/VERSION index 896292a7f38..025265dcf42 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.3.15.0 +9.3.16.0-SNAPSHOT diff --git a/core/pom.xml b/core/pom.xml index 5a8b14d2bc2..cff2cd4f472 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.3.15.0 + 9.3.16.0-SNAPSHOT jruby-base JRuby Base diff --git a/lib/pom.xml b/lib/pom.xml index eaea280aa8b..3ec96278246 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.3.15.0 + 9.3.16.0-SNAPSHOT jruby-stdlib JRuby Lib Setup @@ -28,7 +28,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-core - 9.3.15.0 + 9.3.16.0-SNAPSHOT test diff --git a/pom.xml b/pom.xml index ac917263dac..ef4c093b176 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.3.15.0 + 9.3.16.0-SNAPSHOT pom JRuby JRuby is the effort to recreate the Ruby (https://www.ruby-lang.org) interpreter in Java. @@ -274,9 +274,6 @@ DO NOT MODIFY - GENERATED CODE [3.3.0,) - - No Snapshots Allowed! - diff --git a/shaded/pom.xml b/shaded/pom.xml index 77769b5339c..27fc4387af3 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.3.15.0 + 9.3.16.0-SNAPSHOT jruby-core JRuby Core From baec29ea417a0261ac747cf7c442d487e9ade11d Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 26 Jun 2024 15:51:07 -0500 Subject: [PATCH 07/18] Update net-ftp to JRuby-compatible 0.3.7 Previous versions were missing our patch to disable OpenSSL session caching. See ruby/net-ftp#38 --- lib/pom.rb | 2 +- lib/pom.xml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pom.rb b/lib/pom.rb index d5b4dc5a7d9..c1ecaa0f731 100644 --- a/lib/pom.rb +++ b/lib/pom.rb @@ -126,7 +126,7 @@ def log(message=nil) # ['debug', '1.4.0'], ['matrix', '0.4.2'], ['minitest', '5.15.0'], - ['net-ftp', '0.1.3'], + ['net-ftp', '0.3.7'], ['net-imap', '0.2.3'], ['net-pop', '0.1.1'], ['net-smtp', '0.3.1'], diff --git a/lib/pom.xml b/lib/pom.xml index 019211b55f6..0125db6408a 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -931,7 +931,7 @@ DO NOT MODIFY - GENERATED CODE rubygems net-ftp - 0.1.3 + 0.3.7 gem provided @@ -1148,7 +1148,7 @@ DO NOT MODIFY - GENERATED CODE specifications/yaml-0.2.0* specifications/matrix-0.4.2* specifications/minitest-5.15.0* - specifications/net-ftp-0.1.3* + specifications/net-ftp-0.3.7* specifications/net-imap-0.2.3* specifications/net-pop-0.1.1* specifications/net-smtp-0.3.1* @@ -1227,7 +1227,7 @@ DO NOT MODIFY - GENERATED CODE gems/yaml-0.2.0*/**/* gems/matrix-0.4.2*/**/* gems/minitest-5.15.0*/**/* - gems/net-ftp-0.1.3*/**/* + gems/net-ftp-0.3.7*/**/* gems/net-imap-0.2.3*/**/* gems/net-pop-0.1.1*/**/* gems/net-smtp-0.3.1*/**/* @@ -1306,7 +1306,7 @@ DO NOT MODIFY - GENERATED CODE cache/yaml-0.2.0* cache/matrix-0.4.2* cache/minitest-5.15.0* - cache/net-ftp-0.1.3* + cache/net-ftp-0.3.7* cache/net-imap-0.2.3* cache/net-pop-0.1.1* cache/net-smtp-0.3.1* From 33e821eeb7e9e80d5d43b347bb9d52123baa445b Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 27 Jun 2024 12:48:09 -0500 Subject: [PATCH 08/18] Properly fall back if can't setAccessible Changes in e8dbe9c26 broke this fallback by silencing errors from the old setAccessible (as part of becoming more module-friendly). This patch checks if the setAccessible was successful and returns null for any fields that could not be made accessible. This in turn allows the USABLE fallback to properly reflect actual usability. --- .../org/jruby/util/io/SeekableByteChannelImpl.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/jruby/util/io/SeekableByteChannelImpl.java b/core/src/main/java/org/jruby/util/io/SeekableByteChannelImpl.java index c5dddfd2aa2..6827c53f185 100644 --- a/core/src/main/java/org/jruby/util/io/SeekableByteChannelImpl.java +++ b/core/src/main/java/org/jruby/util/io/SeekableByteChannelImpl.java @@ -149,15 +149,17 @@ private static int readIntField(ByteArrayInputStream self, Field field) { private static Field accessibleField(final String name) { try { Field field = ByteArrayInputStream.class.getDeclaredField(name); - Java.trySetAccessible(field); - return field; + if (Java.trySetAccessible(field)) { + return field; + } } catch (NoSuchFieldException ex) { - return null; // should never happen + // should never happen } catch (SecurityException ex) { - return null; } + + return null; } } From 2fe4801fbe5a0e67aaec7b30bcc1bfd575c2974a Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 27 Jun 2024 13:20:58 -0500 Subject: [PATCH 09/18] Version-guard these specs net-ftp 0.3.6+ returns the response from `put` and `puttextfile`. See https://github.com/ruby/net-ftp/pull/34 --- spec/ruby/library/net-ftp/shared/puttextfile.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/spec/ruby/library/net-ftp/shared/puttextfile.rb b/spec/ruby/library/net-ftp/shared/puttextfile.rb index 4722439674a..e2c04533524 100644 --- a/spec/ruby/library/net-ftp/shared/puttextfile.rb +++ b/spec/ruby/library/net-ftp/shared/puttextfile.rb @@ -34,8 +34,16 @@ remote_lines.should == local_lines.gsub("\n", "\r\n") end - it "returns nil" do - @ftp.send(@method, @local_fixture_file, "text").should be_nil + guard -> { Net::FTP::VERSION < '0.3.6' } do + it "returns nil" do + @ftp.send(@method, @local_fixture_file, "text").should be_nil + end + end + + guard -> { Net::FTP::VERSION >= '0.3.6' } do + it "returns the response" do + @ftp.send(@method, @local_fixture_file, "text").should == @ftp.last_response + end end describe "when passed a block" do From 1fe1fa35e95c4d152cff213dda2c617f85b9ec0a Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 27 Jun 2024 14:03:52 -0500 Subject: [PATCH 10/18] Remove exclude for passing test See #7803 --- test/mri/excludes/TestM17NComb.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/mri/excludes/TestM17NComb.rb b/test/mri/excludes/TestM17NComb.rb index 1904e4f4015..b7d1912919b 100644 --- a/test/mri/excludes/TestM17NComb.rb +++ b/test/mri/excludes/TestM17NComb.rb @@ -2,5 +2,4 @@ exclude :test_str_intern, "needs investigation" exclude :test_str_count, "does not raise compatibility error" exclude :test_str_crypt_nonstrict, "#crypt failing: Errno::EINVAL: Invalid argument" -exclude :test_str_squeeze, "fails in CI with indy, see jruby/jruby#7803" if org.jruby.util.cli.Options::COMPILE_INVOKEDYNAMIC.load exclude :test_str_smart_chomp, "see jruby/jruby#3692" From c9b3e3a432884d64b213c051af687980c68844b9 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Fri, 28 Jun 2024 15:28:51 -0500 Subject: [PATCH 11/18] Default Windows filesystem encoding to UTF-8 This change was applied to CRuby in 3.0. See https://bugs.ruby-lang.org/issues/12654 Fixes #7750 --- .../main/java/org/jruby/runtime/encoding/EncodingService.java | 3 ++- core/src/main/java/org/jruby/util/cli/Options.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/jruby/runtime/encoding/EncodingService.java b/core/src/main/java/org/jruby/runtime/encoding/EncodingService.java index 863b353c081..0425f004b28 100644 --- a/core/src/main/java/org/jruby/runtime/encoding/EncodingService.java +++ b/core/src/main/java/org/jruby/runtime/encoding/EncodingService.java @@ -27,6 +27,7 @@ import org.jcodings.specific.USASCIIEncoding; import org.jruby.RubyString; import org.jruby.util.SafePropertyAccessor; +import org.jruby.util.cli.Options; import org.jruby.util.io.EncodingUtils; public final class EncodingService { @@ -430,7 +431,7 @@ private void checkAsciiEncodingName(ByteList name) { } public Encoding getWindowsFilesystemEncoding(Ruby ruby) { - String encoding = SafePropertyAccessor.getProperty("file.encoding", "UTF-8"); + String encoding = Options.WINDOWS_FILESYSTEM_ENCODING.load(); Encoding filesystemEncoding = loadEncoding(ByteList.create(encoding)); // Use default external if file.encoding does not point at an encoding we recognize diff --git a/core/src/main/java/org/jruby/util/cli/Options.java b/core/src/main/java/org/jruby/util/cli/Options.java index 3561a252b2d..d25dce8be9e 100644 --- a/core/src/main/java/org/jruby/util/cli/Options.java +++ b/core/src/main/java/org/jruby/util/cli/Options.java @@ -164,6 +164,7 @@ public class Options { public static final Option PACKED_ARRAYS = bool(MISCELLANEOUS, "packed.arrays", true, "Toggle whether to use \"packed\" arrays for small tuples."); public static final Option REGEXP_INTERRUPTIBLE = bool(MISCELLANEOUS, "regexp.interruptible", true, "Allow regexp operations to be interruptible from Ruby."); public static final Option JAR_CACHE_EXPIRATION = integer(MISCELLANEOUS, "jar.cache.expiration", 750, "The time (ms) between checks if a JAR file containing resources has been updated."); + public static final Option WINDOWS_FILESYSTEM_ENCODING = string(MISCELLANEOUS, "windows.filesystem.encoding", "UTF-8", "The encoding to use for filesystem names and paths on Windows."); public static final Option DEBUG_LOADSERVICE = bool(DEBUG, "debug.loadService", false, "Log require/load file searches."); public static final Option DEBUG_LOADSERVICE_TIMING = bool(DEBUG, "debug.loadService.timing", false, "Log require/load parse+evaluate times."); From ea218233512913c48e07e5d829b69d4f20a19c56 Mon Sep 17 00:00:00 2001 From: Andrew Konchin Date: Mon, 1 Jul 2024 15:57:43 +0300 Subject: [PATCH 12/18] Update to ruby/mspec@5354a40 --- spec/mspec/lib/mspec/helpers/tmp.rb | 15 ++++++++++++++- spec/mspec/lib/mspec/runner/formatters/multi.rb | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/spec/mspec/lib/mspec/helpers/tmp.rb b/spec/mspec/lib/mspec/helpers/tmp.rb index 4c0eddab756..e903dd9f50c 100644 --- a/spec/mspec/lib/mspec/helpers/tmp.rb +++ b/spec/mspec/lib/mspec/helpers/tmp.rb @@ -36,7 +36,20 @@ 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 "/" diff --git a/spec/mspec/lib/mspec/runner/formatters/multi.rb b/spec/mspec/lib/mspec/runner/formatters/multi.rb index a723ae8eb92..fa1da3766b9 100644 --- a/spec/mspec/lib/mspec/runner/formatters/multi.rb +++ b/spec/mspec/lib/mspec/runner/formatters/multi.rb @@ -42,6 +42,6 @@ def aggregate_results(files) end def print_exception(exc, count) - print "\n#{count})\n#{exc}\n" + @err.print "\n#{count})\n#{exc}\n" end end From 16933595c7ff9d39a395f6e22639bf109874932d Mon Sep 17 00:00:00 2001 From: Andrew Konchin Date: Mon, 1 Jul 2024 15:57:48 +0300 Subject: [PATCH 13/18] Update to ruby/spec@f8987ac --- spec/ruby/CONTRIBUTING.md | 4 +- spec/ruby/README.md | 2 +- spec/ruby/core/array/each_spec.rb | 2 +- spec/ruby/core/array/plus_spec.rb | 2 +- spec/ruby/core/array/to_h_spec.rb | 6 + spec/ruby/core/dir/chdir_spec.rb | 16 +- spec/ruby/core/dir/shared/exist.rb | 1 + spec/ruby/core/enumerable/shared/inject.rb | 37 +++- spec/ruby/core/enumerable/to_h_spec.rb | 8 + spec/ruby/core/env/to_h_spec.rb | 12 ++ .../core/exception/detailed_message_spec.rb | 27 ++- spec/ruby/core/hash/replace_spec.rb | 67 ++++++- spec/ruby/core/hash/shared/replace.rb | 51 ----- spec/ruby/core/hash/to_h_spec.rb | 10 + spec/ruby/core/integer/chr_spec.rb | 18 +- spec/ruby/core/io/autoclose_spec.rb | 77 ++++++++ spec/ruby/core/kernel/caller_spec.rb | 32 ++- spec/ruby/core/kernel/sleep_spec.rb | 2 +- .../core/matchdata/element_reference_spec.rb | 5 + spec/ruby/core/regexp/shared/new.rb | 12 +- spec/ruby/core/string/encode_spec.rb | 16 +- spec/ruby/core/string/shared/encode.rb | 184 ++++++++++++++++++ spec/ruby/core/struct/to_h_spec.rb | 12 ++ .../core/thread/thread_variable_get_spec.rb | 32 ++- .../core/thread/thread_variable_set_spec.rb | 42 +++- spec/ruby/core/thread/thread_variable_spec.rb | 36 +++- .../ruby/core/thread/thread_variables_spec.rb | 22 ++- spec/ruby/core/time/yday_spec.rb | 13 +- spec/ruby/core/tracepoint/inspect_spec.rb | 13 ++ spec/ruby/core/warning/warn_spec.rb | 24 +++ spec/ruby/language/for_spec.rb | 103 ++++++++++ spec/ruby/language/heredoc_spec.rb | 10 + spec/ruby/language/pattern_matching_spec.rb | 19 +- spec/ruby/language/rescue_spec.rb | 10 + .../coverage/fixtures/code_with_begin.rb | 3 + spec/ruby/library/coverage/result_spec.rb | 14 ++ spec/ruby/library/date/accessor_spec.rb | 2 +- spec/ruby/library/date/yday_spec.rb | 3 +- spec/ruby/library/datetime/yday_spec.rb | 7 + spec/ruby/library/find/fixtures/common.rb | 14 +- spec/ruby/library/rbconfig/rbconfig_spec.rb | 12 +- .../library/socket/unixsocket/pair_spec.rb | 22 +-- .../library/socket/unixsocket/shared/pair.rb | 29 +++ .../socket/unixsocket/socketpair_spec.rb | 38 +--- spec/ruby/library/stringio/initialize_spec.rb | 22 ++- .../library/stringscanner/scan_until_spec.rb | 6 +- spec/ruby/optional/capi/ext/kernel_spec.c | 2 +- spec/ruby/shared/time/yday.rb | 18 ++ 48 files changed, 933 insertions(+), 186 deletions(-) delete mode 100644 spec/ruby/core/hash/shared/replace.rb create mode 100644 spec/ruby/core/io/autoclose_spec.rb create mode 100644 spec/ruby/library/coverage/fixtures/code_with_begin.rb create mode 100644 spec/ruby/library/datetime/yday_spec.rb create mode 100644 spec/ruby/library/socket/unixsocket/shared/pair.rb create mode 100644 spec/ruby/shared/time/yday.rb diff --git a/spec/ruby/CONTRIBUTING.md b/spec/ruby/CONTRIBUTING.md index c82eb5ea4fa..73fba01de2e 100644 --- a/spec/ruby/CONTRIBUTING.md +++ b/spec/ruby/CONTRIBUTING.md @@ -277,13 +277,13 @@ describe :kernel_sprintf, shared: true do 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 115392835f9..964efbec48b 100644 --- a/spec/ruby/README.md +++ b/spec/ruby/README.md @@ -35,7 +35,7 @@ More precisely, every latest stable MRI release should [pass](https://github.com ### Synchronization with Ruby Implementations -The specs are synchronized both ways around once a month by @eregon between ruby/spec, MRI, JRuby and TruffleRuby, +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. diff --git a/spec/ruby/core/array/each_spec.rb b/spec/ruby/core/array/each_spec.rb index 57d6082f012..f4b5b758d0a 100644 --- a/spec/ruby/core/array/each_spec.rb +++ b/spec/ruby/core/array/each_spec.rb @@ -7,7 +7,7 @@ # 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]; end` +# like `i = 0; while i < size; yield self[i]; i += 1; end` describe "Array#each" do it "yields each element to the block" do diff --git a/spec/ruby/core/array/plus_spec.rb b/spec/ruby/core/array/plus_spec.rb index 635bd131c9a..b7153fd3ef1 100644 --- a/spec/ruby/core/array/plus_spec.rb +++ b/spec/ruby/core/array/plus_spec.rb @@ -21,7 +21,7 @@ ([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 + it "raises a TypeError if the given argument can't be converted to an array" do -> { [1, 2, 3] + nil }.should raise_error(TypeError) -> { [1, 2, 3] + "abc" }.should raise_error(TypeError) end diff --git a/spec/ruby/core/array/to_h_spec.rb b/spec/ruby/core/array/to_h_spec.rb index f4578211a1f..1c814f3d012 100644 --- a/spec/ruby/core/array/to_h_spec.rb +++ b/spec/ruby/core/array/to_h_spec.rb @@ -45,6 +45,12 @@ [: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] } diff --git a/spec/ruby/core/dir/chdir_spec.rb b/spec/ruby/core/dir/chdir_spec.rb index 729ac403e35..7ced2a70570 100644 --- a/spec/ruby/core/dir/chdir_spec.rb +++ b/spec/ruby/core/dir/chdir_spec.rb @@ -19,14 +19,14 @@ 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 @@ def to_str; DirSpecs.mock_dir; end 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 = "" diff --git a/spec/ruby/core/dir/shared/exist.rb b/spec/ruby/core/dir/shared/exist.rb index 2ea4f88a80f..3097f577151 100644 --- a/spec/ruby/core/dir/shared/exist.rb +++ b/spec/ruby/core/dir/shared/exist.rb @@ -34,6 +34,7 @@ end it "doesn't expand paths" do + skip "$HOME not valid directory" unless ENV['HOME'] && File.directory?(ENV['HOME']) Dir.send(@method, File.expand_path('~')).should be_true Dir.send(@method, '~').should be_false end diff --git a/spec/ruby/core/enumerable/shared/inject.rb b/spec/ruby/core/enumerable/shared/inject.rb index 693d34d675d..92f220efa4c 100644 --- a/spec/ruby/core/enumerable/shared/inject.rb +++ b/spec/ruby/core/enumerable/shared/inject.rb @@ -16,6 +16,23 @@ 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_error(TypeError, /is not a symbol nor a string/) + -> { [1, 2, 3].send(@method, 10, Object.new) }.should raise_error(TypeError, /is not a symbol nor a string/) end it "ignores the block if two arguments" do @@ -39,6 +56,25 @@ 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_error(TypeError, /is not a symbol nor a string/) + -> { [10, 1, 2, 3].send(@method, Object.new) }.should raise_error(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 @@ -77,7 +113,6 @@ 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< do @enum.to_h { |k| [k, k.to_s, 1] } diff --git a/spec/ruby/core/env/to_h_spec.rb b/spec/ruby/core/env/to_h_spec.rb index 3c4a92aa579..58ea2d2030e 100644 --- a/spec/ruby/core/env/to_h_spec.rb +++ b/spec/ruby/core/env/to_h_spec.rb @@ -18,6 +18,18 @@ 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 } diff --git a/spec/ruby/core/exception/detailed_message_spec.rb b/spec/ruby/core/exception/detailed_message_spec.rb index fbe4443daab..8178278b2bc 100644 --- a/spec/ruby/core/exception/detailed_message_spec.rb +++ b/spec/ruby/core/exception/detailed_message_spec.rb @@ -15,14 +15,6 @@ def exception.detailed_message(**) exception.full_message(highlight: false).should.include? "new error" 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 "allows and ignores other keyword arguments" do - RuntimeError.new("new error").detailed_message(foo: true).should == "new error (RuntimeError)" - end - it "returns just a message if exception class is anonymous" do Class.new(RuntimeError).new("message").detailed_message.should == "message" end @@ -31,13 +23,30 @@ def exception.detailed_message(**) RuntimeError.new("").detailed_message.should == "unhandled exception" end - it "returns just class name for an instance of RuntimeError subclass with empty message" do + 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#\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 end diff --git a/spec/ruby/core/hash/replace_spec.rb b/spec/ruby/core/hash/replace_spec.rb index 92b2118fd33..a26a31f5f9a 100644 --- a/spec/ruby/core/hash/replace_spec.rb +++ b/spec/ruby/core/hash/replace_spec.rb @@ -1,7 +1,70 @@ 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 "transfers the compare_by_identity flag" do + hash_a = { a: 1 } + hash_b = { b: 2 } + hash_b.compare_by_identity + hash_a.should_not.compare_by_identity? + hash_a.replace(hash_b) + hash_a.should.compare_by_identity? + + hash_a = { a: 1 } + hash_b = { b: 2 } + hash_a.compare_by_identity + hash_a.should.compare_by_identity? + hash_a.replace(hash_b) + hash_a.should_not.compare_by_identity? + end + + it "does not transfer default values" do + hash_a = {} + hash_b = Hash.new(5) + hash_a.replace(hash_b) + hash_a.default.should == 5 + + hash_a = {} + hash_b = Hash.new { |h, k| k * 2 } + hash_a.replace(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.replace(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.replace(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.replace(HashSpecs.empty_frozen_hash) + end.should raise_error(FrozenError) + 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 bea64384bba..00000000000 --- 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/to_h_spec.rb b/spec/ruby/core/hash/to_h_spec.rb index 75ebce68b1e..e17ca7e6711 100644 --- a/spec/ruby/core/hash/to_h_spec.rb +++ b/spec/ruby/core/hash/to_h_spec.rb @@ -37,6 +37,16 @@ { 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] } diff --git a/spec/ruby/core/integer/chr_spec.rb b/spec/ruby/core/integer/chr_spec.rb index 8fe20ff8127..39cafe28745 100644 --- a/spec/ruby/core/integer/chr_spec.rb +++ b/spec/ruby/core/integer/chr_spec.rb @@ -10,12 +10,12 @@ 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_error(RangeError, /-1 out of char range/) + -> { (-bignum_value).chr }.should raise_error(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_error(RangeError, /2206368128 out of char range/) end describe "when Encoding.default_internal is nil" do @@ -48,8 +48,8 @@ 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_error(RangeError, /256 out of char range/) + -> { bignum_value.chr }.should raise_error(RangeError, /bignum out of char range/) end end @@ -137,7 +137,7 @@ [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_error(RangeError, /(invalid codepoint|out of char range)/) end end end @@ -165,12 +165,12 @@ # 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_error(RangeError, /-1 out of char range/) + -> { (-bignum_value).chr(Encoding::EUC_JP) }.should raise_error(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_error(RangeError, /2206368128 out of char range/) end it "returns a String with the specified encoding" do diff --git a/spec/ruby/core/io/autoclose_spec.rb b/spec/ruby/core/io/autoclose_spec.rb new file mode 100644 index 00000000000..715ada7c934 --- /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_error(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_error(IOError, /closed stream/) + end +end diff --git a/spec/ruby/core/kernel/caller_spec.rb b/spec/ruby/core/kernel/caller_spec.rb index c3d63ccb00a..33c7929a311 100644 --- a/spec/ruby/core/kernel/caller_spec.rb +++ b/spec/ruby/core/kernel/caller_spec.rb @@ -43,16 +43,44 @@ lines[1].should =~ /\A#{path}:2:in [`']block in
'\n\z/ end + 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.map(&:to_s).should == locations1[2..-1].map(&:to_s) + locations2.should == locations1[2..-1] end it "works with beginless ranges" do locations1 = KernelSpecs::CallerTest.locations(0) locations2 = KernelSpecs::CallerTest.locations((..5)) - locations2.map(&:to_s)[eval("(2..)")].should == locations1[(..5)].map(&:to_s)[eval("(2..)")] + 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 diff --git a/spec/ruby/core/kernel/sleep_spec.rb b/spec/ruby/core/kernel/sleep_spec.rb index 0570629723b..d35e3130063 100644 --- a/spec/ruby/core/kernel/sleep_spec.rb +++ b/spec/ruby/core/kernel/sleep_spec.rb @@ -21,7 +21,7 @@ sleep(Rational(1, 999)).should >= 0 end - it "accepts any Object that reponds to divmod" do + it "accepts any Object that responds to divmod" do o = Object.new def o.divmod(*); [0, 0.001]; end sleep(o).should >= 0 diff --git a/spec/ruby/core/matchdata/element_reference_spec.rb b/spec/ruby/core/matchdata/element_reference_spec.rb index 806db2d7b50..0924be0aaec 100644 --- a/spec/ruby/core/matchdata/element_reference_spec.rb +++ b/spec/ruby/core/matchdata/element_reference_spec.rb @@ -20,6 +20,11 @@ # 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| diff --git a/spec/ruby/core/regexp/shared/new.rb b/spec/ruby/core/regexp/shared/new.rb index 7c3fabf6129..7052bcab638 100644 --- a/spec/ruby/core/regexp/shared/new.rb +++ b/spec/ruby/core/regexp/shared/new.rb @@ -56,7 +56,7 @@ def obj.to_str() [] end end it "raises a RegexpError when passed an incorrect regexp" do - -> { Regexp.send(@method, "^[$", 0) }.should raise_error(RegexpError) + -> { Regexp.send(@method, "^[$", 0) }.should raise_error(RegexpError, Regexp.new(Regexp.escape("premature end of char-class: /^[$/"))) end it "does not set Regexp options if only given one argument" do @@ -261,7 +261,7 @@ def obj.to_int() ScratchPad.record(:called) end describe "with escaped characters" do it "raises a Regexp error if there is a trailing backslash" do - -> { Regexp.send(@method, "\\") }.should raise_error(RegexpError) + -> { Regexp.send(@method, "\\") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("too short escape sequence: /\\/"))) end it "does not raise a Regexp error if there is an escaped trailing backslash" do @@ -293,7 +293,7 @@ def obj.to_int() ScratchPad.record(:called) end end it "raises a RegexpError if \\x is not followed by any hexadecimal digits" do - -> { Regexp.send(@method, "\\" + "xn") }.should raise_error(RegexpError) + -> { Regexp.send(@method, "\\" + "xn") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid hex escape: /\\xn/"))) end it "accepts an escaped string interpolation" do @@ -453,15 +453,15 @@ def obj.to_int() ScratchPad.record(:called) end end it "raises a RegexpError if less than four digits are given for \\uHHHH" do - -> { Regexp.send(@method, "\\" + "u304") }.should raise_error(RegexpError) + -> { Regexp.send(@method, "\\" + "u304") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid Unicode escape: /\\u304/"))) end it "raises a RegexpError if the \\u{} escape is empty" do - -> { Regexp.send(@method, "\\" + "u{}") }.should raise_error(RegexpError) + -> { Regexp.send(@method, "\\" + "u{}") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid Unicode list: /\\u{}/"))) end it "raises a RegexpError if more than six hexadecimal digits are given" do - -> { Regexp.send(@method, "\\" + "u{0ffffff}") }.should raise_error(RegexpError) + -> { Regexp.send(@method, "\\" + "u{0ffffff}") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid Unicode range: /\\u{0ffffff}/"))) end it "returns a Regexp with US-ASCII encoding if only 7-bit ASCII characters are present regardless of the input String's encoding" do diff --git a/spec/ruby/core/string/encode_spec.rb b/spec/ruby/core/string/encode_spec.rb index 97dd753b629..cd449498a35 100644 --- a/spec/ruby/core/string/encode_spec.rb +++ b/spec/ruby/core/string/encode_spec.rb @@ -61,10 +61,22 @@ str.encode(invalid: :replace).should_not equal(str) end - it "normalizes newlines" do - "\r\nfoo".encode(universal_newline: true).should == "\nfoo" + it "normalizes newlines with cr_newline option" do + "\r\nfoo".encode(cr_newline: true).should == "\r\rfoo" + "\rfoo".encode(cr_newline: true).should == "\rfoo" + "\nfoo".encode(cr_newline: true).should == "\rfoo" + end + it "normalizes newlines with crlf_newline option" do + "\r\nfoo".encode(crlf_newline: true).should == "\r\r\nfoo" + "\rfoo".encode(crlf_newline: true).should == "\rfoo" + "\nfoo".encode(crlf_newline: true).should == "\r\nfoo" + end + + it "normalizes newlines with universal_newline option" do + "\r\nfoo".encode(universal_newline: true).should == "\nfoo" "\rfoo".encode(universal_newline: true).should == "\nfoo" + "\nfoo".encode(universal_newline: true).should == "\nfoo" end it "replaces invalid encoding in source with default replacement" do diff --git a/spec/ruby/core/string/shared/encode.rb b/spec/ruby/core/string/shared/encode.rb index 3776e0d709b..9466308886e 100644 --- a/spec/ruby/core/string/shared/encode.rb +++ b/spec/ruby/core/string/shared/encode.rb @@ -194,6 +194,190 @@ end end + describe "given the fallback option" do + context "given a hash" do + it "looks up the replacement value from the hash" do + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: { "\ufffd" => "bar" }) + encoded.should == "Bbar" + end + + it "calls to_str on the returned value" do + obj = Object.new + obj.should_receive(:to_str).and_return("bar") + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: { "\ufffd" => obj }) + encoded.should == "Bbar" + end + + it "does not call to_s on the returned value" do + obj = Object.new + obj.should_not_receive(:to_s) + -> { + "B\ufffd".encode(Encoding::US_ASCII, fallback: { "\ufffd" => obj }) + }.should raise_error(TypeError, "no implicit conversion of Object into String") + end + + it "raises an error if the key is not present in the hash" do + -> { + "B\ufffd".encode(Encoding::US_ASCII, fallback: { "foo" => "bar" }) + }.should raise_error(Encoding::UndefinedConversionError, "U+FFFD from UTF-8 to US-ASCII") + end + + it "raises an error if the value is itself invalid" do + -> { + "B\ufffd".encode(Encoding::US_ASCII, fallback: { "\ufffd" => "\uffee" }) + }.should raise_error(ArgumentError, "too big fallback string") + end + + it "uses the hash's default value if set" do + hash = {} + hash.default = "bar" + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: hash) + encoded.should == "Bbar" + end + + it "uses the result of calling default_proc if set" do + hash = {} + hash.default_proc = -> _, _ { "bar" } + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: hash) + encoded.should == "Bbar" + end + end + + context "given an object inheriting from Hash" do + before do + klass = Class.new(Hash) + @hash_like = klass.new + @hash_like["\ufffd"] = "bar" + end + + it "looks up the replacement value from the object" do + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: @hash_like) + encoded.should == "Bbar" + end + end + + context "given an object responding to []" do + before do + klass = Class.new do + def [](c) = c.bytes.inspect + end + @hash_like = klass.new + end + + it "calls [] on the object, passing the invalid character" do + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: @hash_like) + encoded.should == "B[239, 191, 189]" + end + end + + context "given an object not responding to []" do + before do + @non_hash_like = Object.new + end + + it "raises an error" do + -> { + "B\ufffd".encode(Encoding::US_ASCII, fallback: @non_hash_like) + }.should raise_error(Encoding::UndefinedConversionError, "U+FFFD from UTF-8 to US-ASCII") + end + end + + context "given a proc" do + it "calls the proc to get the replacement value, passing in the invalid character" do + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: proc { |c| c.bytes.inspect }) + encoded.should == "B[239, 191, 189]" + end + + it "calls to_str on the returned value" do + obj = Object.new + obj.should_receive(:to_str).and_return("bar") + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: proc { |c| obj }) + encoded.should == "Bbar" + end + + it "does not call to_s on the returned value" do + obj = Object.new + obj.should_not_receive(:to_s) + -> { + "B\ufffd".encode(Encoding::US_ASCII, fallback: proc { |c| obj }) + }.should raise_error(TypeError, "no implicit conversion of Object into String") + end + + it "raises an error if the returned value is itself invalid" do + -> { + "B\ufffd".encode(Encoding::US_ASCII, fallback: -> c { "\uffee" }) + }.should raise_error(ArgumentError, "too big fallback string") + end + end + + context "given a lambda" do + it "calls the lambda to get the replacement value, passing in the invalid character" do + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: -> c { c.bytes.inspect }) + encoded.should == "B[239, 191, 189]" + end + + it "calls to_str on the returned value" do + obj = Object.new + obj.should_receive(:to_str).and_return("bar") + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: -> c { obj }) + encoded.should == "Bbar" + end + + it "does not call to_s on the returned value" do + obj = Object.new + obj.should_not_receive(:to_s) + -> { + "B\ufffd".encode(Encoding::US_ASCII, fallback: -> c { obj }) + }.should raise_error(TypeError, "no implicit conversion of Object into String") + end + + it "raises an error if the returned value is itself invalid" do + -> { + "B\ufffd".encode(Encoding::US_ASCII, fallback: -> c { "\uffee" }) + }.should raise_error(ArgumentError, "too big fallback string") + end + end + + context "given a method" do + def replace(c) = c.bytes.inspect + def replace_bad(c) = "\uffee" + + def replace_to_str(c) + obj = Object.new + obj.should_receive(:to_str).and_return("bar") + obj + end + + def replace_to_s(c) + obj = Object.new + obj.should_not_receive(:to_s) + obj + end + + it "calls the method to get the replacement value, passing in the invalid character" do + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: method(:replace)) + encoded.should == "B[239, 191, 189]" + end + + it "calls to_str on the returned value" do + encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: method(:replace_to_str)) + encoded.should == "Bbar" + end + + it "does not call to_s on the returned value" do + -> { + "B\ufffd".encode(Encoding::US_ASCII, fallback: method(:replace_to_s)) + }.should raise_error(TypeError, "no implicit conversion of Object into String") + end + + it "raises an error if the returned value is itself invalid" do + -> { + "B\ufffd".encode(Encoding::US_ASCII, fallback: method(:replace_bad)) + }.should raise_error(ArgumentError, "too big fallback string") + end + end + end + describe "given the xml: :text option" do it "replaces all instances of '&' with '&'" do '& and &'.send(@method, "UTF-8", xml: :text).should == '& and &' diff --git a/spec/ruby/core/struct/to_h_spec.rb b/spec/ruby/core/struct/to_h_spec.rb index bfb0af07baf..861ce3f49df 100644 --- a/spec/ruby/core/struct/to_h_spec.rb +++ b/spec/ruby/core/struct/to_h_spec.rb @@ -21,6 +21,18 @@ h.should == { "make" => "ford", "model" => "ranger", "year" => "" } end + it "passes to a block each pair's key and value as separate arguments" do + s = StructClasses::Ruby.new('3.2.4', 'macos') + + ScratchPad.record [] + s.to_h { |k, v| ScratchPad << [k, v]; [k, v] } + ScratchPad.recorded.sort.should == [[:platform, 'macos'], [:version, '3.2.4']] + + ScratchPad.record [] + s.to_h { |*args| ScratchPad << args; [args[0], args[1]] } + ScratchPad.recorded.sort.should == [[:platform, 'macos'], [:version, '3.2.4']] + end + it "raises ArgumentError if block returns longer or shorter array" do -> do StructClasses::Car.new.to_h { |k, v| [k.to_s, "#{v}".downcase, 1] } diff --git a/spec/ruby/core/thread/thread_variable_get_spec.rb b/spec/ruby/core/thread/thread_variable_get_spec.rb index 0ad19bfd885..67017771fdf 100644 --- a/spec/ruby/core/thread/thread_variable_get_spec.rb +++ b/spec/ruby/core/thread/thread_variable_get_spec.rb @@ -14,12 +14,40 @@ end it "returns the value previously set by #thread_variable_set" do - @t.thread_variable_set :a, 49 + @t.thread_variable_set(:a, 49) @t.thread_variable_get(:a).should == 49 end it "returns a value private to self" do - @t.thread_variable_set :thread_variable_get_spec, 82 + @t.thread_variable_set(:thread_variable_get_spec, 82) Thread.current.thread_variable_get(:thread_variable_get_spec).should be_nil end + + it "accepts String and Symbol keys interchangeably" do + @t.thread_variable_set("a", 49) + @t.thread_variable_get("a").should == 49 + @t.thread_variable_get(:a).should == 49 + end + + it "converts a key that is neither String nor Symbol with #to_str" do + key = mock('key') + key.should_receive(:to_str).and_return('a') + @t.thread_variable_set(:a, 49) + @t.thread_variable_get(key).should == 49 + end + + it "does not raise FrozenError if the thread is frozen" do + @t.freeze + @t.thread_variable_get(:a).should be_nil + end + + it "does not raise a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do + @t.thread_variable_get(123).should be_nil + end + + it "does not try to convert the key with #to_sym" do + key = mock('key') + key.should_not_receive(:to_sym) + @t.thread_variable_get(key).should be_nil + end end diff --git a/spec/ruby/core/thread/thread_variable_set_spec.rb b/spec/ruby/core/thread/thread_variable_set_spec.rb index 1338c306c77..c262a6614ef 100644 --- a/spec/ruby/core/thread/thread_variable_set_spec.rb +++ b/spec/ruby/core/thread/thread_variable_set_spec.rb @@ -10,17 +10,53 @@ end it "returns the value set" do - (@t.thread_variable_set :a, 2).should == 2 + @t.thread_variable_set(:a, 2).should == 2 end it "sets a value that will be returned by #thread_variable_get" do - @t.thread_variable_set :a, 49 + @t.thread_variable_set(:a, 49) @t.thread_variable_get(:a).should == 49 end it "sets a value private to self" do - @t.thread_variable_set :thread_variable_get_spec, 82 + @t.thread_variable_set(:thread_variable_get_spec, 82) @t.thread_variable_get(:thread_variable_get_spec).should == 82 Thread.current.thread_variable_get(:thread_variable_get_spec).should be_nil end + + it "accepts String and Symbol keys interchangeably" do + @t.thread_variable_set('a', 49) + @t.thread_variable_get('a').should == 49 + + @t.thread_variable_set(:a, 50) + @t.thread_variable_get('a').should == 50 + end + + it "converts a key that is neither String nor Symbol with #to_str" do + key = mock('key') + key.should_receive(:to_str).and_return('a') + @t.thread_variable_set(key, 49) + @t.thread_variable_get(:a).should == 49 + end + + it "removes a key if the value is nil" do + @t.thread_variable_set(:a, 52) + @t.thread_variable_set(:a, nil) + @t.thread_variable?(:a).should be_false + end + + it "raises a FrozenError if the thread is frozen" do + @t.freeze + -> { @t.thread_variable_set(:a, 1) }.should raise_error(FrozenError, "can't modify frozen thread locals") + end + + it "raises a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do + -> { @t.thread_variable_set(123, 1) }.should raise_error(TypeError, '123 is not a symbol') + end + + it "does not try to convert the key with #to_sym" do + key = mock('key') + key.should_not_receive(:to_sym) + -> { @t.thread_variable_set(key, 42) }.should raise_error(TypeError, "#{key.inspect} is not a symbol") + end end diff --git a/spec/ruby/core/thread/thread_variable_spec.rb b/spec/ruby/core/thread/thread_variable_spec.rb index 6bd1950c04c..d64e6ec63dc 100644 --- a/spec/ruby/core/thread/thread_variable_spec.rb +++ b/spec/ruby/core/thread/thread_variable_spec.rb @@ -10,12 +10,44 @@ end it "returns false if the thread variables do not contain 'key'" do - @t.thread_variable_set :a, 2 + @t.thread_variable_set(:a, 2) @t.thread_variable?(:b).should be_false end it "returns true if the thread variables contain 'key'" do - @t.thread_variable_set :a, 2 + @t.thread_variable_set(:a, 2) @t.thread_variable?(:a).should be_true end + + it "accepts String and Symbol keys interchangeably" do + @t.thread_variable?('a').should be_false + @t.thread_variable?(:a).should be_false + + @t.thread_variable_set(:a, 49) + + @t.thread_variable?('a').should be_true + @t.thread_variable?(:a).should be_true + end + + it "converts a key that is neither String nor Symbol with #to_str" do + key = mock('key') + key.should_receive(:to_str).and_return('a') + @t.thread_variable_set(:a, 49) + @t.thread_variable?(key).should be_true + end + + it "does not raise FrozenError if the thread is frozen" do + @t.freeze + @t.thread_variable?(:a).should be_false + end + + it "does not raise a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do + @t.thread_variable?(123).should be_false + end + + it "does not try to convert the key with #to_sym" do + key = mock('key') + key.should_not_receive(:to_sym) + @t.thread_variable?(key).should be_false + end end diff --git a/spec/ruby/core/thread/thread_variables_spec.rb b/spec/ruby/core/thread/thread_variables_spec.rb index 1bd68b17f14..51ceef3376e 100644 --- a/spec/ruby/core/thread/thread_variables_spec.rb +++ b/spec/ruby/core/thread/thread_variables_spec.rb @@ -10,15 +10,15 @@ end it "returns the keys of all the values set" do - @t.thread_variable_set :a, 2 - @t.thread_variable_set :b, 4 - @t.thread_variable_set :c, 6 + @t.thread_variable_set(:a, 2) + @t.thread_variable_set(:b, 4) + @t.thread_variable_set(:c, 6) @t.thread_variables.sort.should == [:a, :b, :c] end - it "sets a value private to self" do - @t.thread_variable_set :a, 82 - @t.thread_variable_set :b, 82 + it "returns the keys private to self" do + @t.thread_variable_set(:a, 82) + @t.thread_variable_set(:b, 82) Thread.current.thread_variables.should_not include(:a, :b) end @@ -26,4 +26,14 @@ Thread.current.thread_variables.should == [] @t.thread_variables.should == [] end + + it "returns keys as Symbols" do + key = mock('key') + key.should_receive(:to_str).and_return('a') + + @t.thread_variable_set(key, 49) + @t.thread_variable_set('b', 50) + @t.thread_variable_set(:c, 51) + @t.thread_variables.sort.should == [:a, :b, :c] + end end diff --git a/spec/ruby/core/time/yday_spec.rb b/spec/ruby/core/time/yday_spec.rb index 6ea5ff8f1bd..e920c2e28de 100644 --- a/spec/ruby/core/time/yday_spec.rb +++ b/spec/ruby/core/time/yday_spec.rb @@ -1,4 +1,5 @@ require_relative '../../spec_helper' +require_relative '../../shared/time/yday' describe "Time#yday" do it "returns an integer representing the day of the year, 1..366" do @@ -7,15 +8,5 @@ end end - it 'returns the correct value for each day of each month' do - mdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - - yday = 1 - mdays.each_with_index do |days, month| - days.times do |day| - Time.new(2014, month+1, day+1).yday.should == yday - yday += 1 - end - end - end + it_behaves_like :time_yday, -> year, month, day { Time.new(year, month, day).yday } end diff --git a/spec/ruby/core/tracepoint/inspect_spec.rb b/spec/ruby/core/tracepoint/inspect_spec.rb index cc6bf0f8423..d0e633c49b0 100644 --- a/spec/ruby/core/tracepoint/inspect_spec.rb +++ b/spec/ruby/core/tracepoint/inspect_spec.rb @@ -24,6 +24,8 @@ line = nil TracePoint.new(:line) { |tp| next unless TracePointSpec.target_thread? + next unless tp.path == __FILE__ + inspect ||= tp.inspect }.enable do line = __LINE__ @@ -37,6 +39,8 @@ line = nil TracePoint.new(:call) { |tp| next unless TracePointSpec.target_thread? + next unless tp.path == __FILE__ + inspect ||= tp.inspect }.enable do line = __LINE__ + 1 @@ -52,6 +56,8 @@ def trace_point_spec_test_call; end line = nil TracePoint.new(:return) { |tp| next unless TracePointSpec.target_thread? + next unless tp.path == __FILE__ + inspect ||= tp.inspect }.enable do line = __LINE__ + 4 @@ -61,6 +67,7 @@ def trace_point_spec_test_return end trace_point_spec_test_return end + ruby_version_is("3.4") { line = "(?:#{line}|#{line-1})" } inspect.should =~ /\A#\z/ end @@ -69,6 +76,8 @@ def trace_point_spec_test_return inspect = nil tracepoint = TracePoint.new(:c_call) { |tp| next unless TracePointSpec.target_thread? + next unless tp.path == __FILE__ + inspect ||= tp.inspect } line = __LINE__ + 2 @@ -84,6 +93,8 @@ def trace_point_spec_test_return line = nil TracePoint.new(:class) { |tp| next unless TracePointSpec.target_thread? + next unless tp.path == __FILE__ + inspect ||= tp.inspect }.enable do line = __LINE__ + 1 @@ -100,6 +111,7 @@ class TracePointSpec::C thread_inspection = nil TracePoint.new(:thread_begin) { |tp| next unless Thread.current == thread + inspect ||= tp.inspect }.enable(target_thread: nil) do thread = Thread.new {} @@ -116,6 +128,7 @@ class TracePointSpec::C thread_inspection = nil TracePoint.new(:thread_end) { |tp| next unless Thread.current == thread + inspect ||= tp.inspect }.enable(target_thread: nil) do thread = Thread.new {} diff --git a/spec/ruby/core/warning/warn_spec.rb b/spec/ruby/core/warning/warn_spec.rb index 8f96fe9287c..1c21fe29e06 100644 --- a/spec/ruby/core/warning/warn_spec.rb +++ b/spec/ruby/core/warning/warn_spec.rb @@ -121,6 +121,30 @@ def Warning.warn(msg) end end + ruby_bug '#19530', ''...'3.4' do + it "isn't called by Kernel.warn when category is :deprecated but Warning[:deprecated] is false" do + warn_deprecated = Warning[:deprecated] + begin + Warning[:deprecated] = false + Warning.should_not_receive(:warn) + Kernel.warn("foo", category: :deprecated) + ensure + Warning[:deprecated] = warn_deprecated + end + end + + it "isn't called by Kernel.warn when category is :experimental but Warning[:experimental] is false" do + warn_experimental = Warning[:experimental] + begin + Warning[:experimental] = false + Warning.should_not_receive(:warn) + Kernel.warn("foo", category: :experimental) + ensure + Warning[:experimental] = warn_experimental + end + end + end + it "prints the message when VERBOSE is false" do -> { Warning.warn("foo") }.should complain("foo") end diff --git a/spec/ruby/language/for_spec.rb b/spec/ruby/language/for_spec.rb index 0ad5ea88af9..b8ddfe5f0dd 100644 --- a/spec/ruby/language/for_spec.rb +++ b/spec/ruby/language/for_spec.rb @@ -19,6 +19,27 @@ end end + it "iterates over a list of arrays and destructures with an empty splat" do + for i, * in [[1,2]] + i.should == 1 + end + end + + it "iterates over a list of arrays and destructures with a splat" do + for i, *j in [[1,2]] + i.should == 1 + j.should == [2] + end + end + + it "iterates over a list of arrays and destructures with a splat and additional targets" do + for i, *j, k in [[1,2,3,4]] + i.should == 1 + j.should == [2,3] + k.should == 4 + end + end + it "iterates over an Hash passing each key-value pair to the block" do k = 0 l = 0 @@ -81,6 +102,88 @@ class OFor end end + it "allows a global variable as an iterator name" do + old_global_var = $var + m = [1,2,3] + n = 0 + for $var in m + n += 1 + end + $var.should == 3 + n.should == 3 + $var = old_global_var + end + + it "allows an attribute as an iterator name" do + class OFor + attr_accessor :target + end + + ofor = OFor.new + m = [1,2,3] + n = 0 + for ofor.target in m + n += 1 + end + ofor.target.should == 3 + n.should == 3 + end + + # Segfault in MRI 3.3 and lower: https://bugs.ruby-lang.org/issues/20468 + ruby_bug "#20468", ""..."3.4" do + it "allows an attribute with safe navigation as an iterator name" do + class OFor + attr_accessor :target + end + + ofor = OFor.new + m = [1,2,3] + n = 0 + eval <<~RUBY + for ofor&.target in m + n += 1 + end + RUBY + ofor.target.should == 3 + n.should == 3 + end + + it "allows an attribute with safe navigation on a nil base as an iterator name" do + ofor = nil + m = [1,2,3] + n = 0 + eval <<~RUBY + for ofor&.target in m + n += 1 + end + RUBY + ofor.should be_nil + n.should == 3 + end + end + + it "allows an array index writer as an iterator name" do + arr = [:a, :b, :c] + m = [1,2,3] + n = 0 + for arr[1] in m + n += 1 + end + arr.should == [:a, 3, :c] + n.should == 3 + end + + it "allows a hash index writer as an iterator name" do + hash = { a: 10, b: 20, c: 30 } + m = [1,2,3] + n = 0 + for hash[:b] in m + n += 1 + end + hash.should == { a: 10, b: 3, c: 30 } + n.should == 3 + end + # 1.9 behaviour verified by nobu in # http://redmine.ruby-lang.org/issues/show/2053 it "yields only as many values as there are arguments" do diff --git a/spec/ruby/language/heredoc_spec.rb b/spec/ruby/language/heredoc_spec.rb index b3b160fd111..47ee9c2c519 100644 --- a/spec/ruby/language/heredoc_spec.rb +++ b/spec/ruby/language/heredoc_spec.rb @@ -106,4 +106,14 @@ SquigglyHeredocSpecs.least_indented_on_the_first_line_single.should == "a\n b\n c\n" SquigglyHeredocSpecs.least_indented_on_the_last_line_single.should == " a\n b\nc\n" end + + it "reports line numbers inside HEREDOC with method call" do + -> { + <<-HERE.chomp + a + b + #{c} + HERE + }.should raise_error(NameError) { |e| e.backtrace[0].should.start_with?("#{__FILE__}:#{__LINE__ - 2}") } + end end diff --git a/spec/ruby/language/pattern_matching_spec.rb b/spec/ruby/language/pattern_matching_spec.rb index 8c5df06a179..52608b48bea 100644 --- a/spec/ruby/language/pattern_matching_spec.rb +++ b/spec/ruby/language/pattern_matching_spec.rb @@ -5,7 +5,7 @@ ScratchPad.record [] end - describe "can be standalone assoc operator that" do + describe "Rightward assignment (`=>`) that can be standalone assoc operator that" do it "deconstructs value" do suppress_warning do [0, 1] => [a, b] @@ -22,6 +22,23 @@ [a, defined?(b)].should == [0, nil] end end + + it "can work with keywords" do + { a: 0, b: 1 } => { a:, b: } + [a, b].should == [0, 1] + end + end + + describe "One-line pattern matching" do + it "can be used to check if a pattern matches for Array-like entities" do + ([0, 1] in [a, b]).should == true + ([0, 1] in [a, b, c]).should == false + end + + it "can be used to check if a pattern matches for Hash-like entities" do + ({ a: 0, b: 1 } in { a:, b: }).should == true + ({ a: 0, b: 1 } in { a:, b:, c: }).should == false + end end describe "find pattern" do diff --git a/spec/ruby/language/rescue_spec.rb b/spec/ruby/language/rescue_spec.rb index a3ee4807acd..4dc25a5b459 100644 --- a/spec/ruby/language/rescue_spec.rb +++ b/spec/ruby/language/rescue_spec.rb @@ -52,6 +52,16 @@ class ArbitraryException < StandardError RescueSpecs::SafeNavigationSetterCaptor.should_capture_exception end + it 'using a safely navigated setter method on a nil target' do + target = nil + begin + raise SpecificExampleException, "Raising this to be handled below" + rescue SpecificExampleException => target&.captured_error + :caught + end.should == :caught + target.should be_nil + end + it 'using a setter method' do RescueSpecs::SetterCaptor.should_capture_exception end diff --git a/spec/ruby/library/coverage/fixtures/code_with_begin.rb b/spec/ruby/library/coverage/fixtures/code_with_begin.rb new file mode 100644 index 00000000000..9a6384e337c --- /dev/null +++ b/spec/ruby/library/coverage/fixtures/code_with_begin.rb @@ -0,0 +1,3 @@ +begin + 'coverage with begin' +end diff --git a/spec/ruby/library/coverage/result_spec.rb b/spec/ruby/library/coverage/result_spec.rb index 33276778e85..54a3249e451 100644 --- a/spec/ruby/library/coverage/result_spec.rb +++ b/spec/ruby/library/coverage/result_spec.rb @@ -6,6 +6,7 @@ @class_file = fixture __FILE__, 'some_class.rb' @config_file = fixture __FILE__, 'start_coverage.rb' @eval_code_file = fixture __FILE__, 'eval_code.rb' + @with_begin_file = fixture __FILE__, 'code_with_begin.rb' end before :each do @@ -16,6 +17,7 @@ $LOADED_FEATURES.delete(@class_file) $LOADED_FEATURES.delete(@config_file) $LOADED_FEATURES.delete(@eval_code_file) + $LOADED_FEATURES.delete(@with_begin_file) Coverage.result if Coverage.running? end @@ -354,4 +356,16 @@ Coverage.peek_result.should == result end + + it 'covers 100% lines with begin' do + Coverage.start + require @with_begin_file.chomp('.rb') + result = Coverage.result + + result.should == { + @with_begin_file => [ + nil, 1, nil + ] + } + end end diff --git a/spec/ruby/library/date/accessor_spec.rb b/spec/ruby/library/date/accessor_spec.rb index 68a2d9f3dec..74ed0e9c217 100644 --- a/spec/ruby/library/date/accessor_spec.rb +++ b/spec/ruby/library/date/accessor_spec.rb @@ -38,7 +38,7 @@ end describe "Date#yday" do - it "determines the year" do + it "determines the day of the year" do Date.civil(2007, 1, 17).yday.should == 17 Date.civil(2008, 10, 28).yday.should == 302 end diff --git a/spec/ruby/library/date/yday_spec.rb b/spec/ruby/library/date/yday_spec.rb index cfb174a4c25..7dd42e52a54 100644 --- a/spec/ruby/library/date/yday_spec.rb +++ b/spec/ruby/library/date/yday_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' +require_relative '../../shared/time/yday' require 'date' describe "Date#yday" do - it "needs to be reviewed for spec completeness" + it_behaves_like :time_yday, -> year, month, day { Date.new(year, month, day).yday } end diff --git a/spec/ruby/library/datetime/yday_spec.rb b/spec/ruby/library/datetime/yday_spec.rb new file mode 100644 index 00000000000..08a72c6480f --- /dev/null +++ b/spec/ruby/library/datetime/yday_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' +require_relative '../../shared/time/yday' +require 'date' + +describe "DateTime#yday" do + it_behaves_like :time_yday, -> year, month, day { DateTime.new(year, month, day).yday } +end diff --git a/spec/ruby/library/find/fixtures/common.rb b/spec/ruby/library/find/fixtures/common.rb index 14a7edb09aa..99f3bbb45a0 100644 --- a/spec/ruby/library/find/fixtures/common.rb +++ b/spec/ruby/library/find/fixtures/common.rb @@ -71,13 +71,17 @@ def self.mock_dir_files end def self.create_mock_dirs + tmp('') # make sure there is an tmpdir umask = File.umask 0 - mock_dir_files.each do |name| - file = File.join mock_dir, name - mkdir_p File.dirname(file) - touch file + begin + mock_dir_files.each do |name| + file = File.join mock_dir, name + mkdir_p File.dirname(file) + touch file + end + ensure + File.umask umask end - File.umask umask end def self.delete_mock_dirs diff --git a/spec/ruby/library/rbconfig/rbconfig_spec.rb b/spec/ruby/library/rbconfig/rbconfig_spec.rb index 8e38227c21e..3e06219621f 100644 --- a/spec/ruby/library/rbconfig/rbconfig_spec.rb +++ b/spec/ruby/library/rbconfig/rbconfig_spec.rb @@ -144,11 +144,13 @@ end describe "RUBY_DESCRIPTION" do - it "contains version" do - RUBY_DESCRIPTION.should.include? RUBY_VERSION - end + guard_not -> { RUBY_ENGINE == "ruby" && !RbConfig::TOPDIR } do + it "contains version" do + RUBY_DESCRIPTION.should.include? RUBY_VERSION + end - it "contains RUBY_PLATFORM" do - RUBY_DESCRIPTION.should.include? RUBY_PLATFORM + it "contains RUBY_PLATFORM" do + RUBY_DESCRIPTION.should.include? RUBY_PLATFORM + end end end diff --git a/spec/ruby/library/socket/unixsocket/pair_spec.rb b/spec/ruby/library/socket/unixsocket/pair_spec.rb index d80b60894d9..b0a3b2af99d 100644 --- a/spec/ruby/library/socket/unixsocket/pair_spec.rb +++ b/spec/ruby/library/socket/unixsocket/pair_spec.rb @@ -1,9 +1,11 @@ require_relative '../spec_helper' require_relative '../fixtures/classes' require_relative '../shared/partially_closable_sockets' +require_relative 'shared/pair' with_feature :unix_socket do describe "UNIXSocket.pair" do + it_should_behave_like :unixsocket_pair it_should_behave_like :partially_closable_sockets before :each do @@ -14,25 +16,5 @@ @s1.close @s2.close end - - it "returns a pair of connected sockets" do - @s1.puts "foo" - @s2.gets.should == "foo\n" - end - - it "returns sockets with no name" do - @s1.path.should == @s2.path - @s1.path.should == "" - end - - it "returns sockets with no address" do - @s1.addr.should == ["AF_UNIX", ""] - @s2.addr.should == ["AF_UNIX", ""] - end - - it "returns sockets with no peeraddr" do - @s1.peeraddr.should == ["AF_UNIX", ""] - @s2.peeraddr.should == ["AF_UNIX", ""] - end end end diff --git a/spec/ruby/library/socket/unixsocket/shared/pair.rb b/spec/ruby/library/socket/unixsocket/shared/pair.rb new file mode 100644 index 00000000000..ade7fc98523 --- /dev/null +++ b/spec/ruby/library/socket/unixsocket/shared/pair.rb @@ -0,0 +1,29 @@ +require_relative '../../spec_helper' +require_relative '../../fixtures/classes' + +describe :unixsocket_pair, shared: true do + it "returns two UNIXSockets" do + @s1.should be_an_instance_of(UNIXSocket) + @s2.should be_an_instance_of(UNIXSocket) + end + + it "returns a pair of connected sockets" do + @s1.puts "foo" + @s2.gets.should == "foo\n" + end + + it "sets the socket paths to empty Strings" do + @s1.path.should == "" + @s2.path.should == "" + end + + it "sets the socket addresses to empty Strings" do + @s1.addr.should == ["AF_UNIX", ""] + @s2.addr.should == ["AF_UNIX", ""] + end + + it "sets the socket peer addresses to empty Strings" do + @s1.peeraddr.should == ["AF_UNIX", ""] + @s2.peeraddr.should == ["AF_UNIX", ""] + end +end diff --git a/spec/ruby/library/socket/unixsocket/socketpair_spec.rb b/spec/ruby/library/socket/unixsocket/socketpair_spec.rb index 3e9646f76ba..5f34008df3e 100644 --- a/spec/ruby/library/socket/unixsocket/socketpair_spec.rb +++ b/spec/ruby/library/socket/unixsocket/socketpair_spec.rb @@ -1,40 +1,20 @@ require_relative '../spec_helper' +require_relative '../fixtures/classes' +require_relative '../shared/partially_closable_sockets' +require_relative 'shared/pair' with_feature :unix_socket do - describe 'UNIXSocket.socketpair' do - before do + describe "UNIXSocket.socketpair" do + it_should_behave_like :unixsocket_pair + it_should_behave_like :partially_closable_sockets + + before :each do @s1, @s2 = UNIXSocket.socketpair end - after do + after :each do @s1.close @s2.close end - - it 'returns two UNIXSockets' do - @s1.should be_an_instance_of(UNIXSocket) - @s2.should be_an_instance_of(UNIXSocket) - end - - it 'connects the sockets to each other' do - @s1.write('hello') - - @s2.recv(5).should == 'hello' - end - - it 'sets the socket paths to empty Strings' do - @s1.path.should == '' - @s2.path.should == '' - end - - it 'sets the socket addresses to empty Strings' do - @s1.addr.should == ['AF_UNIX', ''] - @s2.addr.should == ['AF_UNIX', ''] - end - - it 'sets the socket peer addresses to empty Strings' do - @s1.peeraddr.should == ['AF_UNIX', ''] - @s2.peeraddr.should == ['AF_UNIX', ''] - end end end diff --git a/spec/ruby/library/stringio/initialize_spec.rb b/spec/ruby/library/stringio/initialize_spec.rb index ad067a0be17..6f4d2e456c2 100644 --- a/spec/ruby/library/stringio/initialize_spec.rb +++ b/spec/ruby/library/stringio/initialize_spec.rb @@ -130,6 +130,26 @@ -> { @io.send(:initialize, str, "w") }.should raise_error(Errno::EACCES) -> { @io.send(:initialize, str, "a") }.should raise_error(Errno::EACCES) end + + it "truncates all the content if passed w mode" do + io = StringIO.allocate + source = +"example".encode(Encoding::ISO_8859_1); + + io.send(:initialize, source, "w") + + io.string.should.empty? + io.string.encoding.should == Encoding::ISO_8859_1 + end + + it "truncates all the content if passed IO::TRUNC mode" do + io = StringIO.allocate + source = +"example".encode(Encoding::ISO_8859_1); + + io.send(:initialize, source, IO::TRUNC) + + io.string.should.empty? + io.string.encoding.should == Encoding::ISO_8859_1 + end end describe "StringIO#initialize when passed [Object]" do @@ -172,7 +192,7 @@ # NOTE: Synchronise with core/io/new_spec.rb (core/io/shared/new.rb) describe "StringIO#initialize when passed keyword arguments" do it "sets the mode based on the passed :mode option" do - io = StringIO.new("example", "r") + io = StringIO.new("example", mode: "r") io.closed_read?.should be_false io.closed_write?.should be_true end diff --git a/spec/ruby/library/stringscanner/scan_until_spec.rb b/spec/ruby/library/stringscanner/scan_until_spec.rb index 6b7782572d3..a8162f1f039 100644 --- a/spec/ruby/library/stringscanner/scan_until_spec.rb +++ b/spec/ruby/library/stringscanner/scan_until_spec.rb @@ -7,9 +7,9 @@ end it "returns the substring up to and including the end of the match" do - @s.scan_until(/a/).should == "This is a" - @s.pre_match.should == "This is " - @s.post_match.should == " test" + @s.scan_until(/a/).should == "This is a" + @s.pre_match.should == "This is " + @s.post_match.should == " test" end it "returns nil if there's no match" do diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c index 1761599081a..04252b28488 100644 --- a/spec/ruby/optional/capi/ext/kernel_spec.c +++ b/spec/ruby/optional/capi/ext/kernel_spec.c @@ -221,7 +221,7 @@ static VALUE kernel_spec_rb_eval_string_protect(VALUE self, VALUE str, VALUE ary VALUE kernel_spec_rb_sys_fail(VALUE self, VALUE msg) { errno = 1; if (msg == Qnil) { - rb_sys_fail(0); + rb_sys_fail(NULL); } else if (self != Qundef) { rb_sys_fail(StringValuePtr(msg)); } diff --git a/spec/ruby/shared/time/yday.rb b/spec/ruby/shared/time/yday.rb new file mode 100644 index 00000000000..f81c45261cf --- /dev/null +++ b/spec/ruby/shared/time/yday.rb @@ -0,0 +1,18 @@ +describe :time_yday, shared: true do + it 'returns the correct value for each day of each month' do + mdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + + yday = 1 + mdays.each_with_index do |days, month| + days.times do |day| + @method.call(2014, month+1, day+1).should == yday + yday += 1 + end + end + end + + it 'supports leap years' do + @method.call(2016, 2, 29).should == 31 + 29 + @method.call(2016, 3, 1).should == 31 + 29 + 1 + end +end From 53b07bb0970c11c99dd69253b49e59a13aae0c69 Mon Sep 17 00:00:00 2001 From: Andrew Konchin Date: Mon, 1 Jul 2024 16:34:28 +0300 Subject: [PATCH 14/18] Add tags for new failing specs --- spec/tags/ruby/core/enumerable/inject_tags.txt | 2 ++ spec/tags/ruby/core/enumerable/reduce_tags.txt | 2 ++ spec/tags/ruby/core/kernel/caller_tags.txt | 2 ++ spec/tags/ruby/core/string/encode_tags.txt | 12 ++++++++++++ .../ruby/core/thread/thread_variable_get_tags.txt | 3 +++ .../ruby/core/thread/thread_variable_set_tags.txt | 5 +++++ spec/tags/ruby/core/thread/thread_variable_tags.txt | 3 +++ spec/tags/ruby/core/thread/thread_variables_tags.txt | 1 + spec/tags/ruby/core/warning/warn_tags.txt | 2 ++ spec/tags/ruby/language/for_tags.txt | 3 +++ spec/tags/ruby/language/heredoc_tags.txt | 1 + spec/tags/ruby/library/stringio/initialize_tags.txt | 1 + 12 files changed, 37 insertions(+) create mode 100644 spec/tags/ruby/core/enumerable/inject_tags.txt create mode 100644 spec/tags/ruby/core/enumerable/reduce_tags.txt create mode 100644 spec/tags/ruby/core/kernel/caller_tags.txt create mode 100644 spec/tags/ruby/core/string/encode_tags.txt create mode 100644 spec/tags/ruby/core/thread/thread_variable_get_tags.txt create mode 100644 spec/tags/ruby/core/thread/thread_variable_set_tags.txt create mode 100644 spec/tags/ruby/core/thread/thread_variable_tags.txt create mode 100644 spec/tags/ruby/core/thread/thread_variables_tags.txt create mode 100644 spec/tags/ruby/core/warning/warn_tags.txt create mode 100644 spec/tags/ruby/language/for_tags.txt diff --git a/spec/tags/ruby/core/enumerable/inject_tags.txt b/spec/tags/ruby/core/enumerable/inject_tags.txt new file mode 100644 index 00000000000..9134b239bc8 --- /dev/null +++ b/spec/tags/ruby/core/enumerable/inject_tags.txt @@ -0,0 +1,2 @@ +fails:Enumerable#inject raises TypeError when the second argument is not Symbol or String and it cannot be converted to String if two arguments +fails:Enumerable#inject raises TypeError when passed not Symbol or String method name argument and it cannot be converted to String diff --git a/spec/tags/ruby/core/enumerable/reduce_tags.txt b/spec/tags/ruby/core/enumerable/reduce_tags.txt new file mode 100644 index 00000000000..2979932afc7 --- /dev/null +++ b/spec/tags/ruby/core/enumerable/reduce_tags.txt @@ -0,0 +1,2 @@ +fails:Enumerable#reduce raises TypeError when the second argument is not Symbol or String and it cannot be converted to String if two arguments +fails:Enumerable#reduce raises TypeError when passed not Symbol or String method name argument and it cannot be converted to String diff --git a/spec/tags/ruby/core/kernel/caller_tags.txt b/spec/tags/ruby/core/kernel/caller_tags.txt new file mode 100644 index 00000000000..35fe4a5a9d3 --- /dev/null +++ b/spec/tags/ruby/core/kernel/caller_tags.txt @@ -0,0 +1,2 @@ +fails:Kernel#caller can be called with a range whose end is negative +fails:Kernel#caller must return the same locations when called with 1..-1 and when called with no arguments diff --git a/spec/tags/ruby/core/string/encode_tags.txt b/spec/tags/ruby/core/string/encode_tags.txt new file mode 100644 index 00000000000..d592f6b14af --- /dev/null +++ b/spec/tags/ruby/core/string/encode_tags.txt @@ -0,0 +1,12 @@ +fails:String#encode given the fallback option given a hash raises an error if the key is not present in the hash +fails:String#encode given the fallback option given a hash raises an error if the value is itself invalid +fails:String#encode given the fallback option given an object not responding to [] raises an error +fails:String#encode given the fallback option given a proc raises an error if the returned value is itself invalid +fails:String#encode given the fallback option given a lambda raises an error if the returned value is itself invalid +fails:String#encode given the fallback option given a method raises an error if the returned value is itself invalid +fails:String#encode! given the fallback option given a hash raises an error if the key is not present in the hash +fails:String#encode! given the fallback option given a hash raises an error if the value is itself invalid +fails:String#encode! given the fallback option given an object not responding to [] raises an error +fails:String#encode! given the fallback option given a proc raises an error if the returned value is itself invalid +fails:String#encode! given the fallback option given a lambda raises an error if the returned value is itself invalid +fails:String#encode! given the fallback option given a method raises an error if the returned value is itself invalid diff --git a/spec/tags/ruby/core/thread/thread_variable_get_tags.txt b/spec/tags/ruby/core/thread/thread_variable_get_tags.txt new file mode 100644 index 00000000000..add1fe87b1f --- /dev/null +++ b/spec/tags/ruby/core/thread/thread_variable_get_tags.txt @@ -0,0 +1,3 @@ +fails:Thread#thread_variable_get converts a key that is neither String nor Symbol with #to_str +fails:Thread#thread_variable_get does not raise a TypeError if the key is neither Symbol nor String, nor responds to #to_str +fails:Thread#thread_variable_get does not try to convert the key with #to_sym diff --git a/spec/tags/ruby/core/thread/thread_variable_set_tags.txt b/spec/tags/ruby/core/thread/thread_variable_set_tags.txt new file mode 100644 index 00000000000..47794ff655a --- /dev/null +++ b/spec/tags/ruby/core/thread/thread_variable_set_tags.txt @@ -0,0 +1,5 @@ +fails:Thread#thread_variable_set converts a key that is neither String nor Symbol with #to_str +fails:Thread#thread_variable_set removes a key if the value is nil +fails:Thread#thread_variable_set raises a FrozenError if the thread is frozen +fails:Thread#thread_variable_set raises a TypeError if the key is neither Symbol nor String, nor responds to #to_str +fails:Thread#thread_variable_set does not try to convert the key with #to_sym diff --git a/spec/tags/ruby/core/thread/thread_variable_tags.txt b/spec/tags/ruby/core/thread/thread_variable_tags.txt new file mode 100644 index 00000000000..28a4b31040b --- /dev/null +++ b/spec/tags/ruby/core/thread/thread_variable_tags.txt @@ -0,0 +1,3 @@ +fails:Thread#thread_variable? converts a key that is neither String nor Symbol with #to_str +fails:Thread#thread_variable? does not raise a TypeError if the key is neither Symbol nor String, nor responds to #to_str +fails:Thread#thread_variable? does not try to convert the key with #to_sym diff --git a/spec/tags/ruby/core/thread/thread_variables_tags.txt b/spec/tags/ruby/core/thread/thread_variables_tags.txt new file mode 100644 index 00000000000..db24ec653e0 --- /dev/null +++ b/spec/tags/ruby/core/thread/thread_variables_tags.txt @@ -0,0 +1 @@ +fails:Thread#thread_variables returns keys as Symbols diff --git a/spec/tags/ruby/core/warning/warn_tags.txt b/spec/tags/ruby/core/warning/warn_tags.txt new file mode 100644 index 00000000000..553db0116d6 --- /dev/null +++ b/spec/tags/ruby/core/warning/warn_tags.txt @@ -0,0 +1,2 @@ +fails:Warning.warn isn't called by Kernel.warn when category is :deprecated but Warning[:deprecated] is false +fails:Warning.warn isn't called by Kernel.warn when category is :experimental but Warning[:experimental] is false diff --git a/spec/tags/ruby/language/for_tags.txt b/spec/tags/ruby/language/for_tags.txt new file mode 100644 index 00000000000..bd5d0f1eff6 --- /dev/null +++ b/spec/tags/ruby/language/for_tags.txt @@ -0,0 +1,3 @@ +fails:The for expression iterates over a list of arrays and destructures with a splat +fails:The for expression iterates over a list of arrays and destructures with a splat and additional targets +fails:The for expression allows an attribute with safe navigation on a nil base as an iterator name diff --git a/spec/tags/ruby/language/heredoc_tags.txt b/spec/tags/ruby/language/heredoc_tags.txt index 4e382976c3c..1db5b909eb9 100644 --- a/spec/tags/ruby/language/heredoc_tags.txt +++ b/spec/tags/ruby/language/heredoc_tags.txt @@ -1 +1,2 @@ fails:Heredoc string raises SyntaxError if quoted HEREDOC identifier is ending not on same line +fails:Heredoc string reports line numbers inside HEREDOC with method call diff --git a/spec/tags/ruby/library/stringio/initialize_tags.txt b/spec/tags/ruby/library/stringio/initialize_tags.txt index 356d96849fb..09be365118d 100644 --- a/spec/tags/ruby/library/stringio/initialize_tags.txt +++ b/spec/tags/ruby/library/stringio/initialize_tags.txt @@ -2,3 +2,4 @@ fails:StringIO#initialize when passed keyword arguments accepts a mode argument fails:StringIO#initialize when passed keyword arguments accepts a mode argument with a :mode option set to nil fails:StringIO#initialize when passed keyword arguments sets binmode from :binmode option fails:StringIO#initialize when passed keyword arguments does not set binmode from false :binmode +fails:StringIO#initialize when passed keyword arguments sets the mode based on the passed :mode option From e690825f2ab514155b7e3fc4adab0cc99bcfb898 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Fri, 28 Jun 2024 17:23:07 -0500 Subject: [PATCH 15/18] Enhance reflective pid-getting for Windows Java 8+ This is a modified version of a patch proposed in https://github.com/jruby/jruby/issues/7515#issuecomment-1368041882 that uses a third mechanism for getting the PID: the `pid` method added to ProcessImpl in 8 and made public in Process in 9. For JRuby 10, which will target Java 17 or 21, we can just call `pid` directly. Fixes #7515 --- .../java/org/jruby/util/ShellLauncher.java | 125 +++++++++++------- 1 file changed, 76 insertions(+), 49 deletions(-) diff --git a/core/src/main/java/org/jruby/util/ShellLauncher.java b/core/src/main/java/org/jruby/util/ShellLauncher.java index 638ee070ab1..d686a8a496e 100644 --- a/core/src/main/java/org/jruby/util/ShellLauncher.java +++ b/core/src/main/java/org/jruby/util/ShellLauncher.java @@ -42,6 +42,7 @@ import java.io.PipedOutputStream; import java.io.PrintStream; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; @@ -659,6 +660,7 @@ public static long getPidFromProcess(Process process) { private static final Field UNIXProcess_pid; private static final Class ProcessImpl; private static final Field ProcessImpl_handle; + private static final Method ProcessImpl_pid; private interface PidGetter { long getPid(Process process); } private static final PidGetter PID_GETTER; @@ -681,7 +683,7 @@ private interface PidGetter { long getPid(Process process); } if (up != null) { try { pid = up.getDeclaredField("pid"); - Java.trySetAccessible(pid); + if (!Java.trySetAccessible(pid)) pid = null; } catch (NoSuchFieldException | SecurityException e) { // ignore and try windows version } @@ -694,69 +696,43 @@ private interface PidGetter { long getPid(Process process); } try { pi = Class.forName("java.lang.ProcessImpl"); handle = pi.getDeclaredField("handle"); - Java.trySetAccessible(handle); + if (!Java.trySetAccessible(handle)) { + handle = null; + } } catch (Exception e) { // ignore and use hashcode } + + Method pi_pid = null; + if (pi != null) { + try { + pi_pid = pi.getMethod("pid"); + if (!Java.trySetAccessible(pi_pid)) { + pi_pid = null; + if (handle == null) pi = null; + } + } catch (Exception e) { + // ignore and use hashcode + } + } ProcessImpl = pi; ProcessImpl_handle = handle; + ProcessImpl_pid = pi_pid; if (UNIXProcess_pid != null) { if (ProcessImpl_handle != null) { // try both - pidGetter = new PidGetter() { - public long getPid(Process process) { - try { - if (UNIXProcess.isInstance(process)) { - return (Integer)UNIXProcess_pid.get(process); - } else if (ProcessImpl.isInstance(process)) { - Long hproc = (Long) ProcessImpl_handle.get(process); - return WindowsFFI.getKernel32().GetProcessId(hproc); - } - } catch (Exception e) { - // ignore and use hashcode - } - return process.hashCode(); - } - }; + pidGetter = ShellLauncher::getPidBoth; } else { // just unix - pidGetter = new PidGetter() { - public long getPid(Process process) { - try { - if (UNIXProcess.isInstance(process)) { - return (Integer)UNIXProcess_pid.get(process); - } - } catch (Exception e) { - // ignore and use hashcode - } - return process.hashCode(); - } - }; + pidGetter = ShellLauncher::getPidUnix; } - } else if (ProcessImpl_handle != null) { + } else if (ProcessImpl_handle != null || ProcessImpl_pid != null) { // just windows - pidGetter = new PidGetter() { - public long getPid(Process process) { - try { - if (ProcessImpl.isInstance(process)) { - Long hproc = (Long) ProcessImpl_handle.get(process); - return WindowsFFI.getKernel32().GetProcessId(hproc); - } - - } catch (Exception e) { - // ignore and use hashcode - } - return process.hashCode(); - } - }; + pidGetter = ShellLauncher::getPidWindows; } else { // neither - default PidGetter - pidGetter = new PidGetter() { - public long getPid(Process process) { - return process.hashCode(); - } - }; + pidGetter = Object::hashCode; } PID_GETTER = pidGetter; } @@ -765,6 +741,57 @@ public static long reflectPidFromProcess(Process process) { return PID_GETTER.getPid(process); } + private static long getPidBoth(Process process) { + try { + if (UNIXProcess.isInstance(process)) { + return getPidUnix(process); + } else if (ProcessImpl.isInstance(process)) { + return getPidWindows(process); + } + } catch (Exception e) { + // fall back on hashcode + } + + return process.hashCode(); + } + + private static long getPidWindows(Process process) { + long pid = -1; + if (ProcessImpl_handle != null) { + try { + if (ProcessImpl.isInstance(process)) { + Long hproc = (Long) ProcessImpl_handle.get(process); + return WindowsFFI.getKernel32().GetProcessId(hproc); + } + } catch (Exception e) { + // fall back on pid logic + } + } + + if (pid == -1 && ProcessImpl_pid != null) { + // JDK > 8 has a new way to look it up and also can't use "handle" field anymore + try { + return (long) ProcessImpl_pid.invoke(process); + } catch (Exception e2) { + // fall back on hashcode + } + } + + return process.hashCode(); + } + + private static long getPidUnix(Process process) { + try { + if (UNIXProcess.isInstance(process)) { + return (Integer) UNIXProcess_pid.get(process); + } + } catch (Exception e) { + // fall back on hashcode + } + + return process.hashCode(); + } + public static Process run(Ruby runtime, IRubyObject string) throws IOException { return run(runtime, new IRubyObject[] {string}, false); } From 4d41e55a6700cc793c1f45d6b604beea5f6c9cd8 Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Tue, 2 Jul 2024 09:50:40 -0400 Subject: [PATCH 16/18] Version 9.4.8.0 updated for release --- VERSION | 2 +- core/pom.xml | 4 ++-- lib/pom.xml | 12 ++++++------ pom.xml | 5 ++++- shaded/pom.xml | 2 +- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index 848d712a28a..9ff92044ddd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.4.8.0-SNAPSHOT +9.4.8.0 diff --git a/core/pom.xml b/core/pom.xml index e999a8d95c6..8aa5ceb9103 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.8.0-SNAPSHOT + 9.4.8.0 jruby-base JRuby Base @@ -711,7 +711,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-base - 9.4.8.0-SNAPSHOT + 9.4.8.0 diff --git a/lib/pom.xml b/lib/pom.xml index 0125db6408a..4ead0798419 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.8.0-SNAPSHOT + 9.4.8.0 jruby-stdlib JRuby Lib Setup @@ -28,7 +28,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-core - 9.4.8.0-SNAPSHOT + 9.4.8.0 test @@ -398,7 +398,7 @@ DO NOT MODIFY - GENERATED CODE rubygems jruby-openssl - 0.14.6 + 0.15.0 gem provided @@ -1107,7 +1107,7 @@ DO NOT MODIFY - GENERATED CODE specifications/irb-1.4.2* specifications/jar-dependencies-0.4.1* specifications/jruby-readline-1.3.7* - specifications/jruby-openssl-0.14.6* + specifications/jruby-openssl-0.15.0* specifications/json-2.7.1* specifications/logger-1.5.1* specifications/mutex_m-0.1.1* @@ -1186,7 +1186,7 @@ DO NOT MODIFY - GENERATED CODE gems/irb-1.4.2*/**/* gems/jar-dependencies-0.4.1*/**/* gems/jruby-readline-1.3.7*/**/* - gems/jruby-openssl-0.14.6*/**/* + gems/jruby-openssl-0.15.0*/**/* gems/json-2.7.1*/**/* gems/logger-1.5.1*/**/* gems/mutex_m-0.1.1*/**/* @@ -1265,7 +1265,7 @@ DO NOT MODIFY - GENERATED CODE cache/irb-1.4.2* cache/jar-dependencies-0.4.1* cache/jruby-readline-1.3.7* - cache/jruby-openssl-0.14.6* + cache/jruby-openssl-0.15.0* cache/json-2.7.1* cache/logger-1.5.1* cache/mutex_m-0.1.1* diff --git a/pom.xml b/pom.xml index 738fc04d371..4001bc7a1f4 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.8.0-SNAPSHOT + 9.4.8.0 pom JRuby JRuby is the effort to recreate the Ruby (https://www.ruby-lang.org) interpreter in Java. @@ -274,6 +274,9 @@ DO NOT MODIFY - GENERATED CODE [3.3.0,) + + No Snapshots Allowed! + diff --git a/shaded/pom.xml b/shaded/pom.xml index 0e06af25fae..3482ee29120 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.8.0-SNAPSHOT + 9.4.8.0 jruby-core JRuby Core From e9d27071ceb2ef95035f8dba7ef8a3e69d26cfa5 Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Tue, 2 Jul 2024 10:43:43 -0400 Subject: [PATCH 17/18] Update for next development cycle --- VERSION | 2 +- core/pom.xml | 4 ++-- lib/pom.xml | 4 ++-- pom.xml | 5 +---- shaded/pom.xml | 2 +- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/VERSION b/VERSION index 9ff92044ddd..756943524da 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.4.8.0 +9.4.9.0-SNAPSHOT diff --git a/core/pom.xml b/core/pom.xml index 8aa5ceb9103..e793d7653d7 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.8.0 + 9.4.9.0-SNAPSHOT jruby-base JRuby Base @@ -711,7 +711,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-base - 9.4.8.0 + 9.4.9.0-SNAPSHOT diff --git a/lib/pom.xml b/lib/pom.xml index 4ead0798419..f9abf20322f 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.8.0 + 9.4.9.0-SNAPSHOT jruby-stdlib JRuby Lib Setup @@ -28,7 +28,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-core - 9.4.8.0 + 9.4.9.0-SNAPSHOT test diff --git a/pom.xml b/pom.xml index 4001bc7a1f4..de33ac28840 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.8.0 + 9.4.9.0-SNAPSHOT pom JRuby JRuby is the effort to recreate the Ruby (https://www.ruby-lang.org) interpreter in Java. @@ -274,9 +274,6 @@ DO NOT MODIFY - GENERATED CODE [3.3.0,) - - No Snapshots Allowed! - diff --git a/shaded/pom.xml b/shaded/pom.xml index 3482ee29120..b3579bf41ef 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -12,7 +12,7 @@ DO NOT MODIFY - GENERATED CODE org.jruby jruby-parent - 9.4.8.0 + 9.4.9.0-SNAPSHOT jruby-core JRuby Core From 7d01a1e839348a1d0cda5f7d172427fcd814a93d Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 10 Jul 2024 11:44:30 -0500 Subject: [PATCH 18/18] Remove all double semicolons These are rejected by some tools. --- core/src/main/java/org/jruby/RubyIO.java | 2 +- core/src/main/java/org/jruby/RubyInstanceConfig.java | 2 +- core/src/main/java/org/jruby/ir/builder/IRBuilderAST.java | 2 +- .../src/main/java/org/jruby/javasupport/ext/JavaExtensions.java | 2 +- core/src/main/java/org/jruby/javasupport/ext/JavaLang.java | 2 +- core/src/main/java/org/jruby/javasupport/ext/JavaUtil.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyIO.java b/core/src/main/java/org/jruby/RubyIO.java index 41548690d53..8c67c8f0250 100644 --- a/core/src/main/java/org/jruby/RubyIO.java +++ b/core/src/main/java/org/jruby/RubyIO.java @@ -5385,7 +5385,7 @@ public final OpenFile MakeOpenFile() { private static int rb_io_fptr_finalize(Ruby runtime, OpenFile fptr) { if (fptr == null) return 0; - fptr.setPath(null);; + fptr.setPath(null); if (fptr.fd() != null) fptr.cleanup(runtime, true); fptr.write_lock = null; diff --git a/core/src/main/java/org/jruby/RubyInstanceConfig.java b/core/src/main/java/org/jruby/RubyInstanceConfig.java index 88b2c72265f..0609818d280 100644 --- a/core/src/main/java/org/jruby/RubyInstanceConfig.java +++ b/core/src/main/java/org/jruby/RubyInstanceConfig.java @@ -1526,7 +1526,7 @@ public static ClassLoader defaultClassLoader() { private ProfilingMode profilingMode = Options.CLI_PROFILING_MODE.load(); private ProfileOutput profileOutput = new ProfileOutput(System.err); - private String profilingService = Options.CLI_PROFILING_SERVICE.load();; + private String profilingService = Options.CLI_PROFILING_SERVICE.load(); private ClassLoader loader = defaultClassLoader(); diff --git a/core/src/main/java/org/jruby/ir/builder/IRBuilderAST.java b/core/src/main/java/org/jruby/ir/builder/IRBuilderAST.java index 0f01ec1af2c..f8112198887 100644 --- a/core/src/main/java/org/jruby/ir/builder/IRBuilderAST.java +++ b/core/src/main/java/org/jruby/ir/builder/IRBuilderAST.java @@ -32,7 +32,7 @@ import org.jruby.ir.operands.Float; import org.jruby.ir.operands.FrozenString; import org.jruby.ir.operands.Hash; -import org.jruby.ir.operands.ImmutableLiteral;; +import org.jruby.ir.operands.ImmutableLiteral; import org.jruby.ir.operands.Integer; import org.jruby.ir.operands.Label; import org.jruby.ir.operands.LocalVariable; diff --git a/core/src/main/java/org/jruby/javasupport/ext/JavaExtensions.java b/core/src/main/java/org/jruby/javasupport/ext/JavaExtensions.java index cd809b0a759..7ad03881e82 100644 --- a/core/src/main/java/org/jruby/javasupport/ext/JavaExtensions.java +++ b/core/src/main/java/org/jruby/javasupport/ext/JavaExtensions.java @@ -14,7 +14,7 @@ */ public class JavaExtensions { - private static final boolean LAZY = Options.JI_LOAD_LAZY.load();; + private static final boolean LAZY = Options.JI_LOAD_LAZY.load(); private JavaExtensions() { /* hidden */ } diff --git a/core/src/main/java/org/jruby/javasupport/ext/JavaLang.java b/core/src/main/java/org/jruby/javasupport/ext/JavaLang.java index 14d1adb0add..be8fcf7a4d2 100644 --- a/core/src/main/java/org/jruby/javasupport/ext/JavaLang.java +++ b/core/src/main/java/org/jruby/javasupport/ext/JavaLang.java @@ -895,7 +895,7 @@ static final class InspectRawValue extends JavaMethod.JavaMethodZero { @Override public IRubyObject call(final ThreadContext context, final IRubyObject self, final RubyModule clazz, final java.lang.String name) { - java.lang.Object val = unwrapIfJavaObject(self);; + java.lang.Object val = unwrapIfJavaObject(self); return context.runtime.newString(val.toString()); } diff --git a/core/src/main/java/org/jruby/javasupport/ext/JavaUtil.java b/core/src/main/java/org/jruby/javasupport/ext/JavaUtil.java index 018abb3ac7a..255967f6931 100644 --- a/core/src/main/java/org/jruby/javasupport/ext/JavaUtil.java +++ b/core/src/main/java/org/jruby/javasupport/ext/JavaUtil.java @@ -531,7 +531,7 @@ public static RubyArray to_a(final ThreadContext context, final IRubyObject self final java.util.List list = unwrapIfJavaObject(self); final IRubyObject[] array = new IRubyObject[ list.size() ]; int i = 0; for ( Object elem : list ) { - array[i++] = convertJavaToUsableRubyObject(runtime, elem);; + array[i++] = convertJavaToUsableRubyObject(runtime, elem); } return RubyArray.newArrayMayCopy(runtime, array); }