diff options
Diffstat (limited to 'spec/ruby/command_line')
63 files changed, 1389 insertions, 0 deletions
diff --git a/spec/ruby/command_line/backtrace_limit_spec.rb b/spec/ruby/command_line/backtrace_limit_spec.rb new file mode 100644 index 0000000000..4d57bc268b --- /dev/null +++ b/spec/ruby/command_line/backtrace_limit_spec.rb @@ -0,0 +1,93 @@ +require_relative '../spec_helper' + +describe "The --backtrace-limit command line option" do + ruby_version_is ""..."3.4" do + it "limits top-level backtraces to a given number of entries" do + file = fixture(__FILE__ , "backtrace.rb") + out = ruby_exe(file, options: "--backtrace-limit=2", args: "top 2>&1", exit_status: 1) + out = out.gsub(__dir__, '') + + out.should == <<-MSG +top +/fixtures/backtrace.rb:2:in `a': oops (RuntimeError) +\tfrom /fixtures/backtrace.rb:6:in `b' +\tfrom /fixtures/backtrace.rb:10:in `c' +\t ... 2 levels... + MSG + end + + it "affects Exception#full_message" do + file = fixture(__FILE__ , "backtrace.rb") + out = ruby_exe(file, options: "--backtrace-limit=2", args: "full_message 2>&1") + out = out.gsub(__dir__, '') + + out.should == <<-MSG +full_message +/fixtures/backtrace.rb:2:in `a': oops (RuntimeError) +\tfrom /fixtures/backtrace.rb:6:in `b' +\tfrom /fixtures/backtrace.rb:10:in `c' +\t ... 2 levels... + MSG + end + + it "does not affect Exception#backtrace" do + file = fixture(__FILE__ , "backtrace.rb") + out = ruby_exe(file, options: "--backtrace-limit=2", args: "backtrace 2>&1") + out = out.gsub(__dir__, '') + + out.should == <<-MSG +backtrace +/fixtures/backtrace.rb:2:in `a' +/fixtures/backtrace.rb:6:in `b' +/fixtures/backtrace.rb:10:in `c' +/fixtures/backtrace.rb:14:in `d' +/fixtures/backtrace.rb:29:in `<main>' + MSG + end + end + + ruby_version_is "3.4" do + it "limits top-level backtraces to a given number of entries" do + file = fixture(__FILE__ , "backtrace.rb") + out = ruby_exe(file, options: "--backtrace-limit=2", args: "top 2>&1", exit_status: 1) + out = out.gsub(__dir__, '') + + out.should == <<-MSG +top +/fixtures/backtrace.rb:2:in 'Object#a': oops (RuntimeError) +\tfrom /fixtures/backtrace.rb:6:in 'Object#b' +\tfrom /fixtures/backtrace.rb:10:in 'Object#c' +\t ... 2 levels... + MSG + end + + it "affects Exception#full_message" do + file = fixture(__FILE__ , "backtrace.rb") + out = ruby_exe(file, options: "--backtrace-limit=2", args: "full_message 2>&1") + out = out.gsub(__dir__, '') + + out.should == <<-MSG +full_message +/fixtures/backtrace.rb:2:in 'Object#a': oops (RuntimeError) +\tfrom /fixtures/backtrace.rb:6:in 'Object#b' +\tfrom /fixtures/backtrace.rb:10:in 'Object#c' +\t ... 2 levels... + MSG + end + + it "does not affect Exception#backtrace" do + file = fixture(__FILE__ , "backtrace.rb") + out = ruby_exe(file, options: "--backtrace-limit=2", args: "backtrace 2>&1") + out = out.gsub(__dir__, '') + + out.should == <<-MSG +backtrace +/fixtures/backtrace.rb:2:in 'Object#a' +/fixtures/backtrace.rb:6:in 'Object#b' +/fixtures/backtrace.rb:10:in 'Object#c' +/fixtures/backtrace.rb:14:in 'Object#d' +/fixtures/backtrace.rb:29:in '<main>' + MSG + end + end +end diff --git a/spec/ruby/command_line/dash_0_spec.rb b/spec/ruby/command_line/dash_0_spec.rb new file mode 100755 index 0000000000..2ce4f49b5e --- /dev/null +++ b/spec/ruby/command_line/dash_0_spec.rb @@ -0,0 +1,13 @@ +require_relative '../spec_helper' + +describe "The -0 command line option" do + it "sets $/ and $-0" do + ruby_exe("puts $/, $-0", options: "-072").should == ":\n:\n" + end + + ruby_version_is "4.0" do + it "sets $/ and $-0 as a frozen string" do + ruby_exe("puts $/.frozen?, $-0.frozen?", options: "-072").should == "true\ntrue\n" + end + end +end diff --git a/spec/ruby/command_line/dash_a_spec.rb b/spec/ruby/command_line/dash_a_spec.rb new file mode 100644 index 0000000000..43d923ce16 --- /dev/null +++ b/spec/ruby/command_line/dash_a_spec.rb @@ -0,0 +1,19 @@ +require_relative '../spec_helper' + +describe "The -a command line option" do + before :each do + @names = fixture __FILE__, "full_names.txt" + end + + it "runs the code in loop conditional on Kernel.gets()" do + ruby_exe("puts $F.last", options: "-n -a", + args: " < #{@names}").should == + "jones\nfield\ngrey\n" + end + + it "sets $-a" do + ruby_exe("puts $-a", options: "-n -a", + args: " < #{@names}").should == + "true\ntrue\ntrue\n" + end +end diff --git a/spec/ruby/command_line/dash_c_spec.rb b/spec/ruby/command_line/dash_c_spec.rb new file mode 100644 index 0000000000..6b3a5de685 --- /dev/null +++ b/spec/ruby/command_line/dash_c_spec.rb @@ -0,0 +1,13 @@ +require_relative '../spec_helper' + +describe "The -c command line option" do + it "checks syntax in given file" do + ruby_exe(nil, args: "-c #{__FILE__}").chomp.should == "Syntax OK" + end + + it "checks syntax in -e strings" do + ruby_exe(nil, args: "-c -e 'puts 1' -e 'hello world'").chomp.should == "Syntax OK" + end + + #Also needs spec for reading from STDIN +end diff --git a/spec/ruby/command_line/dash_d_spec.rb b/spec/ruby/command_line/dash_d_spec.rb new file mode 100644 index 0000000000..26891b4791 --- /dev/null +++ b/spec/ruby/command_line/dash_d_spec.rb @@ -0,0 +1,22 @@ +require_relative '../spec_helper' + +describe "The -d command line option" do + before :each do + @script = fixture __FILE__, "debug.rb" + end + + it "sets $DEBUG to true" do + ruby_exe(@script, options: "-d", + args: "0 2> #{File::NULL}").chomp.should == "$DEBUG true" + end + + it "sets $VERBOSE to true" do + ruby_exe(@script, options: "-d", + args: "1 2> #{File::NULL}").chomp.should == "$VERBOSE true" + end + + it "sets $-d to true" do + ruby_exe(@script, options: "-d", + args: "2 2> #{File::NULL}").chomp.should == "$-d true" + end +end diff --git a/spec/ruby/command_line/dash_e_spec.rb b/spec/ruby/command_line/dash_e_spec.rb new file mode 100644 index 0000000000..24ed34376d --- /dev/null +++ b/spec/ruby/command_line/dash_e_spec.rb @@ -0,0 +1,41 @@ +require_relative '../spec_helper' + +describe "The -e command line option" do + it "evaluates the given string" do + ruby_exe("puts 'foo'").chomp.should == "foo" + end + + it "joins multiple strings with newlines" do + ruby_exe(nil, args: %Q{-e "puts 'hello" -e "world'" 2>&1}).chomp.should == "hello\nworld" + end + + it "uses 'main' as self" do + ruby_exe("puts self", escape: false).chomp.should == "main" + end + + it "uses '-e' as file" do + ruby_exe("puts __FILE__", escape: false).chomp.should == "-e" + end + + it "uses '-e' in $0" do + system(*ruby_exe, '-e', 'exit $0 == "-e"').should == true + end + + #needs to test return => LocalJumpError + + describe "with -n and an Integer range" do + before :each do + @script = "-ne 'print if %s' #{fixture(__FILE__, "conditional_range.txt")}" + end + + it "mimics an awk conditional by comparing an inclusive-end range with $." do + ruby_exe(nil, args: (@script % "2..3")).should == "2\n3\n" + ruby_exe(nil, args: (@script % "2..2")).should == "2\n" + end + + it "mimics a sed conditional by comparing an exclusive-end range with $." do + ruby_exe(nil, args: (@script % "2...3")).should == "2\n3\n" + ruby_exe(nil, args: (@script % "2...2")).should == "2\n3\n4\n5\n" + end + end +end diff --git a/spec/ruby/command_line/dash_encoding_spec.rb b/spec/ruby/command_line/dash_encoding_spec.rb new file mode 100644 index 0000000000..5803d328c1 --- /dev/null +++ b/spec/ruby/command_line/dash_encoding_spec.rb @@ -0,0 +1,36 @@ +require_relative '../spec_helper' + +describe "The --encoding command line option" do + before :each do + @test_string = "print [Encoding.default_external.name, Encoding.default_internal&.name].inspect" + @enc2 = Encoding::ISO_8859_1 + end + + describe "sets Encoding.default_external and optionally Encoding.default_internal" do + it "if given a single encoding with an =" do + ruby_exe(@test_string, options: "--disable-gems --encoding=big5").should == [Encoding::Big5.name, nil].inspect + end + + it "if given a single encoding as a separate argument" do + ruby_exe(@test_string, options: "--disable-gems --encoding big5").should == [Encoding::Big5.name, nil].inspect + end + + it "if given two encodings with an =" do + ruby_exe(@test_string, options: "--disable-gems --encoding=big5:#{@enc2}").should == [Encoding::Big5.name, @enc2.name].inspect + end + + it "if given two encodings as a separate argument" do + ruby_exe(@test_string, options: "--disable-gems --encoding big5:#{@enc2}").should == [Encoding::Big5.name, @enc2.name].inspect + end + end + + it "does not accept a third encoding" do + options = { + options: "--disable-gems --encoding big5:#{@enc2}:utf-32le", + args: "2>&1", + exit_status: 1 + } + + ruby_exe(@test_string, options).should =~ /extra argument for --encoding: utf-32le/ + end +end diff --git a/spec/ruby/command_line/dash_external_encoding_spec.rb b/spec/ruby/command_line/dash_external_encoding_spec.rb new file mode 100644 index 0000000000..f052674dc8 --- /dev/null +++ b/spec/ruby/command_line/dash_external_encoding_spec.rb @@ -0,0 +1,15 @@ +require_relative '../spec_helper' + +describe 'The --external-encoding command line option sets Encoding.default_external' do + before :each do + @test_string = "print Encoding.default_external.name" + end + + it "if given an encoding with an =" do + ruby_exe(@test_string, options: '--external-encoding=big5').should == Encoding::Big5.name + end + + it "if given an encoding as a separate argument" do + ruby_exe(@test_string, options: '--external-encoding big5').should == Encoding::Big5.name + end +end diff --git a/spec/ruby/command_line/dash_internal_encoding_spec.rb b/spec/ruby/command_line/dash_internal_encoding_spec.rb new file mode 100644 index 0000000000..3049040bb4 --- /dev/null +++ b/spec/ruby/command_line/dash_internal_encoding_spec.rb @@ -0,0 +1,15 @@ +require_relative '../spec_helper' + +describe 'The --internal-encoding command line option sets Encoding.default_internal' do + before :each do + @test_string = "print Encoding.default_internal.name" + end + + it "if given an encoding with an =" do + ruby_exe(@test_string, options: '--internal-encoding=big5').should == Encoding::Big5.name + end + + it "if given an encoding as a separate argument" do + ruby_exe(@test_string, options: '--internal-encoding big5').should == Encoding::Big5.name + end +end diff --git a/spec/ruby/command_line/dash_l_spec.rb b/spec/ruby/command_line/dash_l_spec.rb new file mode 100644 index 0000000000..44a98445f3 --- /dev/null +++ b/spec/ruby/command_line/dash_l_spec.rb @@ -0,0 +1,31 @@ +require_relative '../spec_helper' + +describe "The -l command line option" do + before :each do + @names = fixture __FILE__, "full_names.txt" + end + + it "chomps lines with default separator" do + ruby_exe('puts $_.end_with?("\n")', options: "-n -l", + args: " < #{@names}").should == + "false\nfalse\nfalse\n" + end + + it "chomps last line based on $/" do + ruby_exe('BEGIN { $/ = "ones\n" }; puts $_', options: "-W0 -n -l", + args: " < #{@names}").should == + "alice j\nbob field\njames grey\n" + end + + it "sets $\\ to the value of $/" do + ruby_exe("puts $\\ == $/", options: "-W0 -n -l", + args: " < #{@names}").should == + "true\ntrue\ntrue\n" + end + + it "sets $-l" do + ruby_exe("puts $-l", options: "-n -l", + args: " < #{@names}").should == + "true\ntrue\ntrue\n" + end +end diff --git a/spec/ruby/command_line/dash_n_spec.rb b/spec/ruby/command_line/dash_n_spec.rb new file mode 100644 index 0000000000..1dd9379259 --- /dev/null +++ b/spec/ruby/command_line/dash_n_spec.rb @@ -0,0 +1,36 @@ +require_relative '../spec_helper' + +describe "The -n command line option" do + before :each do + @names = fixture __FILE__, "names.txt" + end + + it "runs the code in loop conditional on Kernel.gets()" do + ruby_exe("puts $_", options: "-n", + args: " < #{@names}").should == + "alice\nbob\njames\n" + end + + it "only evaluates BEGIN blocks once" do + ruby_exe("BEGIN { puts \"hi\" }; puts $_", options: "-n", + args: " < #{@names}").should == + "hi\nalice\nbob\njames\n" + end + + it "only evaluates END blocks once" do + ruby_exe("puts $_; END {puts \"bye\"}", options: "-n", + args: " < #{@names}").should == + "alice\nbob\njames\nbye\n" + end + + it "allows summing over a whole file" do + script = <<-script + BEGIN { $total = 0 } + $total += 1 + END { puts $total } + script + ruby_exe(script, options: "-n", + args: " < #{@names}").should == + "3\n" + end +end diff --git a/spec/ruby/command_line/dash_p_spec.rb b/spec/ruby/command_line/dash_p_spec.rb new file mode 100644 index 0000000000..967e3796de --- /dev/null +++ b/spec/ruby/command_line/dash_p_spec.rb @@ -0,0 +1,19 @@ +require_relative '../spec_helper' + +describe "The -p command line option" do + before :each do + @names = fixture __FILE__, "names.txt" + end + + it "runs the code in loop conditional on Kernel.gets() and prints $_" do + ruby_exe("$_ = $_.upcase", options: "-p", + args: " < #{@names}").should == + "ALICE\nBOB\nJAMES\n" + end + + it "sets $-p" do + ruby_exe("$_ = $-p", options: "-p", + args: " < #{@names}").should == + "truetruetrue" + end +end diff --git a/spec/ruby/command_line/dash_r_spec.rb b/spec/ruby/command_line/dash_r_spec.rb new file mode 100644 index 0000000000..9f673c53dc --- /dev/null +++ b/spec/ruby/command_line/dash_r_spec.rb @@ -0,0 +1,31 @@ +require_relative '../spec_helper' + +describe "The -r command line option" do + before :each do + @script = fixture __FILE__, "require.rb" + @test_file = fixture __FILE__, "test_file" + end + + it "requires the specified file" do + out = ruby_exe(@script, options: "-r #{@test_file}") + out.should include("REQUIRED") + out.should include(@test_file + ".rb") + end + + it "requires the file before parsing the main script" do + out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), options: "-r #{@test_file}", args: "2>&1", exit_status: 1) + $?.should_not.success? + out.should include("REQUIRED") + + # it's tempting not to rely on error message and rely only on exception class name, + # but CRuby before 3.2 doesn't print class name for syntax error + out.should include_any_of("syntax error", "SyntaxError") + end + + it "does not require the file if the main script file does not exist" do + out = `#{ruby_exe.to_a.join(' ')} -r #{@test_file} #{fixture(__FILE__, "does_not_exist.rb")} 2>&1` + $?.should_not.success? + out.should_not.include?("REQUIRED") + out.should.include?("No such file or directory") + end +end diff --git a/spec/ruby/command_line/dash_s_spec.rb b/spec/ruby/command_line/dash_s_spec.rb new file mode 100644 index 0000000000..eaaeea7c96 --- /dev/null +++ b/spec/ruby/command_line/dash_s_spec.rb @@ -0,0 +1,52 @@ +require_relative '../spec_helper' + +describe "The -s command line option" do + describe "when using -- to stop parsing" do + it "sets the value to true without an explicit value" do + ruby_exe(nil, options: "-s -e 'p $n'", + args: "-- -n").chomp.should == "true" + end + + it "parses single letter args into globals" do + ruby_exe(nil, options: "-s -e 'puts $n'", + args: "-- -n=blah").chomp.should == "blah" + end + + it "parses long args into globals" do + ruby_exe(nil, options: "-s -e 'puts $_name'", + args: "-- --name=blah").chomp.should == "blah" + end + + it "converts extra dashes into underscores" do + ruby_exe(nil, options: "-s -e 'puts $___name__test__'", + args: "-- ----name--test--=blah").chomp.should == "blah" + end + end + + describe "when running a script" do + before :all do + @script = fixture __FILE__, "dash_s_script.rb" + end + + it "sets the value to true without an explicit value" do + ruby_exe(@script, options: "-s", + args: "-n 0").chomp.should == "true" + end + + it "parses single letter args into globals" do + ruby_exe(@script, options: "-s", + args: "-n=blah 1").chomp.should == "blah" + end + + it "parses long args into globals" do + ruby_exe(@script, options: "-s", + args: "--name=blah 2").chomp.should == "blah" + end + + it "converts extra dashes into underscores" do + ruby_exe(@script, options: "-s", + args: "----name--test--=blah 3").chomp.should == "blah" + end + + end +end diff --git a/spec/ruby/command_line/dash_upper_c_spec.rb b/spec/ruby/command_line/dash_upper_c_spec.rb new file mode 100644 index 0000000000..ece1b32105 --- /dev/null +++ b/spec/ruby/command_line/dash_upper_c_spec.rb @@ -0,0 +1,6 @@ +require_relative '../spec_helper' +require_relative 'shared/change_directory' + +describe "The -C command line option" do + it_behaves_like :command_line_change_directory, "-C" +end diff --git a/spec/ruby/command_line/dash_upper_e_spec.rb b/spec/ruby/command_line/dash_upper_e_spec.rb new file mode 100644 index 0000000000..5a83962583 --- /dev/null +++ b/spec/ruby/command_line/dash_upper_e_spec.rb @@ -0,0 +1,37 @@ +require_relative '../spec_helper' + +describe "ruby -E" do + it "sets the external encoding with '-E external'" do + result = ruby_exe("print Encoding.default_external", options: '-E euc-jp') + result.should == "EUC-JP" + end + + platform_is_not :windows do + it "also sets the filesystem encoding with '-E external'" do + result = ruby_exe("print Encoding.find('filesystem')", options: '-E euc-jp') + result.should == "EUC-JP" + end + end + + it "sets the external encoding with '-E external:'" do + result = ruby_exe("print Encoding.default_external", options: '-E Shift_JIS:') + result.should == "Shift_JIS" + end + + it "sets the internal encoding with '-E :internal'" do + ruby_exe("print Encoding.default_internal", options: '-E :SHIFT_JIS'). + should == 'Shift_JIS' + end + + it "sets the external and internal encodings with '-E external:internal'" do + ruby_exe("puts Encoding.default_external, Encoding.default_internal", options: '-E euc-jp:SHIFT_JIS'). + should == "EUC-JP\nShift_JIS\n" + end + + it "raises a RuntimeError if used with -U" do + ruby_exe("p 1", + options: '-Eascii:ascii -U', + args: '2>&1', + exit_status: 1).should =~ /RuntimeError/ + end +end diff --git a/spec/ruby/command_line/dash_upper_f_spec.rb b/spec/ruby/command_line/dash_upper_f_spec.rb new file mode 100644 index 0000000000..5c10a7140d --- /dev/null +++ b/spec/ruby/command_line/dash_upper_f_spec.rb @@ -0,0 +1,13 @@ +require_relative '../spec_helper' + +describe "the -F command line option" do + before :each do + @passwd = fixture __FILE__, "passwd_file.txt" + end + + it "specifies the field separator pattern for -a" do + ruby_exe("puts $F[0]", options: "-naF:", + args: " < #{@passwd}").should == + "nobody\nroot\ndaemon\n" + end +end diff --git a/spec/ruby/command_line/dash_upper_i_spec.rb b/spec/ruby/command_line/dash_upper_i_spec.rb new file mode 100644 index 0000000000..4cafb724e3 --- /dev/null +++ b/spec/ruby/command_line/dash_upper_i_spec.rb @@ -0,0 +1,51 @@ +require_relative '../spec_helper' + +describe "The -I command line option" do + before :each do + @script = fixture __FILE__, "loadpath.rb" + end + + it "adds the path to the load path ($:)" do + ruby_exe(@script, options: "-I fixtures").should include("fixtures") + end + + it "adds the path at the front of $LOAD_PATH" do + lines = ruby_exe(@script, options: "-I fixtures").lines + if PlatformGuard.implementation? :ruby + # In a MRI checkout, $PWD ends up as the first entry in $LOAD_PATH. + # So just assert that it's at the beginning. + idx = lines.index { |l| l.include?("fixtures") } + idx.should < 2 + idx.should < lines.size-1 + else + lines[0].should include("fixtures") + end + end + + it "adds the path expanded from CWD to $LOAD_PATH" do + ruby_exe(@script, options: "-I fixtures").lines.should include "#{Dir.pwd}/fixtures\n" + end + + it "expands a path from CWD even if it does not exist" do + ruby_exe(@script, options: "-I not_exist/not_exist").lines.should include "#{Dir.pwd}/not_exist/not_exist\n" + end +end + +platform_is_not :windows do + describe "The -I command line option" do + before :each do + @script = fixture __FILE__, "loadpath.rb" + @fixtures = File.dirname(@script) + @symlink = tmp("loadpath_symlink") + File.symlink(@fixtures, @symlink) + end + + after :each do + rm_r @symlink + end + + it "does not expand symlinks" do + ruby_exe(@script, options: "-I #{@symlink}").lines.should include "#{@symlink}\n" + end + end +end diff --git a/spec/ruby/command_line/dash_upper_k_spec.rb b/spec/ruby/command_line/dash_upper_k_spec.rb new file mode 100644 index 0000000000..7e71532295 --- /dev/null +++ b/spec/ruby/command_line/dash_upper_k_spec.rb @@ -0,0 +1,65 @@ +require_relative '../spec_helper' + +describe 'The -K command line option' do + before :each do + @test_string = "print [__ENCODING__&.name, Encoding.default_external&.name, Encoding.default_internal&.name].inspect" + end + + describe 'sets __ENCODING__ and Encoding.default_external' do + it "to Encoding::BINARY with -Ka" do + ruby_exe(@test_string, options: '-Ka').should == + [Encoding::BINARY.name, Encoding::BINARY.name, nil].inspect + end + + it "to Encoding::BINARY with -KA" do + ruby_exe(@test_string, options: '-KA').should == + [Encoding::BINARY.name, Encoding::BINARY.name, nil].inspect + end + + it "to Encoding::BINARY with -Kn" do + ruby_exe(@test_string, options: '-Kn').should == + [Encoding::BINARY.name, Encoding::BINARY.name, nil].inspect + end + + it "to Encoding::BINARY with -KN" do + ruby_exe(@test_string, options: '-KN').should == + [Encoding::BINARY.name, Encoding::BINARY.name, nil].inspect + end + + it "to Encoding::EUC_JP with -Ke" do + ruby_exe(@test_string, options: '-Ke').should == + [Encoding::EUC_JP.name, Encoding::EUC_JP.name, nil].inspect + end + + it "to Encoding::EUC_JP with -KE" do + ruby_exe(@test_string, options: '-KE').should == + [Encoding::EUC_JP.name, Encoding::EUC_JP.name, nil].inspect + end + + it "to Encoding::UTF_8 with -Ku" do + ruby_exe(@test_string, options: '-Ku').should == + [Encoding::UTF_8.name, Encoding::UTF_8.name, nil].inspect + end + + it "to Encoding::UTF_8 with -KU" do + ruby_exe(@test_string, options: '-KU').should == + [Encoding::UTF_8.name, Encoding::UTF_8.name, nil].inspect + end + + it "to Encoding::Windows_31J with -Ks" do + ruby_exe(@test_string, options: '-Ks').should == + [Encoding::Windows_31J.name, Encoding::Windows_31J.name, nil].inspect + end + + it "to Encoding::Windows_31J with -KS" do + ruby_exe(@test_string, options: '-KS').should == + [Encoding::Windows_31J.name, Encoding::Windows_31J.name, nil].inspect + end + end + + it "ignores unknown codes" do + external = Encoding.find('external') + ruby_exe(@test_string, options: '-KZ').should == + [Encoding::UTF_8.name, external.name, nil].inspect + end +end diff --git a/spec/ruby/command_line/dash_upper_s_spec.rb b/spec/ruby/command_line/dash_upper_s_spec.rb new file mode 100644 index 0000000000..17991503f1 --- /dev/null +++ b/spec/ruby/command_line/dash_upper_s_spec.rb @@ -0,0 +1,29 @@ +require_relative '../spec_helper' + +describe 'The -S command line option' do + before :each do + @path = [ENV['PATH'], fixture(__FILE__, "bin")].join(':') + end + + platform_is_not :windows do + # On VirtualBox shared directory (vboxsf) all files are world writable + # and MRI shows warning when including world writable path in ENV['PATH']. + # This is why we are using /success$/ matching in the following cases. + + it "runs launcher found in PATH, but only code after the first /\#!.*ruby.*/-ish line in target file" do + result = ruby_exe(nil, options: '-S hybrid_launcher.sh', env: { 'PATH' => @path }, args: '2>&1') + result.should =~ /success$/ + end + + it "runs launcher found in PATH" do + result = ruby_exe(nil, options: '-S launcher.rb', env: { 'PATH' => @path }, args: '2>&1') + result.should =~ /success$/ + end + + it "runs launcher found in PATH and sets the exit status to 1 if it fails" do + result = ruby_exe(nil, options: '-S dash_s_fail', env: { 'PATH' => @path }, args: '2>&1', exit_status: 1) + result.should =~ /\bdie\b/ + $?.exitstatus.should == 1 + end + end +end diff --git a/spec/ruby/command_line/dash_upper_u_spec.rb b/spec/ruby/command_line/dash_upper_u_spec.rb new file mode 100644 index 0000000000..2c210eb603 --- /dev/null +++ b/spec/ruby/command_line/dash_upper_u_spec.rb @@ -0,0 +1,52 @@ +require_relative '../spec_helper' + +describe "ruby -U" do + it "sets Encoding.default_internal to UTF-8" do + ruby_exe('print Encoding.default_internal.name', + options: '-U').should == 'UTF-8' + end + + it "sets Encoding.default_internal to UTF-8 when RUBYOPT is empty or only spaces" do + ruby_exe('p Encoding.default_internal', + options: '-U', env: { 'RUBYOPT' => '' }).should == "#<Encoding:UTF-8>\n" + ruby_exe('p Encoding.default_internal', + options: '-U', env: { 'RUBYOPT' => ' ' }).should == "#<Encoding:UTF-8>\n" + end + + it "does nothing different if specified multiple times" do + ruby_exe('print Encoding.default_internal.name', + options: '-U -U').should == 'UTF-8' + end + + it "is overruled by Encoding.default_internal=" do + ruby_exe('Encoding.default_internal="ascii"; print Encoding.default_internal.name', + options: '-U').should == 'US-ASCII' + end + + it "does not affect the default external encoding" do + ruby_exe('Encoding.default_external="ascii"; print Encoding.default_external.name', + options: '-U').should == 'US-ASCII' + end + + it "does not affect the source encoding" do + ruby_exe("print __ENCODING__.name", + options: '-U -KE').should == 'EUC-JP' + ruby_exe("print __ENCODING__.name", + options: '-KE -U').should == 'EUC-JP' + end + + # I assume IO redirection will break on Windows... + it "raises a RuntimeError if used with -Eext:int" do + ruby_exe("p 1", + options: '-U -Eascii:ascii', + args: '2>&1', + exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError if used with -E:int" do + ruby_exe("p 1", + options: '-U -E:ascii', + args: '2>&1', + exit_status: 1).should =~ /RuntimeError/ + end +end diff --git a/spec/ruby/command_line/dash_upper_w_spec.rb b/spec/ruby/command_line/dash_upper_w_spec.rb new file mode 100644 index 0000000000..4019510d42 --- /dev/null +++ b/spec/ruby/command_line/dash_upper_w_spec.rb @@ -0,0 +1,44 @@ +require_relative '../spec_helper' +require_relative 'shared/verbose' + +describe "The -W command line option" do + before :each do + @script = fixture __FILE__, "verbose.rb" + end + + it "with 0 sets $VERBOSE to nil" do + ruby_exe(@script, options: "-W0").chomp.should == "nil" + end + + it "with 1 sets $VERBOSE to false" do + ruby_exe(@script, options: "-W1").chomp.should == "false" + end +end + +describe "The -W command line option with 2" do + it_behaves_like :command_line_verbose, "-W2" +end + +describe "The -W command line option with :deprecated" do + it "enables deprecation warnings" do + ruby_exe('p Warning[:deprecated]', options: '-W:deprecated').should == "true\n" + end +end + +describe "The -W command line option with :no-deprecated" do + it "suppresses deprecation warnings" do + ruby_exe('p Warning[:deprecated]', options: '-w -W:no-deprecated').should == "false\n" + end +end + +describe "The -W command line option with :experimental" do + it "enables experimental warnings" do + ruby_exe('p Warning[:experimental]', options: '-W:experimental').should == "true\n" + end +end + +describe "The -W command line option with :no-experimental" do + it "suppresses experimental warnings" do + ruby_exe('p Warning[:experimental]', options: '-w -W:no-experimental').should == "false\n" + end +end diff --git a/spec/ruby/command_line/dash_upper_x_spec.rb b/spec/ruby/command_line/dash_upper_x_spec.rb new file mode 100644 index 0000000000..8ef9aae4b1 --- /dev/null +++ b/spec/ruby/command_line/dash_upper_x_spec.rb @@ -0,0 +1,6 @@ +require_relative '../spec_helper' +require_relative 'shared/change_directory' + +describe "The -X command line option" do + it_behaves_like :command_line_change_directory, "-X" +end diff --git a/spec/ruby/command_line/dash_v_spec.rb b/spec/ruby/command_line/dash_v_spec.rb new file mode 100644 index 0000000000..b13350404c --- /dev/null +++ b/spec/ruby/command_line/dash_v_spec.rb @@ -0,0 +1,15 @@ +require_relative '../spec_helper' +require_relative 'shared/verbose' + +describe "The -v command line option" do + it_behaves_like :command_line_verbose, "-v" + + describe "when used alone" do + it "prints version and ends" do + ruby_exe(nil, args: '-v').gsub("+PRISM ", "").should include(RUBY_DESCRIPTION.gsub("+PRISM ", "")) + end unless (defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?) || + (defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?) || + (ENV['RUBY_GC_LIBRARY'] && ENV['RUBY_GC_LIBRARY'].length > 0) || + (ENV['RUBY_MN_THREADS'] == '1') + end +end diff --git a/spec/ruby/command_line/dash_w_spec.rb b/spec/ruby/command_line/dash_w_spec.rb new file mode 100644 index 0000000000..f310dca3ed --- /dev/null +++ b/spec/ruby/command_line/dash_w_spec.rb @@ -0,0 +1,10 @@ +require_relative '../spec_helper' +require_relative 'shared/verbose' + +describe "The -w command line option" do + it_behaves_like :command_line_verbose, "-w" + + it "enables both deprecated and experimental warnings" do + ruby_exe('p Warning[:deprecated]; p Warning[:experimental]', options: '-w').should == "true\ntrue\n" + end +end diff --git a/spec/ruby/command_line/dash_x_spec.rb b/spec/ruby/command_line/dash_x_spec.rb new file mode 100644 index 0000000000..ae14b61070 --- /dev/null +++ b/spec/ruby/command_line/dash_x_spec.rb @@ -0,0 +1,21 @@ +require_relative '../spec_helper' + +describe "The -x command line option" do + it "runs code after the first /\#!.*ruby.*/-ish line in target file" do + embedded_ruby = fixture __FILE__, "bin/embedded_ruby.txt" + result = ruby_exe(embedded_ruby, options: '-x') + result.should == "success\n" + end + + it "fails when /\#!.*ruby.*/-ish line in target file is not found" do + bad_embedded_ruby = fixture __FILE__, "bin/bad_embedded_ruby.txt" + result = ruby_exe(bad_embedded_ruby, options: '-x', args: '2>&1', exit_status: 1) + result.should include "no Ruby script found in input" + end + + it "behaves as -x was set when non-ruby shebang is encountered on first line" do + embedded_ruby = fixture __FILE__, "bin/hybrid_launcher.sh" + result = ruby_exe(embedded_ruby) + result.should == "success\n" + end +end diff --git a/spec/ruby/command_line/error_message_spec.rb b/spec/ruby/command_line/error_message_spec.rb new file mode 100644 index 0000000000..02150f30ce --- /dev/null +++ b/spec/ruby/command_line/error_message_spec.rb @@ -0,0 +1,11 @@ +require_relative '../spec_helper' + +describe "The error message caused by an exception" do + it "is not printed to stdout" do + out = ruby_exe("this_does_not_exist", args: "2> #{File::NULL}", exit_status: 1) + out.chomp.should.empty? + + out = ruby_exe("end #syntax error", args: "2> #{File::NULL}", exit_status: 1) + out.chomp.should.empty? + end +end diff --git a/spec/ruby/command_line/feature_spec.rb b/spec/ruby/command_line/feature_spec.rb new file mode 100644 index 0000000000..838581d04a --- /dev/null +++ b/spec/ruby/command_line/feature_spec.rb @@ -0,0 +1,71 @@ +require_relative '../spec_helper' + +describe "The --enable and --disable flags" do + before :all do + # Since some specs disable reading RUBYOPT, we instead pass its contents as :options for those specs + rubyopt = [ENV["RUBYOPT"]] + rubyopt << ENV["#{RUBY_ENGINE.upcase}OPT"] unless RUBY_ENGINE == 'ruby' + @rubyopt = RUBY_ENGINE == "ruby" ? "" : rubyopt.compact.join(" ") + end + + it "can be used with gems" do + ruby_exe("p defined?(Gem)", options: "--enable=gems").chomp.should == "\"constant\"" + ruby_exe("p defined?(Gem)", options: "--disable=gems").chomp.should == "nil" + ruby_exe("p defined?(Gem)", options: "--enable-gems").chomp.should == "\"constant\"" + ruby_exe("p defined?(Gem)", options: "--disable-gems").chomp.should == "nil" + end + + it "can be used with gem" do + ruby_exe("p defined?(Gem)", options: "--enable=gem").chomp.should == "\"constant\"" + ruby_exe("p defined?(Gem)", options: "--disable=gem").chomp.should == "nil" + ruby_exe("p defined?(Gem)", options: "--enable-gem").chomp.should == "\"constant\"" + ruby_exe("p defined?(Gem)", options: "--disable-gem").chomp.should == "nil" + end + + it "can be used with did_you_mean" do + ruby_exe("p defined?(DidYouMean)", options: "--enable=did_you_mean").chomp.should == "\"constant\"" + ruby_exe("p defined?(DidYouMean)", options: "--disable=did_you_mean").chomp.should == "nil" + ruby_exe("p defined?(DidYouMean)", options: "--enable-did_you_mean").chomp.should == "\"constant\"" + ruby_exe("p defined?(DidYouMean)", options: "--disable-did_you_mean").chomp.should == "nil" + end + + it "can be used with rubyopt" do + ruby_exe("p $VERBOSE", options: "--enable=rubyopt", env: {'RUBYOPT' => '-w'}).chomp.should == "true" + ruby_exe("p $VERBOSE", options: "#{@rubyopt} --disable=rubyopt", env: {'RUBYOPT' => '-w'}).chomp.should == "false" + ruby_exe("p $VERBOSE", options: "--enable-rubyopt", env: {'RUBYOPT' => '-w'}).chomp.should == "true" + ruby_exe("p $VERBOSE", options: "#{@rubyopt} --disable-rubyopt", env: {'RUBYOPT' => '-w'}).chomp.should == "false" + end + + it "can be used with frozen-string-literal" do + ruby_exe("p 'foo'.frozen?", options: "--enable=frozen-string-literal").chomp.should == "true" + ruby_exe("p 'foo'.frozen?", options: "--disable=frozen-string-literal").chomp.should == "false" + ruby_exe("p 'foo'.frozen?", options: "--enable-frozen-string-literal").chomp.should == "true" + ruby_exe("p 'foo'.frozen?", options: "--disable-frozen-string-literal").chomp.should == "false" + end + + # frequently hangs for >60s on GitHub Actions macos-latest + # MinGW's YJIT support seems broken + platform_is_not :darwin, :mingw do + it "can be used with all for enable" do + e = "p [defined?(Gem), defined?(DidYouMean), $VERBOSE, 'foo'.frozen?]" + env = {'RUBYOPT' => '-w'} + # Use a single variant here because it can be quite slow as it might enable jit, etc + ruby_exe(e, options: "--enable-all", env: env).chomp.should == "[\"constant\", \"constant\", true, true]" + end unless defined?(RubyVM::YJIT) && defined?(RubyVM::ZJIT) && RubyVM::ZJIT.enabled? # You're not supposed to enable YJIT with --enable-all when ZJIT options are passed. + end + + it "can be used with all for disable" do + e = "p [defined?(Gem), defined?(DidYouMean), $VERBOSE, 'foo'.frozen?]" + env = {'RUBYOPT' => '-w'} + ruby_exe(e, options: "#{@rubyopt} --disable=all", env: env).chomp.should == "[nil, nil, false, false]" + ruby_exe(e, options: "#{@rubyopt} --disable-all", env: env).chomp.should == "[nil, nil, false, false]" + end + + it "prints a warning for unknown features" do + ruby_exe("p 14", options: "--enable=ruby-spec-feature-does-not-exist 2>&1").chomp.should include('warning: unknown argument for --enable') + ruby_exe("p 14", options: "--disable=ruby-spec-feature-does-not-exist 2>&1").chomp.should include('warning: unknown argument for --disable') + ruby_exe("p 14", options: "--enable-ruby-spec-feature-does-not-exist 2>&1").chomp.should include('warning: unknown argument for --enable') + ruby_exe("p 14", options: "--disable-ruby-spec-feature-does-not-exist 2>&1").chomp.should include('warning: unknown argument for --disable') + end + +end diff --git a/spec/ruby/command_line/fixtures/backtrace.rb b/spec/ruby/command_line/fixtures/backtrace.rb new file mode 100644 index 0000000000..99acae95c8 --- /dev/null +++ b/spec/ruby/command_line/fixtures/backtrace.rb @@ -0,0 +1,35 @@ +def a + raise 'oops' +end + +def b + a +end + +def c + b +end + +def d + c +end + +arg = ARGV.first +$stderr.puts arg + +case arg +when 'full_message' + begin + d + rescue => exc + puts exc.full_message + end +when 'backtrace' + begin + d + rescue => exc + puts exc.backtrace + end +else + d +end diff --git a/spec/ruby/command_line/fixtures/bad_syntax.rb b/spec/ruby/command_line/fixtures/bad_syntax.rb new file mode 100644 index 0000000000..e7b8c7a357 --- /dev/null +++ b/spec/ruby/command_line/fixtures/bad_syntax.rb @@ -0,0 +1 @@ +f { diff --git a/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt b/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt new file mode 100644 index 0000000000..a2b7ad085f --- /dev/null +++ b/spec/ruby/command_line/fixtures/bin/bad_embedded_ruby.txt @@ -0,0 +1,3 @@ +@@@This line is not value Ruby +#!rub_y +puts 'success' diff --git a/spec/ruby/command_line/fixtures/bin/dash_s_fail b/spec/ruby/command_line/fixtures/bin/dash_s_fail new file mode 100644 index 0000000000..70c1b8759c --- /dev/null +++ b/spec/ruby/command_line/fixtures/bin/dash_s_fail @@ -0,0 +1 @@ +raise 'die' diff --git a/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt b/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt new file mode 100644 index 0000000000..1da779b1b9 --- /dev/null +++ b/spec/ruby/command_line/fixtures/bin/embedded_ruby.txt @@ -0,0 +1,3 @@ +@@@This line is not value Ruby +#!ruby +puts 'success' diff --git a/spec/ruby/command_line/fixtures/bin/hybrid_launcher.sh b/spec/ruby/command_line/fixtures/bin/hybrid_launcher.sh new file mode 100644 index 0000000000..fd3249f0e5 --- /dev/null +++ b/spec/ruby/command_line/fixtures/bin/hybrid_launcher.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +echo 'error' && exit 1 +#!ruby +puts 'success' diff --git a/spec/ruby/command_line/fixtures/bin/launcher.rb b/spec/ruby/command_line/fixtures/bin/launcher.rb new file mode 100755 index 0000000000..92a0ee2b49 --- /dev/null +++ b/spec/ruby/command_line/fixtures/bin/launcher.rb @@ -0,0 +1,2 @@ +#!ruby +puts 'success' diff --git a/spec/ruby/command_line/fixtures/change_directory_script.rb b/spec/ruby/command_line/fixtures/change_directory_script.rb new file mode 100644 index 0000000000..abe244705f --- /dev/null +++ b/spec/ruby/command_line/fixtures/change_directory_script.rb @@ -0,0 +1 @@ +print Dir.pwd diff --git a/spec/ruby/command_line/fixtures/conditional_range.txt b/spec/ruby/command_line/fixtures/conditional_range.txt new file mode 100644 index 0000000000..8a1218a102 --- /dev/null +++ b/spec/ruby/command_line/fixtures/conditional_range.txt @@ -0,0 +1,5 @@ +1 +2 +3 +4 +5 diff --git a/spec/ruby/command_line/fixtures/dash_s_script.rb b/spec/ruby/command_line/fixtures/dash_s_script.rb new file mode 100644 index 0000000000..500eccbb84 --- /dev/null +++ b/spec/ruby/command_line/fixtures/dash_s_script.rb @@ -0,0 +1,12 @@ +which = ARGV.shift.to_i + +case which +when 0 + p $n +when 1 + puts $n +when 2 + puts $_name +when 3 + puts $___name__test__ +end diff --git a/spec/ruby/command_line/fixtures/debug.rb b/spec/ruby/command_line/fixtures/debug.rb new file mode 100644 index 0000000000..2d84c5faf6 --- /dev/null +++ b/spec/ruby/command_line/fixtures/debug.rb @@ -0,0 +1,10 @@ +which = ARGV.first.to_i + +case which +when 0 + puts "$DEBUG #{$DEBUG}" +when 1 + puts "$VERBOSE #{$VERBOSE}" +when 2 + puts "$-d #{$-d}" +end diff --git a/spec/ruby/command_line/fixtures/debug_info.rb b/spec/ruby/command_line/fixtures/debug_info.rb new file mode 100644 index 0000000000..f02b041920 --- /dev/null +++ b/spec/ruby/command_line/fixtures/debug_info.rb @@ -0,0 +1,10 @@ +a = 'string' +b = a +c = b +d = c +e = d +begin + a << 'new part' +rescue Exception => e + print e.message +end diff --git a/spec/ruby/command_line/fixtures/freeze_flag_across_files.rb b/spec/ruby/command_line/fixtures/freeze_flag_across_files.rb new file mode 100644 index 0000000000..b258249f3a --- /dev/null +++ b/spec/ruby/command_line/fixtures/freeze_flag_across_files.rb @@ -0,0 +1,3 @@ +require_relative 'freeze_flag_required' + +p "abc".object_id == $second_literal_id diff --git a/spec/ruby/command_line/fixtures/freeze_flag_across_files_diff_enc.rb b/spec/ruby/command_line/fixtures/freeze_flag_across_files_diff_enc.rb new file mode 100644 index 0000000000..e9f045e9ea --- /dev/null +++ b/spec/ruby/command_line/fixtures/freeze_flag_across_files_diff_enc.rb @@ -0,0 +1,3 @@ +require_relative 'freeze_flag_required_diff_enc' + +p "abc".object_id != $second_literal_id diff --git a/spec/ruby/command_line/fixtures/freeze_flag_one_literal.rb b/spec/ruby/command_line/fixtures/freeze_flag_one_literal.rb new file mode 100644 index 0000000000..3718899d61 --- /dev/null +++ b/spec/ruby/command_line/fixtures/freeze_flag_one_literal.rb @@ -0,0 +1,2 @@ +ids = Array.new(2) { "abc".object_id } +p ids.first == ids.last diff --git a/spec/ruby/command_line/fixtures/freeze_flag_required.rb b/spec/ruby/command_line/fixtures/freeze_flag_required.rb new file mode 100644 index 0000000000..e09232a5f4 --- /dev/null +++ b/spec/ruby/command_line/fixtures/freeze_flag_required.rb @@ -0,0 +1 @@ +$second_literal_id = "abc".object_id diff --git a/spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rb b/spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rb new file mode 100644 index 0000000000..df4b952c46 --- /dev/null +++ b/spec/ruby/command_line/fixtures/freeze_flag_required_diff_enc.rb @@ -0,0 +1,3 @@ +# encoding: euc-jp # built-in for old regexp option + +$second_literal_id = "abc".object_id diff --git a/spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb b/spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb new file mode 100644 index 0000000000..f5547a5bae --- /dev/null +++ b/spec/ruby/command_line/fixtures/freeze_flag_two_literals.rb @@ -0,0 +1 @@ +p "abc".equal?("abc") diff --git a/spec/ruby/command_line/fixtures/full_names.txt b/spec/ruby/command_line/fixtures/full_names.txt new file mode 100644 index 0000000000..602a20b9dd --- /dev/null +++ b/spec/ruby/command_line/fixtures/full_names.txt @@ -0,0 +1,3 @@ +alice jones +bob field +james grey diff --git a/spec/ruby/command_line/fixtures/loadpath.rb b/spec/ruby/command_line/fixtures/loadpath.rb new file mode 100644 index 0000000000..d7fdf45d46 --- /dev/null +++ b/spec/ruby/command_line/fixtures/loadpath.rb @@ -0,0 +1 @@ +puts $: diff --git a/spec/ruby/command_line/fixtures/names.txt b/spec/ruby/command_line/fixtures/names.txt new file mode 100644 index 0000000000..ae4bf4c8ad --- /dev/null +++ b/spec/ruby/command_line/fixtures/names.txt @@ -0,0 +1,3 @@ +alice +bob +james diff --git a/spec/ruby/command_line/fixtures/passwd_file.txt b/spec/ruby/command_line/fixtures/passwd_file.txt new file mode 100644 index 0000000000..08a4b23bbd --- /dev/null +++ b/spec/ruby/command_line/fixtures/passwd_file.txt @@ -0,0 +1,3 @@ +nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false +root:*:0:0:System Administrator:/var/root:/bin/sh +daemon:*:1:1:System Services:/var/root:/usr/bin/false diff --git a/spec/ruby/command_line/fixtures/require.rb b/spec/ruby/command_line/fixtures/require.rb new file mode 100644 index 0000000000..0be7049c66 --- /dev/null +++ b/spec/ruby/command_line/fixtures/require.rb @@ -0,0 +1 @@ +puts $" diff --git a/spec/ruby/command_line/fixtures/rubyopt.rb b/spec/ruby/command_line/fixtures/rubyopt.rb new file mode 100644 index 0000000000..48d81e1bca --- /dev/null +++ b/spec/ruby/command_line/fixtures/rubyopt.rb @@ -0,0 +1 @@ +puts "rubyopt.rb required" diff --git a/spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb b/spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb new file mode 100644 index 0000000000..fb84b546c0 --- /dev/null +++ b/spec/ruby/command_line/fixtures/string_literal_frozen_comment.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true +frozen = "test".frozen? +interned = "test".equal?("test") +puts "frozen:#{frozen} interned:#{interned}" diff --git a/spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb b/spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb new file mode 100644 index 0000000000..381a742001 --- /dev/null +++ b/spec/ruby/command_line/fixtures/string_literal_mutable_comment.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: false +frozen = "test".frozen? +interned = "test".equal?("test") +puts "frozen:#{frozen} interned:#{interned}" diff --git a/spec/ruby/command_line/fixtures/string_literal_raw.rb b/spec/ruby/command_line/fixtures/string_literal_raw.rb new file mode 100644 index 0000000000..56b1841296 --- /dev/null +++ b/spec/ruby/command_line/fixtures/string_literal_raw.rb @@ -0,0 +1,3 @@ +frozen = "test".frozen? +interned = "test".equal?("test") +puts "frozen:#{frozen} interned:#{interned}" diff --git a/spec/ruby/command_line/fixtures/test_file.rb b/spec/ruby/command_line/fixtures/test_file.rb new file mode 100644 index 0000000000..30a832299e --- /dev/null +++ b/spec/ruby/command_line/fixtures/test_file.rb @@ -0,0 +1 @@ +puts "REQUIRED" diff --git a/spec/ruby/command_line/fixtures/verbose.rb b/spec/ruby/command_line/fixtures/verbose.rb new file mode 100644 index 0000000000..2aa99ed44d --- /dev/null +++ b/spec/ruby/command_line/fixtures/verbose.rb @@ -0,0 +1 @@ +puts $VERBOSE.inspect diff --git a/spec/ruby/command_line/frozen_strings_spec.rb b/spec/ruby/command_line/frozen_strings_spec.rb new file mode 100644 index 0000000000..8acab5bc1d --- /dev/null +++ b/spec/ruby/command_line/frozen_strings_spec.rb @@ -0,0 +1,94 @@ +require_relative '../spec_helper' + +describe "The --enable-frozen-string-literal flag causes string literals to" do + + it "produce the same object each time" do + ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb"), options: "--enable-frozen-string-literal").chomp.should == "true" + end + + it "produce the same object for literals with the same content" do + ruby_exe(fixture(__FILE__, "freeze_flag_two_literals.rb"), options: "--enable-frozen-string-literal").chomp.should == "true" + end + + it "produce the same object for literals with the same content in different files" do + ruby_exe(fixture(__FILE__, "freeze_flag_across_files.rb"), options: "--enable-frozen-string-literal").chomp.should == "true" + end + + it "produce different objects for literals with the same content in different files if they have different encodings" do + ruby_exe(fixture(__FILE__, "freeze_flag_across_files_diff_enc.rb"), options: "--enable-frozen-string-literal").chomp.should == "true" + end +end + +describe "The --disable-frozen-string-literal flag causes string literals to" do + + it "produce a different object each time" do + ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb"), options: "--disable-frozen-string-literal").chomp.should == "false" + end + +end + +describe "With neither --enable-frozen-string-literal nor --disable-frozen-string-literal flag set" do + before do + # disable --enable-frozen-string-literal and --disable-frozen-string-literal passed in $RUBYOPT + @rubyopt = ENV["RUBYOPT"] + ENV["RUBYOPT"] = "" + end + + after do + ENV["RUBYOPT"] = @rubyopt + end + + it "produce a different object each time" do + ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb")).chomp.should == "false" + end + + context "if file has no frozen_string_literal comment" do + it "produce different mutable strings each time" do + ruby_exe(fixture(__FILE__, "string_literal_raw.rb")).chomp.should == "frozen:false interned:false" + end + + guard -> { ruby_version_is "3.4" and !"test".frozen? } do + it "complain about modification of produced mutable strings" do + -> { eval(<<~RUBY) }.should complain(/warning: literal string will be frozen in the future \(run with --debug-frozen-string-literal for more information\)/) + "test" << "!" + RUBY + end + + it "does not complain about modification if Warning[:deprecated] is false" do + deprecated = Warning[:deprecated] + Warning[:deprecated] = false + -> { eval(<<~RUBY) }.should_not complain + "test" << "!" + RUBY + ensure + Warning[:deprecated] = deprecated + end + end + end + + it "if file has frozen_string_literal:true comment produce same frozen strings each time" do + ruby_exe(fixture(__FILE__, "string_literal_frozen_comment.rb")).chomp.should == "frozen:true interned:true" + end + + it "if file has frozen_string_literal:false comment produce different mutable strings each time" do + ruby_exe(fixture(__FILE__, "string_literal_mutable_comment.rb")).chomp.should == "frozen:false interned:false" + end +end + +describe "The --debug flag produces" do + it "debugging info on attempted frozen string modification" do + error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '--enable-frozen-string-literal --debug', args: "2>&1") + error_str.should include("can't modify frozen String") + error_str.should include("created at") + error_str.should include("command_line/fixtures/debug_info.rb:1") + end + + guard -> { ruby_version_is "3.4" and !"test".frozen? } do + it "debugging info on mutating chilled string" do + error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '-w --debug', args: "2>&1") + error_str.should include("literal string will be frozen in the future") + error_str.should include("the string was created here") + error_str.should include("command_line/fixtures/debug_info.rb:1") + end + end +end diff --git a/spec/ruby/command_line/rubylib_spec.rb b/spec/ruby/command_line/rubylib_spec.rb new file mode 100644 index 0000000000..b45919b997 --- /dev/null +++ b/spec/ruby/command_line/rubylib_spec.rb @@ -0,0 +1,69 @@ +require_relative '../spec_helper' + +describe "The RUBYLIB environment variable" do + before :each do + @rubylib, ENV["RUBYLIB"] = ENV["RUBYLIB"], nil + @pre = @rubylib.nil? ? '' : @rubylib + File::PATH_SEPARATOR + end + + after :each do + ENV["RUBYLIB"] = @rubylib + end + + it "adds a directory to $LOAD_PATH" do + dir = tmp("rubylib/incl") + ENV["RUBYLIB"] = @pre + dir + paths = ruby_exe("puts $LOAD_PATH").lines.map(&:chomp) + paths.should include(dir) + end + + it "adds a File::PATH_SEPARATOR-separated list of directories to $LOAD_PATH" do + dir1, dir2 = tmp("rubylib/incl1"), tmp("rubylib/incl2") + ENV["RUBYLIB"] = @pre + "#{dir1}#{File::PATH_SEPARATOR}#{dir2}" + paths = ruby_exe("puts $LOAD_PATH").lines.map(&:chomp) + paths.should include(dir1) + paths.should include(dir2) + paths.index(dir1).should < paths.index(dir2) + end + + it "adds the directory at the front of $LOAD_PATH" do + dir = tmp("rubylib/incl_front") + ENV["RUBYLIB"] = @pre + dir + paths = ruby_exe("puts $LOAD_PATH").lines.map(&:chomp) + paths.shift if paths.first.end_with?('/gem-rehash') + if PlatformGuard.implementation? :ruby + # In a MRI checkout, $PWD and some extra -I entries end up as + # the first entries in $LOAD_PATH. So just assert that it's not last. + idx = paths.index(dir) + idx.should < paths.size-1 + else + paths[0].should == dir + end + end + + it "adds the directory after directories added by -I" do + dash_i_dir = tmp("dash_I_include") + rubylib_dir = tmp("rubylib_include") + ENV["RUBYLIB"] = @pre + rubylib_dir + paths = ruby_exe("puts $LOAD_PATH", options: "-I #{dash_i_dir}").lines.map(&:chomp) + paths.should include(dash_i_dir) + paths.should include(rubylib_dir) + paths.index(dash_i_dir).should < paths.index(rubylib_dir) + end + + it "adds the directory after directories added by -I within RUBYOPT" do + rubyopt_dir = tmp("rubyopt_include") + rubylib_dir = tmp("rubylib_include") + ENV["RUBYLIB"] = @pre + rubylib_dir + paths = ruby_exe("puts $LOAD_PATH", env: { "RUBYOPT" => "-I#{rubyopt_dir}" }).lines.map(&:chomp) + paths.should include(rubyopt_dir) + paths.should include(rubylib_dir) + paths.index(rubyopt_dir).should < paths.index(rubylib_dir) + end + + it "keeps spaces in the value" do + ENV["RUBYLIB"] = @pre + " rubylib/incl " + out = ruby_exe("puts $LOAD_PATH") + out.should include(" rubylib/incl ") + end +end diff --git a/spec/ruby/command_line/rubyopt_spec.rb b/spec/ruby/command_line/rubyopt_spec.rb new file mode 100644 index 0000000000..eb297cd6fe --- /dev/null +++ b/spec/ruby/command_line/rubyopt_spec.rb @@ -0,0 +1,185 @@ +require_relative '../spec_helper' + +describe "Processing RUBYOPT" do + before :each do + @rubyopt, ENV["RUBYOPT"] = ENV["RUBYOPT"], nil + end + + after :each do + ENV["RUBYOPT"] = @rubyopt + end + + it "adds the -I path to $LOAD_PATH" do + ENV["RUBYOPT"] = "-Ioptrubyspecincl" + result = ruby_exe("puts $LOAD_PATH.grep(/byspecin/)") + result.chomp[-15..-1].should == "optrubyspecincl" + end + + it "sets $DEBUG to true for '-d'" do + ENV["RUBYOPT"] = '-d' + command = %[puts "value of $DEBUG is \#{$DEBUG}"] + result = ruby_exe(command, args: "2>&1") + result.should =~ /value of \$DEBUG is true/ + end + + guard -> { RbConfig::CONFIG["CROSS_COMPILING"] != "yes" } do + it "prints the version number for '-v'" do + ENV["RUBYOPT"] = '-v' + ruby_exe("")[/\A.*/].gsub(/\s\+(YJIT( \w+)?|ZJIT( \w+)?|PRISM|GC(\[\w+\])?)(?=\s)/, "").should == RUBY_DESCRIPTION.gsub(/\s\+(YJIT( \w+)?|ZJIT( \w+)?|PRISM|GC(\[\w+\])?)(?=\s)/, "") + end + + it "ignores whitespace around the option" do + ENV["RUBYOPT"] = ' -v ' + ruby_exe("")[/\A.*/].gsub(/\s\+(YJIT( \w+)?|ZJIT( \w+)?|PRISM|GC(\[\w+\])?)(?=\s)/, "").should == RUBY_DESCRIPTION.gsub(/\s\+(YJIT( \w+)?|ZJIT( \w+)?|PRISM|GC(\[\w+\])?)(?=\s)/, "") + end + end + + it "sets $VERBOSE to true for '-w'" do + ENV["RUBYOPT"] = '-w' + ruby_exe("p $VERBOSE").chomp.should == "true" + end + + it "sets $VERBOSE to true for '-W'" do + ENV["RUBYOPT"] = '-W' + ruby_exe("p $VERBOSE").chomp.should == "true" + end + + it "sets $VERBOSE to nil for '-W0'" do + ENV["RUBYOPT"] = '-W0' + ruby_exe("p $VERBOSE").chomp.should == "nil" + end + + it "sets $VERBOSE to false for '-W1'" do + ENV["RUBYOPT"] = '-W1' + ruby_exe("p $VERBOSE").chomp.should == "false" + end + + it "sets $VERBOSE to true for '-W2'" do + ENV["RUBYOPT"] = '-W2' + ruby_exe("p $VERBOSE").chomp.should == "true" + end + + it "suppresses deprecation warnings for '-W:no-deprecated'" do + ENV["RUBYOPT"] = '-W:no-deprecated' + result = ruby_exe('$; = ""', args: '2>&1') + result.should == "" + end + + it "suppresses experimental warnings for '-W:no-experimental'" do + ENV["RUBYOPT"] = '-W:no-experimental' + result = ruby_exe('case 0; in a; end', args: '2>&1') + result.should == "" + end + + it "suppresses deprecation and experimental warnings for '-W:no-deprecated -W:no-experimental'" do + ENV["RUBYOPT"] = '-W:no-deprecated -W:no-experimental' + result = ruby_exe('case ($; = ""); in a; end', args: '2>&1') + result.should == "" + end + + it "requires the file for '-r'" do + f = fixture __FILE__, "rubyopt" + ENV["RUBYOPT"] = "-r#{f}" + ruby_exe("0", args: '2>&1').should =~ /^rubyopt.rb required/ + end + + it "raises a RuntimeError for '-a'" do + ENV["RUBYOPT"] = '-a' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-p'" do + ENV["RUBYOPT"] = '-p' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-n'" do + ENV["RUBYOPT"] = '-n' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-y'" do + ENV["RUBYOPT"] = '-y' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-c'" do + ENV["RUBYOPT"] = '-c' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-s'" do + ENV["RUBYOPT"] = '-s' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-h'" do + ENV["RUBYOPT"] = '-h' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '--help'" do + ENV["RUBYOPT"] = '--help' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-l'" do + ENV["RUBYOPT"] = '-l' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-S'" do + ENV["RUBYOPT"] = '-S irb' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-e'" do + ENV["RUBYOPT"] = '-e0' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-i'" do + ENV["RUBYOPT"] = '-i.bak' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-x'" do + ENV["RUBYOPT"] = '-x' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-C'" do + ENV["RUBYOPT"] = '-C' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-X'" do + ENV["RUBYOPT"] = '-X.' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-F'" do + ENV["RUBYOPT"] = '-F' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-0'" do + ENV["RUBYOPT"] = '-0' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '--copyright'" do + ENV["RUBYOPT"] = '--copyright' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '--version'" do + ENV["RUBYOPT"] = '--version' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '--yydebug'" do + ENV["RUBYOPT"] = '--yydebug' + ruby_exe("", args: '2>&1', exit_status: 1).should =~ /RuntimeError/ + end +end diff --git a/spec/ruby/command_line/shared/change_directory.rb b/spec/ruby/command_line/shared/change_directory.rb new file mode 100644 index 0000000000..9cb6e90ac6 --- /dev/null +++ b/spec/ruby/command_line/shared/change_directory.rb @@ -0,0 +1,21 @@ +describe :command_line_change_directory, shared: true do + before :all do + @script = fixture(__FILE__, 'change_directory_script.rb') + @tempdir = File.dirname(@script) + end + + it 'changes the PWD when using a file' do + output = ruby_exe(@script, options: "#{@method} #{@tempdir}") + output.should == @tempdir + end + + it 'does not need a space after -C for the argument' do + output = ruby_exe(@script, options: "#{@method}#{@tempdir}") + output.should == @tempdir + end + + it 'changes the PWD when using -e' do + output = ruby_exe(nil, options: "#{@method} #{@tempdir} -e 'print Dir.pwd'") + output.should == @tempdir + end +end diff --git a/spec/ruby/command_line/shared/verbose.rb b/spec/ruby/command_line/shared/verbose.rb new file mode 100644 index 0000000000..c5c44c269e --- /dev/null +++ b/spec/ruby/command_line/shared/verbose.rb @@ -0,0 +1,9 @@ +describe :command_line_verbose, shared: true do + before :each do + @script = fixture __FILE__, "verbose.rb" + end + + it "sets $VERBOSE to true" do + ruby_exe(@script, options: @method).chomp.b.split.last.should == "true" + end +end diff --git a/spec/ruby/command_line/syntax_error_spec.rb b/spec/ruby/command_line/syntax_error_spec.rb new file mode 100644 index 0000000000..9ba87b9e22 --- /dev/null +++ b/spec/ruby/command_line/syntax_error_spec.rb @@ -0,0 +1,19 @@ +require_relative '../spec_helper' + +describe "The interpreter" do + it "prints an error when given a file with invalid syntax" do + out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), args: "2>&1", exit_status: 1) + + # it's tempting not to rely on error message and rely only on exception class name, + # but CRuby before 3.2 doesn't print class name for syntax error + out.should include_any_of("syntax error", "SyntaxError") + end + + it "prints an error when given code via -e with invalid syntax" do + out = ruby_exe(nil, args: "-e 'a{' 2>&1", exit_status: 1) + + # it's tempting not to rely on error message and rely only on exception class name, + # but CRuby before 3.2 doesn't print class name for syntax error + out.should include_any_of("syntax error", "SyntaxError") + end +end |
