diff options
Diffstat (limited to 'spec/ruby/language/case_spec.rb')
| -rw-r--r-- | spec/ruby/language/case_spec.rb | 435 |
1 files changed, 295 insertions, 140 deletions
diff --git a/spec/ruby/language/case_spec.rb b/spec/ruby/language/case_spec.rb index 25f5d0efc4..41881bf20a 100644 --- a/spec/ruby/language/case_spec.rb +++ b/spec/ruby/language/case_spec.rb @@ -1,17 +1,17 @@ -require File.expand_path('../../spec_helper', __FILE__) +require_relative '../spec_helper' describe "The 'case'-construct" do it "evaluates the body of the when clause matching the case target expression" do case 1 - when 2; false - when 1; true + when 2; false + when 1; true end.should == true end it "evaluates the body of the when clause whose array expression includes the case target expression" do case 2 - when 3, 4; false - when 1, 2; true + when 3, 4; false + when 1, 2; true end.should == true end @@ -21,39 +21,74 @@ describe "The 'case'-construct" do def bar; @calls << :bar; end case true - when foo, bar; + when foo, bar; end @calls.should == [:foo, :bar] end + it "matches an Integer literal whose value does not fit in a 32-bit int" do + big = 10_000_000_000 + case big + when 10_000_000_000; true + else false + end.should == true + + case -3_000_000_000 + when -3_000_000_000; true + else false + end.should == true + end + + it "matches an arbitrary-precision Integer literal" do + huge = 1267650600228229401496703205376 + case huge + when 1267650600228229401496703205376; true + else false + end.should == true + end + + it "dispatches correctly with mixed small and large Integer literals" do + pick = -> x { + case x + when 1267650600228229401496703205376 then :beyond_long + when 10_000_000_000 then :beyond_int + when 1 then :fits_int + else :other + end + } + + [1267650600228229401496703205376, 10_000_000_000, 1, :nope].map(&pick).should == + [:beyond_long, :beyond_int, :fits_int, :other] + end + it "evaluates the body of the when clause whose range expression includes the case target expression" do case 5 - when 21..30; false - when 1..20; true + when 21..30; false + when 1..20; true end.should == true end it "returns nil when no 'then'-bodies are given" do case "a" - when "a" - when "b" + when "a" + when "b" end.should == nil end it "evaluates the 'else'-body when no other expression matches" do case "c" - when "a"; 'foo' - when "b"; 'bar' - else 'zzz' + when "a"; 'foo' + when "b"; 'bar' + else 'zzz' end.should == 'zzz' end it "returns nil when no expression matches and 'else'-body is empty" do case "c" - when "a"; "a" - when "b"; "b" - else + when "a"; "a" + when "b"; "b" + else end.should == nil end @@ -70,105 +105,152 @@ describe "The 'case'-construct" do it "returns the statement following 'then'" do case "a" - when "a" then 'foo' - when "b" then 'bar' + when "a" then 'foo' + when "b" then 'bar' end.should == 'foo' end it "tests classes with case equality" do case "a" - when String - 'foo' - when Symbol - 'bar' + when String + 'foo' + when Symbol + 'bar' end.should == 'foo' end it "tests with matching regexps" do case "hello" - when /abc/; false - when /^hell/; true + when /abc/; false + when /^hell/; true end.should == true end + it "tests with matching regexps and sets $~ and captures" do + case "foo42" + when /oo(\d+)/ + $~.should.is_a?(MatchData) + $1.should == "42" + else + flunk + end + $~.should.is_a?(MatchData) + $1.should == "42" + end + + it "tests with a string interpolated in a regexp" do + digits = '\d+' + case "foo44" + when /oo(#{digits})/ + $~.should.is_a?(MatchData) + $1.should == "44" + else + flunk + end + $~.should.is_a?(MatchData) + $1.should == "44" + end + + it "tests with a regexp interpolated within another regexp" do + digits_regexp = /\d+/ + case "foo43" + when /oo(#{digits_regexp})/ + $~.should.is_a?(MatchData) + $1.should == "43" + else + flunk + end + $~.should.is_a?(MatchData) + $1.should == "43" + end + it "does not test with equality when given classes" do case :symbol.class - when Symbol - "bar" - when String - "bar" - else - "foo" + when Symbol + "bar" + when String + "bar" + else + "foo" end.should == "foo" end it "takes lists of values" do case 'z' - when 'a', 'b', 'c', 'd' - "foo" - when 'x', 'y', 'z' - "bar" + when 'a', 'b', 'c', 'd' + "foo" + when 'x', 'y', 'z' + "bar" end.should == "bar" case 'b' - when 'a', 'b', 'c', 'd' - "foo" - when 'x', 'y', 'z' - "bar" + when 'a', 'b', 'c', 'd' + "foo" + when 'x', 'y', 'z' + "bar" end.should == "foo" end + it "tests an empty array" do + case [] + when [] + 'foo' + else + 'bar' + end.should == 'foo' + end + it "expands arrays to lists of values" do case 'z' - when *['a', 'b', 'c', 'd'] - "foo" - when *['x', 'y', 'z'] - "bar" + when *['a', 'b', 'c', 'd'] + "foo" + when *['x', 'y', 'z'] + "bar" end.should == "bar" end it "takes an expanded array in addition to a list of values" do case 'f' - when 'f', *['a', 'b', 'c', 'd'] - "foo" - when *['x', 'y', 'z'] - "bar" + when 'f', *['a', 'b', 'c', 'd'] + "foo" + when *['x', 'y', 'z'] + "bar" end.should == "foo" case 'b' - when 'f', *['a', 'b', 'c', 'd'] - "foo" - when *['x', 'y', 'z'] - "bar" + when 'f', *['a', 'b', 'c', 'd'] + "foo" + when *['x', 'y', 'z'] + "bar" end.should == "foo" end it "takes an expanded array before additional listed values" do case 'f' - when *['a', 'b', 'c', 'd'], 'f' - "foo" - when *['x', 'y', 'z'] - "bar" + when *['a', 'b', 'c', 'd'], 'f' + "foo" + when *['x', 'y', 'z'] + "bar" end.should == 'foo' end it "expands arrays from variables before additional listed values" do a = ['a', 'b', 'c'] case 'a' - when *a, 'd', 'e' - "foo" - when 'x' - "bar" + when *a, 'd', 'e' + "foo" + when 'x' + "bar" end.should == "foo" end it "expands arrays from variables before a single additional listed value" do a = ['a', 'b', 'c'] case 'a' - when *a, 'd' - "foo" - when 'x' - "bar" + when *a, 'd' + "foo" + when 'x' + "bar" end.should == "foo" end @@ -177,10 +259,10 @@ describe "The 'case'-construct" do b = ['d', 'e', 'f'] case 'f' - when *a, *b, 'g', 'h' - "foo" - when 'x' - "bar" + when *a, *b, 'g', 'h' + "foo" + when 'x' + "bar" end.should == "foo" end @@ -190,50 +272,50 @@ describe "The 'case'-construct" do b = ['f'] case 'f' - when 'f', *a|b - "foo" - when *['x', 'y', 'z'] - "bar" + when 'f', *a|b + "foo" + when *['x', 'y', 'z'] + "bar" end.should == "foo" end it "never matches when clauses with no values" do case nil - when *[] - "foo" + when *[] + "foo" end.should == nil end it "lets you define a method after the case statement" do case (def foo; 'foo'; end; 'f') - when 'a' - 'foo' - when 'f' - 'bar' + when 'a' + 'foo' + when 'f' + 'bar' end.should == 'bar' end it "raises a SyntaxError when 'else' is used when no 'when' is given" do - lambda { + -> { eval <<-CODE case 4 - else - true + else + true end CODE - }.should raise_error(SyntaxError) + }.should.raise(SyntaxError) end it "raises a SyntaxError when 'else' is used before a 'when' was given" do - lambda { + -> { eval <<-CODE case 4 - else - true - when 4; false + else + true + when 4; false end CODE - }.should raise_error(SyntaxError) + }.should.raise(SyntaxError) end it "supports nested case statements" do @@ -282,61 +364,18 @@ describe "The 'case'-construct" do 100 end.should == 100 end -end - -describe "The 'case'-construct with no target expression" do - it "evaluates the body of the first clause when at least one of its condition expressions is true" do - case - when true, false; 'foo' - end.should == 'foo' - end - - it "evaluates the body of the first when clause that is not false/nil" do - case - when false; 'foo' - when 2; 'bar' - when 1 == 1; 'baz' - end.should == 'bar' - - case - when false; 'foo' - when nil; 'foo' - when 1 == 1; 'bar' - end.should == 'bar' - end - - it "evaluates the body of the else clause if all when clauses are false/nil" do - case - when false; 'foo' - when nil; 'foo' - when 1 == 2; 'bar' - else 'baz' - end.should == 'baz' - end - - it "evaluates multiple conditional expressions as a boolean disjunction" do - case - when true, false; 'foo' - else 'bar' - end.should == 'foo' - - case - when false, true; 'foo' - else 'bar' - end.should == 'foo' - end it "evaluates true as only 'true' when true is the first clause" do case 1 - when true; "bad" - when Integer; "good" + when true; "bad" + when Integer; "good" end.should == "good" end it "evaluates false as only 'false' when false is the first clause" do case nil - when false; "bad" - when nil; "good" + when false; "bad" + when nil; "good" end.should == "good" end @@ -352,17 +391,17 @@ describe "The 'case'-construct with no target expression" do a2 = ['b', 'a', 'r'] case 'f' - when *a1, *['x', 'y', 'z'] - "foo" - when *a2, *['x', 'y', 'z'] - "bar" + when *a1, *['x', 'y', 'z'] + "foo" + when *a2, *['x', 'y', 'z'] + "bar" end.should == "foo" case 'b' - when *a1, *['x', 'y', 'z'] - "foo" - when *a2, *['x', 'y', 'z'] - "bar" + when *a1, *['x', 'y', 'z'] + "foo" + when *a2, *['x', 'y', 'z'] + "bar" end.should == "bar" end @@ -386,4 +425,120 @@ describe "The 'case'-construct with no target expression" do :called end.should == :called end + + it "only matches last value in complex expressions within ()" do + case 'a' + when ('a'; 'b') + :wrong_called + when ('b'; 'a') + :called + end.should == :called + end + + it "supports declaring variables in the case target expression" do + def test(v) + case new_variable_in_expression = v + when true + # This extra block is a test that `new_variable_in_expression` is declared outside of it and not inside + self.then { new_variable_in_expression } + else + # Same + self.then { new_variable_in_expression.casecmp?("foo") } + end + end + + self.test("bar").should == false + self.test(true).should == true + end + + ruby_version_is ""..."3.4" do + it "warns if there are identical when clauses" do + -> { + eval <<~RUBY + case 1 + when 2 + :foo + when 2 + :bar + end + RUBY + }.should complain(/warning: (duplicated .when' clause with line \d+ is ignored|'when' clause on line \d+ duplicates 'when' clause on line \d+ and is ignored)/, verbose: true) + end + end + + ruby_version_is "3.4" do + it "warns if there are identical when clauses" do + -> { + eval <<~RUBY + case 1 + when 2 + :foo + when 2 + :bar + end + RUBY + }.should complain(/warning: 'when' clause on line \d+ duplicates 'when' clause on line \d+ and is ignored/, verbose: true) + end + end +end + +describe "The 'case'-construct with no target expression" do + it "evaluates the body of the first clause when at least one of its condition expressions is true" do + case + when true, false; 'foo' + end.should == 'foo' + end + + it "evaluates the body of the first when clause that is not false/nil" do + case + when false; 'foo' + when 2; 'bar' + when 1 == 1; 'baz' + end.should == 'bar' + + case + when false; 'foo' + when nil; 'foo' + when 1 == 1; 'bar' + end.should == 'bar' + end + + it "evaluates the body of the else clause if all when clauses are false/nil" do + case + when false; 'foo' + when nil; 'foo' + when 1 == 2; 'bar' + else 'baz' + end.should == 'baz' + end + + it "evaluates multiple conditional expressions as a boolean disjunction" do + case + when true, false; 'foo' + else 'bar' + end.should == 'foo' + + case + when false, true; 'foo' + else 'bar' + end.should == 'foo' + end + + # Homogeneous cases are often optimized to avoid === using a jump table, and should be tested separately. + # See https://github.com/jruby/jruby/issues/6440 + it "handles homogeneous cases" do + case + when 1; 'foo' + when 2; 'bar' + end.should == 'foo' + end + + it "expands arrays to lists of values" do + case + when *[false] + "foo" + when *[true] + "bar" + end.should == "bar" + end end |
